Add buttons to allow loading of incomplete diffs (#16829)

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 #16775
    
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)
tokarchuk/v1.17
zeripath 3 years ago committed by GitHub
parent bdfd751af8
commit a889d0cc8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      modules/git/repo_compare.go
  2. 3
      options/locale/locale_en-US.ini
  3. 6
      routers/api/v1/repo/pull.go
  4. 27
      routers/web/repo/commit.go
  5. 73
      routers/web/repo/compare.go
  6. 8
      routers/web/repo/pull.go
  7. 34
      services/gitdiff/gitdiff.go
  8. 2
      services/gitdiff/gitdiff_test.go
  9. 2
      services/pull/pull.go
  10. 180
      templates/repo/diff/box.tmpl
  11. 24
      web_src/js/features/diff.js
  12. 2
      web_src/js/index.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,13 +87,17 @@ 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
logs, err := NewCommand("log", baseCommitID+separator+headBranch, prettyLogFormat).RunInDirBytes(repo.Path) if !fileOnly {
if err != nil { logs, err := NewCommand("log", baseCommitID+separator+headBranch, prettyLogFormat).RunInDirBytes(repo.Path)
return nil, err if err != nil {
} return nil, err
compareInfo.Commits, err = repo.parsePrettyFormatLogToList(logs) }
if err != nil { compareInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err) if err != nil {
return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err)
}
} else {
compareInfo.Commits = []*Commit{}
} }
} else { } else {
compareInfo.Commits = []*Commit{} compareInfo.Commits = []*Commit{}

@ -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,15 +414,19 @@ 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 err != nil { if canRead {
ctx.ServerError("GetBranchesForRepo", err)
return nil
}
if perm {
ctx.Data["RootRepo"] = rootRepo ctx.Data["RootRepo"] = rootRepo
ctx.Data["RootRepoBranches"] = branches if !fileOnly {
ctx.Data["RootRepoTags"] = tags branches, tags, err := getBranchesAndTagsForRepo(ctx.User, rootRepo)
if err != nil {
ctx.ServerError("GetBranchesForRepo", err)
return nil
}
ctx.Data["RootRepoBranches"] = branches
ctx.Data["RootRepoTags"] = tags
}
} }
} }
@ -432,15 +439,18 @@ 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 err != nil { if canRead {
ctx.ServerError("GetBranchesForRepo", err)
return nil
}
if perm {
ctx.Data["OwnForkRepo"] = ownForkRepo ctx.Data["OwnForkRepo"] = ownForkRepo
ctx.Data["OwnForkRepoBranches"] = branches if !fileOnly {
ctx.Data["OwnForkRepoTags"] = tags branches, tags, err := getBranchesAndTagsForRepo(ctx.User, ownForkRepo)
if err != nil {
ctx.ServerError("GetBranchesForRepo", err)
return nil
}
ctx.Data["OwnForkRepoBranches"] = branches
ctx.Data["OwnForkRepoTags"] = tags
}
} }
} }
@ -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,105 +42,115 @@
<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>
{{range $i, $file := .Diff.Files}} <div id="diff-file-boxes">
{{$blobBase := call $.GetBlobByPathForCommit $.BaseCommit $file.OldName}} {{range $i, $file := .Diff.Files}}
{{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}} {{$blobBase := call $.GetBlobByPathForCommit $.BaseCommit $file.OldName}}
{{$isImage := or (call $.IsBlobAnImage $blobBase) (call $.IsBlobAnImage $blobHead)}} {{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}}
{{$isCsv := (call $.IsCsvFile $file)}} {{$isImage := or (call $.IsBlobAnImage $blobBase) (call $.IsBlobAnImage $blobHead)}}
{{$showFileViewToggle := or $isImage (and (not $file.IsIncomplete) $isCsv)}} {{$isCsv := (call $.IsCsvFile $file)}}
<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}}> {{$showFileViewToggle := or $isImage (and (not $file.IsIncomplete) $isCsv)}}
<h4 class="diff-file-header sticky-2nd-row ui top attached normal header df ac sb"> <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}}>
<div class="fold-file df ac"> <h4 class="diff-file-header sticky-2nd-row ui top attached normal header df ac sb">
<a role="button" class="chevron muted mr-2"> <div class="fold-file df ac">
<a role="button" class="fold-file muted mr-2">
{{if $file.IsGenerated}}
{{svg "octicon-chevron-right" 18}}
{{else}}
{{svg "octicon-chevron-down" 18}}
{{end}}
</a>
<div class="bold df ac">
{{if $file.IsBin}}
<span class="ml-1 mr-3">
{{$.i18n.Tr "repo.diff.bin"}}
</span>
{{else}}
{{template "repo/diff/stats" dict "file" . "root" $}}
{{end}}
</div>
<span class="file mono">{{if $file.IsRenamed}}{{$file.OldName}} &rarr; {{end}}{{$file.Name}}{{if .IsLFSFile}} ({{$.i18n.Tr "repo.stored_lfs"}}){{end}}</span>
{{if $file.IsGenerated}} {{if $file.IsGenerated}}
{{svg "octicon-chevron-right" 18}} <span class="ui label ml-3">{{$.i18n.Tr "repo.diff.generated"}}</span>
{{else}}
{{svg "octicon-chevron-down" 18}}
{{end}} {{end}}
</a> {{if $file.IsVendored}}
<div class="bold df ac"> <span class="ui label ml-3">{{$.i18n.Tr "repo.diff.vendored"}}</span>
{{if $file.IsBin}}
<span class="ml-1 mr-3">
{{$.i18n.Tr "repo.diff.bin"}}
</span>
{{else}}
{{template "repo/diff/stats" dict "file" . "root" $}}
{{end}} {{end}}
</div> </div>
<span class="file mono">{{if $file.IsRenamed}}{{$file.OldName}} &rarr; {{end}}{{$file.Name}}{{if .IsLFSFile}} ({{$.i18n.Tr "repo.stored_lfs"}}){{end}}</span> <div class="diff-file-header-actions df ac">
{{if $file.IsGenerated}} {{if $showFileViewToggle}}
<span class="ui label ml-3">{{$.i18n.Tr "repo.diff.generated"}}</span> <div class="ui compact icon buttons">
{{end}} <span class="ui tiny basic button poping up file-view-toggle" data-toggle-selector="#diff-source-{{$i}}" data-content="{{$.i18n.Tr "repo.file_view_source"}}" data-position="bottom center" data-variation="tiny inverted">{{svg "octicon-code"}}</span>
{{if $file.IsVendored}} <span class="ui tiny basic button poping up file-view-toggle active" data-toggle-selector="#diff-rendered-{{$i}}" data-content="{{$.i18n.Tr "repo.file_view_rendered"}}" data-position="bottom center" data-variation="tiny inverted">{{svg "octicon-file"}}</span>
<span class="ui label ml-3">{{$.i18n.Tr "repo.diff.vendored"}}</span> </div>
{{end}} {{end}}
</div> {{if $file.IsProtected}}
<div class="diff-file-header-actions df ac"> <span class="ui basic label">{{$.i18n.Tr "repo.diff.protected"}}</span>
{{if $showFileViewToggle}} {{end}}
<div class="ui compact icon buttons"> {{if and (not $file.IsSubmodule) (not $.PageIsWiki)}}
<span class="ui tiny basic button poping up file-view-toggle" data-toggle-selector="#diff-source-{{$i}}" data-content="{{$.i18n.Tr "repo.file_view_source"}}" data-position="bottom center" data-variation="tiny inverted">{{svg "octicon-code"}}</span> {{if $file.IsDeleted}}
<span class="ui tiny basic button poping up file-view-toggle active" data-toggle-selector="#diff-rendered-{{$i}}" data-content="{{$.i18n.Tr "repo.file_view_rendered"}}" data-position="bottom center" data-variation="tiny inverted">{{svg "octicon-file"}}</span> <a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
</div> {{else}}
{{end}} <a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
{{if $file.IsProtected}} {{end}}
<span class="ui basic label">{{$.i18n.Tr "repo.diff.protected"}}</span> {{end}}
{{end}} </div>
{{if and (not $file.IsSubmodule) (not $.PageIsWiki)}} </h4>
{{if $file.IsDeleted}} <div class="diff-file-body ui attached unstackable table segment">
<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a> <div id="diff-source-{{$i}}" class="file-body file-code code-diff{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $showFileViewToggle}} hide{{end}}">
{{if or $file.IsIncomplete $file.IsBin}}
<div class="diff-file-body binary" style="padding: 5px 10px;">
{{if $file.IsIncomplete}}
{{if $file.IsIncompleteLineTooLong}}
{{$.i18n.Tr "repo.diff.file_suppressed_line_too_long"}}
{{else}}
{{$.i18n.Tr "repo.diff.file_suppressed"}}
{{end}}
{{else}}
{{$.i18n.Tr "repo.diff.bin_not_shown"}}
{{end}}
</div>
{{else}} {{else}}
<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a> <table class="chroma">
{{if $.IsSplitStyle}}
{{template "repo/diff/section_split" dict "file" . "root" $}}
{{else}}
{{template "repo/diff/section_unified" dict "file" . "root" $}}
{{end}}
</table>
{{end}} {{end}}
{{end}} </div>
</div> {{if $showFileViewToggle}}
</h4> <div id="diff-rendered-{{$i}}" class="file-body file-code {{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}">
<div class="diff-file-body ui attached unstackable table segment"> <table class="chroma w-100">
<div id="diff-source-{{$i}}" class="file-body file-code code-diff{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $showFileViewToggle}} hide{{end}}"> {{if $isImage}}
{{if or $file.IsIncomplete $file.IsBin}} {{template "repo/diff/image_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead}}
<div class="diff-file-body binary" style="padding: 5px 10px;">
{{if $file.IsIncomplete}}
{{if $file.IsIncompleteLineTooLong}}
{{$.i18n.Tr "repo.diff.file_suppressed_line_too_long"}}
{{else}} {{else}}
{{$.i18n.Tr "repo.diff.file_suppressed"}} {{template "repo/diff/csv_diff" dict "file" . "root" $}}
{{end}} {{end}}
{{else}} </table>
{{$.i18n.Tr "repo.diff.bin_not_shown"}}
{{end}}
</div> </div>
{{else}}
<table class="chroma">
{{if $.IsSplitStyle}}
{{template "repo/diff/section_split" dict "file" . "root" $}}
{{else}}
{{template "repo/diff/section_unified" dict "file" . "root" $}}
{{end}}
</table>
{{end}} {{end}}
</div> </div>
{{if $showFileViewToggle}}
<div id="diff-rendered-{{$i}}" class="file-body file-code {{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}">
<table class="chroma w-100">
{{if $isImage}}
{{template "repo/diff/image_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead}}
{{else}}
{{template "repo/diff/csv_diff" dict "file" . "root" $}}
{{end}}
</table>
</div>
{{end}}
</div> </div>
</div> {{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"}}
</h4> <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>
</div> </h4>
{{end}} </div>
{{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();

Loading…
Cancel
Save