mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 01:34:27 +00:00 
			
		
		
		
	Fix bug when review pull request commits (#35192)
The commit range in the UI follows a half-open, half-closed convention: (,]. When reviewing a range of commits, the beforeCommitID should be set to the commit immediately preceding the first selected commit. For single-commit reviews, we must identify and use the previous commit of that specific commit. The endpoint ViewPullFilesStartingFromCommit is currently unused and can be safely removed. Fix #35157 Replace #35184 Partially extract from #35077
This commit is contained in:
		@@ -643,8 +643,17 @@ func ViewPullCommits(ctx *context.Context) {
 | 
				
			|||||||
	ctx.HTML(http.StatusOK, tplPullCommits)
 | 
						ctx.HTML(http.StatusOK, tplPullCommits)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func indexCommit(commits []*git.Commit, commitID string) *git.Commit {
 | 
				
			||||||
 | 
						for i := range commits {
 | 
				
			||||||
 | 
							if commits[i].ID.String() == commitID {
 | 
				
			||||||
 | 
								return commits[i]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ViewPullFiles render pull request changed files list page
 | 
					// ViewPullFiles render pull request changed files list page
 | 
				
			||||||
func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommit string, willShowSpecifiedCommitRange, willShowSpecifiedCommit bool) {
 | 
					func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) {
 | 
				
			||||||
	ctx.Data["PageIsPullList"] = true
 | 
						ctx.Data["PageIsPullList"] = true
 | 
				
			||||||
	ctx.Data["PageIsPullFiles"] = true
 | 
						ctx.Data["PageIsPullFiles"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -654,11 +663,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	pull := issue.PullRequest
 | 
						pull := issue.PullRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var (
 | 
						gitRepo := ctx.Repo.GitRepo
 | 
				
			||||||
		startCommitID string
 | 
					 | 
				
			||||||
		endCommitID   string
 | 
					 | 
				
			||||||
		gitRepo       = ctx.Repo.GitRepo
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	prInfo := preparePullViewPullInfo(ctx, issue)
 | 
						prInfo := preparePullViewPullInfo(ctx, issue)
 | 
				
			||||||
	if ctx.Written() {
 | 
						if ctx.Written() {
 | 
				
			||||||
@@ -668,77 +673,68 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Validate the given commit sha to show (if any passed)
 | 
					 | 
				
			||||||
	if willShowSpecifiedCommit || willShowSpecifiedCommitRange {
 | 
					 | 
				
			||||||
		foundStartCommit := len(specifiedStartCommit) == 0
 | 
					 | 
				
			||||||
		foundEndCommit := len(specifiedEndCommit) == 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !(foundStartCommit && foundEndCommit) {
 | 
					 | 
				
			||||||
			for _, commit := range prInfo.Commits {
 | 
					 | 
				
			||||||
				if commit.ID.String() == specifiedStartCommit {
 | 
					 | 
				
			||||||
					foundStartCommit = true
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if commit.ID.String() == specifiedEndCommit {
 | 
					 | 
				
			||||||
					foundEndCommit = true
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if foundStartCommit && foundEndCommit {
 | 
					 | 
				
			||||||
					break
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !(foundStartCommit && foundEndCommit) {
 | 
					 | 
				
			||||||
			ctx.NotFound(nil)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ctx.Written() {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitHeadRefName())
 | 
						headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitHeadRefName())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("GetRefCommitID", err)
 | 
							ctx.ServerError("GetRefCommitID", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["IsShowingOnlySingleCommit"] = willShowSpecifiedCommit
 | 
						isSingleCommit := beforeCommitID == "" && afterCommitID != ""
 | 
				
			||||||
 | 
						ctx.Data["IsShowingOnlySingleCommit"] = isSingleCommit
 | 
				
			||||||
 | 
						isShowAllCommits := (beforeCommitID == "" || beforeCommitID == prInfo.MergeBase) && (afterCommitID == "" || afterCommitID == headCommitID)
 | 
				
			||||||
 | 
						ctx.Data["IsShowingAllCommits"] = isShowAllCommits
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if willShowSpecifiedCommit || willShowSpecifiedCommitRange {
 | 
						if afterCommitID == "" || afterCommitID == headCommitID {
 | 
				
			||||||
		if len(specifiedEndCommit) > 0 {
 | 
							afterCommitID = headCommitID
 | 
				
			||||||
			endCommitID = specifiedEndCommit
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			endCommitID = headCommitID
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		if len(specifiedStartCommit) > 0 {
 | 
						afterCommit := indexCommit(prInfo.Commits, afterCommitID)
 | 
				
			||||||
			startCommitID = specifiedStartCommit
 | 
						if afterCommit == nil {
 | 
				
			||||||
		} else {
 | 
							ctx.HTTPError(http.StatusBadRequest, "after commit not found in PR commits")
 | 
				
			||||||
			startCommitID = prInfo.MergeBase
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var beforeCommit *git.Commit
 | 
				
			||||||
 | 
						if !isSingleCommit {
 | 
				
			||||||
 | 
							if beforeCommitID == "" || beforeCommitID == prInfo.MergeBase {
 | 
				
			||||||
 | 
								beforeCommitID = prInfo.MergeBase
 | 
				
			||||||
 | 
								// mergebase commit is not in the list of the pull request commits
 | 
				
			||||||
 | 
								beforeCommit, err = gitRepo.GetCommit(beforeCommitID)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									ctx.ServerError("GetCommit", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		ctx.Data["IsShowingAllCommits"] = false
 | 
					 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
		endCommitID = headCommitID
 | 
								beforeCommit = indexCommit(prInfo.Commits, beforeCommitID)
 | 
				
			||||||
		startCommitID = prInfo.MergeBase
 | 
								if beforeCommit == nil {
 | 
				
			||||||
		ctx.Data["IsShowingAllCommits"] = true
 | 
									ctx.HTTPError(http.StatusBadRequest, "before commit not found in PR commits")
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							beforeCommit, err = afterCommit.Parent(0)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.ServerError("Parent", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							beforeCommitID = beforeCommit.ID.String()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["Username"] = ctx.Repo.Owner.Name
 | 
						ctx.Data["Username"] = ctx.Repo.Owner.Name
 | 
				
			||||||
	ctx.Data["Reponame"] = ctx.Repo.Repository.Name
 | 
						ctx.Data["Reponame"] = ctx.Repo.Repository.Name
 | 
				
			||||||
	ctx.Data["AfterCommitID"] = endCommitID
 | 
						ctx.Data["MergeBase"] = prInfo.MergeBase
 | 
				
			||||||
	ctx.Data["BeforeCommitID"] = startCommitID
 | 
						ctx.Data["AfterCommitID"] = afterCommitID
 | 
				
			||||||
 | 
						ctx.Data["BeforeCommitID"] = beforeCommitID
 | 
				
			||||||
	fileOnly := ctx.FormBool("file-only")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	maxLines, maxFiles := setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles
 | 
						maxLines, maxFiles := setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles
 | 
				
			||||||
	files := ctx.FormStrings("files")
 | 
						files := ctx.FormStrings("files")
 | 
				
			||||||
 | 
						fileOnly := ctx.FormBool("file-only")
 | 
				
			||||||
	if fileOnly && (len(files) == 2 || len(files) == 1) {
 | 
						if fileOnly && (len(files) == 2 || len(files) == 1) {
 | 
				
			||||||
		maxLines, maxFiles = -1, -1
 | 
							maxLines, maxFiles = -1, -1
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	diffOptions := &gitdiff.DiffOptions{
 | 
						diffOptions := &gitdiff.DiffOptions{
 | 
				
			||||||
		AfterCommitID:      endCommitID,
 | 
							BeforeCommitID:     beforeCommitID,
 | 
				
			||||||
 | 
							AfterCommitID:      afterCommitID,
 | 
				
			||||||
		SkipTo:             ctx.FormString("skip-to"),
 | 
							SkipTo:             ctx.FormString("skip-to"),
 | 
				
			||||||
		MaxLines:           maxLines,
 | 
							MaxLines:           maxLines,
 | 
				
			||||||
		MaxLineCharacters:  setting.Git.MaxGitDiffLineCharacters,
 | 
							MaxLineCharacters:  setting.Git.MaxGitDiffLineCharacters,
 | 
				
			||||||
@@ -746,10 +742,6 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 | 
				
			|||||||
		WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
 | 
							WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !willShowSpecifiedCommit {
 | 
					 | 
				
			||||||
		diffOptions.BeforeCommitID = startCommitID
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	diff, err := gitdiff.GetDiffForRender(ctx, ctx.Repo.RepoLink, gitRepo, diffOptions, files...)
 | 
						diff, err := gitdiff.GetDiffForRender(ctx, ctx.Repo.RepoLink, gitRepo, diffOptions, files...)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("GetDiff", err)
 | 
							ctx.ServerError("GetDiff", err)
 | 
				
			||||||
@@ -761,7 +753,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 | 
				
			|||||||
	// as the viewed information is designed to be loaded only on latest PR
 | 
						// as the viewed information is designed to be loaded only on latest PR
 | 
				
			||||||
	// diff and if you're signed in.
 | 
						// diff and if you're signed in.
 | 
				
			||||||
	var reviewState *pull_model.ReviewState
 | 
						var reviewState *pull_model.ReviewState
 | 
				
			||||||
	if ctx.IsSigned && !willShowSpecifiedCommit && !willShowSpecifiedCommitRange {
 | 
						if ctx.IsSigned && isShowAllCommits {
 | 
				
			||||||
		reviewState, err = gitdiff.SyncUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diff, diffOptions)
 | 
							reviewState, err = gitdiff.SyncUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diff, diffOptions)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("SyncUserSpecificDiff", err)
 | 
								ctx.ServerError("SyncUserSpecificDiff", err)
 | 
				
			||||||
@@ -769,7 +761,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	diffShortStat, err := gitdiff.GetDiffShortStat(ctx.Repo.GitRepo, startCommitID, endCommitID)
 | 
						diffShortStat, err := gitdiff.GetDiffShortStat(ctx.Repo.GitRepo, beforeCommitID, afterCommitID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("GetDiffShortStat", err)
 | 
							ctx.ServerError("GetDiffShortStat", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -816,7 +808,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if !fileOnly {
 | 
						if !fileOnly {
 | 
				
			||||||
		// note: use mergeBase is set to false because we already have the merge base from the pull request info
 | 
							// note: use mergeBase is set to false because we already have the merge base from the pull request info
 | 
				
			||||||
		diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, startCommitID, endCommitID)
 | 
							diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, beforeCommitID, afterCommitID)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("GetDiffTree", err)
 | 
								ctx.ServerError("GetDiffTree", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -836,17 +828,6 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 | 
				
			|||||||
	ctx.Data["Diff"] = diff
 | 
						ctx.Data["Diff"] = diff
 | 
				
			||||||
	ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0
 | 
						ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	baseCommit, err := ctx.Repo.GitRepo.GetCommit(startCommitID)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		ctx.ServerError("GetCommit", err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	commit, err := gitRepo.GetCommit(endCommitID)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		ctx.ServerError("GetCommit", err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ctx.IsSigned && ctx.Doer != nil {
 | 
						if ctx.IsSigned && ctx.Doer != nil {
 | 
				
			||||||
		if ctx.Data["CanMarkConversation"], err = issues_model.CanMarkConversation(ctx, issue, ctx.Doer); err != nil {
 | 
							if ctx.Data["CanMarkConversation"], err = issues_model.CanMarkConversation(ctx, issue, ctx.Doer); err != nil {
 | 
				
			||||||
			ctx.ServerError("CanMarkConversation", err)
 | 
								ctx.ServerError("CanMarkConversation", err)
 | 
				
			||||||
@@ -854,7 +835,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setCompareContext(ctx, baseCommit, commit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 | 
						setCompareContext(ctx, beforeCommit, afterCommit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assigneeUsers, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
 | 
						assigneeUsers, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -901,7 +882,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 | 
				
			|||||||
	ctx.Data["CanBlockUser"] = func(blocker, blockee *user_model.User) bool {
 | 
						ctx.Data["CanBlockUser"] = func(blocker, blockee *user_model.User) bool {
 | 
				
			||||||
		return user_service.CanBlockUser(ctx, ctx.Doer, blocker, blockee)
 | 
							return user_service.CanBlockUser(ctx, ctx.Doer, blocker, blockee)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !willShowSpecifiedCommit && !willShowSpecifiedCommitRange && pull.Flow == issues_model.PullRequestFlowGithub {
 | 
						if isShowAllCommits && pull.Flow == issues_model.PullRequestFlowGithub {
 | 
				
			||||||
		if err := pull.LoadHeadRepo(ctx); err != nil {
 | 
							if err := pull.LoadHeadRepo(ctx); err != nil {
 | 
				
			||||||
			ctx.ServerError("LoadHeadRepo", err)
 | 
								ctx.ServerError("LoadHeadRepo", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -930,19 +911,17 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ViewPullFilesForSingleCommit(ctx *context.Context) {
 | 
					func ViewPullFilesForSingleCommit(ctx *context.Context) {
 | 
				
			||||||
	viewPullFiles(ctx, "", ctx.PathParam("sha"), true, true)
 | 
						// it doesn't support showing files from mergebase to the special commit
 | 
				
			||||||
 | 
						// otherwise it will be ambiguous
 | 
				
			||||||
 | 
						viewPullFiles(ctx, "", ctx.PathParam("sha"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ViewPullFilesForRange(ctx *context.Context) {
 | 
					func ViewPullFilesForRange(ctx *context.Context) {
 | 
				
			||||||
	viewPullFiles(ctx, ctx.PathParam("shaFrom"), ctx.PathParam("shaTo"), true, false)
 | 
						viewPullFiles(ctx, ctx.PathParam("shaFrom"), ctx.PathParam("shaTo"))
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func ViewPullFilesStartingFromCommit(ctx *context.Context) {
 | 
					 | 
				
			||||||
	viewPullFiles(ctx, "", ctx.PathParam("sha"), true, false)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ViewPullFilesForAllCommitsOfPr(ctx *context.Context) {
 | 
					func ViewPullFilesForAllCommitsOfPr(ctx *context.Context) {
 | 
				
			||||||
	viewPullFiles(ctx, "", "", false, false)
 | 
						viewPullFiles(ctx, "", "")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdatePullRequest merge PR's baseBranch into headBranch
 | 
					// UpdatePullRequest merge PR's baseBranch into headBranch
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1531,7 +1531,7 @@ func registerWebRoutes(m *web.Router) {
 | 
				
			|||||||
			m.Group("/commits", func() {
 | 
								m.Group("/commits", func() {
 | 
				
			||||||
				m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
 | 
									m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
 | 
				
			||||||
				m.Get("/list", repo.GetPullCommits)
 | 
									m.Get("/list", repo.GetPullCommits)
 | 
				
			||||||
				m.Get("/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
 | 
									m.Get("/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
			m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest)
 | 
								m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest)
 | 
				
			||||||
			m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest)
 | 
								m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest)
 | 
				
			||||||
@@ -1540,8 +1540,7 @@ func registerWebRoutes(m *web.Router) {
 | 
				
			|||||||
			m.Post("/cleanup", context.RepoMustNotBeArchived(), repo.CleanUpPullRequest)
 | 
								m.Post("/cleanup", context.RepoMustNotBeArchived(), repo.CleanUpPullRequest)
 | 
				
			||||||
			m.Group("/files", func() {
 | 
								m.Group("/files", func() {
 | 
				
			||||||
				m.Get("", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr)
 | 
									m.Get("", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr)
 | 
				
			||||||
				m.Get("/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit)
 | 
									m.Get("/{shaFrom:[a-f0-9]{7,64}}..{shaTo:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
 | 
				
			||||||
				m.Get("/{shaFrom:[a-f0-9]{7,40}}..{shaTo:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
 | 
					 | 
				
			||||||
				m.Group("/reviews", func() {
 | 
									m.Group("/reviews", func() {
 | 
				
			||||||
					m.Get("/new_comment", repo.RenderNewCodeCommentForm)
 | 
										m.Get("/new_comment", repo.RenderNewCodeCommentForm)
 | 
				
			||||||
					m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCodeComment)
 | 
										m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCodeComment)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@
 | 
				
			|||||||
			{{template "repo/diff/whitespace_dropdown" .}}
 | 
								{{template "repo/diff/whitespace_dropdown" .}}
 | 
				
			||||||
			{{template "repo/diff/options_dropdown" .}}
 | 
								{{template "repo/diff/options_dropdown" .}}
 | 
				
			||||||
			{{if .PageIsPullFiles}}
 | 
								{{if .PageIsPullFiles}}
 | 
				
			||||||
				<div id="diff-commit-select" data-issuelink="{{$.Issue.Link}}" data-queryparams="?style={{if $.IsSplitStyle}}split{{else}}unified{{end}}&whitespace={{$.WhitespaceBehavior}}&show-outdated={{$.ShowOutdatedComments}}" data-filter_changes_by_commit="{{ctx.Locale.Tr "repo.pulls.filter_changes_by_commit"}}">
 | 
									<div id="diff-commit-select" data-merge-base="{{.MergeBase}}" data-issuelink="{{$.Issue.Link}}" data-queryparams="?style={{if $.IsSplitStyle}}split{{else}}unified{{end}}&whitespace={{$.WhitespaceBehavior}}&show-outdated={{$.ShowOutdatedComments}}" data-filter_changes_by_commit="{{ctx.Locale.Tr "repo.pulls.filter_changes_by_commit"}}">
 | 
				
			||||||
					{{/* the following will be replaced by vue component, but this avoids any loading artifacts till the vue component is initialized */}}
 | 
										{{/* the following will be replaced by vue component, but this avoids any loading artifacts till the vue component is initialized */}}
 | 
				
			||||||
					<div class="ui jump dropdown tiny basic button custom">
 | 
										<div class="ui jump dropdown tiny basic button custom">
 | 
				
			||||||
						{{svg "octicon-git-commit"}}
 | 
											{{svg "octicon-git-commit"}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,10 +25,6 @@ func TestPullDiff_CommitRangePRDiff(t *testing.T) {
 | 
				
			|||||||
	doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/4ca8bcaf27e28504df7bf996819665986b01c847..23576dd018294e476c06e569b6b0f170d0558705", true, []string{"test2.txt", "test3.txt", "test4.txt"})
 | 
						doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/4ca8bcaf27e28504df7bf996819665986b01c847..23576dd018294e476c06e569b6b0f170d0558705", true, []string{"test2.txt", "test3.txt", "test4.txt"})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestPullDiff_StartingFromBaseToCommitPRDiff(t *testing.T) {
 | 
					 | 
				
			||||||
	doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2", true, []string{"test1.txt", "test2.txt", "test3.txt"})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func doTestPRDiff(t *testing.T, prDiffURL string, reviewBtnDisabled bool, expectedFilenames []string) {
 | 
					func doTestPRDiff(t *testing.T, prDiffURL string, reviewBtnDisabled bool, expectedFilenames []string) {
 | 
				
			||||||
	defer tests.PrepareTestEnv(t)()
 | 
						defer tests.PrepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,7 @@ export default defineComponent({
 | 
				
			|||||||
      locale: {
 | 
					      locale: {
 | 
				
			||||||
        filter_changes_by_commit: el.getAttribute('data-filter_changes_by_commit'),
 | 
					        filter_changes_by_commit: el.getAttribute('data-filter_changes_by_commit'),
 | 
				
			||||||
      } as Record<string, string>,
 | 
					      } as Record<string, string>,
 | 
				
			||||||
 | 
					      mergeBase: el.getAttribute('data-merge-base'),
 | 
				
			||||||
      commits: [] as Array<Commit>,
 | 
					      commits: [] as Array<Commit>,
 | 
				
			||||||
      hoverActivated: false,
 | 
					      hoverActivated: false,
 | 
				
			||||||
      lastReviewCommitSha: '',
 | 
					      lastReviewCommitSha: '',
 | 
				
			||||||
@@ -176,32 +177,38 @@ export default defineComponent({
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * When a commit is clicked with shift this enables the range
 | 
					     * When a commit is clicked while holding Shift, it enables range selection.
 | 
				
			||||||
     * selection. Second click (with shift) defines the end of the
 | 
					     * - The range selection is a half-open, half-closed range, meaning it excludes the start commit but includes the end commit.
 | 
				
			||||||
     * range. This opens the diff of this range
 | 
					     * - The start of the commit range is always the previous commit of the first clicked commit.
 | 
				
			||||||
     * Exception: first commit is the first commit of this PR. Then
 | 
					     * - If the first commit in the list is clicked, the mergeBase will be used as the start of the range instead.
 | 
				
			||||||
     * the diff from beginning of PR up to the second clicked commit is
 | 
					     * - The second Shift-click defines the end of the range.
 | 
				
			||||||
     * opened
 | 
					     * - Once both are selected, the diff view for the selected commit range will open.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    commitClickedShift(commit: Commit) {
 | 
					    commitClickedShift(commit: Commit) {
 | 
				
			||||||
      this.hoverActivated = !this.hoverActivated;
 | 
					      this.hoverActivated = !this.hoverActivated;
 | 
				
			||||||
      commit.selected = true;
 | 
					      commit.selected = true;
 | 
				
			||||||
      // Second click -> determine our range and open links accordingly
 | 
					      // Second click -> determine our range and open links accordingly
 | 
				
			||||||
      if (!this.hoverActivated) {
 | 
					      if (!this.hoverActivated) {
 | 
				
			||||||
 | 
					        // since at least one commit is selected, we can determine the range
 | 
				
			||||||
        // find all selected commits and generate a link
 | 
					        // find all selected commits and generate a link
 | 
				
			||||||
        if (this.commits[0].selected) {
 | 
					        const firstSelected = this.commits.findIndex((x) => x.selected);
 | 
				
			||||||
          // first commit is selected - generate a short url with only target sha
 | 
					        const lastSelected = this.commits.findLastIndex((x) => x.selected);
 | 
				
			||||||
          const lastCommitIdx = this.commits.findLastIndex((x) => x.selected);
 | 
					        let beforeCommitID: string;
 | 
				
			||||||
          if (lastCommitIdx === this.commits.length - 1) {
 | 
					        if (firstSelected === 0) {
 | 
				
			||||||
            // user selected all commits - just show the normal diff page
 | 
					          beforeCommitID = this.mergeBase;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          beforeCommitID = this.commits[firstSelected - 1].id;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const afterCommitID = this.commits[lastSelected].id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (firstSelected === lastSelected) {
 | 
				
			||||||
 | 
					          // if the start and end are the same, we show this single commit
 | 
				
			||||||
 | 
					          window.location.assign(`${this.issueLink}/commits/${afterCommitID}${this.queryParams}`);
 | 
				
			||||||
 | 
					        } else if (beforeCommitID === this.mergeBase && afterCommitID === this.commits.at(-1).id) {
 | 
				
			||||||
 | 
					          // if the first commit is selected and the last commit is selected, we show all commits
 | 
				
			||||||
          window.location.assign(`${this.issueLink}/files${this.queryParams}`);
 | 
					          window.location.assign(`${this.issueLink}/files${this.queryParams}`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            window.location.assign(`${this.issueLink}/files/${this.commits[lastCommitIdx].id}${this.queryParams}`);
 | 
					          window.location.assign(`${this.issueLink}/files/${beforeCommitID}..${afterCommitID}${this.queryParams}`);
 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          const start = this.commits[this.commits.findIndex((x) => x.selected) - 1].id;
 | 
					 | 
				
			||||||
          const end = this.commits.findLast((x) => x.selected).id;
 | 
					 | 
				
			||||||
          window.location.assign(`${this.issueLink}/files/${start}..${end}${this.queryParams}`);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user