mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-20 21:33:07 +02:00
basic PR feature
This commit is contained in:
1 parent
6ea28f2a47
commit
953bb06857
15 files changed
+387
-94
No files matched your search
@@ -531,6 +531,7 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Group("/pulls/:index", func() {
|
||||
m.Get("/commits", repo.ViewPullCommits)
|
||||
m.Get("/files", repo.ViewPullFiles)
|
||||
m.Post("/merge", reqRepoAdmin, repo.MergePullRequest)
|
||||
})
|
||||
|
||||
m.Group("", func() {
|
||||
|
||||
@@ -470,10 +470,14 @@ pulls.nothing_to_compare = There is nothing to compare because base and head bra
|
||||
pulls.has_pull_request = `There is already a pull request between these two targets: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.create = Create Pull Request
|
||||
pulls.title_desc = wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code>
|
||||
pulls.merged_title_desc = merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s
|
||||
pulls.tab_conversation = Conversation
|
||||
pulls.tab_commits = Commits
|
||||
pulls.tab_files = Files changed
|
||||
pulls.reopen_to_merge = Please reopen this pull request to perform merge operation.
|
||||
pulls.merged = Merged
|
||||
pulls.has_merged = This pull request has been merged successfully!
|
||||
pulls.data_borken = Data of this pull request has been borken due to deletion of fork information.
|
||||
pulls.can_auto_merge_desc = You can perform auto-merge operation on this pull request.
|
||||
pulls.cannot_auto_merge_desc = You can't perform auto-merge operation because there are conflicts between commits.
|
||||
pulls.cannot_auto_merge_helper = Please use commond line tool to solve it.
|
||||
|
||||
+98
-9
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
@@ -850,20 +851,24 @@ const (
|
||||
|
||||
// PullRequest represents relation between pull request and repositories.
|
||||
type PullRequest struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
PullID int64 `xorm:"INDEX"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
PullID int64 `xorm:"INDEX"`
|
||||
Pull *Issue `xorm:"-"`
|
||||
PullIndex int64
|
||||
HeadRepoID int64 `xorm:"UNIQUE(s)"`
|
||||
HeadRepoID int64
|
||||
HeadRepo *Repository `xorm:"-"`
|
||||
BaseRepoID int64 `xorm:"UNIQUE(s)"`
|
||||
BaseRepoID int64
|
||||
HeadUserName string
|
||||
HeadBarcnh string `xorm:"UNIQUE(s)"`
|
||||
BaseBranch string `xorm:"UNIQUE(s)"`
|
||||
HeadBarcnh string
|
||||
BaseBranch string
|
||||
MergeBase string `xorm:"VARCHAR(40)"`
|
||||
MergedCommitID string `xorm:"VARCHAR(40)"`
|
||||
Type PullRequestType
|
||||
CanAutoMerge bool
|
||||
HasMerged bool
|
||||
Merged time.Time
|
||||
MergerID int64
|
||||
Merger *User `xorm:"-"`
|
||||
}
|
||||
|
||||
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
|
||||
@@ -874,9 +879,93 @@ func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
|
||||
if err != nil {
|
||||
log.Error(3, "GetRepositoryByID[%d]: %v", pr.ID, err)
|
||||
}
|
||||
case "merger_id":
|
||||
if !pr.HasMerged {
|
||||
return
|
||||
}
|
||||
|
||||
pr.Merger, err = GetUserByID(pr.MergerID)
|
||||
if err != nil {
|
||||
if IsErrUserNotExist(err) {
|
||||
pr.MergerID = -1
|
||||
pr.Merger = NewFakeUser()
|
||||
} else {
|
||||
log.Error(3, "GetUserByID[%d]: %v", pr.ID, err)
|
||||
}
|
||||
}
|
||||
case "merged":
|
||||
if !pr.HasMerged {
|
||||
return
|
||||
}
|
||||
|
||||
pr.Merged = regulateTimeZone(pr.Merged)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges pull request to base repository.
|
||||
func (pr *PullRequest) Merge(baseGitRepo *git.Repository) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pr.Pull.IsClosed = true
|
||||
if _, err = sess.Id(pr.Pull.ID).AllCols().Update(pr.Pull); err != nil {
|
||||
return fmt.Errorf("update pull: %v", err)
|
||||
}
|
||||
|
||||
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
|
||||
headGitRepo, err := git.OpenRepository(headRepoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
pr.MergedCommitID, err = headGitRepo.GetCommitIdOfBranch(pr.HeadBarcnh)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetCommitIdOfBranch: %v", err)
|
||||
}
|
||||
|
||||
pr.HasMerged = true
|
||||
if _, err = sess.Id(pr.ID).AllCols().Update(pr); err != nil {
|
||||
return fmt.Errorf("update pull request: %v", err)
|
||||
}
|
||||
|
||||
// Clone base repo.
|
||||
tmpBasePath := path.Join("data/tmp/repos", com.ToStr(time.Now().Nanosecond())+".git")
|
||||
os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm)
|
||||
defer os.RemoveAll(path.Dir(tmpBasePath))
|
||||
|
||||
var stderr string
|
||||
if _, stderr, err = process.ExecTimeout(5*time.Minute,
|
||||
fmt.Sprintf("PullRequest.Merge(git clone): %s", tmpBasePath),
|
||||
"git", "clone", baseGitRepo.Path, tmpBasePath); err != nil {
|
||||
return fmt.Errorf("git clone: %s", stderr)
|
||||
}
|
||||
|
||||
// Check out base branch.
|
||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||
fmt.Sprintf("PullRequest.Merge(git checkout): %s", tmpBasePath),
|
||||
"git", "checkout", pr.BaseBranch); err != nil {
|
||||
return fmt.Errorf("git checkout: %s", stderr)
|
||||
}
|
||||
|
||||
// Pull commits.
|
||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||
fmt.Sprintf("PullRequest.Merge(git pull): %s", tmpBasePath),
|
||||
"git", "pull", headRepoPath, pr.HeadBarcnh); err != nil {
|
||||
return fmt.Errorf("git pull: %s", stderr)
|
||||
}
|
||||
|
||||
// Push back to upstream.
|
||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
|
||||
fmt.Sprintf("PullRequest.Merge(git push): %s", tmpBasePath),
|
||||
"git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil {
|
||||
return fmt.Errorf("git push: %s", stderr)
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// NewPullRequest creates new pull request with labels for repository.
|
||||
func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
|
||||
sess := x.NewSession()
|
||||
@@ -937,8 +1026,8 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// GetPullRequest returnss a pull request by given info.
|
||||
func GetPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) {
|
||||
// GetUnmergedPullRequest returnss a pull request hasn't been merged by given info.
|
||||
func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) {
|
||||
pr := &PullRequest{
|
||||
HeadRepoID: headRepoID,
|
||||
BaseRepoID: baseRepoID,
|
||||
@@ -946,7 +1035,7 @@ func GetPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string)
|
||||
BaseBranch: baseBranch,
|
||||
}
|
||||
|
||||
has, err := x.Get(pr)
|
||||
has, err := x.Where("has_merged=?", false).Get(pr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
|
||||
+5
-3
@@ -469,20 +469,20 @@ func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
|
||||
if _, stderr, err = process.ExecDir(-1,
|
||||
tmpPath, fmt.Sprintf("initRepoCommit(git add): %s", tmpPath),
|
||||
"git", "add", "--all"); err != nil {
|
||||
return errors.New("git add: " + stderr)
|
||||
return fmt.Errorf("git add: %s", stderr)
|
||||
}
|
||||
|
||||
if _, stderr, err = process.ExecDir(-1,
|
||||
tmpPath, fmt.Sprintf("initRepoCommit(git commit): %s", tmpPath),
|
||||
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
|
||||
"-m", "initial commit"); err != nil {
|
||||
return errors.New("git commit: " + stderr)
|
||||
return fmt.Errorf("git commit: %s", stderr)
|
||||
}
|
||||
|
||||
if _, stderr, err = process.ExecDir(-1,
|
||||
tmpPath, fmt.Sprintf("initRepoCommit(git push): %s", tmpPath),
|
||||
"git", "push", "origin", "master"); err != nil {
|
||||
return errors.New("git push: " + stderr)
|
||||
return fmt.Errorf("git push: %s", stderr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1004,6 +1004,8 @@ func DeleteRepository(uid, repoID int64) error {
|
||||
return err
|
||||
} else if _, err = sess.Delete(&Collaboration{RepoID: repoID}); err != nil {
|
||||
return err
|
||||
} else if _, err = sess.Delete(&PullRequest{BaseRepoID: repoID}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete comments and attachments.
|
||||
|
||||
@@ -4364,7 +4364,7 @@ func confLocaleLocale_deDeIni() (*asset, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xc4\x7d\xfd\x72\xdc\x48\x8e\xe7\xff\x7c\x0a\xb6\x37\x7c\xee\x8e\x90\xcb\xd1\xdd\x71\x1f\xd1\x61\xbb\x4f\x96\xda\x1f\xbb\x96\xa5\xb1\xe4\x99\x9b\x73\x38\xd8\xac\x22\xab\x8a\xe3\x2a\xb2\x9a\x64\xb9\xac\xd9\xd8\x88\x7b\x8d\x7b\xbd\x7b\x92\x03\x7e\x00\xf2\x83\x64\xc9\xf6\xec\xc6\xdd\x3f\x12\x2b\x13\xf9\x85\x44\x22\x01\x24\x12\x99\xef\x76\x59\x51\x76\x8b\xf4\x49\x7a\x9a\xee\xf2\xaa\xde\x94\x5d\x97\x76\xe5\x66\xf9\x70\xdd\x74\x7d\x59\xa4\x2f\xaa\x9e\x7e\xb7\x9f\xaa\x45\x99\x24\xeb\x66\x5b\x12\xe8\x4b\xfa\x97\x14\x79\xb7\x9e\x37\x79\x5b\x50\xc2\xb9\x7d\x27\xe5\xe7\xdd\xa6\x69\x19\xe8\x37\xf9\x4a\xd6\xe5\x66\xc7\x65\xe8\x5f\xd2\x55\xab\x3a\xab\x6a\xfa\x79\x4d\x5f\xe9\xab\x3a\xe9\x9a\x45\x95\x6f\xb2\x20\x03\x09\x96\xff\x4b\xfa\x53\x5d\xa4\xd7\x7d\xb9\x4b\x1f\x77\xdb\x7c\xb3\x79\x9a\x77\x28\xd2\x97\x69\xbe\x58\x34\xfb\xba\x7f\xfc\x48\x32\xa4\xf2\x66\xdf\x5b\xed\x97\xfb\x5e\xd2\xf6\x3b\x4b\x7a\xb7\x4b\xda\x72\x55\xd1\xc0\x5a\x4a\x7a\xab\x9f\xc9\xa1\x9c\x77\x55\xcf\x9d\xfe\x8b\x7c\x25\x9f\xca\xb6\xab\x1a\xee\xcf\x9f\xe5\x2b\xd9\xe5\x2b\x06\xb8\xa2\x7f\x49\x5f\x6e\x77\x9b\x1c\x05\x6e\xf4\x33\xd9\xe4\xf5\x6a\x2f\x30\xaf\xf5\x33\x59\xb4\x25\x65\x65\x75\x79\xa0\xd4\x33\xfc\x48\xe9\xc7\x6c\x36\x4b\xf6\x84\xd3\x6c\xd7\x36\xcb\x6a\x53\x66\x79\x5d\x64\x5b\xc1\xda\x3b\x4a\x4f\x35\x3d\xa5\xf4\x94\xd3\x31\x8c\xb2\x20\x04\x65\x79\xa7\x63\xa1\xa9\x21\x7c\xe5\x5d\x82\xaa\xea\x7c\x6b\xa5\xf9\x33\x29\xb7\x79\xb5\xe1\x49\x78\xc8\x1f\xd4\xf9\xae\x3b\x34\x98\xaa\x2b\xfd\x24\x44\x64\xfd\xed\xae\x04\x1e\x1e\xde\xd0\x57\xb2\xc8\x77\xfd\x62\x9d\x73\x5f\xe5\x2b\x21\xa0\x5d\x43\x08\x69\xda\x5b\xc0\xd9\x8f\xa4\x69\x57\x79\x5d\xfd\x3d\xef\x05\x49\x97\xc1\xcf\x64\x5b\xb5\x6d\xc3\xf8\xbd\xc0\x47\x42\x23\xce\xb8\x1e\x4a\x79\x43\x98\x08\x6a\xe1\x9c\x6d\xb5\x6a\x05\x95\x9c\x79\x81\x5f\x5c\x0b\xe7\x2d\x9b\xf6\xa3\x66\x3c\xe7\xcf\x41\x51\xea\x84\xe6\xc6\xed\xe7\x35\x21\x5f\x73\x2f\xf0\x23\x02\xe8\x92\xbc\xd8\x12\x2a\x77\x79\x5d\x32\x8e\x4e\xf9\x17\xe1\x85\x7e\x25\x4a\x53\x59\x57\xf6\x7d\x55\xaf\x18\xd9\xa7\x92\x94\x5e\x6b\x52\x12\xe4\xb9\xb4\xdb\x66\xef\xa6\x93\xd2\xff\x4a\x3f\xd3\x2b\xf9\x29\x79\x41\x21\x64\xba\x92\x3c\x92\x2e\x5b\x96\x65\x21\x63\xe9\xd2\xe7\xf4\x9d\xec\xf6\x9b\x0d\x61\xed\x8f\x7d\xd9\xf5\x5c\xe8\x8a\x7e\xd3\xf8\xe5\x77\x52\x75\x1d\x7d\x50\xf2\x2b\x7c\x24\x34\x75\xf5\x02\x83\x39\xc3\x47\x92\xbc\xef\xca\xbc\x5d\xac\x3f\x24\xf2\x1f\x7d\xe5\x0f\xa6\xbd\x63\x93\xca\x84\xa4\x44\x24\x2d\x58\x03\xc9\xa2\x29\xf8\xc7\x19\xfd\xa3\xaa\xab\xba\xeb\x69\xc5\x7d\x48\xf4\x83\xc1\xe4\x4b\x26\xa0\xaf\x7a\x60\x41\x13\xb1\x7c\x3b\x9e\xc1\xf4\x79\xd5\x76\xfd\xc3\xbe\x22\x62\x7d\xbb\xaf\x13\x1e\x1f\xad\xb6\xac\x98\x1b\x13\x7a\xd1\x10\x8a\x90\xdc\xd2\xf8\x2e\x6e\xaf\xff\xf4\xfa\x24\xbd\x22\x4e\xb4\x6a\x4b\xfa\x4e\xa9\x0e\xfa\x47\x65\x7e\x9e\x25\x54\xca\x5a\x3a\xcf\xfb\x7c\x9e\x77\xa5\x47\x2b\x67\x0a\x75\xbb\x3c\xd0\x38\x73\x35\x70\xb0\xae\x8f\xc6\x3b\xb5\x42\xa8\x0e\x5d\x57\xae\x8e\x37\xbc\xb8\x28\x9d\x99\x1a\x0a\x5f\x6d\x4a\x4e\xa7\xaa\xd2\x57\x6f\xde\x5c\x9e\x3f\x4b\xcb\x7a\x55\xd5\x65\x7a\xa8\xfa\x75\xba\xef\x97\xff\x2d\x5b\x95\x75\xd9\x12\x8f\x5b\x54\x29\xad\xa9\x96\x28\x21\x25\xc2\x96\xc1\xcd\x92\xae\xdb\xd0\xda\x07\x7a\xaf\xaf\x5f\xa7\x17\x8c\xe2\x5d\xde\xaf\xd1\x91\x7e\x9d\x74\x7f\x6c\x18\x45\xae\xc1\x9b\x75\x99\x82\xca\x00\xd4\x2c\x0d\x1f\x69\xa1\x7d\x9c\x25\x65\xdb\x66\xc4\x96\xfa\xdb\x4c\x0b\x6b\x7d\x43\x48\xa9\x82\x48\xa7\x6e\xfa\x74\x5e\xa6\x28\x33\x4b\x12\xeb\xb0\x61\xf7\x74\xb7\xdb\x54\x0b\x59\xeb\x2f\x24\xcf\x23\x9a\x77\x10\xc5\x52\x08\x07\x44\x59\x5e\x80\x2e\x62\xcf\xbc\x1e\xd2\x88\x81\xa0\xfc\xba\x24\x06\xb8\xde\xaf\x84\xed\x6d\x9a\x7d\xf1\x1d\x28\xd5\x7a\xef\x09\x35\x7d\xdb\x50\x87\x81\x1d\x07\xe0\x9b\x38\x25\x8a\xe3\x4d\xab\x2d\xb7\x0d\xf1\x15\x47\xec\x15\x11\xd4\xa1\xa2\x4c\x1a\x69\x97\x7f\xa2\xf5\xd6\x37\x69\xbf\xae\xba\xb4\x20\x62\x5b\x70\xc5\xb4\x34\xf6\xb4\x5d\x08\x59\x10\x81\x0a\x69\x58\x5a\x3c\x07\x80\xda\xee\x89\x9a\xd6\x54\x19\x6f\x46\xbc\x73\x52\x95\x53\xfd\xc4\x90\xa8\x1e\xd0\x37\x51\x6e\x43\x5c\x99\xf9\xe6\x39\x3e\xf4\x77\x58\x3f\xf5\x2a\x5f\x2e\xa9\x57\x1d\x51\xc5\xcb\x74\xb1\x69\x88\xa4\xde\xbd\x7d\xdd\x31\xc1\xac\xb3\x5d\xd3\x62\x9b\xa3\xac\x2b\xfa\x74\x69\x01\xa2\x19\xa2\xde\x6f\xe7\xf4\xeb\xb0\xae\x88\x03\x00\xed\x5c\x82\x77\x73\x4a\xa5\x26\xf6\x1d\x4d\xe1\x49\x4a\x24\x4c\x23\x20\x94\x81\x00\x78\x0c\x45\xd5\xe5\x73\x9a\x7b\x06\x5f\xd2\xb6\xb5\x6f\x89\xac\xd6\x7d\xbf\xb3\x96\x5f\xde\xdc\x5c\x49\xd3\x2e\xf5\xae\xb6\xf3\x80\x32\x30\x07\x1b\xde\x78\xeb\xb4\xa9\x67\x20\x92\x7d\xbb\x19\xd0\x0f\x8d\xd5\x72\x8e\xe0\x85\xbb\xf0\x88\xff\x5c\x7b\xf4\x00\xcf\x1d\x49\x27\x07\x50\x13\xe1\xb8\xc4\x06\x48\x44\xdd\xec\xb8\xde\x80\xaa\x2f\x35\xc1\x93\x32\x36\x4d\x97\x2f\x5b\x27\xe5\x42\xf6\x09\xd8\xff\x96\x06\xac\x6c\xe4\xfa\x82\xd0\x00\x5e\x82\xd4\x65\xdb\x6c\x29\xf5\x39\xfd\xf3\x09\xbe\xfb\x17\x5c\x1f\x60\xf2\xa2\x20\xfe\xd6\x9d\xa4\x6f\x9f\x9f\xa5\xff\xf9\xe7\x9f\x7e\x9a\xa5\xaf\x7a\x5e\x89\x4c\x9c\x7f\x63\xa2\xa2\x4f\xd9\xc3\x1d\x28\xb1\x8c\x9e\xe8\xee\x1Line truncated
|
||||
var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xc4\xbd\xeb\x72\xdc\x48\xae\x27\xfe\x9d\x4f\xc1\xf6\x09\xff\xdd\x1d\x21\x97\xa3\xbb\xff\x7b\x89\x0e\xdb\xbd\xb2\xd4\xbe\xcc\xb1\x2c\x8d\x25\xcf\xec\xac\xc3\xc1\x61\x15\x59\x55\x1c\x57\x91\xd5\x24\xcb\x65\xcd\x89\x13\xb1\xaf\xb1\xaf\xb7\x4f\xb2\xc0\x0f\x40\x5e\x48\x96\x6c\xcf\x9c\xd8\xfd\x22\xb1\x32\x91\x37\x24\x12\x09\x20\x91\xc8\x7c\xb7\xcb\x8a\xb2\x5b\xa4\x4f\xd2\xd3\x74\x97\x57\xf5\xa6\xec\xba\xb4\x2b\x37\xcb\x87\xeb\xa6\xeb\xcb\x22\x7d\x51\xf5\xf4\xbb\xfd\x54\x2d\xca\x24\x59\x37\xdb\x92\x40\x5f\xd2\xbf\xa4\xc8\xbb\xf5\xbc\xc9\xdb\x82\x12\xce\xed\x3b\x29\x3f\xef\x36\x4d\xcb\x40\xbf\xc9\x57\xb2\x2e\x37\x3b\x2e\x43\xff\x92\xae\x5a\xd5\x59\x55\xd3\xcf\x6b\xfa\x4a\x5f\xd5\x49\xd7\x2c\xaa\x7c\x93\x05\x19\x48\xb0\xfc\x5f\xd2\x9f\xea\x22\xbd\xee\xcb\x5d\xfa\xb8\xdb\xe6\x9b\xcd\xd3\xbc\x43\x91\xbe\x4c\xf3\xc5\xa2\xd9\xd7\xfd\xe3\x47\x92\x21\x95\x37\xfb\xde\x6a\xbf\xdc\xf7\x92\xb6\xdf\x59\xd2\xbb\x5d\xd2\x96\xab\x8a\x06\xd6\x52\xd2\x5b\xfd\x4c\x0e\xe5\xbc\xab\x7a\xee\xf4\x9f\xe5\x2b\xf9\x54\xb6\x5d\xd5\x70\x7f\xfe\x24\x5f\xc9\x2e\x5f\x31\xc0\x15\xfd\x4b\xfa\x72\xbb\xdb\xe4\x28\x70\xa3\x9f\xc9\x26\xaf\x57\x7b\x81\x79\xad\x9f\xc9\xa2\x2d\x29\x2b\xab\xcb\x03\xa5\x9e\xe1\x47\x4a\x3f\x66\xb3\x59\xb2\x27\x9c\x66\xbb\xb6\x59\x56\x9b\x32\xcb\xeb\x22\xdb\x0a\xd6\xde\x51\x7a\xaa\xe9\x29\xa5\xa7\x9c\x8e\x61\x94\x05\x21\x28\xcb\x3b\x1d\x0b\x4d\x0d\xe1\x2b\xef\x12\x54\x55\xe7\x5b\x2b\xcd\x9f\x49\xb9\xcd\xab\x0d\x4f\xc2\x43\xfe\xa0\xce\x77\xdd\xa1\xc1\x54\x5d\xe9\x27\x21\x22\xeb\x6f\x77\x25\xf0\xf0\xf0\x86\xbe\x92\x45\xbe\xeb\x17\xeb\x9c\xfb\x2a\x5f\x09\x01\xed\x1a\x42\x48\xd3\xde\x02\xce\x7e\x24\x4d\xbb\xca\xeb\xea\xef\x79\x2f\x48\xba\x0c\x7e\x26\xdb\xaa\x6d\x1b\xc6\xef\x05\x3e\x12\x1a\x71\xc6\xf5\x50\xca\x1b\xc2\x44\x50\x0b\xe7\x6c\xab\x55\x2b\xa8\xe4\xcc\x0b\xfc\xe2\x5a\x38\x6f\xd9\xb4\x1f\x35\xe3\x39\x7f\x0e\x8a\x52\x27\x34\x37\x6e\x3f\xaf\x09\xf9\x9a\x7b\x81\x1f\x11\x40\x97\xe4\xc5\x96\x50\xb9\xcb\xeb\x92\x71\x74\xca\xbf\x08\x2f\xf4\x2b\x51\x9a\xca\xba\xb2\xef\xab\x7a\xc5\xc8\x3e\x95\xa4\xf4\x5a\x93\x92\x20\xcf\xa5\xdd\x36\x7b\x37\x9d\x94\xfe\x17\xfa\x99\x5e\xc9\x4f\xc9\x0b\x0a\x21\xd3\x95\xe4\x91\x74\xd9\xb2\x2c\x0b\x19\x4b\x97\x3e\xa7\xef\x64\xb7\xdf\x6c\x08\x6b\xbf\xef\xcb\xae\xe7\x42\x57\xf4\x9b\xc6\x2f\xbf\x93\xaa\xeb\xe8\x83\x92\x5f\xe1\x23\xa1\xa9\xab\x17\x18\xcc\x19\x3e\x92\xe4\x7d\x57\xe6\xed\x62\xfd\x21\x91\xff\xe8\x2b\x7f\x30\xed\x1d\x9b\x54\x26\x24\x25\x22\x69\xc1\x1a\x48\x16\x4d\xc1\x3f\xce\xe8\x1f\x55\x5d\xd5\x5d\x4f\x2b\xee\x43\xa2\x1f\x0c\x26\x5f\x32\x01\x7d\xd5\x03\x0b\x9a\x88\xe5\xdb\xf1\x0c\xa6\xcf\xab\xb6\xeb\x1f\xf6\x15\x11\xeb\xdb\x7d\x9d\xf0\xf8\x68\xb5\x65\xc5\xdc\x98\xd0\x8b\x86\x50\x84\xe4\x96\xc6\x77\x71\x7b\xfd\xc7\xd7\x27\xe9\x15\x71\xa2\x55\x5b\xd2\x77\x4a\x75\xd0\x3f\x2a\xf3\xf3\x2c\xa1\x52\xd6\xd2\x79\xde\xe7\xf3\xbc\x2b\x3d\x5a\x39\x53\xa8\xdb\xe5\x81\xc6\x99\xab\x81\x83\x75\x7d\x34\xde\xa9\x15\x42\x75\xe8\xba\x72\x75\xbc\xe1\xc5\x45\xe9\xcc\xd4\x50\xf8\x6a\x53\x72\x3a\x55\x95\xbe\x7a\xf3\xe6\xf2\xfc\x59\x5a\xd6\xab\xaa\x2e\xd3\x43\xd5\xaf\xd3\x7d\xbf\xfc\xaf\xd9\xaa\xac\xcb\x96\x78\xdc\xa2\x4a\x69\x4d\xb5\x44\x09\x29\x11\xb6\x0c\x6e\x96\x74\xdd\x86\xd6\x3e\xd0\x7b\x7d\xfd\x3a\xbd\x60\x14\xef\xf2\x7e\x8d\x8e\xf4\xeb\xa4\xfb\x7d\xc3\x28\x72\x0d\xde\xac\xcb\x14\x54\x06\xa0\x66\x69\xf8\x48\x0b\xed\xe3\x2c\x29\xdb\x36\x23\xb6\xd4\xdf\x66\x5a\x58\xeb\x1b\x42\x4a\x15\x44\x3a\x75\xd3\xa7\xf3\x32\x45\x99\x59\x92\x58\x87\x0d\xbb\xa7\xbb\xdd\xa6\x5a\xc8\x5a\x7f\x21\x79\x1e\xd1\xbc\x83\x28\x96\x42\x38\x20\xca\xf2\x02\x74\x11\x7b\xe6\xf5\x90\x46\x0c\x04\xe5\xd7\x25\x31\xc0\xf5\x7e\x25\x6c\x6f\xd3\xec\x8b\xef\x40\xa9\xd6\x7b\x4f\xa8\xe9\xdb\x86\x3a\x0c\xec\x38\x00\xdf\xc4\x29\x51\x1c\x6f\x5a\x6d\xb9\x6d\x88\xaf\x38\x62\xaf\x88\xa0\x0e\x15\x65\xd2\x48\xbb\xfc\x13\xad\xb7\xbe\x49\xfb\x75\xd5\xa5\x05\x11\xdb\x82\x2b\xa6\xa5\xb1\xa7\xed\x42\xc8\x82\x08\x54\x48\xc3\xd2\xe2\x39\x00\xd4\x76\x4f\xd4\xb4\xa6\xca\x78\x33\xe2\x9d\x93\xaa\x9c\xea\x27\x86\x44\xf5\x80\xbe\x89\x72\x1b\xe2\xca\xcc\x37\xcf\xf1\xa1\xbf\xc3\xfa\xa9\x57\xf9\x72\x49\xbd\xea\x88\x2a\x5e\xa6\x8b\x4d\x43\x24\xf5\xee\xed\xeb\x8e\x09\x66\x9d\xed\x9a\x16\xdb\x1c\x65\x5d\xd1\xa7\x4b\x0b\x10\xcd\x10\xf5\x7e\x3b\xa7\x5f\x87\x75\x45\x1c\x00\x68\xe7\x12\xbc\x9b\x53\x2a\x35\xb1\xef\x68\x0a\x4f\x52\x22\x61\x1a\x01\xa1\x0c\x04\xc0\x63\x28\xaa\x2e\x9f\xd3\xdc\x33\xf8\x92\xb6\xad\x7d\x4b\x64\xb5\xee\xfb\x9d\xb5\xfc\xf2\xe6\xe6\x4a\x9a\x76\xa9\x77\xb5\x9d\x07\x94\x81\x39\xd8\xf0\xc6\x5b\xa7\x4d\x3d\x03\x91\xec\xdb\xcd\x80\x7e\x68\xac\x96\x73\x04\x2f\xdc\x85\x47\xfc\xe7\xda\xa3\x07\x78\xee\x48\x3a\x39\x80\x9a\x08\xc7\x25\x36\x40\x22\xea\x66\xc7\xf5\x06\x54\x7d\xa9\x09\x9e\x94\xb1\x69\xba\x7c\xd9\x3a\x29\x17\xb2\x4f\xc0\xfe\xb7\x34\x60\x65\x23\xd7\x17\x84\x06\xf0\x12\xa4\x2e\xdb\x66\x4b\xa9\xcf\xe9\x9f\x4f\xf0\xdd\xbf\xe0\xfa\x00\x93\x17\x05\xf1\xb7\xee\x24\x7d\xfb\xfc\x2c\xfd\x4f\x3f\xff\xf4\xd3\x2c\x7d\xd5\xf3\x4a\x64\xe2\xfc\x1b\x13\x15\x7d\xca\x1e\xee\x40\x89\x65\xf4\x44\x7Line truncated
|
||||
|
||||
func confLocaleLocale_enUsIniBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@@ -4379,7 +4379,7 @@ func confLocaleLocale_enUsIni() (*asset, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 41832, mode: os.FileMode(420), modTime: time.Unix(1441184579, 0)}
|
||||
info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 42121, mode: os.FileMode(420), modTime: time.Unix(1441200263, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -138,7 +139,8 @@ func (repo *Repository) GetCommit(commitId string) (*Commit, error) {
|
||||
|
||||
func (repo *Repository) commitsCount(id sha1) (int, error) {
|
||||
if gitVer.LessThan(MustParseVersion("1.8.0")) {
|
||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", "--pretty=format:''", id.String())
|
||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log",
|
||||
"--pretty=format:''", id.String())
|
||||
if err != nil {
|
||||
return 0, errors.New(string(stderr))
|
||||
}
|
||||
@@ -152,6 +154,53 @@ func (repo *Repository) commitsCount(id sha1) (int, error) {
|
||||
return com.StrTo(strings.TrimSpace(stdout)).Int()
|
||||
}
|
||||
|
||||
func (repo *Repository) CommitsCount(commitId string) (int, error) {
|
||||
id, err := NewIdFromString(commitId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return repo.commitsCount(id)
|
||||
}
|
||||
|
||||
func (repo *Repository) commitsCountBetween(start, end sha1) (int, error) {
|
||||
if gitVer.LessThan(MustParseVersion("1.8.0")) {
|
||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log",
|
||||
"--pretty=format:''", start.String()+"..."+end.String())
|
||||
if err != nil {
|
||||
return 0, errors.New(string(stderr))
|
||||
}
|
||||
return len(bytes.Split(stdout, []byte("\n"))), nil
|
||||
}
|
||||
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count",
|
||||
start.String()+"..."+end.String())
|
||||
if err != nil {
|
||||
return 0, errors.New(stderr)
|
||||
}
|
||||
return com.StrTo(strings.TrimSpace(stdout)).Int()
|
||||
}
|
||||
|
||||
func (repo *Repository) CommitsCountBetween(startCommitID, endCommitID string) (int, error) {
|
||||
start, err := NewIdFromString(startCommitID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
end, err := NewIdFromString(endCommitID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return repo.commitsCountBetween(start, end)
|
||||
}
|
||||
|
||||
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "diff", "--name-only",
|
||||
startCommitID+"..."+endCommitID)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("list changed files: %v", concatenateError(err, stderr))
|
||||
}
|
||||
return len(strings.Split(stdout, "\n")) - 1, nil
|
||||
}
|
||||
|
||||
// used only for single tree, (]
|
||||
func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) {
|
||||
l := list.New()
|
||||
@@ -231,14 +280,6 @@ func (repo *Repository) commitsBefore(lock *sync.Mutex, l *list.List, parent *li
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *Repository) CommitsCount(commitId string) (int, error) {
|
||||
id, err := NewIdFromString(commitId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return repo.commitsCount(id)
|
||||
}
|
||||
|
||||
func (repo *Repository) FileCommitsCount(branch, file string) (int, error) {
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count",
|
||||
branch, "--", file)
|
||||
|
||||
@@ -84,3 +84,9 @@ func (repo *Repository) GetPatch(basePath, baseBranch, headBranch string) ([]byt
|
||||
|
||||
return stdout, nil
|
||||
}
|
||||
|
||||
// Merge merges pull request from head repository and branch.
|
||||
func (repo *Repository) Merge(headRepoPath string, baseBranch, headBranch string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
@font-face{font-family:octicons;src:url(../fonts/octicons.eot?#iefix&v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('embedded-opentype'),url(../fonts/octicons.woff?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('woff'),url(../fonts/octicons.ttf?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('truetype'),url(../fonts/octicons.svg?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d#octicons) format('svg');font-weight:400;font-style:normal}.mega-octicon,.octicon{font:normal normal normal 16px/1 octicons;display:inline-block;text-decoration:none;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mega-octicon{font-size:32px}.octicon-alert:before{content:'\f02d'}.octicon-arrow-down:before{content:'\f03f'}.octicon-arrow-left:before{content:'\f040'}.octicon-arrow-right:before{content:'\f03e'}.octicon-arrow-small-down:before{content:'\f0a0'}.octicon-arrow-small-left:before{content:'\f0a1'}.octicon-arrow-small-right:before{content:'\f071'}.octicon-arrow-small-up:before{content:'\f09f'}.octicon-arrow-up:before{content:'\f03d'}.octicon-beaker:before,.octicon-microscope:before{content:'\f0dd'}.octicon-bell:before{content:'\f0de'}.octicon-book:before{content:'\f007'}.octicon-bookmark:before{content:'\f07b'}.octicon-briefcase:before{content:'\f0d3'}.octicon-broadcast:before{content:'\f048'}.octicon-browser:before{content:'\f0c5'}.octicon-bug:before{content:'\f091'}.octicon-calendar:before{content:'\f068'}.octicon-check:before{content:'\f03a'}.octicon-checklist:before{content:'\f076'}.octicon-chevron-down:before{content:'\f0a3'}.octicon-chevron-left:before{content:'\f0a4'}.octicon-chevron-right:before{content:'\f078'}.octicon-chevron-up:before{content:'\f0a2'}.octicon-circle-slash:before{content:'\f084'}.octicon-circuit-board:before{content:'\f0d6'}.octicon-clippy:before{content:'\f035'}.octicon-clock:before{content:'\f046'}.octicon-cloud-download:before{content:'\f00b'}.octicon-cloud-upload:before{content:'\f00c'}.octicon-code:before{content:'\f05f'}.octicon-color-mode:before{content:'\f065'}.octicon-comment-add:before,.octicon-comment:before{content:'\f02b'}.octicon-comment-discussion:before{content:'\f04f'}.octicon-credit-card:before{content:'\f045'}.octicon-dash:before{content:'\f0ca'}.octicon-dashboard:before{content:'\f07d'}.octicon-database:before{content:'\f096'}.octicon-clone:before,.octicon-desktop-download:before{content:'\f0dc'}.octicon-device-camera:before{content:'\f056'}.octicon-device-camera-video:before{content:'\f057'}.octicon-device-desktop:before{content:'\f27c'}.octicon-device-mobile:before{content:'\f038'}.octicon-diff:before{content:'\f04d'}.octicon-diff-added:before{content:'\f06b'}.octicon-diff-ignored:before{content:'\f099'}.octicon-diff-modified:before{content:'\f06d'}.octicon-diff-removed:before{content:'\f06c'}.octicon-diff-renamed:before{content:'\f06e'}.octicon-ellipsis:before{content:'\f09a'}.octicon-eye-unwatch:before,.octicon-eye-watch:before,.octicon-eye:before{content:'\f04e'}.octicon-file-binary:before{content:'\f094'}.octicon-file-code:before{content:'\f010'}.octicon-file-directory:before{content:'\f016'}.octicon-file-media:before{content:'\f012'}.octicon-file-pdf:before{content:'\f014'}.octicon-file-submodule:before{content:'\f017'}.octicon-file-symlink-directory:before{content:'\f0b1'}.octicon-file-symlink-file:before{content:'\f0b0'}.octicon-file-text:before{content:'\f011'}.octicon-file-zip:before{content:'\f013'}.octicon-flame:before{content:'\f0d2'}.octicon-fold:before{content:'\f0cc'}.octicon-gear:before{content:'\f02f'}.octicon-gift:before{content:'\f042'}.octicon-gist:before{content:'\f00e'}.octicon-gist-secret:before{content:'\f08c'}.octicon-git-branch-create:before,.octicon-git-branch-delete:before,.octicon-git-branch:before{content:'\f020'}.octicon-git-commit:before{content:'\f01f'}.octicon-git-compare:before{content:'\f0ac'}.octicon-git-merge:before{content:'\f023'}.octicon-git-pull-request-abandoned:before,.octicon-git-pull-request:before{content:'\f009'}.octicon-globe:before{content:'\f0b6'}.octicon-graph:before{content:'\f043'}.octicon-heart:before{content:'\2665'}.octicon-history:before{content:'\f07e'}.octicon-home:before{content:'\f08d'}.octicon-horizontal-rule:before{content:'\f070'}.octicon-hubot:before{content:'\f09d'}.octicon-inbox:before{content:'\f0cf'}.octicon-info:before{content:'\f059'}.octicon-issue-closed:before{content:'\f028'}.octicon-issue-opened:before{content:'\f026'}.octicon-issue-reopened:before{content:'\f027'}.octicon-jersey:before{content:'\f019'}.octicon-key:before{content:'\f049'}.octicon-keyboard:before{content:'\f00d'}.octicon-law:before{content:'\f0d8'}.octicon-light-bulb:before{content:'\f000'}.octicon-link:before{content:'\f05c'}.octicon-link-external:before{content:'\f07f'}.octicon-list-ordered:before{content:'\f062'}.octicon-list-unordered:before{content:'\f061'}.octicon-location:before{content:'\f060'}.octicon-gLine truncated
|
||||
@font-face{font-family:octicons;src:url(../fonts/octicons.eot?#iefix&v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('embedded-opentype'),url(../fonts/octicons.woff?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('woff'),url(../fonts/octicons.ttf?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d) format('truetype'),url(../fonts/octicons.svg?v=396334ee3da78f4302d25c758ae3e3ce5dc3c97d#octicons) format('svg');font-weight:400;font-style:normal}.mega-octicon,.octicon{font:normal normal normal 16px/1 octicons;display:inline-block;text-decoration:none;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mega-octicon{font-size:32px}.octicon-alert:before{content:'\f02d'}.octicon-arrow-down:before{content:'\f03f'}.octicon-arrow-left:before{content:'\f040'}.octicon-arrow-right:before{content:'\f03e'}.octicon-arrow-small-down:before{content:'\f0a0'}.octicon-arrow-small-left:before{content:'\f0a1'}.octicon-arrow-small-right:before{content:'\f071'}.octicon-arrow-small-up:before{content:'\f09f'}.octicon-arrow-up:before{content:'\f03d'}.octicon-beaker:before,.octicon-microscope:before{content:'\f0dd'}.octicon-bell:before{content:'\f0de'}.octicon-book:before{content:'\f007'}.octicon-bookmark:before{content:'\f07b'}.octicon-briefcase:before{content:'\f0d3'}.octicon-broadcast:before{content:'\f048'}.octicon-browser:before{content:'\f0c5'}.octicon-bug:before{content:'\f091'}.octicon-calendar:before{content:'\f068'}.octicon-check:before{content:'\f03a'}.octicon-checklist:before{content:'\f076'}.octicon-chevron-down:before{content:'\f0a3'}.octicon-chevron-left:before{content:'\f0a4'}.octicon-chevron-right:before{content:'\f078'}.octicon-chevron-up:before{content:'\f0a2'}.octicon-circle-slash:before{content:'\f084'}.octicon-circuit-board:before{content:'\f0d6'}.octicon-clippy:before{content:'\f035'}.octicon-clock:before{content:'\f046'}.octicon-cloud-download:before{content:'\f00b'}.octicon-cloud-upload:before{content:'\f00c'}.octicon-code:before{content:'\f05f'}.octicon-color-mode:before{content:'\f065'}.octicon-comment-add:before,.octicon-comment:before{content:'\f02b'}.octicon-comment-discussion:before{content:'\f04f'}.octicon-credit-card:before{content:'\f045'}.octicon-dash:before{content:'\f0ca'}.octicon-dashboard:before{content:'\f07d'}.octicon-database:before{content:'\f096'}.octicon-clone:before,.octicon-desktop-download:before{content:'\f0dc'}.octicon-device-camera:before{content:'\f056'}.octicon-device-camera-video:before{content:'\f057'}.octicon-device-desktop:before{content:'\f27c'}.octicon-device-mobile:before{content:'\f038'}.octicon-diff:before{content:'\f04d'}.octicon-diff-added:before{content:'\f06b'}.octicon-diff-ignored:before{content:'\f099'}.octicon-diff-modified:before{content:'\f06d'}.octicon-diff-removed:before{content:'\f06c'}.octicon-diff-renamed:before{content:'\f06e'}.octicon-ellipsis:before{content:'\f09a'}.octicon-eye-unwatch:before,.octicon-eye-watch:before,.octicon-eye:before{content:'\f04e'}.octicon-file-binary:before{content:'\f094'}.octicon-file-code:before{content:'\f010'}.octicon-file-directory:before{content:'\f016'}.octicon-file-media:before{content:'\f012'}.octicon-file-pdf:before{content:'\f014'}.octicon-file-submodule:before{content:'\f017'}.octicon-file-symlink-directory:before{content:'\f0b1'}.octicon-file-symlink-file:before{content:'\f0b0'}.octicon-file-text:before{content:'\f011'}.octicon-file-zip:before{content:'\f013'}.octicon-flame:before{content:'\f0d2'}.octicon-fold:before{content:'\f0cc'}.octicon-gear:before{content:'\f02f'}.octicon-gift:before{content:'\f042'}.octicon-gist:before{content:'\f00e'}.octicon-gist-secret:before{content:'\f08c'}.octicon-git-branch-create:before,.octicon-git-branch-delete:before,.octicon-git-branch:before{content:'\f020'}.octicon-git-commit:before{content:'\f01f'}.octicon-git-compare:before{content:'\f0ac'}.octicon-git-merge:before{content:'\f023'}.octicon-git-pull-request-abandoned:before,.octicon-git-pull-request:before{content:'\f009'}.octicon-globe:before{content:'\f0b6'}.octicon-graph:before{content:'\f043'}.octicon-heart:before{content:'\2665'}.octicon-history:before{content:'\f07e'}.octicon-home:before{content:'\f08d'}.octicon-horizontal-rule:before{content:'\f070'}.octicon-hubot:before{content:'\f09d'}.octicon-inbox:before{content:'\f0cf'}.octicon-info:before{content:'\f059'}.octicon-issue-closed:before{content:'\f028'}.octicon-issue-opened:before{content:'\f026'}.octicon-issue-reopened:before{content:'\f027'}.octicon-jersey:before{content:'\f019'}.octicon-key:before{content:'\f049'}.octicon-keyboard:before{content:'\f00d'}.octicon-law:before{content:'\f0d8'}.octicon-light-bulb:before{content:'\f000'}.octicon-link:before{content:'\f05c'}.octicon-link-external:before{content:'\f07f'}.octicon-list-ordered:before{content:'\f062'}.octicon-list-unordered:before{content:'\f061'}.octicon-location:before{content:'\f060'}.octicon-gLine truncated
|
||||
@@ -114,6 +114,10 @@ pre {
|
||||
&.green {
|
||||
color: #6cc644!important;
|
||||
}
|
||||
&.purple {
|
||||
color: #6e5494!important;
|
||||
}
|
||||
|
||||
&.left {
|
||||
text-align: left!important;
|
||||
}
|
||||
|
||||
@@ -466,7 +466,12 @@ func ViewIssue(ctx *middleware.Context) {
|
||||
|
||||
// Get more information if it's a pull request.
|
||||
if issue.IsPull {
|
||||
PrepareViewPullInfo(ctx, issue)
|
||||
if issue.HasMerged {
|
||||
ctx.Data["DisableStatusChange"] = issue.HasMerged
|
||||
PrepareMergedViewPullInfo(ctx, issue)
|
||||
} else {
|
||||
PrepareViewPullInfo(ctx, issue)
|
||||
}
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -730,7 +735,8 @@ func NewComment(ctx *middleware.Context, form auth.CreateCommentForm) {
|
||||
|
||||
// Check if issue owner/poster changes the status of issue.
|
||||
if (ctx.Repo.IsOwner() || (ctx.IsSigned && issue.IsPoster(ctx.User.Id))) &&
|
||||
(form.Status == "reopen" || form.Status == "close") {
|
||||
(form.Status == "reopen" || form.Status == "close") &&
|
||||
!(issue.IsPull && issue.HasMerged) {
|
||||
issue.Repo = ctx.Repo.Repository
|
||||
if err = issue.ChangeStatus(ctx.User, form.Status == "close"); err != nil {
|
||||
ctx.Handle(500, "ChangeStatus", err)
|
||||
|
||||
+155
-35
@@ -5,6 +5,7 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
@@ -167,6 +168,26 @@ func checkPullInfo(ctx *middleware.Context) *models.Issue {
|
||||
return pull
|
||||
}
|
||||
|
||||
func PrepareMergedViewPullInfo(ctx *middleware.Context, pull *models.Issue) {
|
||||
ctx.Data["HasMerged"] = true
|
||||
|
||||
var err error
|
||||
|
||||
ctx.Data["HeadTarget"] = pull.HeadUserName + "/" + pull.HeadBarcnh
|
||||
ctx.Data["BaseTarget"] = ctx.Repo.Owner.Name + "/" + pull.BaseBranch
|
||||
|
||||
ctx.Data["NumCommits"], err = ctx.Repo.GitRepo.CommitsCountBetween(pull.MergeBase, pull.MergedCommitID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "Repo.GitRepo.CommitsCountBetween", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["NumFiles"], err = ctx.Repo.GitRepo.FilesCountBetween(pull.MergeBase, pull.MergedCommitID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "Repo.GitRepo.FilesCountBetween", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func PrepareViewPullInfo(ctx *middleware.Context, pull *models.Issue) *git.PullRequestInfo {
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
@@ -185,6 +206,14 @@ func PrepareViewPullInfo(ctx *middleware.Context, pull *models.Issue) *git.PullR
|
||||
return nil
|
||||
}
|
||||
|
||||
if pull.HeadRepo == nil || !headGitRepo.IsBranchExist(pull.HeadBarcnh) {
|
||||
ctx.Data["IsPullReuqestBroken"] = true
|
||||
ctx.Data["HeadTarget"] = "deleted"
|
||||
ctx.Data["NumCommits"] = 0
|
||||
ctx.Data["NumFiles"] = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(repo.Owner.Name, repo.Name),
|
||||
pull.BaseBranch, pull.HeadBarcnh)
|
||||
if err != nil {
|
||||
@@ -203,17 +232,46 @@ func ViewPullCommits(ctx *middleware.Context) {
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
prInfo := PrepareViewPullInfo(ctx, pull)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
prInfo.Commits = models.ValidateCommitsWithEmails(prInfo.Commits)
|
||||
ctx.Data["Commits"] = prInfo.Commits
|
||||
ctx.Data["CommitCount"] = prInfo.Commits.Len()
|
||||
|
||||
ctx.Data["Username"] = pull.HeadUserName
|
||||
ctx.Data["Reponame"] = pull.HeadRepo.Name
|
||||
|
||||
var commits *list.List
|
||||
if pull.HasMerged {
|
||||
PrepareMergedViewPullInfo(ctx, pull)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
startCommit, err := ctx.Repo.GitRepo.GetCommit(pull.MergeBase)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "Repo.GitRepo.GetCommit", err)
|
||||
return
|
||||
}
|
||||
endCommit, err := ctx.Repo.GitRepo.GetCommit(pull.MergedCommitID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "Repo.GitRepo.GetCommit", err)
|
||||
return
|
||||
}
|
||||
commits, err = ctx.Repo.GitRepo.CommitsBetween(endCommit, startCommit)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "Repo.GitRepo.CommitsBetween", err)
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
prInfo := PrepareViewPullInfo(ctx, pull)
|
||||
if ctx.Written() {
|
||||
return
|
||||
} else if prInfo == nil {
|
||||
ctx.Handle(404, "ViewPullCommits", nil)
|
||||
return
|
||||
}
|
||||
commits = prInfo.Commits
|
||||
}
|
||||
|
||||
commits = models.ValidateCommitsWithEmails(commits)
|
||||
ctx.Data["Commits"] = commits
|
||||
ctx.Data["CommitCount"] = commits.Len()
|
||||
|
||||
ctx.HTML(200, PULL_COMMITS)
|
||||
}
|
||||
|
||||
@@ -225,27 +283,54 @@ func ViewPullFiles(ctx *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
prInfo := PrepareViewPullInfo(ctx, pull)
|
||||
if ctx.Written() {
|
||||
return
|
||||
var (
|
||||
diffRepoPath string
|
||||
startCommitID string
|
||||
endCommitID string
|
||||
gitRepo *git.Repository
|
||||
)
|
||||
|
||||
if pull.HasMerged {
|
||||
PrepareMergedViewPullInfo(ctx, pull)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
diffRepoPath = ctx.Repo.GitRepo.Path
|
||||
startCommitID = pull.MergeBase
|
||||
endCommitID = pull.MergedCommitID
|
||||
gitRepo = ctx.Repo.GitRepo
|
||||
} else {
|
||||
prInfo := PrepareViewPullInfo(ctx, pull)
|
||||
if ctx.Written() {
|
||||
return
|
||||
} else if prInfo == nil {
|
||||
ctx.Handle(404, "ViewPullFiles", nil)
|
||||
return
|
||||
}
|
||||
|
||||
headRepoPath := models.RepoPath(pull.HeadUserName, pull.HeadRepo.Name)
|
||||
|
||||
headGitRepo, err := git.OpenRepository(headRepoPath)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "OpenRepository", err)
|
||||
return
|
||||
}
|
||||
|
||||
headCommitID, err := headGitRepo.GetCommitIdOfBranch(pull.HeadBarcnh)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetCommitIdOfBranch", err)
|
||||
return
|
||||
}
|
||||
|
||||
diffRepoPath = headRepoPath
|
||||
startCommitID = prInfo.MergeBase
|
||||
endCommitID = headCommitID
|
||||
gitRepo = headGitRepo
|
||||
}
|
||||
|
||||
headRepoPath := models.RepoPath(pull.HeadUserName, pull.HeadRepo.Name)
|
||||
|
||||
headGitRepo, err := git.OpenRepository(headRepoPath)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "OpenRepository", err)
|
||||
return
|
||||
}
|
||||
|
||||
headCommitID, err := headGitRepo.GetCommitIdOfBranch(pull.HeadBarcnh)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetCommitIdOfBranch", err)
|
||||
return
|
||||
}
|
||||
|
||||
diff, err := models.GetDiffRange(headRepoPath,
|
||||
prInfo.MergeBase, headCommitID, setting.Git.MaxGitDiffLines)
|
||||
diff, err := models.GetDiffRange(diffRepoPath,
|
||||
startCommitID, endCommitID, setting.Git.MaxGitDiffLines)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetDiffRange", err)
|
||||
return
|
||||
@@ -253,7 +338,7 @@ func ViewPullFiles(ctx *middleware.Context) {
|
||||
ctx.Data["Diff"] = diff
|
||||
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
|
||||
|
||||
headCommit, err := headGitRepo.GetCommit(headCommitID)
|
||||
commit, err := gitRepo.GetCommit(endCommitID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetCommit", err)
|
||||
return
|
||||
@@ -262,14 +347,49 @@ func ViewPullFiles(ctx *middleware.Context) {
|
||||
headTarget := path.Join(pull.HeadUserName, pull.HeadRepo.Name)
|
||||
ctx.Data["Username"] = pull.HeadUserName
|
||||
ctx.Data["Reponame"] = pull.HeadRepo.Name
|
||||
ctx.Data["IsImageFile"] = headCommit.IsImageFile
|
||||
ctx.Data["SourcePath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "src", headCommitID)
|
||||
ctx.Data["BeforeSourcePath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "src", prInfo.MergeBase)
|
||||
ctx.Data["RawPath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "raw", headCommitID)
|
||||
ctx.Data["IsImageFile"] = commit.IsImageFile
|
||||
ctx.Data["SourcePath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "src", endCommitID)
|
||||
ctx.Data["BeforeSourcePath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "src", startCommitID)
|
||||
ctx.Data["RawPath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "raw", endCommitID)
|
||||
|
||||
ctx.HTML(200, PULL_FILES)
|
||||
}
|
||||
|
||||
func MergePullRequest(ctx *middleware.Context) {
|
||||
pull := checkPullInfo(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
if pull.IsClosed {
|
||||
ctx.Handle(404, "MergePullRequest", nil)
|
||||
return
|
||||
}
|
||||
|
||||
pr, err := models.GetPullRequestByPullID(pull.ID)
|
||||
if err != nil {
|
||||
if models.IsErrPullRequestNotExist(err) {
|
||||
ctx.Handle(404, "GetPullRequestByPullID", nil)
|
||||
} else {
|
||||
ctx.Handle(500, "GetPullRequestByPullID", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !pr.CanAutoMerge || pr.HasMerged {
|
||||
ctx.Handle(404, "MergePullRequest", nil)
|
||||
return
|
||||
}
|
||||
|
||||
pr.Pull = pull
|
||||
if err = pr.Merge(ctx.Repo.GitRepo); err != nil {
|
||||
ctx.Handle(500, "GetPullRequestByPullID", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace("Pull request merged: %d", pr.ID)
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.PullIndex))
|
||||
}
|
||||
|
||||
func ParseCompareInfo(ctx *middleware.Context) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) {
|
||||
// Get compare branch information.
|
||||
infos := strings.Split(ctx.Params("*"), "...")
|
||||
@@ -416,10 +536,10 @@ func CompareAndPullRequest(ctx *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
pr, err := models.GetPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch)
|
||||
pr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch)
|
||||
if err != nil {
|
||||
if !models.IsErrPullRequestNotExist(err) {
|
||||
ctx.Handle(500, "HasPullRequest", err)
|
||||
ctx.Handle(500, "GetUnmergedPullRequest", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
<ul id="repo-file-nav" class="clear menu menu-line">
|
||||
{{if and .IsRepositoryAdmin .Repository.BaseRepo}}
|
||||
{{ $baseRepo := .Repository.BaseRepo}}
|
||||
<!-- <li>
|
||||
<li>
|
||||
<a href="{{AppSubUrl}}/{{$baseRepo.Owner.Name}}/{{$baseRepo.Name}}/compare/{{$.BaseDefaultBranch}}...{{$.Owner.Name}}:{{$.BranchName}}">
|
||||
<button class="btn btn-green btn-small btn-radius" id="repo-compare-btn"><i class="octicon octicon-git-compare"></i></button>
|
||||
</a>
|
||||
</li> -->
|
||||
</li>
|
||||
{{end}}
|
||||
<li id="repo-branch-switch" class="down drop">
|
||||
<a>
|
||||
|
||||
@@ -133,28 +133,40 @@
|
||||
|
||||
{{if .Issue.IsPull}}
|
||||
<div class="comment merge box">
|
||||
<a class="avatar text {{if .Issue.IsClosed}}grey{{else if .Issue.CanAutoMerge}}green{{else}}red{{end}}">
|
||||
<a class="avatar text {{if .Issue.HasMerged}}purple{{else if .Issue.IsClosed}}grey{{else if and .Issue.CanAutoMerge (not .IsPullReuqestBroken)}}green{{else}}red{{end}}">
|
||||
<span class="mega-octicon octicon-git-merge"></span>
|
||||
</a>
|
||||
<div class="content">
|
||||
<div class="ui merge segment">
|
||||
{{if .Issue.IsClosed}}
|
||||
{{if .Issue.HasMerged}}
|
||||
<div class="item text purple">
|
||||
{{$.i18n.Tr "repo.pulls.has_merged"}}
|
||||
</div>
|
||||
{{else if .Issue.IsClosed}}
|
||||
<div class="item text grey">
|
||||
{{$.i18n.Tr "repo.pulls.reopen_to_merge"}}
|
||||
</div>
|
||||
{{else if .IsPullReuqestBroken}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.data_borken"}}
|
||||
</div>
|
||||
{{else if .Issue.CanAutoMerge}}
|
||||
<div class="item text green">
|
||||
<span class="octicon octicon-check"></span>
|
||||
{{$.i18n.Tr "repo.pulls.can_auto_merge_desc"}}
|
||||
</div>
|
||||
{{if .IsRepositoryAdmin}}
|
||||
<div class="ui divider"></div>
|
||||
<div>
|
||||
<button class="ui green button">
|
||||
<span class="octicon octicon-git-merge"></span> {{$.i18n.Tr "repo.pulls.merge_pull_request"}}
|
||||
</button>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="item text green">
|
||||
<span class="octicon octicon-check"></span>
|
||||
{{$.i18n.Tr "repo.pulls.can_auto_merge_desc"}}
|
||||
</div>
|
||||
{{if .IsRepositoryAdmin}}
|
||||
<div class="ui divider"></div>
|
||||
<div>
|
||||
<form class="ui form" action="{{.Link}}/merge" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<button class="ui green button">
|
||||
<span class="octicon octicon-git-merge"></span> {{$.i18n.Tr "repo.pulls.merge_pull_request"}}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
@@ -181,16 +193,16 @@
|
||||
{{.CsrfTokenHtml}}
|
||||
<input id="status" name="status" type="hidden">
|
||||
<div class="text right">
|
||||
{{if .IsIssueOwner}}
|
||||
{{if .Issue.IsClosed}}
|
||||
<div id="status-button" class="ui green basic button" data-status="{{.i18n.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{.i18n.Tr "repo.issues.reopen_comment_issue"}}" data-status-val="reopen">
|
||||
{{.i18n.Tr "repo.issues.reopen_issue"}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div id="status-button" class="ui red basic button" data-status="{{.i18n.Tr "repo.issues.close_issue"}}" data-status-and-comment="{{.i18n.Tr "repo.issues.close_comment_issue"}}" data-status-val="close">
|
||||
{{.i18n.Tr "repo.issues.close_issue"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if and .IsIssueOwner (not .DisableStatusChange)}}
|
||||
{{if .Issue.IsClosed}}
|
||||
<div id="status-button" class="ui green basic button" data-status="{{.i18n.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{.i18n.Tr "repo.issues.reopen_comment_issue"}}" data-status-val="reopen">
|
||||
{{.i18n.Tr "repo.issues.reopen_issue"}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div id="status-button" class="ui red basic button" data-status="{{.i18n.Tr "repo.issues.close_issue"}}" data-status-and-comment="{{.i18n.Tr "repo.issues.close_comment_issue"}}" data-status-val="close">
|
||||
{{.i18n.Tr "repo.issues.close_issue"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<button class="ui green button">
|
||||
{{.i18n.Tr "repo.issues.create_comment"}}
|
||||
|
||||
@@ -16,15 +16,23 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .Issue.IsClosed}}
|
||||
{{if .HasMerged}}
|
||||
<div class="ui purple large label"><i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls.merged"}}</div>
|
||||
{{else if .Issue.IsClosed}}
|
||||
<div class="ui red large label"><i class="octicon octicon-issue-closed"></i> {{.i18n.Tr "repo.issues.closed_title"}}</div>
|
||||
{{else}}
|
||||
<div class="ui green large label"><i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
|
||||
{{end}}
|
||||
|
||||
{{if .Issue.IsPull}}
|
||||
<a {{if gt .Issue.Poster.Id 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.Name}}</a>
|
||||
<span class="pull-desc">{{$.i18n.Tr "repo.pulls.title_desc" .NumCommits .HeadTarget .BaseTarget | Str2html}}</span>
|
||||
{{if .Issue.HasMerged}}
|
||||
{{ $mergedStr:= TimeSince .Issue.Merged $.Lang }}
|
||||
<a {{if gt .Issue.Merger.Id 0}}href="{{.Issue.Merger.HomeLink}}"{{end}}>{{.Issue.Merger.Name}}</a>
|
||||
<span class="pull-desc">{{$.i18n.Tr "repo.pulls.merged_title_desc" .NumCommits .HeadTarget .BaseTarget $mergedStr | Safe}}</span>
|
||||
{{else}}
|
||||
<a {{if gt .Issue.Poster.Id 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.Name}}</a>
|
||||
<span class="pull-desc">{{$.i18n.Tr "repo.pulls.title_desc" .NumCommits .HeadTarget .BaseTarget | Str2html}}</span>
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{ $createdStr:= TimeSince .Issue.Created $.Lang }}
|
||||
<span class="time-desc">
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
{{$.i18n.Tr "repo.pulls.tab_conversation"}}
|
||||
<span class="ui label">{{.Issue.NumComments}}</span>
|
||||
</a>
|
||||
<a class="item {{if .PageIsPullCommits}}active{{end}}" href="{{.RepoLink}}/pulls/{{.Issue.Index}}/commits">
|
||||
<a class="item {{if .PageIsPullCommits}}active{{end}}" {{if .NumCommits}}href="{{.RepoLink}}/pulls/{{.Issue.Index}}/commits"{{end}}>
|
||||
<span class="octicon octicon-git-commit"></span>
|
||||
{{$.i18n.Tr "repo.pulls.tab_commits"}}
|
||||
<span class="ui label">{{.NumCommits}}</span>
|
||||
<span class="ui label">{{if .NumCommits}}{{.NumCommits}}{{else}}N/A{{end}}</span>
|
||||
</a>
|
||||
<a class="item {{if .PageIsPullFiles}}active{{end}}" href="{{.RepoLink}}/pulls/{{.Issue.Index}}/files">
|
||||
<a class="item {{if .PageIsPullFiles}}active{{end}}" {{if .NumFiles}}href="{{.RepoLink}}/pulls/{{.Issue.Index}}/files"{{end}}>
|
||||
<span class="octicon octicon-diff"></span>
|
||||
{{$.i18n.Tr "repo.pulls.tab_files"}}
|
||||
<span class="ui label">{{.NumFiles}}</span>
|
||||
<span class="ui label">{{if .NumFiles}}{{.NumFiles}}{{else}}N/A{{end}}</span>
|
||||
</a>
|
||||
</div>
|
||||
Reference in new issue
Block a user