Add buttons to allow loading of incomplete diffs ()

This PR adds two buttons to the stats and the end of the diffs list to load the (some of) the remaining incomplete diff sections.

Contains 
    
Signed-off-by: Andrew Thornton <art27@cantab.net>


## Screenshots

### Show more button at the end of the diff
![Screenshot from 2021-09-04 11-12-37](https://user-images.githubusercontent.com/1824502/132091009-b1f6113e-2c04-4be5-8a04-b8ecea56887b.png)

### Show more button at the end of the diff stats box
![Screenshot from 2021-09-04 11-14-54](https://user-images.githubusercontent.com/1824502/132091063-86da5a6d-6628-4b82-bea9-3655cd9f40f6.png)
This commit is contained in:
zeripath 2021-10-15 17:05:33 +01:00 committed by GitHub
parent bdfd751af8
commit a889d0cc8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 228 additions and 155 deletions
modules/git
options/locale
routers
services
templates/repo/diff
web_src/js

@ -46,7 +46,7 @@ func (repo *Repository) GetMergeBase(tmpRemote string, base, head string) (strin
} }
// GetCompareInfo generates and returns compare information between base and head branches of repositories. // GetCompareInfo generates and returns compare information between base and head branches of repositories.
func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string, directComparison bool) (_ *CompareInfo, err error) { func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string, directComparison, fileOnly bool) (_ *CompareInfo, err error) {
var ( var (
remoteBranch string remoteBranch string
tmpRemote string tmpRemote string
@ -87,6 +87,7 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string,
} }
// We have a common base - therefore we know that ... should work // We have a common base - therefore we know that ... should work
if !fileOnly {
logs, err := NewCommand("log", baseCommitID+separator+headBranch, prettyLogFormat).RunInDirBytes(repo.Path) logs, err := NewCommand("log", baseCommitID+separator+headBranch, prettyLogFormat).RunInDirBytes(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
@ -95,6 +96,9 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string,
if err != nil { if err != nil {
return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err) return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err)
} }
} else {
compareInfo.Commits = []*Commit{}
}
} else { } else {
compareInfo.Commits = []*Commit{} compareInfo.Commits = []*Commit{}
compareInfo.MergeBase, err = GetFullCommitID(repo.Path, remoteBranch) compareInfo.MergeBase, err = GetFullCommitID(repo.Path, remoteBranch)

@ -2027,7 +2027,8 @@ diff.file_image_height = Height
diff.file_byte_size = Size diff.file_byte_size = Size
diff.file_suppressed = File diff suppressed because it is too large diff.file_suppressed = File diff suppressed because it is too large
diff.file_suppressed_line_too_long = File diff suppressed because one or more lines are too long diff.file_suppressed_line_too_long = File diff suppressed because one or more lines are too long
diff.too_many_files = Some files were not shown because too many files changed in this diff diff.too_many_files = Some files were not shown because too many files have changed in this diff
diff.show_more = Show More
diff.generated = generated diff.generated = generated
diff.vendored = vendored diff.vendored = vendored
diff.comment.placeholder = Leave a comment diff.comment.placeholder = Leave a comment

@ -1016,7 +1016,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch, true) compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch, true, false)
if err != nil { if err != nil {
headGitRepo.Close() headGitRepo.Close()
ctx.Error(http.StatusInternalServerError, "GetCompareInfo", err) ctx.Error(http.StatusInternalServerError, "GetCompareInfo", err)
@ -1193,9 +1193,9 @@ func GetPullRequestCommits(ctx *context.APIContext) {
} }
defer baseGitRepo.Close() defer baseGitRepo.Close()
if pr.HasMerged { if pr.HasMerged {
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitRefName(), true) prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitRefName(), true, false)
} else { } else {
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName(), true) prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName(), true, false)
} }
if err != nil { if err != nil {
ctx.ServerError("GetCompareInfo", err) ctx.ServerError("GetCompareInfo", err)

@ -264,6 +264,8 @@ func Diff(ctx *context.Context) {
err error err error
) )
fileOnly := ctx.FormBool("file-only")
if ctx.Data["PageIsWiki"] != nil { if ctx.Data["PageIsWiki"] != nil {
gitRepo, err = git.OpenRepository(ctx.Repo.Repository.WikiPath()) gitRepo, err = git.OpenRepository(ctx.Repo.Repository.WikiPath())
if err != nil { if err != nil {
@ -288,16 +290,8 @@ func Diff(ctx *context.Context) {
commitID = commit.ID.String() commitID = commit.ID.String()
} }
statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository.ID, commitID, db.ListOptions{})
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
}
ctx.Data["CommitStatus"] = models.CalcCommitStatus(statuses)
ctx.Data["CommitStatuses"] = statuses
diff, err := gitdiff.GetDiffCommitWithWhitespaceBehavior(gitRepo, diff, err := gitdiff.GetDiffCommitWithWhitespaceBehavior(gitRepo,
commitID, setting.Git.MaxGitDiffLines, commitID, ctx.FormString("skip-to"), setting.Git.MaxGitDiffLines,
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles,
gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)), gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
false) false)
@ -333,10 +327,23 @@ func Diff(ctx *context.Context) {
setCompareContext(ctx, parentCommit, commit, headTarget) setCompareContext(ctx, parentCommit, commit, headTarget)
ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID) ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
ctx.Data["Commit"] = commit ctx.Data["Commit"] = commit
ctx.Data["Diff"] = diff
if fileOnly {
ctx.HTML(http.StatusOK, tplDiffBox)
return
}
statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository.ID, commitID, db.ListOptions{})
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
}
ctx.Data["CommitStatus"] = models.CalcCommitStatus(statuses)
ctx.Data["CommitStatuses"] = statuses
verification := models.ParseCommitWithSignature(commit) verification := models.ParseCommitWithSignature(commit)
ctx.Data["Verification"] = verification ctx.Data["Verification"] = verification
ctx.Data["Author"] = models.ValidateCommitWithEmail(commit) ctx.Data["Author"] = models.ValidateCommitWithEmail(commit)
ctx.Data["Diff"] = diff
ctx.Data["Parents"] = parents ctx.Data["Parents"] = parents
ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0 ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0

