Browse Source

basic PR feature

tags/v0.9.99
Unknwon 8 years ago
parent
commit
953bb06857

+ 1
- 0
cmd/web.go View File

m.Group("/pulls/:index", func() { m.Group("/pulls/:index", func() {
m.Get("/commits", repo.ViewPullCommits) m.Get("/commits", repo.ViewPullCommits)
m.Get("/files", repo.ViewPullFiles) m.Get("/files", repo.ViewPullFiles)
m.Post("/merge", reqRepoAdmin, repo.MergePullRequest)
}) })


m.Group("", func() { m.Group("", func() {

+ 4
- 0
conf/locale/locale_en-US.ini View File

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.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.create = Create Pull Request
pulls.title_desc = wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> 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_conversation = Conversation
pulls.tab_commits = Commits pulls.tab_commits = Commits
pulls.tab_files = Files changed pulls.tab_files = Files changed
pulls.reopen_to_merge = Please reopen this pull request to perform merge operation. 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.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_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. pulls.cannot_auto_merge_helper = Please use commond line tool to solve it.

+ 98
- 9
models/issue.go View File

"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"


"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process" "github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"


// PullRequest represents relation between pull request and repositories. // PullRequest represents relation between pull request and repositories.
type PullRequest struct { 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 PullIndex int64
HeadRepoID int64 `xorm:"UNIQUE(s)"`
HeadRepoID int64
HeadRepo *Repository `xorm:"-"` HeadRepo *Repository `xorm:"-"`
BaseRepoID int64 `xorm:"UNIQUE(s)"`
BaseRepoID int64
HeadUserName string HeadUserName string
HeadBarcnh string `xorm:"UNIQUE(s)"`
BaseBranch string `xorm:"UNIQUE(s)"`
HeadBarcnh string
BaseBranch string
MergeBase string `xorm:"VARCHAR(40)"` MergeBase string `xorm:"VARCHAR(40)"`
MergedCommitID string `xorm:"VARCHAR(40)"` MergedCommitID string `xorm:"VARCHAR(40)"`
Type PullRequestType Type PullRequestType
CanAutoMerge bool CanAutoMerge bool
HasMerged bool HasMerged bool
Merged time.Time
MergerID int64
Merger *User `xorm:"-"`
} }


func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) { func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
if err != nil { if err != nil {
log.Error(3, "GetRepositoryByID[%d]: %v", pr.ID, err) 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. // NewPullRequest creates new pull request with labels for repository.
return sess.Commit() 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{ pr := &PullRequest{
HeadRepoID: headRepoID, HeadRepoID: headRepoID,
BaseRepoID: baseRepoID, BaseRepoID: baseRepoID,
BaseBranch: baseBranch, BaseBranch: baseBranch,
} }


has, err := x.Get(pr)
has, err := x.Where("has_merged=?", false).Get(pr)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !has {

+ 5
- 3
models/repo.go View File

if _, stderr, err = process.ExecDir(-1, if _, stderr, err = process.ExecDir(-1,
tmpPath, fmt.Sprintf("initRepoCommit(git add): %s", tmpPath), tmpPath, fmt.Sprintf("initRepoCommit(git add): %s", tmpPath),
"git", "add", "--all"); err != nil { "git", "add", "--all"); err != nil {
return errors.New("git add: " + stderr)
return fmt.Errorf("git add: %s", stderr)
} }


if _, stderr, err = process.ExecDir(-1, if _, stderr, err = process.ExecDir(-1,
tmpPath, fmt.Sprintf("initRepoCommit(git commit): %s", tmpPath), tmpPath, fmt.Sprintf("initRepoCommit(git commit): %s", tmpPath),
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
"-m", "initial commit"); err != nil { "-m", "initial commit"); err != nil {
return errors.New("git commit: " + stderr)
return fmt.Errorf("git commit: %s", stderr)
} }


if _, stderr, err = process.ExecDir(-1, if _, stderr, err = process.ExecDir(-1,
tmpPath, fmt.Sprintf("initRepoCommit(git push): %s", tmpPath), tmpPath, fmt.Sprintf("initRepoCommit(git push): %s", tmpPath),
"git", "push", "origin", "master"); err != nil { "git", "push", "origin", "master"); err != nil {
return errors.New("git push: " + stderr)
return fmt.Errorf("git push: %s", stderr)
} }
return nil return nil
} }
return err return err
} else if _, err = sess.Delete(&Collaboration{RepoID: repoID}); err != nil { } else if _, err = sess.Delete(&Collaboration{RepoID: repoID}); err != nil {
return err return err
} else if _, err = sess.Delete(&PullRequest{BaseRepoID: repoID}); err != nil {
return err
} }


// Delete comments and attachments. // Delete comments and attachments.

+ 2
- 2
modules/bindata/bindata.go
File diff suppressed because it is too large
View File


+ 50
- 9
modules/git/repo_commit.go View File

"bytes" "bytes"
"container/list" "container/list"
"errors" "errors"
"fmt"
"strings" "strings"
"sync" "sync"




func (repo *Repository) commitsCount(id sha1) (int, error) { func (repo *Repository) commitsCount(id sha1) (int, error) {
if gitVer.LessThan(MustParseVersion("1.8.0")) { 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 { if err != nil {
return 0, errors.New(string(stderr)) return 0, errors.New(string(stderr))
} }
return com.StrTo(strings.TrimSpace(stdout)).Int() 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, (] // used only for single tree, (]
func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) { func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) {
l := list.New() l := list.New()
return nil 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) { func (repo *Repository) FileCommitsCount(branch, file string) (int, error) {
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count",
branch, "--", file) branch, "--", file)

+ 6
- 0
modules/git/repo_pull.go View File



return stdout, nil 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
public/css/gogs.min.css
File diff suppressed because it is too large
View File


+ 4
- 0
public/less/_base.less View File

&.green { &.green {
color: #6cc644!important; color: #6cc644!important;
} }
&.purple {
color: #6e5494!important;
}

&.left { &.left {
text-align: left!important; text-align: left!important;
} }

+ 8
- 2
routers/repo/issue.go View File



// Get more information if it's a pull request. // Get more information if it's a pull request.
if issue.IsPull { if issue.IsPull {
PrepareViewPullInfo(ctx, issue)
if issue.HasMerged {
ctx.Data["DisableStatusChange"] = issue.HasMerged
PrepareMergedViewPullInfo(ctx, issue)
} else {
PrepareViewPullInfo(ctx, issue)
}
if ctx.Written() { if ctx.Written() {
return return
} }


// Check if issue owner/poster changes the status of issue. // Check if issue owner/poster changes the status of issue.
if (ctx.Repo.IsOwner() || (ctx.IsSigned && issue.IsPoster(ctx.User.Id))) && 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 issue.Repo = ctx.Repo.Repository
if err = issue.ChangeStatus(ctx.User, form.Status == "close"); err != nil { if err = issue.ChangeStatus(ctx.User, form.Status == "close"); err != nil {
ctx.Handle(500, "ChangeStatus", err) ctx.Handle(500, "ChangeStatus", err)

+ 151
- 31
routers/repo/pull.go View File

package repo package repo


import ( import (
"container/list"
"path" "path"
"strings" "strings"


return pull 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 { func PrepareViewPullInfo(ctx *middleware.Context, pull *models.Issue) *git.PullRequestInfo {
repo := ctx.Repo.Repository repo := ctx.Repo.Repository


return nil 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), prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(repo.Owner.Name, repo.Name),
pull.BaseBranch, pull.HeadBarcnh) pull.BaseBranch, pull.HeadBarcnh)
if err != nil { if err != nil {
if ctx.Written() { if ctx.Written() {
return return
} }
ctx.Data["Username"] = pull.HeadUserName
ctx.Data["Reponame"] = pull.HeadRepo.Name


prInfo := PrepareViewPullInfo(ctx, pull)
if ctx.Written() {
return
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
} }
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
commits = models.ValidateCommitsWithEmails(commits)
ctx.Data["Commits"] = commits
ctx.Data["CommitCount"] = commits.Len()

ctx.HTML(200, PULL_COMMITS) ctx.HTML(200, PULL_COMMITS)
} }


return 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
}


headRepoPath := models.RepoPath(pull.HeadUserName, pull.HeadRepo.Name)
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
}


headGitRepo, err := git.OpenRepository(headRepoPath)
if err != nil {
ctx.Handle(500, "OpenRepository", err)
return
}
headRepoPath := models.RepoPath(pull.HeadUserName, pull.HeadRepo.Name)


headCommitID, err := headGitRepo.GetCommitIdOfBranch(pull.HeadBarcnh)
if err != nil {
ctx.Handle(500, "GetCommitIdOfBranch", err)
return
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
} }


diff, err := models.GetDiffRange(headRepoPath,
prInfo.MergeBase, headCommitID, setting.Git.MaxGitDiffLines)
diff, err := models.GetDiffRange(diffRepoPath,
startCommitID, endCommitID, setting.Git.MaxGitDiffLines)
if err != nil { if err != nil {
ctx.Handle(500, "GetDiffRange", err) ctx.Handle(500, "GetDiffRange", err)
return return
ctx.Data["Diff"] = diff ctx.Data["Diff"] = diff
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0


headCommit, err := headGitRepo.GetCommit(headCommitID)
commit, err := gitRepo.GetCommit(endCommitID)
if err != nil { if err != nil {
ctx.Handle(500, "GetCommit", err) ctx.Handle(500, "GetCommit", err)
return return
headTarget := path.Join(pull.HeadUserName, pull.HeadRepo.Name) headTarget := path.Join(pull.HeadUserName, pull.HeadRepo.Name)
ctx.Data["Username"] = pull.HeadUserName ctx.Data["Username"] = pull.HeadUserName
ctx.Data["Reponame"] = pull.HeadRepo.Name 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) 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) { func ParseCompareInfo(ctx *middleware.Context) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) {
// Get compare branch information. // Get compare branch information.
infos := strings.Split(ctx.Params("*"), "...") infos := strings.Split(ctx.Params("*"), "...")
return 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 err != nil {
if !models.IsErrPullRequestNotExist(err) { if !models.IsErrPullRequestNotExist(err) {
ctx.Handle(500, "HasPullRequest", err)
ctx.Handle(500, "GetUnmergedPullRequest", err)
return return
} }
} else { } else {

+ 2
- 2
templates/repo/home.tmpl View File

<ul id="repo-file-nav" class="clear menu menu-line"> <ul id="repo-file-nav" class="clear menu menu-line">
{{if and .IsRepositoryAdmin .Repository.BaseRepo}} {{if and .IsRepositoryAdmin .Repository.BaseRepo}}
{{ $baseRepo := .Repository.BaseRepo}} {{ $baseRepo := .Repository.BaseRepo}}
<!-- <li>
<li>
<a href="{{AppSubUrl}}/{{$baseRepo.Owner.Name}}/{{$baseRepo.Name}}/compare/{{$.BaseDefaultBranch}}...{{$.Owner.Name}}:{{$.BranchName}}"> <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> <button class="btn btn-green btn-small btn-radius" id="repo-compare-btn"><i class="octicon octicon-git-compare"></i></button>
</a> </a>
</li> -->
</li>
{{end}} {{end}}
<li id="repo-branch-switch" class="down drop"> <li id="repo-branch-switch" class="down drop">
<a> <a>

+ 36
- 24
templates/repo/issue/view_content.tmpl View File



{{if .Issue.IsPull}} {{if .Issue.IsPull}}
<div class="comment merge box"> <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> <span class="mega-octicon octicon-git-merge"></span>
</a> </a>
<div class="content"> <div class="content">
<div class="ui merge segment"> <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"> <div class="item text grey">
{{$.i18n.Tr "repo.pulls.reopen_to_merge"}} {{$.i18n.Tr "repo.pulls.reopen_to_merge"}}
</div> </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>
{{else if .IsPullReuqestBroken}}
<div class="item text red">
<span class="octicon octicon-x"></span>
{{$.i18n.Tr "repo.pulls.data_borken"}}
</div> </div>
{{end}}
{{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>
<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}} {{else}}
<div class="item text red"> <div class="item text red">
<span class="octicon octicon-x"></span> <span class="octicon octicon-x"></span>
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<input id="status" name="status" type="hidden"> <input id="status" name="status" type="hidden">
<div class="text right"> <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}} {{end}}
<button class="ui green button"> <button class="ui green button">
{{.i18n.Tr "repo.issues.create_comment"}} {{.i18n.Tr "repo.issues.create_comment"}}

+ 11
- 3
templates/repo/issue/view_title.tmpl View File

</div> </div>
{{end}} {{end}}
</div> </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> <div class="ui red large label"><i class="octicon octicon-issue-closed"></i> {{.i18n.Tr "repo.issues.closed_title"}}</div>
{{else}} {{else}}
<div class="ui green large label"><i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues.open_title"}}</div> <div class="ui green large label"><i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
{{end}} {{end}}


{{if .Issue.IsPull}} {{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}} {{else}}
{{ $createdStr:= TimeSince .Issue.Created $.Lang }} {{ $createdStr:= TimeSince .Issue.Created $.Lang }}
<span class="time-desc"> <span class="time-desc">

+ 4
- 4
templates/repo/pulls/tab_menu.tmpl View File

{{$.i18n.Tr "repo.pulls.tab_conversation"}} {{$.i18n.Tr "repo.pulls.tab_conversation"}}
<span class="ui label">{{.Issue.NumComments}}</span> <span class="ui label">{{.Issue.NumComments}}</span>
</a> </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> <span class="octicon octicon-git-commit"></span>
{{$.i18n.Tr "repo.pulls.tab_commits"}} {{$.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>
<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> <span class="octicon octicon-diff"></span>
{{$.i18n.Tr "repo.pulls.tab_files"}} {{$.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> </a>
</div> </div>

Loading…
Cancel
Save