Compare branches, commits and tags with each other (#6991)
* Supports tags when comparing commits or branches Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Hide headline when only comparing and don't load unused data Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Merges compare logics to allow comparing branches, commits and tags with eachother Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Display branch or tag instead of commit when used for comparing Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show pull request form after click on button Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Transfers relevant pull.go changes from master to compare.go Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes error when comparing forks against a commit or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Removes console.log from JavaScript file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Show icon next to commit reference when comparing branch or tag Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Updates css file Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Fixes import order * Renames template variable * Update routers/repo/compare.go Co-Authored-By: zeripath <art27@cantab.net> * Update from master Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Allow short-shas in compare * Renames prInfo to compareInfo Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Check PR permissions only if compare is pull request Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Adjusts comment Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com> * Use compareInfo instead of prInfo
This commit is contained in:
		
							parent
							
								
									bd55f6ff36
								
							
						
					
					
						commit
						311ce2d1d0
					
				
					 17 changed files with 584 additions and 527 deletions
				
			
		| 
						 | 
					@ -1144,8 +1144,7 @@ func (pr *PullRequest) UpdatePatch() (err error) {
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		headGitRepo.RemoveRemote(tmpRemote)
 | 
							headGitRepo.RemoveRemote(tmpRemote)
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
	remoteBranch := "remotes/" + tmpRemote + "/" + pr.BaseBranch
 | 
						pr.MergeBase, err = headGitRepo.GetMergeBase(tmpRemote, pr.BaseBranch, pr.HeadBranch)
 | 
				
			||||||
	pr.MergeBase, err = headGitRepo.GetMergeBase(remoteBranch, pr.HeadBranch)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("GetMergeBase: %v", err)
 | 
							return fmt.Errorf("GetMergeBase: %v", err)
 | 
				
			||||||
	} else if err = pr.Update(); err != nil {
 | 
						} else if err = pr.Update(); err != nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,16 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) {
 | 
				
			||||||
	return ref.Hash().String(), nil
 | 
						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.
 | 
					// GetBranchCommitID returns last commit ID string of given branch.
 | 
				
			||||||
func (repo *Repository) GetBranchCommitID(name string) (string, error) {
 | 
					func (repo *Repository) GetBranchCommitID(name string) (string, error) {
 | 
				
			||||||
	return repo.GetRefCommitID(BranchPrefix + name)
 | 
						return repo.GetRefCommitID(BranchPrefix + name)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
 | 
					// 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
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,55 +15,66 @@ import (
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PullRequestInfo represents needed information for a pull request.
 | 
					// CompareInfo represents needed information for comparing references.
 | 
				
			||||||
type PullRequestInfo struct {
 | 
					type CompareInfo struct {
 | 
				
			||||||
	MergeBase string
 | 
						MergeBase string
 | 
				
			||||||
	Commits   *list.List
 | 
						Commits   *list.List
 | 
				
			||||||
	NumFiles  int
 | 
						NumFiles  int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetMergeBase checks and returns merge base of two branches.
 | 
					// 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)
 | 
						stdout, err := NewCommand("merge-base", base, head).RunInDir(repo.Path)
 | 
				
			||||||
	return strings.TrimSpace(stdout), err
 | 
						return strings.TrimSpace(stdout), err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetPullRequestInfo generates and returns pull request information
 | 
					// GetCompareInfo generates and returns compare information between base and head branches of repositories.
 | 
				
			||||||
// between base and head branches of repositories.
 | 
					func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string) (_ *CompareInfo, err error) {
 | 
				
			||||||
func (repo *Repository) GetPullRequestInfo(basePath, baseBranch, headBranch string) (_ *PullRequestInfo, err error) {
 | 
						var (
 | 
				
			||||||
	var remoteBranch string
 | 
							remoteBranch string
 | 
				
			||||||
 | 
							tmpRemote    string
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We don't need a temporary remote for same repository.
 | 
						// We don't need a temporary remote for same repository.
 | 
				
			||||||
	if repo.Path != basePath {
 | 
						if repo.Path != basePath {
 | 
				
			||||||
		// Add a temporary remote
 | 
							// 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 {
 | 
							if err = repo.AddRemote(tmpRemote, basePath, true); err != nil {
 | 
				
			||||||
			return nil, fmt.Errorf("AddRemote: %v", err)
 | 
								return nil, fmt.Errorf("AddRemote: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		defer repo.RemoveRemote(tmpRemote)
 | 
							defer repo.RemoveRemote(tmpRemote)
 | 
				
			||||||
 | 
					 | 
				
			||||||
		remoteBranch = "remotes/" + tmpRemote + "/" + baseBranch
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		remoteBranch = baseBranch
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	prInfo := new(PullRequestInfo)
 | 
						compareInfo := new(CompareInfo)
 | 
				
			||||||
	prInfo.MergeBase, err = repo.GetMergeBase(remoteBranch, headBranch)
 | 
						compareInfo.MergeBase, err = repo.GetMergeBase(tmpRemote, baseBranch, headBranch)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		// We have a common base
 | 
							// 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 {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		prInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
 | 
							compareInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err)
 | 
								return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		prInfo.Commits = list.New()
 | 
							compareInfo.Commits = list.New()
 | 
				
			||||||
		prInfo.MergeBase, err = GetFullCommitID(repo.Path, remoteBranch)
 | 
							compareInfo.MergeBase, err = GetFullCommitID(repo.Path, remoteBranch)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			prInfo.MergeBase = remoteBranch
 | 
								compareInfo.MergeBase = remoteBranch
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,9 +83,9 @@ func (repo *Repository) GetPullRequestInfo(basePath, baseBranch, headBranch stri
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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.
 | 
					// GetPatch generates and returns patch data between given revisions.
 | 
				
			||||||
| 
						 | 
					@ -591,6 +591,7 @@ footer .ui.left,footer .ui.right{line-height:40px}
 | 
				
			||||||
.repository .milestone.list>.item .content{padding-top:10px}
 | 
					.repository .milestone.list>.item .content{padding-top:10px}
 | 
				
			||||||
.repository.new.milestone textarea{height:200px}
 | 
					.repository.new.milestone textarea{height:200px}
 | 
				
			||||||
.repository.new.milestone #deadline{width:150px}
 | 
					.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 .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: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}
 | 
					.repository.compare.pull .comment.form .content:before{border-right-color:#d3d3d4;border-width:9px;margin-top:-9px}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -959,8 +959,15 @@ function initRepository() {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Pull request
 | 
					    // Pull request
 | 
				
			||||||
    if ($('.repository.compare.pull').length > 0) {
 | 
					    var $repoComparePull = $('.repository.compare.pull');
 | 
				
			||||||
 | 
					    if ($repoComparePull.length > 0) {
 | 
				
			||||||
        initFilterSearchDropdown('.choose.branch .dropdown');
 | 
					        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
 | 
					    // Branches
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1109,6 +1109,9 @@
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &.compare.pull {
 | 
					    &.compare.pull {
 | 
				
			||||||
 | 
					        .show-form-container {
 | 
				
			||||||
 | 
					            text-align: left;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        .choose.branch {
 | 
					        .choose.branch {
 | 
				
			||||||
            .octicon {
 | 
					            .octicon {
 | 
				
			||||||
                padding-right: 10px;
 | 
					                padding-right: 10px;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -188,7 +188,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get repo/branch information
 | 
						// 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() {
 | 
						if ctx.Written() {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -240,7 +240,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
 | 
				
			||||||
		milestoneID = milestone.ID
 | 
							milestoneID = milestone.ID
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch)
 | 
						patch, err := headGitRepo.GetPatch(compareInfo.MergeBase, headBranch)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(500, "GetPatch", err)
 | 
							ctx.Error(500, "GetPatch", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -277,7 +277,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
 | 
				
			||||||
		BaseBranch:   baseBranch,
 | 
							BaseBranch:   baseBranch,
 | 
				
			||||||
		HeadRepo:     headRepo,
 | 
							HeadRepo:     headRepo,
 | 
				
			||||||
		BaseRepo:     repo,
 | 
							BaseRepo:     repo,
 | 
				
			||||||
		MergeBase:    prInfo.MergeBase,
 | 
							MergeBase:    compareInfo.MergeBase,
 | 
				
			||||||
		Type:         models.PullRequestGitea,
 | 
							Type:         models.PullRequestGitea,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -600,7 +600,7 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
 | 
				
			||||||
	ctx.Status(200)
 | 
						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
 | 
						baseRepo := ctx.Repo.Repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get compared branches information
 | 
						// Get compared branches information
 | 
				
			||||||
| 
						 | 
					@ -712,11 +712,11 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 | 
				
			||||||
		return nil, nil, nil, nil, "", ""
 | 
							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 {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(500, "GetPullRequestInfo", err)
 | 
							ctx.Error(500, "GetCompareInfo", err)
 | 
				
			||||||
		return nil, nil, nil, nil, "", ""
 | 
							return nil, nil, nil, nil, "", ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch
 | 
						return headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,9 +19,9 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	tplCommits base.TplName = "repo/commits"
 | 
						tplCommits    base.TplName = "repo/commits"
 | 
				
			||||||
	tplGraph   base.TplName = "repo/graph"
 | 
						tplGraph      base.TplName = "repo/graph"
 | 
				
			||||||
	tplDiff    base.TplName = "repo/diff/page"
 | 
						tplCommitPage base.TplName = "repo/commit_page"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RefCommits render commits 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["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", "commit", commitID)
 | 
				
			||||||
	ctx.Data["BranchName"], err = commit.GetBranchName()
 | 
						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
 | 
					// RawDiff dumps diff results of repository in given commit ID to io.Writer
 | 
				
			||||||
| 
						 | 
					@ -276,54 +276,3 @@ func RawDiff(ctx *context.Context) {
 | 
				
			||||||
		return
 | 
							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)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										337
									
								
								routers/repo/compare.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								routers/repo/compare.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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 branch>...[<head repo>:]<head branch>
 | 
				
			||||||
 | 
						// 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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,7 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	tplFork        base.TplName = "repo/pulls/fork"
 | 
						tplFork        base.TplName = "repo/pulls/fork"
 | 
				
			||||||
	tplComparePull base.TplName = "repo/pulls/compare"
 | 
						tplCompareDiff base.TplName = "repo/diff/compare"
 | 
				
			||||||
	tplPullCommits base.TplName = "repo/pulls/commits"
 | 
						tplPullCommits base.TplName = "repo/pulls/commits"
 | 
				
			||||||
	tplPullFiles   base.TplName = "repo/pulls/files"
 | 
						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
 | 
					// 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
 | 
						pull := issue.PullRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setMergeTarget(ctx, pull)
 | 
						setMergeTarget(ctx, pull)
 | 
				
			||||||
	ctx.Data["HasMerged"] = true
 | 
						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())
 | 
							pull.MergeBase, pull.GetGitRefName())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -298,7 +298,7 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.P
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.ServerError("GetPullRequestInfo", err)
 | 
							ctx.ServerError("GetCompareInfo", err)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["NumCommits"] = prInfo.Commits.Len()
 | 
						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
 | 
					// 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
 | 
						repo := ctx.Repo.Repository
 | 
				
			||||||
	pull := issue.PullRequest
 | 
						pull := issue.PullRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -336,7 +336,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullReq
 | 
				
			||||||
		return nil
 | 
							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)
 | 
							pull.BaseBranch, pull.HeadBranch)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if strings.Contains(err.Error(), "fatal: Not a valid object name") {
 | 
							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
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.ServerError("GetPullRequestInfo", err)
 | 
							ctx.ServerError("GetCompareInfo", err)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -628,266 +628,6 @@ func stopTimerIfAvailable(user *models.User, issue *models.Issue) error {
 | 
				
			||||||
	return nil
 | 
						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 branch>...[<head repo>:]<head branch>
 | 
					 | 
				
			||||||
	// 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
 | 
					// CompareAndPullRequestPost response for creating pull request
 | 
				
			||||||
func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) {
 | 
					func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) {
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes")
 | 
						ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes")
 | 
				
			||||||
| 
						 | 
					@ -926,7 +666,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.HTML(200, tplComparePull)
 | 
							ctx.HTML(200, tplCompareDiff)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -936,7 +676,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplComparePull, form)
 | 
							ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplCompareDiff, form)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -732,9 +732,9 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
				
			||||||
		m.Group("/milestone", func() {
 | 
							m.Group("/milestone", func() {
 | 
				
			||||||
			m.Get("/:id", repo.MilestoneIssuesAndPulls)
 | 
								m.Get("/:id", repo.MilestoneIssuesAndPulls)
 | 
				
			||||||
		}, reqRepoIssuesOrPullsReader, context.RepoRef())
 | 
							}, reqRepoIssuesOrPullsReader, context.RepoRef())
 | 
				
			||||||
		m.Combo("/compare/*", context.RepoMustNotBeArchived(), reqRepoCodeReader, reqRepoPullsReader, repo.MustAllowPulls, repo.SetEditorconfigIfExists).
 | 
							m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists).
 | 
				
			||||||
			Get(repo.SetDiffViewStyle, repo.CompareAndPullRequest).
 | 
								Get(repo.SetDiffViewStyle, repo.CompareDiff).
 | 
				
			||||||
			Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
 | 
								Post(context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.Group("", func() {
 | 
							m.Group("", func() {
 | 
				
			||||||
			m.Group("", func() {
 | 
								m.Group("", func() {
 | 
				
			||||||
| 
						 | 
					@ -906,9 +906,6 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
				
			||||||
		}, context.RepoRef(), reqRepoCodeReader)
 | 
							}, context.RepoRef(), reqRepoCodeReader)
 | 
				
			||||||
		m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)",
 | 
							m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)",
 | 
				
			||||||
			repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
 | 
								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())
 | 
						}, ignSignIn, context.RepoAssignment(), context.UnitTypes())
 | 
				
			||||||
	m.Group("/:username/:reponame", func() {
 | 
						m.Group("/:username/:reponame", func() {
 | 
				
			||||||
		m.Get("/stars", repo.Stars)
 | 
							m.Get("/stars", repo.Stars)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										89
									
								
								templates/repo/commit_page.tmpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								templates/repo/commit_page.tmpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,89 @@
 | 
				
			||||||
 | 
					{{template "base/head" .}}
 | 
				
			||||||
 | 
					<div class="repository diff">
 | 
				
			||||||
 | 
						{{template "repo/header" .}}
 | 
				
			||||||
 | 
						<div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}">
 | 
				
			||||||
 | 
							<div class="ui top attached info clearing segment {{if .Commit.Signature}} isSigned {{if .Verification.Verified }} isVerified {{end}}{{end}}">
 | 
				
			||||||
 | 
								<a class="ui floated right blue tiny button" href="{{EscapePound .SourcePath}}">
 | 
				
			||||||
 | 
									{{.i18n.Tr "repo.diff.browse_source"}}
 | 
				
			||||||
 | 
								</a>
 | 
				
			||||||
 | 
								<h3 class="has-emoji">{{RenderCommitMessage .Commit.Message $.RepoLink $.Repository.ComposeMetas}}{{template "repo/commit_status" .CommitStatus}}</h3>
 | 
				
			||||||
 | 
								{{if IsMultilineCommitMessage .Commit.Message}}
 | 
				
			||||||
 | 
									<pre class="commit-body">{{RenderCommitBody .Commit.Message $.RepoLink $.Repository.ComposeMetas}}</pre>
 | 
				
			||||||
 | 
								{{end}}
 | 
				
			||||||
 | 
								<span class="text grey"><i class="octicon octicon-git-branch"></i>{{.BranchName}}</span>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<div class="ui attached info segment {{if .Commit.Signature}} isSigned {{if .Verification.Verified }} isVerified {{end}}{{end}}">
 | 
				
			||||||
 | 
								<div class="ui stackable grid">
 | 
				
			||||||
 | 
									<div class="nine wide column">
 | 
				
			||||||
 | 
										{{if .Author}}
 | 
				
			||||||
 | 
											<img class="ui avatar image" src="{{.Author.RelAvatarLink}}" />
 | 
				
			||||||
 | 
										{{if .Author.FullName}}
 | 
				
			||||||
 | 
										<a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong></a> {{if .IsSigned}}<{{.Commit.Author.Email}}>{{end}}
 | 
				
			||||||
 | 
										{{else}}
 | 
				
			||||||
 | 
										<a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong></a> {{if .IsSigned}}<{{.Commit.Author.Email}}>{{end}}
 | 
				
			||||||
 | 
										{{end}}
 | 
				
			||||||
 | 
										{{else}}
 | 
				
			||||||
 | 
											<img class="ui avatar image" src="{{AvatarLink .Commit.Author.Email}}" />
 | 
				
			||||||
 | 
											<strong>{{.Commit.Author.Name}}</strong>
 | 
				
			||||||
 | 
										{{end}}
 | 
				
			||||||
 | 
										<span class="text grey" id="authored-time">{{TimeSince .Commit.Author.When $.Lang}}</span>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div class="seven wide right aligned column">
 | 
				
			||||||
 | 
										<div class="ui horizontal list">
 | 
				
			||||||
 | 
											{{if .Parents}}
 | 
				
			||||||
 | 
												<div class="item">
 | 
				
			||||||
 | 
													{{.i18n.Tr "repo.diff.parent"}}
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
												<div class="item">
 | 
				
			||||||
 | 
													{{range .Parents}}
 | 
				
			||||||
 | 
														<a class="ui blue sha label" href="{{$.RepoLink}}/commit/{{.}}">{{ShortSha .}}</a>
 | 
				
			||||||
 | 
													{{end}}
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
											{{end}}
 | 
				
			||||||
 | 
											<div class="mobile-only"></div>
 | 
				
			||||||
 | 
											<div class="item">{{.i18n.Tr "repo.diff.commit"}}</div>
 | 
				
			||||||
 | 
											<div class="item"><span class="ui blue sha label">{{ShortSha .CommitID}}</span></div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div><!-- end column -->
 | 
				
			||||||
 | 
								</div><!-- end grid -->
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							{{if .Commit.Signature}}
 | 
				
			||||||
 | 
								{{if .Verification.Verified }}
 | 
				
			||||||
 | 
									<div class="ui bottom attached positive message">
 | 
				
			||||||
 | 
									  <i class="green lock icon"></i>
 | 
				
			||||||
 | 
										<span>{{.i18n.Tr "repo.commits.signed_by"}}:</span>
 | 
				
			||||||
 | 
										<a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a> <{{.Commit.Committer.Email}}>
 | 
				
			||||||
 | 
										<span class="pull-right"><span>{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.KeyID}}</span>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								{{else}}
 | 
				
			||||||
 | 
									<div class="ui bottom attached message">
 | 
				
			||||||
 | 
									  <i class="grey unlock icon"></i>
 | 
				
			||||||
 | 
									  {{.i18n.Tr .Verification.Reason}}
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								{{end}}
 | 
				
			||||||
 | 
							{{end}}
 | 
				
			||||||
 | 
							{{if .Note}}
 | 
				
			||||||
 | 
								<div class="ui top attached info segment message git-notes">
 | 
				
			||||||
 | 
									<i class="sticky note icon"></i>
 | 
				
			||||||
 | 
									{{.i18n.Tr "repo.diff.git-notes"}}:
 | 
				
			||||||
 | 
									{{if .NoteAuthor}}
 | 
				
			||||||
 | 
										<a href="{{.NoteAuthor.HomeLink}}">
 | 
				
			||||||
 | 
											{{if .NoteAuthor.FullName}}
 | 
				
			||||||
 | 
											  <strong>{{.NoteAuthor.FullName}}</strong>
 | 
				
			||||||
 | 
											{{else}}
 | 
				
			||||||
 | 
											  <strong>{{.NoteCommit.Author.Name}}</strong>
 | 
				
			||||||
 | 
											{{end}}
 | 
				
			||||||
 | 
										</a>
 | 
				
			||||||
 | 
									{{else}}
 | 
				
			||||||
 | 
										<strong>{{.NoteCommit.Author.Name}}</strong>
 | 
				
			||||||
 | 
									{{end}}
 | 
				
			||||||
 | 
									<span class="text grey" id="note-authored-time">{{TimeSince .NoteCommit.Author.When $.Lang}}</span>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div class="ui bottom attached info segment git-notes">
 | 
				
			||||||
 | 
									<pre class="commit-body">{{RenderNote .Note $.RepoLink $.Repository.ComposeMetas}}</pre>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							{{end}}
 | 
				
			||||||
 | 
							{{template "repo/diff/box" .}}
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,13 @@
 | 
				
			||||||
<h4 class="ui top attached header">
 | 
					<h4 class="ui top attached header">
 | 
				
			||||||
	<div class="ui stackable grid">
 | 
						<div class="ui stackable grid">
 | 
				
			||||||
		<div class="ten wide column">
 | 
							<div class="five wide column">
 | 
				
			||||||
			{{if or .PageIsCommits (gt .CommitCount 0)}}
 | 
								{{if or .PageIsCommits (gt .CommitCount 0)}}
 | 
				
			||||||
				{{.CommitCount}} {{.i18n.Tr "repo.commits.commits"}} {{if .Branch}}({{.Branch}}){{end}}
 | 
									{{.CommitCount}} {{.i18n.Tr "repo.commits.commits"}} {{if .Branch}}({{.Branch}}){{end}}
 | 
				
			||||||
			{{else}}
 | 
								{{else}}
 | 
				
			||||||
				{{.i18n.Tr "repo.commits.no_commits" $.BaseBranch $.HeadBranch }} {{if .Branch}}({{.Branch}}){{end}}
 | 
									{{.i18n.Tr "repo.commits.no_commits" $.BaseBranch $.HeadBranch }} {{if .Branch}}({{.Branch}}){{end}}
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div class="six wide right aligned column">
 | 
							<div class="eleven wide right aligned column">
 | 
				
			||||||
			{{if .PageIsCommits}}
 | 
								{{if .PageIsCommits}}
 | 
				
			||||||
				<form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.BranchNameSubURL | EscapePound}}/search">
 | 
									<form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.BranchNameSubURL | EscapePound}}/search">
 | 
				
			||||||
					<div class="ui tiny search input">
 | 
										<div class="ui tiny search input">
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,9 @@
 | 
				
			||||||
					<button class="ui black tiny button" data-panel="#add-deploy-key-panel" data-tooltip={{.i18n.Tr "repo.commits.search.tooltip"}}>{{.i18n.Tr "repo.commits.find"}}</button>
 | 
										<button class="ui black tiny button" data-panel="#add-deploy-key-panel" data-tooltip={{.i18n.Tr "repo.commits.search.tooltip"}}>{{.i18n.Tr "repo.commits.find"}}</button>
 | 
				
			||||||
				</form>
 | 
									</form>
 | 
				
			||||||
			{{else if .IsDiffCompare}}
 | 
								{{else if .IsDiffCompare}}
 | 
				
			||||||
				<a href="{{$.CommitRepoLink}}/commit/{{.BeforeCommitID}}" class="ui green sha label">{{ShortSha .BeforeCommitID}}</a> ... <a href="{{$.CommitRepoLink}}/commit/{{.AfterCommitID}}" class="ui green sha label">{{ShortSha .AfterCommitID}}</a>
 | 
									<a href="{{$.CommitRepoLink}}/commit/{{.BeforeCommitID}}" class="ui green sha label">{{if not .BaseIsCommit}}{{if .BaseIsBranch}}<i class="octicon octicon-git-branch"></i>{{else if .BaseIsTag}}<i class="octicon octicon-tag"></i>{{end}}{{.BaseBranch}}{{else}}{{ShortSha .BaseBranch}}{{end}}</a>
 | 
				
			||||||
 | 
									...
 | 
				
			||||||
 | 
									<a href="{{$.CommitRepoLink}}/commit/{{.AfterCommitID}}" class="ui green sha label">{{if not .HeadIsCommit}}{{if .HeadIsBranch}}<i class="octicon octicon-git-branch"></i>{{else if .HeadIsTag}}<i class="octicon octicon-tag"></i>{{end}}{{.HeadBranch}}{{else}}{{ShortSha .HeadBranch}}{{end}}</a>
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										74
									
								
								templates/repo/diff/compare.tmpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								templates/repo/diff/compare.tmpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,74 @@
 | 
				
			||||||
 | 
					{{template "base/head" .}}
 | 
				
			||||||
 | 
					<div class="repository diff {{if .PageIsComparePull}}compare pull{{end}}">
 | 
				
			||||||
 | 
						{{template "repo/header" .}}
 | 
				
			||||||
 | 
						<div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{{if .PageIsComparePull}}
 | 
				
			||||||
 | 
							<h2 class="ui header">
 | 
				
			||||||
 | 
								{{.i18n.Tr "repo.pulls.compare_changes"}}
 | 
				
			||||||
 | 
								<div class="sub header">{{.i18n.Tr "repo.pulls.compare_changes_desc"}}</div>
 | 
				
			||||||
 | 
							</h2>
 | 
				
			||||||
 | 
							<div class="ui segment choose branch">
 | 
				
			||||||
 | 
								<span class="octicon octicon-git-compare"></span>
 | 
				
			||||||
 | 
								<div class="ui floating filter dropdown" data-no-results="{{.i18n.Tr "repo.pulls.no_results"}}">
 | 
				
			||||||
 | 
									<div class="ui basic small button">
 | 
				
			||||||
 | 
										<span class="text">{{.i18n.Tr "repo.pulls.compare_base"}}: {{$.BaseName}}:{{$.BaseBranch}}</span>
 | 
				
			||||||
 | 
										<i class="dropdown icon"></i>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div class="menu">
 | 
				
			||||||
 | 
										<div class="ui icon search input">
 | 
				
			||||||
 | 
											<i class="filter icon"></i>
 | 
				
			||||||
 | 
											<input name="search" placeholder="{{.i18n.Tr "repo.pulls.filter_branch"}}...">
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="scrolling menu">
 | 
				
			||||||
 | 
											{{range .Branches}}
 | 
				
			||||||
 | 
												<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-url="{{$.RepoLink}}/compare/{{EscapePound .}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}:{{end}}{{EscapePound $.HeadBranch}}">{{$.BaseName}}:{{.}}</div>
 | 
				
			||||||
 | 
											{{end}}
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								...
 | 
				
			||||||
 | 
								<div class="ui floating filter dropdown">
 | 
				
			||||||
 | 
									<div class="ui basic small button">
 | 
				
			||||||
 | 
										<span class="text">{{.i18n.Tr "repo.pulls.compare_compare"}}: {{$.HeadUser.Name}}:{{$.HeadBranch}}</span>
 | 
				
			||||||
 | 
										<i class="dropdown icon"></i>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div class="menu">
 | 
				
			||||||
 | 
										<div class="ui icon search input">
 | 
				
			||||||
 | 
											<i class="filter icon"></i>
 | 
				
			||||||
 | 
											<input name="search" placeholder="{{.i18n.Tr "repo.pulls.filter_branch"}}...">
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="scrolling menu">
 | 
				
			||||||
 | 
											{{range .HeadBranches}}
 | 
				
			||||||
 | 
												<div class="{{if eq $.HeadBranch .}}selected{{end}} item" data-url="{{$.RepoLink}}/compare/{{EscapePound $.BaseBranch}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}:{{end}}{{EscapePound .}}">{{$.HeadUser.Name}}:{{.}}</div>
 | 
				
			||||||
 | 
											{{end}}
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						{{end}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{{if .IsNothingToCompare}}
 | 
				
			||||||
 | 
					    	<div class="ui segment">{{.i18n.Tr "repo.pulls.nothing_to_compare"}}</div>
 | 
				
			||||||
 | 
					    {{else if .PageIsComparePull}}
 | 
				
			||||||
 | 
							{{if .HasPullRequest}}
 | 
				
			||||||
 | 
					        	<div class="ui segment">
 | 
				
			||||||
 | 
					        		{{.i18n.Tr "repo.pulls.has_pull_request" $.RepoLink $.RepoRelPath .PullRequest.Index | Safe}}
 | 
				
			||||||
 | 
					        	</div>
 | 
				
			||||||
 | 
					        {{else}}
 | 
				
			||||||
 | 
					        	<div class="ui info message show-form-container">
 | 
				
			||||||
 | 
					        		<button class="ui button green show-form">{{.i18n.Tr "repo.pulls.new"}}</button>
 | 
				
			||||||
 | 
					        	</div>
 | 
				
			||||||
 | 
					        	<div class="pullrequest-form" style="display: none">
 | 
				
			||||||
 | 
					        		{{template "repo/issue/new_form" .}}
 | 
				
			||||||
 | 
					        	</div>
 | 
				
			||||||
 | 
					        	{{template "repo/commits_table" .}}
 | 
				
			||||||
 | 
					        	{{template "repo/diff/box" .}}
 | 
				
			||||||
 | 
					        {{end}}
 | 
				
			||||||
 | 
						{{else}}
 | 
				
			||||||
 | 
							{{template "repo/commits_table" .}}
 | 
				
			||||||
 | 
							{{template "repo/diff/box" .}}
 | 
				
			||||||
 | 
						{{end}}
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
| 
						 | 
					@ -1,94 +0,0 @@
 | 
				
			||||||
{{template "base/head" .}}
 | 
					 | 
				
			||||||
<div class="repository diff">
 | 
					 | 
				
			||||||
	{{template "repo/header" .}}
 | 
					 | 
				
			||||||
	<div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}">
 | 
					 | 
				
			||||||
		{{if .IsDiffCompare }}
 | 
					 | 
				
			||||||
			{{template "repo/commits_table" .}}
 | 
					 | 
				
			||||||
		{{else}}
 | 
					 | 
				
			||||||
			<div class="ui top attached info clearing segment {{if .Commit.Signature}} isSigned {{if .Verification.Verified }} isVerified {{end}}{{end}}">
 | 
					 | 
				
			||||||
				<a class="ui floated right blue tiny button" href="{{EscapePound .SourcePath}}">
 | 
					 | 
				
			||||||
					{{.i18n.Tr "repo.diff.browse_source"}}
 | 
					 | 
				
			||||||
				</a>
 | 
					 | 
				
			||||||
				<h3 class="has-emoji">{{RenderCommitMessage .Commit.Message $.RepoLink $.Repository.ComposeMetas}}{{template "repo/commit_status" .CommitStatus}}</h3>
 | 
					 | 
				
			||||||
				{{if IsMultilineCommitMessage .Commit.Message}}
 | 
					 | 
				
			||||||
					<pre class="commit-body">{{RenderCommitBody .Commit.Message $.RepoLink $.Repository.ComposeMetas}}</pre>
 | 
					 | 
				
			||||||
				{{end}}
 | 
					 | 
				
			||||||
				<span class="text grey"><i class="octicon octicon-git-branch"></i>{{.BranchName}}</span>
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
			<div class="ui attached info segment {{if .Commit.Signature}} isSigned {{if .Verification.Verified }} isVerified {{end}}{{end}}">
 | 
					 | 
				
			||||||
				<div class="ui stackable grid">
 | 
					 | 
				
			||||||
					<div class="nine wide column">
 | 
					 | 
				
			||||||
						{{if .Author}}
 | 
					 | 
				
			||||||
							<img class="ui avatar image" src="{{.Author.RelAvatarLink}}" />
 | 
					 | 
				
			||||||
						{{if .Author.FullName}}
 | 
					 | 
				
			||||||
						<a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong></a> {{if .IsSigned}}<{{.Commit.Author.Email}}>{{end}}
 | 
					 | 
				
			||||||
						{{else}}
 | 
					 | 
				
			||||||
						<a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong></a> {{if .IsSigned}}<{{.Commit.Author.Email}}>{{end}}
 | 
					 | 
				
			||||||
						{{end}}
 | 
					 | 
				
			||||||
						{{else}}
 | 
					 | 
				
			||||||
							<img class="ui avatar image" src="{{AvatarLink .Commit.Author.Email}}" />
 | 
					 | 
				
			||||||
							<strong>{{.Commit.Author.Name}}</strong>
 | 
					 | 
				
			||||||
						{{end}}
 | 
					 | 
				
			||||||
						<span class="text grey" id="authored-time">{{TimeSince .Commit.Author.When $.Lang}}</span>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
					<div class="seven wide right aligned column">
 | 
					 | 
				
			||||||
						<div class="ui horizontal list">
 | 
					 | 
				
			||||||
							{{if .Parents}}
 | 
					 | 
				
			||||||
								<div class="item">
 | 
					 | 
				
			||||||
									{{.i18n.Tr "repo.diff.parent"}}
 | 
					 | 
				
			||||||
								</div>
 | 
					 | 
				
			||||||
								<div class="item">
 | 
					 | 
				
			||||||
									{{range .Parents}}
 | 
					 | 
				
			||||||
										<a class="ui blue sha label" href="{{$.RepoLink}}/commit/{{.}}">{{ShortSha .}}</a>
 | 
					 | 
				
			||||||
									{{end}}
 | 
					 | 
				
			||||||
								</div>
 | 
					 | 
				
			||||||
							{{end}}
 | 
					 | 
				
			||||||
							<div class="mobile-only"></div>
 | 
					 | 
				
			||||||
							<div class="item">{{.i18n.Tr "repo.diff.commit"}}</div>
 | 
					 | 
				
			||||||
							<div class="item"><span class="ui blue sha label">{{ShortSha .CommitID}}</span></div>
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
					</div><!-- end column -->
 | 
					 | 
				
			||||||
				</div><!-- end grid -->
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
			{{if .Commit.Signature}}
 | 
					 | 
				
			||||||
				{{if .Verification.Verified }}
 | 
					 | 
				
			||||||
					<div class="ui bottom attached positive message">
 | 
					 | 
				
			||||||
					  <i class="green lock icon"></i>
 | 
					 | 
				
			||||||
						<span>{{.i18n.Tr "repo.commits.signed_by"}}:</span>
 | 
					 | 
				
			||||||
						<a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a> <{{.Commit.Committer.Email}}>
 | 
					 | 
				
			||||||
						<span class="pull-right"><span>{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.KeyID}}</span>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
				{{else}}
 | 
					 | 
				
			||||||
					<div class="ui bottom attached message">
 | 
					 | 
				
			||||||
					  <i class="grey unlock icon"></i>
 | 
					 | 
				
			||||||
					  {{.i18n.Tr .Verification.Reason}}
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
				{{end}}
 | 
					 | 
				
			||||||
			{{end}}
 | 
					 | 
				
			||||||
			{{if .Note}}
 | 
					 | 
				
			||||||
				<div class="ui top attached info segment message git-notes">
 | 
					 | 
				
			||||||
					<i class="sticky note icon"></i>
 | 
					 | 
				
			||||||
					{{.i18n.Tr "repo.diff.git-notes"}}:
 | 
					 | 
				
			||||||
					{{if .NoteAuthor}}
 | 
					 | 
				
			||||||
						<a href="{{.NoteAuthor.HomeLink}}">
 | 
					 | 
				
			||||||
							{{if .NoteAuthor.FullName}}
 | 
					 | 
				
			||||||
							  <strong>{{.NoteAuthor.FullName}}</strong>
 | 
					 | 
				
			||||||
							{{else}}
 | 
					 | 
				
			||||||
							  <strong>{{.NoteCommit.Author.Name}}</strong>
 | 
					 | 
				
			||||||
							{{end}}
 | 
					 | 
				
			||||||
						</a>
 | 
					 | 
				
			||||||
					{{else}}
 | 
					 | 
				
			||||||
						<strong>{{.NoteCommit.Author.Name}}</strong>
 | 
					 | 
				
			||||||
					{{end}}
 | 
					 | 
				
			||||||
					<span class="text grey" id="note-authored-time">{{TimeSince .NoteCommit.Author.When $.Lang}}</span>
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
				<div class="ui bottom attached info segment git-notes">
 | 
					 | 
				
			||||||
					<pre class="commit-body">{{RenderNote .Note $.RepoLink $.Repository.ComposeMetas}}</pre>
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
			{{end}}
 | 
					 | 
				
			||||||
		{{end}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		{{template "repo/diff/box" .}}
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
{{template "base/footer" .}}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,69 +0,0 @@
 | 
				
			||||||
{{template "base/head" .}}
 | 
					 | 
				
			||||||
<div class="repository compare pull diff">
 | 
					 | 
				
			||||||
	{{template "repo/header" .}}
 | 
					 | 
				
			||||||
	<div class="ui container">
 | 
					 | 
				
			||||||
		<div class="sixteen wide column page grid">
 | 
					 | 
				
			||||||
			<h2 class="ui header">
 | 
					 | 
				
			||||||
				{{.i18n.Tr "repo.pulls.compare_changes"}}
 | 
					 | 
				
			||||||
				<div class="sub header">{{.i18n.Tr "repo.pulls.compare_changes_desc"}}</div>
 | 
					 | 
				
			||||||
			</h2>
 | 
					 | 
				
			||||||
			<div class="ui segment choose branch">
 | 
					 | 
				
			||||||
				<span class="octicon octicon-git-compare"></span>
 | 
					 | 
				
			||||||
				<div class="ui floating filter dropdown" data-no-results="{{.i18n.Tr "repo.pulls.no_results"}}">
 | 
					 | 
				
			||||||
					<div class="ui basic small button">
 | 
					 | 
				
			||||||
						<span class="text">{{.i18n.Tr "repo.pulls.compare_base"}}: {{$.BaseName}}:{{$.BaseBranch}}</span>
 | 
					 | 
				
			||||||
						<i class="dropdown icon"></i>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
					<div class="menu">
 | 
					 | 
				
			||||||
						<div class="ui icon search input">
 | 
					 | 
				
			||||||
							<i class="filter icon"></i>
 | 
					 | 
				
			||||||
							<input name="search" placeholder="{{.i18n.Tr "repo.pulls.filter_branch"}}...">
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
						<div class="scrolling menu">
 | 
					 | 
				
			||||||
							{{range .Branches}}
 | 
					 | 
				
			||||||
								<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-url="{{$.RepoLink}}/compare/{{EscapePound .}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}:{{end}}{{EscapePound $.HeadBranch}}">{{$.BaseName}}:{{.}}</div>
 | 
					 | 
				
			||||||
							{{end}}
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
				...
 | 
					 | 
				
			||||||
				<div class="ui floating filter dropdown">
 | 
					 | 
				
			||||||
					<div class="ui basic small button">
 | 
					 | 
				
			||||||
						<span class="text">{{.i18n.Tr "repo.pulls.compare_compare"}}: {{$.HeadUser.Name}}:{{$.HeadBranch}}</span>
 | 
					 | 
				
			||||||
						<i class="dropdown icon"></i>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
					<div class="menu">
 | 
					 | 
				
			||||||
						<div class="ui icon search input">
 | 
					 | 
				
			||||||
							<i class="filter icon"></i>
 | 
					 | 
				
			||||||
							<input name="search" placeholder="{{.i18n.Tr "repo.pulls.filter_branch"}}...">
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
						<div class="scrolling menu">
 | 
					 | 
				
			||||||
							{{range .HeadBranches}}
 | 
					 | 
				
			||||||
								<div class="{{if eq $.HeadBranch .}}selected{{end}} item" data-url="{{$.RepoLink}}/compare/{{EscapePound $.BaseBranch}}...{{if not $.PullRequestCtx.SameRepo}}{{$.HeadUser.Name}}:{{end}}{{EscapePound .}}">{{$.HeadUser.Name}}:{{.}}</div>
 | 
					 | 
				
			||||||
							{{end}}
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			{{if .IsNothingToCompare}}
 | 
					 | 
				
			||||||
				<div class="ui segment">
 | 
					 | 
				
			||||||
					{{.i18n.Tr "repo.pulls.nothing_to_compare"}}
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
			{{else if .HasPullRequest}}
 | 
					 | 
				
			||||||
				<div class="ui segment">
 | 
					 | 
				
			||||||
					{{.i18n.Tr "repo.pulls.has_pull_request" $.RepoLink $.RepoRelPath .PullRequest.Index | Safe}}
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
			{{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}}
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
{{template "base/footer" .}}
 | 
					 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue