diff --git a/models/pull.go b/models/pull.go index fe18765fc0..5ac1126314 100644 --- a/models/pull.go +++ b/models/pull.go @@ -1144,8 +1144,7 @@ func (pr *PullRequest) UpdatePatch() (err error) { defer func() { headGitRepo.RemoveRemote(tmpRemote) }() - remoteBranch := "remotes/" + tmpRemote + "/" + pr.BaseBranch - pr.MergeBase, err = headGitRepo.GetMergeBase(remoteBranch, pr.HeadBranch) + pr.MergeBase, err = headGitRepo.GetMergeBase(tmpRemote, pr.BaseBranch, pr.HeadBranch) if err != nil { return fmt.Errorf("GetMergeBase: %v", err) } else if err = pr.Update(); err != nil { diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index b631f9341e..501ea88e40 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -27,6 +27,16 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) { return ref.Hash().String(), nil } +// IsCommitExist returns true if given commit exists in current repository. +func (repo *Repository) IsCommitExist(name string) bool { + hash := plumbing.NewHash(name) + _, err := repo.gogitRepo.CommitObject(hash) + if err != nil { + return false + } + return true +} + // GetBranchCommitID returns last commit ID string of given branch. func (repo *Repository) GetBranchCommitID(name string) (string, error) { return repo.GetRefCommitID(BranchPrefix + name) diff --git a/modules/git/repo_pull.go b/modules/git/repo_compare.go similarity index 55% rename from modules/git/repo_pull.go rename to modules/git/repo_compare.go index 65c5414551..e7a1d72a85 100644 --- a/modules/git/repo_pull.go +++ b/modules/git/repo_compare.go @@ -1,4 +1,5 @@ // Copyright 2015 The Gogs Authors. All rights reserved. +// Copyright 2019 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -14,55 +15,66 @@ import ( "time" ) -// PullRequestInfo represents needed information for a pull request. -type PullRequestInfo struct { +// CompareInfo represents needed information for comparing references. +type CompareInfo struct { MergeBase string Commits *list.List NumFiles int } // GetMergeBase checks and returns merge base of two branches. -func (repo *Repository) GetMergeBase(base, head string) (string, error) { +func (repo *Repository) GetMergeBase(tmpRemote string, base, head string) (string, error) { + if tmpRemote == "" { + tmpRemote = "origin" + } + + if tmpRemote != "origin" { + tmpBaseName := "refs/remotes/" + tmpRemote + "/tmp_" + base + // Fetch commit into a temporary branch in order to be able to handle commits and tags + _, err := NewCommand("fetch", tmpRemote, base+":"+tmpBaseName).RunInDir(repo.Path) + if err == nil { + base = tmpBaseName + } + } + stdout, err := NewCommand("merge-base", base, head).RunInDir(repo.Path) return strings.TrimSpace(stdout), err } -// GetPullRequestInfo generates and returns pull request information -// between base and head branches of repositories. -func (repo *Repository) GetPullRequestInfo(basePath, baseBranch, headBranch string) (_ *PullRequestInfo, err error) { - var remoteBranch string +// GetCompareInfo generates and returns compare information between base and head branches of repositories. +func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string) (_ *CompareInfo, err error) { + var ( + remoteBranch string + tmpRemote string + ) // We don't need a temporary remote for same repository. if repo.Path != basePath { // Add a temporary remote - tmpRemote := strconv.FormatInt(time.Now().UnixNano(), 10) + tmpRemote = strconv.FormatInt(time.Now().UnixNano(), 10) if err = repo.AddRemote(tmpRemote, basePath, true); err != nil { return nil, fmt.Errorf("AddRemote: %v", err) } defer repo.RemoveRemote(tmpRemote) - - remoteBranch = "remotes/" + tmpRemote + "/" + baseBranch - } else { - remoteBranch = baseBranch } - prInfo := new(PullRequestInfo) - prInfo.MergeBase, err = repo.GetMergeBase(remoteBranch, headBranch) + compareInfo := new(CompareInfo) + compareInfo.MergeBase, err = repo.GetMergeBase(tmpRemote, baseBranch, headBranch) if err == nil { // We have a common base - logs, err := NewCommand("log", prInfo.MergeBase+"..."+headBranch, prettyLogFormat).RunInDirBytes(repo.Path) + logs, err := NewCommand("log", compareInfo.MergeBase+"..."+headBranch, prettyLogFormat).RunInDirBytes(repo.Path) if err != nil { return nil, err } - prInfo.Commits, err = repo.parsePrettyFormatLogToList(logs) + compareInfo.Commits, err = repo.parsePrettyFormatLogToList(logs) if err != nil { return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err) } } else { - prInfo.Commits = list.New() - prInfo.MergeBase, err = GetFullCommitID(repo.Path, remoteBranch) + compareInfo.Commits = list.New() + compareInfo.MergeBase, err = GetFullCommitID(repo.Path, remoteBranch) if err != nil { - prInfo.MergeBase = remoteBranch + compareInfo.MergeBase = remoteBranch } } @@ -71,9 +83,9 @@ func (repo *Repository) GetPullRequestInfo(basePath, baseBranch, headBranch stri if err != nil { return nil, err } - prInfo.NumFiles = len(strings.Split(stdout, "\n")) - 1 + compareInfo.NumFiles = len(strings.Split(stdout, "\n")) - 1 - return prInfo, nil + return compareInfo, nil } // GetPatch generates and returns patch data between given revisions. diff --git a/modules/git/repo_pull_test.go b/modules/git/repo_compare_test.go similarity index 100% rename from modules/git/repo_pull_test.go rename to modules/git/repo_compare_test.go diff --git a/public/css/index.css b/public/css/index.css index 8950cc7038..d192f43d15 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -591,6 +591,7 @@ footer .ui.left,footer .ui.right{line-height:40px} .repository .milestone.list>.item .content{padding-top:10px} .repository.new.milestone textarea{height:200px} .repository.new.milestone #deadline{width:150px} +.repository.compare.pull .show-form-container{text-align:left} .repository.compare.pull .choose.branch .octicon{padding-right:10px} .repository.compare.pull .comment.form .content:after,.repository.compare.pull .comment.form .content:before{right:100%;top:20px;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none} .repository.compare.pull .comment.form .content:before{border-right-color:#d3d3d4;border-width:9px;margin-top:-9px} diff --git a/public/js/index.js b/public/js/index.js index ed3198594a..28023e1061 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -959,8 +959,15 @@ function initRepository() { }); // Pull request - if ($('.repository.compare.pull').length > 0) { + var $repoComparePull = $('.repository.compare.pull'); + if ($repoComparePull.length > 0) { initFilterSearchDropdown('.choose.branch .dropdown'); + // show pull request form + $repoComparePull.find('button.show-form').on('click', function(e) { + e.preventDefault(); + $repoComparePull.find('.pullrequest-form').show(); + $(this).parent().hide(); + }); } // Branches diff --git a/public/less/_repository.less b/public/less/_repository.less index 9956bbce74..fcc153e31e 100644 --- a/public/less/_repository.less +++ b/public/less/_repository.less @@ -1109,6 +1109,9 @@ } &.compare.pull { + .show-form-container { + text-align: left; + } .choose.branch { .octicon { padding-right: 10px; diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index f53ab4b8f3..0e1db144b1 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -188,7 +188,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption ) // Get repo/branch information - headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := parseCompareInfo(ctx, form) + headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form) if ctx.Written() { return } @@ -240,7 +240,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption milestoneID = milestone.ID } - patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch) + patch, err := headGitRepo.GetPatch(compareInfo.MergeBase, headBranch) if err != nil { ctx.Error(500, "GetPatch", err) return @@ -277,7 +277,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption BaseBranch: baseBranch, HeadRepo: headRepo, BaseRepo: repo, - MergeBase: prInfo.MergeBase, + MergeBase: compareInfo.MergeBase, Type: models.PullRequestGitea, } @@ -600,7 +600,7 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) { ctx.Status(200) } -func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) { +func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*models.User, *models.Repository, *git.Repository, *git.CompareInfo, string, string) { baseRepo := ctx.Repo.Repository // Get compared branches information @@ -712,11 +712,11 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) return nil, nil, nil, nil, "", "" } - prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch) + compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch) if err != nil { - ctx.Error(500, "GetPullRequestInfo", err) + ctx.Error(500, "GetCompareInfo", err) return nil, nil, nil, nil, "", "" } - return headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch + return headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch } diff --git a/routers/repo/commit.go b/routers/repo/commit.go index 870ff568f3..dde6d8f321 100644 --- a/routers/repo/commit.go +++ b/routers/repo/commit.go @@ -19,9 +19,9 @@ import ( ) const ( - tplCommits base.TplName = "repo/commits" - tplGraph base.TplName = "repo/graph" - tplDiff base.TplName = "repo/diff/page" + tplCommits base.TplName = "repo/commits" + tplGraph base.TplName = "repo/graph" + tplCommitPage base.TplName = "repo/commit_page" ) // RefCommits render commits page @@ -261,7 +261,7 @@ func Diff(ctx *context.Context) { } ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", "commit", commitID) ctx.Data["BranchName"], err = commit.GetBranchName() - ctx.HTML(200, tplDiff) + ctx.HTML(200, tplCommitPage) } // RawDiff dumps diff results of repository in given commit ID to io.Writer @@ -276,54 +276,3 @@ func RawDiff(ctx *context.Context) { return } } - -// CompareDiff show different from one commit to another commit -func CompareDiff(ctx *context.Context) { - ctx.Data["IsRepoToolbarCommits"] = true - ctx.Data["IsDiffCompare"] = true - userName := ctx.Repo.Owner.Name - repoName := ctx.Repo.Repository.Name - beforeCommitID := ctx.Params(":before") - afterCommitID := ctx.Params(":after") - - commit, err := ctx.Repo.GitRepo.GetCommit(afterCommitID) - if err != nil { - ctx.NotFound("GetCommit", err) - return - } - - diff, err := models.GetDiffRange(models.RepoPath(userName, repoName), beforeCommitID, - afterCommitID, setting.Git.MaxGitDiffLines, - setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles) - if err != nil { - ctx.NotFound("GetDiffRange", err) - return - } - - commits, err := commit.CommitsBeforeUntil(beforeCommitID) - if err != nil { - ctx.ServerError("CommitsBeforeUntil", err) - return - } - commits = models.ValidateCommitsWithEmails(commits) - commits = models.ParseCommitsWithSignature(commits) - commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository) - - ctx.Data["CommitRepoLink"] = ctx.Repo.RepoLink - ctx.Data["Commits"] = commits - ctx.Data["CommitCount"] = commits.Len() - ctx.Data["BeforeCommitID"] = beforeCommitID - ctx.Data["AfterCommitID"] = afterCommitID - ctx.Data["Username"] = userName - ctx.Data["Reponame"] = repoName - ctx.Data["IsImageFile"] = commit.IsImageFile - ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + "..." + base.ShortSha(afterCommitID) + " ยท " + userName + "/" + repoName - ctx.Data["Commit"] = commit - ctx.Data["Diff"] = diff - ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 - ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", afterCommitID) - ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", beforeCommitID) - ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", "commit", afterCommitID) - ctx.Data["RequireHighlightJS"] = true - ctx.HTML(200, tplDiff) -} diff --git a/routers/repo/compare.go b/routers/repo/compare.go new file mode 100644 index 0000000000..a85084791d --- /dev/null +++ b/routers/repo/compare.go @@ -0,0 +1,337 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "path" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" +) + +const ( + tplCompare base.TplName = "repo/diff/compare" +) + +// ParseCompareInfo parse compare info between two commit for preparing comparing references +func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *git.Repository, *git.CompareInfo, string, string) { + baseRepo := ctx.Repo.Repository + + // Get compared branches information + // format: ...[:] + // base<-head: master...head:feature + // same repo: master...feature + + var ( + headUser *models.User + headBranch string + isSameRepo bool + infoPath string + err error + ) + infoPath = ctx.Params("*") + infos := strings.Split(infoPath, "...") + if len(infos) != 2 { + log.Trace("ParseCompareInfo[%d]: not enough compared branches information %s", baseRepo.ID, infos) + ctx.NotFound("CompareAndPullRequest", nil) + return nil, nil, nil, nil, "", "" + } + + baseBranch := infos[0] + ctx.Data["BaseBranch"] = baseBranch + + // If there is no head repository, it means compare between same repository. + headInfos := strings.Split(infos[1], ":") + if len(headInfos) == 1 { + isSameRepo = true + headUser = ctx.Repo.Owner + headBranch = headInfos[0] + + } else if len(headInfos) == 2 { + headUser, err = models.GetUserByName(headInfos[0]) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.NotFound("GetUserByName", nil) + } else { + ctx.ServerError("GetUserByName", err) + } + return nil, nil, nil, nil, "", "" + } + headBranch = headInfos[1] + isSameRepo = headUser.ID == ctx.Repo.Owner.ID + } else { + ctx.NotFound("CompareAndPullRequest", nil) + return nil, nil, nil, nil, "", "" + } + ctx.Data["HeadUser"] = headUser + ctx.Data["HeadBranch"] = headBranch + ctx.Repo.PullRequest.SameRepo = isSameRepo + + // Check if base branch is valid. + baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(baseBranch) + baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(baseBranch) + baseIsTag := ctx.Repo.GitRepo.IsTagExist(baseBranch) + if !baseIsCommit && !baseIsBranch && !baseIsTag { + // Check if baseBranch is short sha commit hash + if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(baseBranch); baseCommit != nil { + baseBranch = baseCommit.ID.String() + ctx.Data["BaseBranch"] = baseBranch + baseIsCommit = true + } else { + ctx.NotFound("IsRefExist", nil) + return nil, nil, nil, nil, "", "" + } + } + ctx.Data["BaseIsCommit"] = baseIsCommit + ctx.Data["BaseIsBranch"] = baseIsBranch + ctx.Data["BaseIsTag"] = baseIsTag + + // Check if current user has fork of repository or in the same repository. + headRepo, has := models.HasForkedRepo(headUser.ID, baseRepo.ID) + if !has && !isSameRepo { + ctx.Data["PageIsComparePull"] = false + } + + var headGitRepo *git.Repository + if isSameRepo { + headRepo = ctx.Repo.Repository + headGitRepo = ctx.Repo.GitRepo + ctx.Data["BaseName"] = headUser.Name + } else { + headGitRepo, err = git.OpenRepository(models.RepoPath(headUser.Name, headRepo.Name)) + ctx.Data["BaseName"] = baseRepo.OwnerName + if err != nil { + ctx.ServerError("OpenRepository", err) + return nil, nil, nil, nil, "", "" + } + } + + // user should have permission to read baseRepo's codes and pulls, NOT headRepo's + permBase, err := models.GetUserRepoPermission(baseRepo, ctx.User) + if err != nil { + ctx.ServerError("GetUserRepoPermission", err) + return nil, nil, nil, nil, "", "" + } + if !permBase.CanRead(models.UnitTypeCode) { + if log.IsTrace() { + log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in baseRepo has Permissions: %-+v", + ctx.User, + baseRepo, + permBase) + } + ctx.NotFound("ParseCompareInfo", nil) + return nil, nil, nil, nil, "", "" + } + + // user should have permission to read headrepo's codes + permHead, err := models.GetUserRepoPermission(headRepo, ctx.User) + if err != nil { + ctx.ServerError("GetUserRepoPermission", err) + return nil, nil, nil, nil, "", "" + } + if !permHead.CanRead(models.UnitTypeCode) { + if log.IsTrace() { + log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v", + ctx.User, + headRepo, + permHead) + } + ctx.NotFound("ParseCompareInfo", nil) + return nil, nil, nil, nil, "", "" + } + + // Check if head branch is valid. + headIsCommit := ctx.Repo.GitRepo.IsCommitExist(headBranch) + headIsBranch := headGitRepo.IsBranchExist(headBranch) + headIsTag := headGitRepo.IsTagExist(headBranch) + if !headIsCommit && !headIsBranch && !headIsTag { + // Check if headBranch is short sha commit hash + if headCommit, _ := ctx.Repo.GitRepo.GetCommit(headBranch); headCommit != nil { + headBranch = headCommit.ID.String() + ctx.Data["HeadBranch"] = headBranch + headIsCommit = true + } else { + ctx.NotFound("IsRefExist", nil) + return nil, nil, nil, nil, "", "" + } + } + ctx.Data["HeadIsCommit"] = headIsCommit + ctx.Data["HeadIsBranch"] = headIsBranch + ctx.Data["HeadIsTag"] = headIsTag + + // Treat as pull request if both references are branches + if ctx.Data["PageIsComparePull"] == nil { + ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch + } + + if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) { + if log.IsTrace() { + log.Trace("Permission Denied: User: %-v cannot create/read pull requests in Repo: %-v\nUser in baseRepo has Permissions: %-+v", + ctx.User, + baseRepo, + permBase) + } + ctx.NotFound("ParseCompareInfo", nil) + return nil, nil, nil, nil, "", "" + } + + compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch) + if err != nil { + ctx.ServerError("GetCompareInfo", err) + return nil, nil, nil, nil, "", "" + } + ctx.Data["BeforeCommitID"] = compareInfo.MergeBase + + return headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch +} + +// PrepareCompareDiff renders compare diff page +func PrepareCompareDiff( + ctx *context.Context, + headUser *models.User, + headRepo *models.Repository, + headGitRepo *git.Repository, + compareInfo *git.CompareInfo, + baseBranch, headBranch string) bool { + + var ( + repo = ctx.Repo.Repository + err error + title string + ) + + // Get diff information. + ctx.Data["CommitRepoLink"] = headRepo.Link() + + headCommitID := headBranch + if ctx.Data["HeadIsCommit"] == false { + if ctx.Data["HeadIsTag"] == true { + headCommitID, err = headGitRepo.GetTagCommitID(headBranch) + } else { + headCommitID, err = headGitRepo.GetBranchCommitID(headBranch) + } + if err != nil { + ctx.ServerError("GetRefCommitID", err) + return false + } + } + + ctx.Data["AfterCommitID"] = headCommitID + + if headCommitID == compareInfo.MergeBase { + ctx.Data["IsNothingToCompare"] = true + return true + } + + diff, err := models.GetDiffRange(models.RepoPath(headUser.Name, headRepo.Name), + compareInfo.MergeBase, headCommitID, setting.Git.MaxGitDiffLines, + setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles) + if err != nil { + ctx.ServerError("GetDiffRange", err) + return false + } + ctx.Data["Diff"] = diff + ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 + + headCommit, err := headGitRepo.GetCommit(headCommitID) + if err != nil { + ctx.ServerError("GetCommit", err) + return false + } + + compareInfo.Commits = models.ValidateCommitsWithEmails(compareInfo.Commits) + compareInfo.Commits = models.ParseCommitsWithSignature(compareInfo.Commits) + compareInfo.Commits = models.ParseCommitsWithStatus(compareInfo.Commits, headRepo) + ctx.Data["Commits"] = compareInfo.Commits + ctx.Data["CommitCount"] = compareInfo.Commits.Len() + if ctx.Data["CommitCount"] == 0 { + ctx.Data["PageIsComparePull"] = false + } + + if compareInfo.Commits.Len() == 1 { + c := compareInfo.Commits.Front().Value.(models.SignCommitWithStatuses) + title = strings.TrimSpace(c.UserCommit.Summary()) + + body := strings.Split(strings.TrimSpace(c.UserCommit.Message()), "\n") + if len(body) > 1 { + ctx.Data["content"] = strings.Join(body[1:], "\n") + } + } else { + title = headBranch + } + + ctx.Data["title"] = title + ctx.Data["Username"] = headUser.Name + ctx.Data["Reponame"] = headRepo.Name + ctx.Data["IsImageFile"] = headCommit.IsImageFile + + headTarget := path.Join(headUser.Name, repo.Name) + ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", headCommitID) + ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", compareInfo.MergeBase) + ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(headTarget, "raw", "commit", headCommitID) + return false +} + +// CompareDiff show different from one commit to another commit +func CompareDiff(ctx *context.Context) { + headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := ParseCompareInfo(ctx) + if ctx.Written() { + return + } + + nothingToCompare := PrepareCompareDiff(ctx, headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch) + if ctx.Written() { + return + } + + if ctx.Data["PageIsComparePull"] == true { + pr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch) + if err != nil { + if !models.IsErrPullRequestNotExist(err) { + ctx.ServerError("GetUnmergedPullRequest", err) + return + } + } else { + ctx.Data["HasPullRequest"] = true + ctx.Data["PullRequest"] = pr + ctx.HTML(200, tplCompareDiff) + return + } + + if !nothingToCompare { + // Setup information for new form. + RetrieveRepoMetas(ctx, ctx.Repo.Repository) + if ctx.Written() { + return + } + } + + headBranches, err := headGitRepo.GetBranches() + if err != nil { + ctx.ServerError("GetBranches", err) + return + } + ctx.Data["HeadBranches"] = headBranches + } + beforeCommitID := ctx.Data["BeforeCommitID"].(string) + afterCommitID := ctx.Data["AfterCommitID"].(string) + + ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + "..." + base.ShortSha(afterCommitID) + + ctx.Data["IsRepoToolbarCommits"] = true + ctx.Data["IsDiffCompare"] = true + ctx.Data["RequireHighlightJS"] = true + ctx.Data["RequireTribute"] = true + ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes + setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates) + renderAttachmentSettings(ctx) + + ctx.HTML(200, tplCompare) +} diff --git a/routers/repo/pull.go b/routers/repo/pull.go index 412750dd55..182f715545 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -28,7 +28,7 @@ import ( const ( tplFork base.TplName = "repo/pulls/fork" - tplComparePull base.TplName = "repo/pulls/compare" + tplCompareDiff base.TplName = "repo/diff/compare" tplPullCommits base.TplName = "repo/pulls/commits" tplPullFiles base.TplName = "repo/pulls/files" @@ -280,13 +280,13 @@ func setMergeTarget(ctx *context.Context, pull *models.PullRequest) { } // PrepareMergedViewPullInfo show meta information for a merged pull request view page -func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullRequestInfo { +func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.CompareInfo { pull := issue.PullRequest setMergeTarget(ctx, pull) ctx.Data["HasMerged"] = true - prInfo, err := ctx.Repo.GitRepo.GetPullRequestInfo(ctx.Repo.Repository.RepoPath(), + prInfo, err := ctx.Repo.GitRepo.GetCompareInfo(ctx.Repo.Repository.RepoPath(), pull.MergeBase, pull.GetGitRefName()) if err != nil { @@ -298,7 +298,7 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.P return nil } - ctx.ServerError("GetPullRequestInfo", err) + ctx.ServerError("GetCompareInfo", err) return nil } ctx.Data["NumCommits"] = prInfo.Commits.Len() @@ -307,7 +307,7 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.P } // PrepareViewPullInfo show meta information for a pull request preview page -func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullRequestInfo { +func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.CompareInfo { repo := ctx.Repo.Repository pull := issue.PullRequest @@ -336,7 +336,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullReq return nil } - prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(repo.Owner.Name, repo.Name), + prInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(repo.Owner.Name, repo.Name), pull.BaseBranch, pull.HeadBranch) if err != nil { if strings.Contains(err.Error(), "fatal: Not a valid object name") { @@ -347,7 +347,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullReq return nil } - ctx.ServerError("GetPullRequestInfo", err) + ctx.ServerError("GetCompareInfo", err) return nil } @@ -628,266 +628,6 @@ func stopTimerIfAvailable(user *models.User, issue *models.Issue) error { return nil } -// ParseCompareInfo parse compare info between two commit for preparing pull request -func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) { - baseRepo := ctx.Repo.Repository - - // Get compared branches information - // format: ...[:] - // base<-head: master...head:feature - // same repo: master...feature - - var ( - headUser *models.User - headBranch string - isSameRepo bool - infoPath string - err error - ) - infoPath = ctx.Params("*") - infos := strings.Split(infoPath, "...") - if len(infos) != 2 { - log.Trace("ParseCompareInfo[%d]: not enough compared branches information %s", baseRepo.ID, infos) - ctx.NotFound("CompareAndPullRequest", nil) - return nil, nil, nil, nil, "", "" - } - - baseBranch := infos[0] - ctx.Data["BaseBranch"] = baseBranch - - // If there is no head repository, it means pull request between same repository. - headInfos := strings.Split(infos[1], ":") - if len(headInfos) == 1 { - isSameRepo = true - headUser = ctx.Repo.Owner - headBranch = headInfos[0] - - } else if len(headInfos) == 2 { - headUser, err = models.GetUserByName(headInfos[0]) - if err != nil { - if models.IsErrUserNotExist(err) { - ctx.NotFound("GetUserByName", nil) - } else { - ctx.ServerError("GetUserByName", err) - } - return nil, nil, nil, nil, "", "" - } - headBranch = headInfos[1] - isSameRepo = headUser.ID == ctx.Repo.Owner.ID - } else { - ctx.NotFound("CompareAndPullRequest", nil) - return nil, nil, nil, nil, "", "" - } - ctx.Data["HeadUser"] = headUser - ctx.Data["HeadBranch"] = headBranch - ctx.Repo.PullRequest.SameRepo = isSameRepo - - // Check if base branch is valid. - if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) { - ctx.NotFound("IsBranchExist", nil) - return nil, nil, nil, nil, "", "" - } - - // Check if current user has fork of repository or in the same repository. - headRepo, has := models.HasForkedRepo(headUser.ID, baseRepo.ID) - if !has && !isSameRepo { - log.Trace("ParseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID) - ctx.NotFound("ParseCompareInfo", nil) - return nil, nil, nil, nil, "", "" - } - - var headGitRepo *git.Repository - if isSameRepo { - headRepo = ctx.Repo.Repository - headGitRepo = ctx.Repo.GitRepo - ctx.Data["BaseName"] = headUser.Name - } else { - headGitRepo, err = git.OpenRepository(models.RepoPath(headUser.Name, headRepo.Name)) - ctx.Data["BaseName"] = baseRepo.OwnerName - if err != nil { - ctx.ServerError("OpenRepository", err) - return nil, nil, nil, nil, "", "" - } - } - - // user should have permission to read baseRepo's codes and pulls, NOT headRepo's - permBase, err := models.GetUserRepoPermission(baseRepo, ctx.User) - if err != nil { - ctx.ServerError("GetUserRepoPermission", err) - return nil, nil, nil, nil, "", "" - } - if !permBase.CanReadIssuesOrPulls(true) || !permBase.CanRead(models.UnitTypeCode) { - if log.IsTrace() { - log.Trace("Permission Denied: User: %-v cannot create/read pull requests or cannot read code in Repo: %-v\nUser in baseRepo has Permissions: %-+v", - ctx.User, - baseRepo, - permBase) - } - ctx.NotFound("ParseCompareInfo", nil) - return nil, nil, nil, nil, "", "" - } - - // user should have permission to read headrepo's codes - permHead, err := models.GetUserRepoPermission(headRepo, ctx.User) - if err != nil { - ctx.ServerError("GetUserRepoPermission", err) - return nil, nil, nil, nil, "", "" - } - if !permHead.CanRead(models.UnitTypeCode) { - if log.IsTrace() { - log.Trace("Permission Denied: User: %-v cannot read code requests in Repo: %-v\nUser in headRepo has Permissions: %-+v", - ctx.User, - headRepo, - permHead) - } - ctx.NotFound("ParseCompareInfo", nil) - return nil, nil, nil, nil, "", "" - } - - // Check if head branch is valid. - if !headGitRepo.IsBranchExist(headBranch) { - ctx.NotFound("IsBranchExist", nil) - return nil, nil, nil, nil, "", "" - } - - headBranches, err := headGitRepo.GetBranches() - if err != nil { - ctx.ServerError("GetBranches", err) - return nil, nil, nil, nil, "", "" - } - ctx.Data["HeadBranches"] = headBranches - - prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch) - if err != nil { - ctx.ServerError("GetPullRequestInfo", err) - return nil, nil, nil, nil, "", "" - } - ctx.Data["BeforeCommitID"] = prInfo.MergeBase - - return headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch -} - -// PrepareCompareDiff render pull request preview diff page -func PrepareCompareDiff( - ctx *context.Context, - headUser *models.User, - headRepo *models.Repository, - headGitRepo *git.Repository, - prInfo *git.PullRequestInfo, - baseBranch, headBranch string) bool { - - var ( - repo = ctx.Repo.Repository - err error - title string - ) - - // Get diff information. - ctx.Data["CommitRepoLink"] = headRepo.Link() - - headCommitID, err := headGitRepo.GetBranchCommitID(headBranch) - if err != nil { - ctx.ServerError("GetBranchCommitID", err) - return false - } - ctx.Data["AfterCommitID"] = headCommitID - - if headCommitID == prInfo.MergeBase { - ctx.Data["IsNothingToCompare"] = true - return true - } - - diff, err := models.GetDiffRange(models.RepoPath(headUser.Name, headRepo.Name), - prInfo.MergeBase, headCommitID, setting.Git.MaxGitDiffLines, - setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles) - if err != nil { - ctx.ServerError("GetDiffRange", err) - return false - } - ctx.Data["Diff"] = diff - ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 - - headCommit, err := headGitRepo.GetCommit(headCommitID) - if err != nil { - ctx.ServerError("GetCommit", err) - return false - } - - prInfo.Commits = models.ValidateCommitsWithEmails(prInfo.Commits) - prInfo.Commits = models.ParseCommitsWithSignature(prInfo.Commits) - prInfo.Commits = models.ParseCommitsWithStatus(prInfo.Commits, headRepo) - ctx.Data["Commits"] = prInfo.Commits - ctx.Data["CommitCount"] = prInfo.Commits.Len() - - if prInfo.Commits.Len() == 1 { - c := prInfo.Commits.Front().Value.(models.SignCommitWithStatuses) - title = strings.TrimSpace(c.UserCommit.Summary()) - - body := strings.Split(strings.TrimSpace(c.UserCommit.Message()), "\n") - if len(body) > 1 { - ctx.Data["content"] = strings.Join(body[1:], "\n") - } - } else { - title = headBranch - } - - ctx.Data["title"] = title - ctx.Data["Username"] = headUser.Name - ctx.Data["Reponame"] = headRepo.Name - ctx.Data["IsImageFile"] = headCommit.IsImageFile - - headTarget := path.Join(headUser.Name, repo.Name) - ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", headCommitID) - ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", prInfo.MergeBase) - ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(headTarget, "raw", "commit", headCommitID) - return false -} - -// CompareAndPullRequest render pull request preview page -func CompareAndPullRequest(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes") - ctx.Data["PageIsComparePull"] = true - ctx.Data["IsDiffCompare"] = true - ctx.Data["RequireHighlightJS"] = true - ctx.Data["RequireTribute"] = true - ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes - setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates) - renderAttachmentSettings(ctx) - - headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(ctx) - if ctx.Written() { - return - } - - pr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch) - if err != nil { - if !models.IsErrPullRequestNotExist(err) { - ctx.ServerError("GetUnmergedPullRequest", err) - return - } - } else { - ctx.Data["HasPullRequest"] = true - ctx.Data["PullRequest"] = pr - ctx.HTML(200, tplComparePull) - return - } - - nothingToCompare := PrepareCompareDiff(ctx, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch) - if ctx.Written() { - return - } - - if !nothingToCompare { - // Setup information for new form. - RetrieveRepoMetas(ctx, ctx.Repo.Repository) - if ctx.Written() { - return - } - } - - ctx.HTML(200, tplComparePull) -} - // CompareAndPullRequestPost response for creating pull request func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) { ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes") @@ -926,7 +666,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) return } - ctx.HTML(200, tplComparePull) + ctx.HTML(200, tplCompareDiff) return } @@ -936,7 +676,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) return } - ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplComparePull, form) + ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplCompareDiff, form) return } diff --git a/routers/routes/routes.go b/routers/routes/routes.go index eb5f73768e..4c736f63ed 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -732,9 +732,9 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/milestone", func() { m.Get("/:id", repo.MilestoneIssuesAndPulls) }, reqRepoIssuesOrPullsReader, context.RepoRef()) - m.Combo("/compare/*", context.RepoMustNotBeArchived(), reqRepoCodeReader, reqRepoPullsReader, repo.MustAllowPulls, repo.SetEditorconfigIfExists). - Get(repo.SetDiffViewStyle, repo.CompareAndPullRequest). - Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) + m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists). + Get(repo.SetDiffViewStyle, repo.CompareDiff). + Post(context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) m.Group("", func() { m.Group("", func() { @@ -906,9 +906,6 @@ func RegisterRoutes(m *macaron.Macaron) { }, context.RepoRef(), reqRepoCodeReader) m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff) - - m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists, - repo.SetDiffViewStyle, repo.MustBeNotEmpty, reqRepoCodeReader, repo.CompareDiff) }, ignSignIn, context.RepoAssignment(), context.UnitTypes()) m.Group("/:username/:reponame", func() { m.Get("/stars", repo.Stars) diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl new file mode 100644 index 0000000000..0cfdf5156d --- /dev/null +++ b/templates/repo/commit_page.tmpl @@ -0,0 +1,89 @@ +{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+
+ + {{.i18n.Tr "repo.diff.browse_source"}} + +

{{RenderCommitMessage .Commit.Message $.RepoLink $.Repository.ComposeMetas}}{{template "repo/commit_status" .CommitStatus}}

+ {{if IsMultilineCommitMessage .Commit.Message}} +
{{RenderCommitBody .Commit.Message $.RepoLink $.Repository.ComposeMetas}}
+ {{end}} + {{.BranchName}} +
+
+
+
+ {{if .Author}} + + {{if .Author.FullName}} + {{.Author.FullName}} {{if .IsSigned}}<{{.Commit.Author.Email}}>{{end}} + {{else}} + {{.Commit.Author.Name}} {{if .IsSigned}}<{{.Commit.Author.Email}}>{{end}} + {{end}} + {{else}} + + {{.Commit.Author.Name}} + {{end}} + {{TimeSince .Commit.Author.When $.Lang}} +
+
+
+ {{if .Parents}} +
+ {{.i18n.Tr "repo.diff.parent"}} +
+
+ {{range .Parents}} + {{ShortSha .}} + {{end}} +
+ {{end}} +
+
{{.i18n.Tr "repo.diff.commit"}}
+
{{ShortSha .CommitID}}
+
+
+
+
+ {{if .Commit.Signature}} + {{if .Verification.Verified }} +
+ + {{.i18n.Tr "repo.commits.signed_by"}}: + {{.Commit.Committer.Name}} <{{.Commit.Committer.Email}}> + {{.i18n.Tr "repo.commits.gpg_key_id"}}: {{.Verification.SigningKey.KeyID}} +
+ {{else}} +
+ + {{.i18n.Tr .Verification.Reason}} +
+ {{end}} + {{end}} + {{if .Note}} +
+ + {{.i18n.Tr "repo.diff.git-notes"}}: + {{if .NoteAuthor}} + + {{if .NoteAuthor.FullName}} + {{.NoteAuthor.FullName}} + {{else}} + {{.NoteCommit.Author.Name}} + {{end}} + + {{else}} + {{.NoteCommit.Author.Name}} + {{end}} + {{TimeSince .NoteCommit.Author.When $.Lang}} +
+
+
{{RenderNote .Note $.RepoLink $.Repository.ComposeMetas}}
+
+ {{end}} + {{template "repo/diff/box" .}} +
+
+{{template "base/footer" .}} diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl index 66bfd0d831..10f4b60483 100644 --- a/templates/repo/commits_table.tmpl +++ b/templates/repo/commits_table.tmpl @@ -1,13 +1,13 @@

-
+
{{if or .PageIsCommits (gt .CommitCount 0)}} {{.CommitCount}} {{.i18n.Tr "repo.commits.commits"}} {{if .Branch}}({{.Branch}}){{end}} {{else}} {{.i18n.Tr "repo.commits.no_commits" $.BaseBranch $.HeadBranch }} {{if .Branch}}({{.Branch}}){{end}} {{end}}
-
+ diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl new file mode 100644 index 0000000000..cc727422d7 --- /dev/null +++ b/templates/repo/diff/compare.tmpl @@ -0,0 +1,74 @@ +{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+ + {{if .PageIsComparePull}} +

+ {{.i18n.Tr "repo.pulls.compare_changes"}} +
{{.i18n.Tr "repo.pulls.compare_changes_desc"}}
+

+
+ + + ... + +
+ {{end}} + + {{if .IsNothingToCompare}} +
{{.i18n.Tr "repo.pulls.nothing_to_compare"}}
+ {{else if .PageIsComparePull}} + {{if .HasPullRequest}} +
+ {{.i18n.Tr "repo.pulls.has_pull_request" $.RepoLink $.RepoRelPath .PullRequest.Index | Safe}} +
+ {{else}} +
+ +
+ + {{template "repo/commits_table" .}} + {{template "repo/diff/box" .}} + {{end}} + {{else}} + {{template "repo/commits_table" .}} + {{template "repo/diff/box" .}} + {{end}} +
+
+{{template "base/footer" .}} diff --git a/templates/repo/diff/page.tmpl b/templates/repo/diff/page.tmpl deleted file mode 100644 index c35e2a415b..0000000000 --- a/templates/repo/diff/page.tmpl +++ /dev/null @@ -1,94 +0,0 @@ -{{template "base/head" .}} -
- {{template "repo/header" .}} -
- {{if .IsDiffCompare }} - {{template "repo/commits_table" .}} - {{else}} -
- - {{.i18n.Tr "repo.diff.browse_source"}} - -

{{RenderCommitMessage .Commit.Message $.RepoLink $.Repository.ComposeMetas}}{{template "repo/commit_status" .CommitStatus}}

- {{if IsMultilineCommitMessage .Commit.Message}} -
{{RenderCommitBody .Commit.Message $.RepoLink $.Repository.ComposeMetas}}
- {{end}} - {{.BranchName}} -
-
-
-
- {{if .Author}} - - {{if .Author.FullName}} - {{.Author.FullName}} {{if .IsSigned}}<{{.Commit.Author.Email}}>{{end}} - {{else}} - {{.Commit.Author.Name}} {{if .IsSigned}}<{{.Commit.Author.Email}}>{{end}} - {{end}} - {{else}} - - {{.Commit.Author.Name}} - {{end}} - {{TimeSince .Commit.Author.When $.Lang}} -
-
-
- {{if .Parents}} -
- {{.i18n.Tr "repo.diff.parent"}} -
-
- {{range .Parents}} - {{ShortSha .}} - {{end}} -
- {{end}} -
-
{{.i18n.Tr "repo.diff.commit"}}
-
{{ShortSha .CommitID}}
-
-
-
-
- {{if .Commit.Signature}} - {{if .Verification.Verified }} -
- - {{.i18n.Tr "repo.commits.signed_by"}}: - {{.Commit.Committer.Name}} <{{.Commit.Committer.Email}}> - {{.i18n.Tr "repo.commits.gpg_key_id"}}: {{.Verification.SigningKey.KeyID}} -
- {{else}} -
- - {{.i18n.Tr .Verification.Reason}} -
- {{end}} - {{end}} - {{if .Note}} -
- - {{.i18n.Tr "repo.diff.git-notes"}}: - {{if .NoteAuthor}} - - {{if .NoteAuthor.FullName}} - {{.NoteAuthor.FullName}} - {{else}} - {{.NoteCommit.Author.Name}} - {{end}} - - {{else}} - {{.NoteCommit.Author.Name}} - {{end}} - {{TimeSince .NoteCommit.Author.When $.Lang}} -
-
-
{{RenderNote .Note $.RepoLink $.Repository.ComposeMetas}}
-
- {{end}} - {{end}} - - {{template "repo/diff/box" .}} -
-
-{{template "base/footer" .}} diff --git a/templates/repo/pulls/compare.tmpl b/templates/repo/pulls/compare.tmpl deleted file mode 100644 index 2296acf1df..0000000000 --- a/templates/repo/pulls/compare.tmpl +++ /dev/null @@ -1,69 +0,0 @@ -{{template "base/head" .}} -
- {{template "repo/header" .}} -
-
-

- {{.i18n.Tr "repo.pulls.compare_changes"}} -
{{.i18n.Tr "repo.pulls.compare_changes_desc"}}
-

-
- - - ... - -
- - {{if .IsNothingToCompare}} -
- {{.i18n.Tr "repo.pulls.nothing_to_compare"}} -
- {{else if .HasPullRequest}} -
- {{.i18n.Tr "repo.pulls.has_pull_request" $.RepoLink $.RepoRelPath .PullRequest.Index | Safe}} -
- {{else if eq .CommitCount 0 }} - {{template "repo/commits_table" .}} - {{template "repo/diff/box" .}} - {{else}} - {{template "repo/issue/new_form" .}} - {{template "repo/commits_table" .}} - {{template "repo/diff/box" .}} - {{end}} -
- -
-
-{{template "base/footer" .}}