basic PR feature

This commit is contained in:
Unknwon committed 2015-09-02 09:26:56 -04:00
1 parent 6ea28f2a47
commit 953bb06857
15 files changed
+387 -94

No files matched your search

+1
View File
@@ -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() {
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+2 -2
View File
@@ -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
}
+50 -9
View File
@@ -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)
+6
View 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
}
+1 -1
View File
@@ -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
+4
View File
@@ -114,6 +114,10 @@ pre {
&.green {
color: #6cc644!important;
}
&.purple {
color: #6e5494!important;
}
&.left {
text-align: left!important;
}
+8 -2
View File
@@ -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
View File
@@ -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 {
+2 -2
View File
@@ -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>
+36 -24
View File
@@ -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"}}
+11 -3
View File
@@ -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 -4
View File
@@ -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>