basic PR feature
This commit is contained in:
parent
6ea28f2a47
commit
953bb06857
15 changed files with 387 additions and 94 deletions
|
@ -531,6 +531,7 @@ func runWeb(ctx *cli.Context) {
|
||||||
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() {
|
||||||
|
|
|
@ -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.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.
|
||||||
|
|
107
models/issue.go
107
models/issue.go
|
@ -21,6 +21,7 @@ import (
|
||||||
"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"
|
||||||
|
@ -850,20 +851,24 @@ const (
|
||||||
|
|
||||||
// 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"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
PullID int64 `xorm:"INDEX"`
|
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)"`
|
HeadBarcnh string
|
||||||
BaseBranch string `xorm:"UNIQUE(s)"`
|
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) {
|
||||||
|
@ -874,9 +879,93 @@ 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.
|
||||||
func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
|
func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
|
@ -937,8 +1026,8 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPullRequest returnss a pull request by given info.
|
// GetUnmergedPullRequest returnss a pull request hasn't been merged by given info.
|
||||||
func GetPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) {
|
func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) {
|
||||||
pr := &PullRequest{
|
pr := &PullRequest{
|
||||||
HeadRepoID: headRepoID,
|
HeadRepoID: headRepoID,
|
||||||
BaseRepoID: baseRepoID,
|
BaseRepoID: baseRepoID,
|
||||||
|
@ -946,7 +1035,7 @@ func GetPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string)
|
||||||
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 {
|
||||||
|
|
|
@ -469,20 +469,20 @@ func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -1004,6 +1004,8 @@ func DeleteRepository(uid, repoID int64) error {
|
||||||
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.
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -8,6 +8,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"container/list"
|
"container/list"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -138,7 +139,8 @@ func (repo *Repository) GetCommit(commitId string) (*Commit, error) {
|
||||||
|
|
||||||
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))
|
||||||
}
|
}
|
||||||
|
@ -152,6 +154,53 @@ func (repo *Repository) commitsCount(id sha1) (int, error) {
|
||||||
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()
|
||||||
|
@ -231,14 +280,6 @@ func (repo *Repository) commitsBefore(lock *sync.Mutex, l *list.List, parent *li
|
||||||
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)
|
||||||
|
|
|
@ -84,3 +84,9 @@ func (repo *Repository) GetPatch(basePath, baseBranch, headBranch string) ([]byt
|
||||||
|
|
||||||
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
|
||||||
|
}
|
||||||
|
|
2
public/css/gogs.min.css
vendored
2
public/css/gogs.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -114,6 +114,10 @@ pre {
|
||||||
&.green {
|
&.green {
|
||||||
color: #6cc644!important;
|
color: #6cc644!important;
|
||||||
}
|
}
|
||||||
|
&.purple {
|
||||||
|
color: #6e5494!important;
|
||||||
|
}
|
||||||
|
|
||||||
&.left {
|
&.left {
|
||||||
text-align: left!important;
|
text-align: left!important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -466,7 +466,12 @@ func ViewIssue(ctx *middleware.Context) {
|
||||||
|
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
@ -730,7 +735,8 @@ func NewComment(ctx *middleware.Context, form auth.CreateCommentForm) {
|
||||||
|
|
||||||
// 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)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -167,6 +168,26 @@ func checkPullInfo(ctx *middleware.Context) *models.Issue {
|
||||||
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
|
||||||
|
|
||||||
|
@ -185,6 +206,14 @@ func PrepareViewPullInfo(ctx *middleware.Context, pull *models.Issue) *git.PullR
|
||||||
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 {
|
||||||
|
@ -203,17 +232,46 @@ func ViewPullCommits(ctx *middleware.Context) {
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
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["Username"] = pull.HeadUserName
|
||||||
ctx.Data["Reponame"] = pull.HeadRepo.Name
|
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)
|
ctx.HTML(200, PULL_COMMITS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,27 +283,54 @@ func ViewPullFiles(ctx *middleware.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
prInfo := PrepareViewPullInfo(ctx, pull)
|
var (
|
||||||
if ctx.Written() {
|
diffRepoPath string
|
||||||
return
|
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)
|
diff, err := models.GetDiffRange(diffRepoPath,
|
||||||
|
startCommitID, endCommitID, setting.Git.MaxGitDiffLines)
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "GetDiffRange", err)
|
ctx.Handle(500, "GetDiffRange", err)
|
||||||
return
|
return
|
||||||
|
@ -253,7 +338,7 @@ func ViewPullFiles(ctx *middleware.Context) {
|
||||||
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
|
||||||
|
@ -262,14 +347,49 @@ func ViewPullFiles(ctx *middleware.Context) {
|
||||||
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["IsImageFile"] = commit.IsImageFile
|
||||||
ctx.Data["SourcePath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "src", headCommitID)
|
ctx.Data["SourcePath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "src", endCommitID)
|
||||||
ctx.Data["BeforeSourcePath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "src", prInfo.MergeBase)
|
ctx.Data["BeforeSourcePath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "src", startCommitID)
|
||||||
ctx.Data["RawPath"] = setting.AppSubUrl + "/" + path.Join(headTarget, "raw", headCommitID)
|
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("*"), "...")
|
||||||
|
@ -416,10 +536,10 @@ func CompareAndPullRequest(ctx *middleware.Context) {
|
||||||
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 {
|
||||||
|
|
|
@ -11,11 +11,11 @@
|
||||||
<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>
|
||||||
|
|
|
@ -133,28 +133,40 @@
|
||||||
|
|
||||||
{{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 .IsPullReuqestBroken}}
|
||||||
|
<div class="item text red">
|
||||||
|
<span class="octicon octicon-x"></span>
|
||||||
|
{{$.i18n.Tr "repo.pulls.data_borken"}}
|
||||||
|
</div>
|
||||||
{{else if .Issue.CanAutoMerge}}
|
{{else if .Issue.CanAutoMerge}}
|
||||||
<div class="item text green">
|
<div class="item text green">
|
||||||
<span class="octicon octicon-check"></span>
|
<span class="octicon octicon-check"></span>
|
||||||
{{$.i18n.Tr "repo.pulls.can_auto_merge_desc"}}
|
{{$.i18n.Tr "repo.pulls.can_auto_merge_desc"}}
|
||||||
</div>
|
</div>
|
||||||
{{if .IsRepositoryAdmin}}
|
{{if .IsRepositoryAdmin}}
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div>
|
<div>
|
||||||
<button class="ui green button">
|
<form class="ui form" action="{{.Link}}/merge" method="post">
|
||||||
<span class="octicon octicon-git-merge"></span> {{$.i18n.Tr "repo.pulls.merge_pull_request"}}
|
{{.CsrfTokenHtml}}
|
||||||
</button>
|
<button class="ui green button">
|
||||||
</div>
|
<span class="octicon octicon-git-merge"></span> {{$.i18n.Tr "repo.pulls.merge_pull_request"}}
|
||||||
{{end}}
|
</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>
|
||||||
|
@ -181,16 +193,16 @@
|
||||||
{{.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 and .IsIssueOwner (not .DisableStatusChange)}}
|
||||||
{{if .Issue.IsClosed}}
|
{{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">
|
<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"}}
|
{{.i18n.Tr "repo.issues.reopen_issue"}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{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">
|
<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"}}
|
{{.i18n.Tr "repo.issues.close_issue"}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{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"}}
|
||||||
|
|
|
@ -16,15 +16,23 @@
|
||||||
</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>
|
{{if .Issue.HasMerged}}
|
||||||
<span class="pull-desc">{{$.i18n.Tr "repo.pulls.title_desc" .NumCommits .HeadTarget .BaseTarget | Str2html}}</span>
|
{{ $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,14 +4,14 @@
|
||||||
{{$.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>
|
Reference in a new issue