@ -31,6 +31,7 @@ import (
const ( const (
tplCompare base.TplName = "repo/diff/compare" tplCompare base.TplName = "repo/diff/compare"
tplBlobExcerpt base.TplName = "repo/diff/blob_excerpt" tplBlobExcerpt base.TplName = "repo/diff/blob_excerpt"
tplDiffBox base.TplName = "repo/diff/box"
) )
// setCompareContext sets context data. // setCompareContext sets context data.
@ -161,6 +162,8 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
baseRepo := ctx.Repo.Repository baseRepo := ctx.Repo.Repository
ci := &CompareInfo{} ci := &CompareInfo{}
fileOnly := ctx.FormBool("file-only")
// Get compared branches information // Get compared branches information
// A full compare url is of the form: // A full compare url is of the form:
// //
@ -411,17 +414,21 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
if rootRepo != nil && if rootRepo != nil &&
rootRepo.ID != ci.HeadRepo.ID && rootRepo.ID != ci.HeadRepo.ID &&
rootRepo.ID != baseRepo.ID { rootRepo.ID != baseRepo.ID {
perm, branches, tags, err := getBranchesAndTagsForRepo(ctx.User, rootRepo) canRead := rootRepo.CheckUnitUser(ctx.User, models.UnitTypeCode)
if canRead {
ctx.Data["RootRepo"] = rootRepo
if !fileOnly {
branches, tags, err := getBranchesAndTagsForRepo(ctx.User, rootRepo)
if err != nil { if err != nil {
ctx.ServerError("GetBranchesForRepo", err) ctx.ServerError("GetBranchesForRepo", err)
return nil return nil
} }
if perm {
ctx.Data["RootRepo"] = rootRepo
ctx.Data["RootRepoBranches"] = branches ctx.Data["RootRepoBranches"] = branches
ctx.Data["RootRepoTags"] = tags ctx.Data["RootRepoTags"] = tags
} }
} }
}
// If we have a ownForkRepo and it's different from: // If we have a ownForkRepo and it's different from:
// 1. The computed base // 1. The computed base
@ -432,17 +439,20 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
ownForkRepo.ID != ci.HeadRepo.ID && ownForkRepo.ID != ci.HeadRepo.ID &&
ownForkRepo.ID != baseRepo.ID && ownForkRepo.ID != baseRepo.ID &&
(rootRepo == nil || ownForkRepo.ID != rootRepo.ID) { (rootRepo == nil || ownForkRepo.ID != rootRepo.ID) {
perm, branches, tags, err := getBranchesAndTagsForRepo(ctx.User, ownForkRepo) canRead := ownForkRepo.CheckUnitUser(ctx.User, models.UnitTypeCode)
if canRead {
ctx.Data["OwnForkRepo"] = ownForkRepo
if !fileOnly {
branches, tags, err := getBranchesAndTagsForRepo(ctx.User, ownForkRepo)
if err != nil { if err != nil {
ctx.ServerError("GetBranchesForRepo", err) ctx.ServerError("GetBranchesForRepo", err)
return nil return nil
} }
if perm {
ctx.Data["OwnForkRepo"] = ownForkRepo
ctx.Data["OwnForkRepoBranches"] = branches ctx.Data["OwnForkRepoBranches"] = branches
ctx.Data["OwnForkRepoTags"] = tags ctx.Data["OwnForkRepoTags"] = tags
} }
} }
}
// Check if head branch is valid. // Check if head branch is valid.
headIsCommit := ci.HeadGitRepo.IsCommitExist(ci.HeadBranch) headIsCommit := ci.HeadGitRepo.IsCommitExist(ci.HeadBranch)
@ -492,7 +502,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
headBranchRef = git.TagPrefix + ci.HeadBranch headBranchRef = git.TagPrefix + ci.HeadBranch
} }
ci.CompareInfo, err = ci.HeadGitRepo.GetCompareInfo(baseRepo.RepoPath(), baseBranchRef, headBranchRef, ci.DirectComparison) ci.CompareInfo, err = ci.HeadGitRepo.GetCompareInfo(baseRepo.RepoPath(), baseBranchRef, headBranchRef, ci.DirectComparison, fileOnly)
if err != nil { if err != nil {
ctx.ServerError("GetCompareInfo", err) ctx.ServerError("GetCompareInfo", err)
return nil return nil
@ -545,7 +555,7 @@ func PrepareCompareDiff(
} }
diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(ci.HeadGitRepo, diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(ci.HeadGitRepo,
beforeCommitID, headCommitID, setting.Git.MaxGitDiffLines, beforeCommitID, headCommitID, ctx.FormString("skip-to"), setting.Git.MaxGitDiffLines,
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, whitespaceBehavior, ci.DirectComparison) setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, whitespaceBehavior, ci.DirectComparison)
if err != nil { if err != nil {
ctx.ServerError("GetDiffRangeWithWhitespaceBehavior", err) ctx.ServerError("GetDiffRangeWithWhitespaceBehavior", err)
@ -606,29 +616,22 @@ func PrepareCompareDiff(
return false return false
} }
func getBranchesAndTagsForRepo(user *models.User, repo *models.Repository) (bool, []string, []string, error) { func getBranchesAndTagsForRepo(user *models.User, repo *models.Repository) (branches, tags []string, err error) {
perm, err := models.GetUserRepoPermission(repo, user)
if err != nil {
return false, nil, nil, err
}
if !perm.CanRead(models.UnitTypeCode) {
return false, nil, nil, nil
}
gitRepo, err := git.OpenRepository(repo.RepoPath()) gitRepo, err := git.OpenRepository(repo.RepoPath())
if err != nil { if err != nil {
return false, nil, nil, err return nil, nil, err
} }
defer gitRepo.Close() defer gitRepo.Close()
branches, _, err := gitRepo.GetBranches(0, 0) branches, _, err = gitRepo.GetBranches(0, 0)
if err != nil { if err != nil {
return false, nil, nil, err return nil, nil, err
} }
tags, err := gitRepo.GetTags(0, 0) tags, err = gitRepo.GetTags(0, 0)
if err != nil { if err != nil {
return false, nil, nil, err return nil, nil, err
} }
return true, branches, tags, nil return branches, tags, nil
} }
// CompareDiff show different from one commit to another commit // CompareDiff show different from one commit to another commit
@ -665,6 +668,12 @@ func CompareDiff(ctx *context.Context) {
} }
ctx.Data["Tags"] = baseTags ctx.Data["Tags"] = baseTags
fileOnly := ctx.FormBool("file-only")
if fileOnly {
ctx.HTML(http.StatusOK, tplDiffBox)
return
}
headBranches, _, err := ci.HeadGitRepo.GetBranches(0, 0) headBranches, _, err := ci.HeadGitRepo.GetBranches(0, 0)
if err != nil { if err != nil {
ctx.ServerError("GetBranches", err) ctx.ServerError("GetBranches", err)

@ -318,7 +318,7 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.C
ctx.Data["HasMerged"] = true ctx.Data["HasMerged"] = true
compareInfo, err := ctx.Repo.GitRepo.GetCompareInfo(ctx.Repo.Repository.RepoPath(), compareInfo, err := ctx.Repo.GitRepo.GetCompareInfo(ctx.Repo.Repository.RepoPath(),
pull.MergeBase, pull.GetGitRefName(), true) pull.MergeBase, pull.GetGitRefName(), true, false)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "fatal: Not a valid object name") || strings.Contains(err.Error(), "unknown revision or path not in the working tree") { if strings.Contains(err.Error(), "fatal: Not a valid object name") || strings.Contains(err.Error(), "unknown revision or path not in the working tree") {
ctx.Data["IsPullRequestBroken"] = true ctx.Data["IsPullRequestBroken"] = true
@ -401,7 +401,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
} }
compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(),
pull.MergeBase, pull.GetGitRefName(), true) pull.MergeBase, pull.GetGitRefName(), true, false)
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") {
ctx.Data["IsPullRequestBroken"] = true ctx.Data["IsPullRequestBroken"] = true
@ -517,7 +517,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
} }
compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(),
git.BranchPrefix+pull.BaseBranch, pull.GetGitRefName(), true) git.BranchPrefix+pull.BaseBranch, pull.GetGitRefName(), true, false)
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") {
ctx.Data["IsPullRequestBroken"] = true ctx.Data["IsPullRequestBroken"] = true
@ -633,7 +633,7 @@ func ViewPullFiles(ctx *context.Context) {
ctx.Data["AfterCommitID"] = endCommitID ctx.Data["AfterCommitID"] = endCommitID
diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(gitRepo, diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(gitRepo,
startCommitID, endCommitID, setting.Git.MaxGitDiffLines, startCommitID, endCommitID, ctx.FormString("skip-to"), setting.Git.MaxGitDiffLines,
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles,
gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)), false) gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)), false)
if err != nil { if err != nil {

@ -653,6 +653,7 @@ func getCommitFileLineCount(commit *git.Commit, filePath string) int {
// Diff represents a difference between two git trees. // Diff represents a difference between two git trees.
type Diff struct { type Diff struct {
Start, End string
NumFiles, TotalAddition, TotalDeletion int NumFiles, TotalAddition, TotalDeletion int
Files []*DiffFile Files []*DiffFile
IsIncomplete bool IsIncomplete bool
@ -719,6 +720,9 @@ parsingLoop:
// TODO: Handle skipping first n files // TODO: Handle skipping first n files
if len(diff.Files) >= maxFiles { if len(diff.Files) >= maxFiles {
lastFile := createDiffFile(diff, line)
diff.End = lastFile.Name
diff.IsIncomplete = true diff.IsIncomplete = true
_, err := io.Copy(io.Discard, reader) _, err := io.Copy(io.Discard, reader)
if err != nil { if err != nil {
@ -1217,7 +1221,7 @@ func readFileName(rd *strings.Reader) (string, bool) {
// GetDiffRangeWithWhitespaceBehavior builds a Diff between two commits of a repository. // GetDiffRangeWithWhitespaceBehavior builds a Diff between two commits of a repository.
// Passing the empty string as beforeCommitID returns a diff from the parent commit. // Passing the empty string as beforeCommitID returns a diff from the parent commit.
// The whitespaceBehavior is either an empty string or a git flag // The whitespaceBehavior is either an empty string or a git flag
func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID, afterCommitID string, maxLines, maxLineCharacters, maxFiles int, whitespaceBehavior string, directComparison bool) (*Diff, error) { func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID, afterCommitID, skipTo string, maxLines, maxLineCharacters, maxFiles int, whitespaceBehavior string, directComparison bool) (*Diff, error) {
repoPath := gitRepo.Path repoPath := gitRepo.Path
commit, err := gitRepo.GetCommit(afterCommitID) commit, err := gitRepo.GetCommit(afterCommitID)
@ -1228,31 +1232,42 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
ctx, cancel := context.WithTimeout(git.DefaultContext, time.Duration(setting.Git.Timeout.Default)*time.Second) ctx, cancel := context.WithTimeout(git.DefaultContext, time.Duration(setting.Git.Timeout.Default)*time.Second)
defer cancel() defer cancel()
var cmd *exec.Cmd argsLength := 6
if len(whitespaceBehavior) > 0 {
argsLength++
}
if len(skipTo) > 0 {
argsLength++
}
diffArgs := make([]string, 0, argsLength)
if (len(beforeCommitID) == 0 || beforeCommitID == git.EmptySHA) && commit.ParentCount() == 0 { if (len(beforeCommitID) == 0 || beforeCommitID == git.EmptySHA) && commit.ParentCount() == 0 {
diffArgs := []string{"diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M"} diffArgs = append(diffArgs, "diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M")
if len(whitespaceBehavior) != 0 { if len(whitespaceBehavior) != 0 {
diffArgs = append(diffArgs, whitespaceBehavior) diffArgs = append(diffArgs, whitespaceBehavior)
} }
// append empty tree ref // append empty tree ref
diffArgs = append(diffArgs, "4b825dc642cb6eb9a060e54bf8d69288fbee4904") diffArgs = append(diffArgs, "4b825dc642cb6eb9a060e54bf8d69288fbee4904")
diffArgs = append(diffArgs, afterCommitID) diffArgs = append(diffArgs, afterCommitID)
cmd = exec.CommandContext(ctx, git.GitExecutable, diffArgs...)
} else { } else {
actualBeforeCommitID := beforeCommitID actualBeforeCommitID := beforeCommitID
if len(actualBeforeCommitID) == 0 { if len(actualBeforeCommitID) == 0 {
parentCommit, _ := commit.Parent(0) parentCommit, _ := commit.Parent(0)
actualBeforeCommitID = parentCommit.ID.String() actualBeforeCommitID = parentCommit.ID.String()
} }
diffArgs := []string{"diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M"} diffArgs = append(diffArgs, "diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M")
if len(whitespaceBehavior) != 0 { if len(whitespaceBehavior) != 0 {
diffArgs = append(diffArgs, whitespaceBehavior) diffArgs = append(diffArgs, whitespaceBehavior)
} }
diffArgs = append(diffArgs, actualBeforeCommitID) diffArgs = append(diffArgs, actualBeforeCommitID)
diffArgs = append(diffArgs, afterCommitID) diffArgs = append(diffArgs, afterCommitID)
cmd = exec.CommandContext(ctx, git.GitExecutable, diffArgs...)
beforeCommitID = actualBeforeCommitID beforeCommitID = actualBeforeCommitID
} }
if skipTo != "" {
diffArgs = append(diffArgs, "--skip-to="+skipTo)
}
cmd := exec.CommandContext(ctx, git.GitExecutable, diffArgs...)
cmd.Dir = repoPath cmd.Dir = repoPath
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@ -1272,6 +1287,7 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
if err != nil { if err != nil {
return nil, fmt.Errorf("ParsePatch: %v", err) return nil, fmt.Errorf("ParsePatch: %v", err)
} }
diff.Start = skipTo
var checker *git.CheckAttributeReader var checker *git.CheckAttributeReader
@ -1299,7 +1315,7 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
log.Error("Unable to open checker for %s. Error: %v", afterCommitID, err) log.Error("Unable to open checker for %s. Error: %v", afterCommitID, err)
} else { } else {
go func() { go func() {
err = checker.Run() err := checker.Run()
if err != nil && err != ctx.Err() { if err != nil && err != ctx.Err() {
log.Error("Unable to open checker for %s. Error: %v", afterCommitID, err) log.Error("Unable to open checker for %s. Error: %v", afterCommitID, err)
} }
@ -1382,8 +1398,8 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
// GetDiffCommitWithWhitespaceBehavior builds a Diff representing the given commitID. // GetDiffCommitWithWhitespaceBehavior builds a Diff representing the given commitID.
// The whitespaceBehavior is either an empty string or a git flag // The whitespaceBehavior is either an empty string or a git flag
func GetDiffCommitWithWhitespaceBehavior(gitRepo *git.Repository, commitID string, maxLines, maxLineCharacters, maxFiles int, whitespaceBehavior string, directComparison bool) (*Diff, error) { func GetDiffCommitWithWhitespaceBehavior(gitRepo *git.Repository, commitID, skipTo string, maxLines, maxLineCharacters, maxFiles int, whitespaceBehavior string, directComparison bool) (*Diff, error) {
return GetDiffRangeWithWhitespaceBehavior(gitRepo, "", commitID, maxLines, maxLineCharacters, maxFiles, whitespaceBehavior, directComparison) return GetDiffRangeWithWhitespaceBehavior(gitRepo, "", commitID, skipTo, maxLines, maxLineCharacters, maxFiles, whitespaceBehavior, directComparison)
} }
// CommentAsDiff returns c.Patch as *Diff // CommentAsDiff returns c.Patch as *Diff

@ -522,7 +522,7 @@ func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) {
} }
defer gitRepo.Close() defer gitRepo.Close()
for _, behavior := range []string{"-w", "--ignore-space-at-eol", "-b", ""} { for _, behavior := range []string{"-w", "--ignore-space-at-eol", "-b", ""} {
diffs, err := GetDiffRangeWithWhitespaceBehavior(gitRepo, "559c156f8e0178b71cb44355428f24001b08fc68", "bd7063cc7c04689c4d082183d32a604ed27a24f9", diffs, err := GetDiffRangeWithWhitespaceBehavior(gitRepo, "559c156f8e0178b71cb44355428f24001b08fc68", "bd7063cc7c04689c4d082183d32a604ed27a24f9", "",
setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles, behavior, false) setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles, behavior, false)
assert.NoError(t, err, fmt.Sprintf("Error when diff with %s", behavior)) assert.NoError(t, err, fmt.Sprintf("Error when diff with %s", behavior))
for _, f := range diffs.Files { for _, f := range diffs.Files {

@ -80,7 +80,7 @@ func NewPullRequest(repo *models.Repository, pull *models.Issue, labelIDs []int6
defer baseGitRepo.Close() defer baseGitRepo.Close()
compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(),
git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), true) git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), true, false)
if err != nil { if err != nil {
return err return err
} }

@ -42,17 +42,25 @@
<a class="file mono" href="#diff-{{.Index}}">{{.Name}}</a> <a class="file mono" href="#diff-{{.Index}}">{{.Name}}</a>
</li> </li>
{{end}} {{end}}
{{if .Diff.IsIncomplete}}
<li id="diff-too-many-files-stats" class="pt-2">
<span class="file df ac sb">{{$.i18n.Tr "repo.diff.too_many_files"}}
<a class="ui basic tiny button" id="diff-show-more-files-stats" data-href="{{$.Link}}?skip-to={{.Diff.End}}&file-only=true">{{.i18n.Tr "repo.diff.show_more"}}</a>
</span>
</li>
{{end}}
</ol> </ol>
<div id="diff-file-boxes">
{{range $i, $file := .Diff.Files}} {{range $i, $file := .Diff.Files}}
{{$blobBase := call $.GetBlobByPathForCommit $.BaseCommit $file.OldName}} {{$blobBase := call $.GetBlobByPathForCommit $.BaseCommit $file.OldName}}
{{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}} {{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}}
{{$isImage := or (call $.IsBlobAnImage $blobBase) (call $.IsBlobAnImage $blobHead)}} {{$isImage := or (call $.IsBlobAnImage $blobBase) (call $.IsBlobAnImage $blobHead)}}
{{$isCsv := (call $.IsCsvFile $file)}} {{$isCsv := (call $.IsCsvFile $file)}}
{{$showFileViewToggle := or $isImage (and (not $file.IsIncomplete) $isCsv)}} {{$showFileViewToggle := or $isImage (and (not $file.IsIncomplete) $isCsv)}}
<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} mt-3" id="diff-{{.Index}}" {{if $file.IsGenerated}}data-folded="true"{{end}}> <div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} mt-3" id="diff-{{.Index}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if $file.IsGenerated}}data-folded="true"{{end}}>
<h4 class="diff-file-header sticky-2nd-row ui top attached normal header df ac sb"> <h4 class="diff-file-header sticky-2nd-row ui top attached normal header df ac sb">
<div class="fold-file df ac"> <div class="fold-file df ac">
<a role="button" class="chevron muted mr-2"> <a role="button" class="fold-file muted mr-2">
{{if $file.IsGenerated}} {{if $file.IsGenerated}}
{{svg "octicon-chevron-right" 18}} {{svg "octicon-chevron-right" 18}}
{{else}} {{else}}
@ -135,12 +143,14 @@
{{end}} {{end}}
{{if .Diff.IsIncomplete}} {{if .Diff.IsIncomplete}}
<div class="diff-file-box diff-box file-content mt-3"> <div class="diff-file-box diff-box file-content mt-3" id="diff-incomplete">
<h4 class="ui top attached normal header"> <h4 class="ui top attached normal header df ac sb">
{{$.i18n.Tr "repo.diff.too_many_files"}} {{$.i18n.Tr "repo.diff.too_many_files"}}
<a class="ui basic tiny button" id="diff-show-more-files" data-href="{{$.Link}}?skip-to={{.Diff.End}}&file-only=true">{{.i18n.Tr "repo.diff.show_more"}}</a>
</h4> </h4>
</div> </div>
{{end}} {{end}}
</div>
{{if not $.Repository.IsArchived}} {{if not $.Repository.IsArchived}}
<div class="hide" id="edit-content-form"> <div class="hide" id="edit-content-form">

@ -0,0 +1,24 @@
export function initDiffShowMore() {
$('#diff-files, #diff-file-boxes').on('click', '#diff-show-more-files, #diff-show-more-files-stats', (e) => {
e.preventDefault();
if ($(e.target).hasClass('disabled')) {
return;
}
$('#diff-show-more-files, #diff-show-more-files-stats').addClass('disabled');
const url = $('#diff-show-more-files, #diff-show-more-files-stats').data('href');
$.ajax({
type: 'GET',
url,
}).done((resp) => {
if (!resp || resp.html === '' || resp.empty) {
$('#diff-show-more-files, #diff-show-more-files-stats').removeClass('disabled');
return;
}
$('#diff-too-many-files-stats').remove();
$('#diff-files').append($(resp).find('#diff-files li'));
$('#diff-incomplete').replaceWith($(resp).find('#diff-file-boxes').children());
});
});
}

@ -27,6 +27,7 @@ import {initNotificationsTable, initNotificationCount} from './features/notifica
import {initLastCommitLoader} from './features/lastcommitloader.js'; import {initLastCommitLoader} from './features/lastcommitloader.js';
import {initIssueContentHistory} from './features/issue-content-history.js'; import {initIssueContentHistory} from './features/issue-content-history.js';
import {initStopwatch} from './features/stopwatch.js'; import {initStopwatch} from './features/stopwatch.js';
import {initDiffShowMore} from './features/diff.js';
import {showLineButton} from './code/linebutton.js'; import {showLineButton} from './code/linebutton.js';
import {initMarkupContent, initCommentContent} from './markup/content.js'; import {initMarkupContent, initCommentContent} from './markup/content.js';
import {stripTags, mqBinarySearch} from './utils.js'; import {stripTags, mqBinarySearch} from './utils.js';
@ -2881,6 +2882,7 @@ $(document).ready(async () => {
initFileViewToggle(); initFileViewToggle();
initReleaseEditor(); initReleaseEditor();
initRelease(); initRelease();
initDiffShowMore();
initIssueContentHistory(); initIssueContentHistory();
initAdminUserListSearchForm(); initAdminUserListSearchForm();