Skip to content

Commit

Permalink
Decouple diff stats query from actual diffing (#33810)
Browse files Browse the repository at this point in the history
The diff stats are no longer part of the diff generation.
Use `GetDiffShortStat` instead to get the total number of changed files,
added lines, and deleted lines.
As such, `gitdiff.GetDiff` can be simplified:
It should not do more than expected.

And do not run "git diff --shortstat" for pull list. Fix #31492
  • Loading branch information
wxiaoguang authored Mar 8, 2025
1 parent 1b2dfff commit 6422f05
Show file tree
Hide file tree
Showing 17 changed files with 154 additions and 210 deletions.
14 changes: 3 additions & 11 deletions modules/git/repo_compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,9 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
return w.numLines, nil
}

// GetDiffShortStat counts number of changed files, number of additions and deletions
func (repo *Repository) GetDiffShortStat(base, head string) (numFiles, totalAdditions, totalDeletions int, err error) {
numFiles, totalAdditions, totalDeletions, err = GetDiffShortStat(repo.Ctx, repo.Path, nil, base+"..."+head)
if err != nil && strings.Contains(err.Error(), "no merge base") {
return GetDiffShortStat(repo.Ctx, repo.Path, nil, base, head)
}
return numFiles, totalAdditions, totalDeletions, err
}

// GetDiffShortStat counts number of changed files, number of additions and deletions
func GetDiffShortStat(ctx context.Context, repoPath string, trustedArgs TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) {
// GetDiffShortStatByCmdArgs counts number of changed files, number of additions and deletions
// TODO: it can be merged with another "GetDiffShortStat" in the future
func GetDiffShortStatByCmdArgs(ctx context.Context, repoPath string, trustedArgs TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) {
// Now if we call:
// $ git diff --shortstat 1ebb35b98889ff77299f24d82da426b434b0cca0...788b8b1440462d477f45b0088875
// we get:
Expand Down
10 changes: 6 additions & 4 deletions modules/structs/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ type PullRequest struct {
Draft bool `json:"draft"`
IsLocked bool `json:"is_locked"`
Comments int `json:"comments"`

// number of review comments made on the diff of a PR review (not including comments on commits or issues in a PR)
ReviewComments int `json:"review_comments"`
Additions int `json:"additions"`
Deletions int `json:"deletions"`
ChangedFiles int `json:"changed_files"`
ReviewComments int `json:"review_comments,omitempty"`

Additions *int `json:"additions,omitempty"`
Deletions *int `json:"deletions,omitempty"`
ChangedFiles *int `json:"changed_files,omitempty"`

HTMLURL string `json:"html_url"`
DiffURL string `json:"diff_url"`
Expand Down
8 changes: 7 additions & 1 deletion routers/api/v1/repo/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -1591,6 +1591,7 @@ func GetPullRequestFiles(ctx *context.APIContext) {
maxLines := setting.Git.MaxGitDiffLines

// FIXME: If there are too many files in the repo, may cause some unpredictable issues.
// FIXME: it doesn't need to call "GetDiff" to do various parsing and highlighting
diff, err := gitdiff.GetDiff(ctx, baseGitRepo,
&gitdiff.DiffOptions{
BeforeCommitID: startCommitID,
Expand All @@ -1606,9 +1607,14 @@ func GetPullRequestFiles(ctx *context.APIContext) {
return
}

diffShortStat, err := gitdiff.GetDiffShortStat(baseGitRepo, startCommitID, endCommitID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
listOptions := utils.GetListOptions(ctx)

totalNumberOfFiles := diff.NumFiles
totalNumberOfFiles := diffShortStat.NumFiles
totalNumberOfPages := int(math.Ceil(float64(totalNumberOfFiles) / float64(listOptions.PageSize)))

start, limit := listOptions.GetSkipTake()
Expand Down
9 changes: 7 additions & 2 deletions routers/web/repo/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,12 +321,17 @@ func Diff(ctx *context.Context) {
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: maxFiles,
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
FileOnly: fileOnly,
}, files...)
if err != nil {
ctx.NotFound(err)
return
}
diffShortStat, err := gitdiff.GetDiffShortStat(gitRepo, "", commitID)
if err != nil {
ctx.ServerError("GetDiffShortStat", err)
return
}
ctx.Data["DiffShortStat"] = diffShortStat

parents := make([]string, commit.ParentCount())
for i := 0; i < commit.ParentCount(); i++ {
Expand Down Expand Up @@ -383,7 +388,7 @@ func Diff(ctx *context.Context) {
ctx.Data["Verification"] = verification
ctx.Data["Author"] = user_model.ValidateCommitWithEmail(ctx, commit)
ctx.Data["Parents"] = parents
ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0

if err := asymkey_model.CalculateTrustStatus(verification, ctx.Repo.Repository.GetTrustModel(), func(user *user_model.User) (bool, error) {
return repo_model.IsOwnerMemberCollaborator(ctx, ctx.Repo.Repository, user.ID)
Expand Down
11 changes: 8 additions & 3 deletions routers/web/repo/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,14 +624,19 @@ func PrepareCompareDiff(
MaxFiles: maxFiles,
WhitespaceBehavior: whitespaceBehavior,
DirectComparison: ci.DirectComparison,
FileOnly: fileOnly,
}, ctx.FormStrings("files")...)
if err != nil {
ctx.ServerError("GetDiffRangeWithWhitespaceBehavior", err)
ctx.ServerError("GetDiff", err)
return false
}
diffShortStat, err := gitdiff.GetDiffShortStat(ci.HeadGitRepo, beforeCommitID, headCommitID)
if err != nil {
ctx.ServerError("GetDiffShortStat", err)
return false
}
ctx.Data["DiffShortStat"] = diffShortStat
ctx.Data["Diff"] = diff
ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0

if !fileOnly {
diffTree, err := gitdiff.GetDiffTree(ctx, ci.HeadGitRepo, false, beforeCommitID, headCommitID)
Expand Down
2 changes: 1 addition & 1 deletion routers/web/repo/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ func DiffPreviewPost(ctx *context.Context) {
return
}

if diff.NumFiles != 0 {
if len(diff.Files) != 0 {
ctx.Data["File"] = diff.Files[0]
}

Expand Down
44 changes: 21 additions & 23 deletions routers/web/repo/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,22 +200,13 @@ func GetPullDiffStats(ctx *context.Context) {
return
}

diffOptions := &gitdiff.DiffOptions{
BeforeCommitID: mergeBaseCommitID,
AfterCommitID: headCommitID,
MaxLines: setting.Git.MaxGitDiffLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: setting.Git.MaxGitDiffFiles,
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
}

diff, err := gitdiff.GetPullDiffStats(ctx.Repo.GitRepo, diffOptions)
diffShortStat, err := gitdiff.GetDiffShortStat(ctx.Repo.GitRepo, mergeBaseCommitID, headCommitID)
if err != nil {
ctx.ServerError("GetPullDiffStats", err)
ctx.ServerError("GetDiffShortStat", err)
return
}

ctx.Data["Diff"] = diff
ctx.Data["DiffShortStat"] = diffShortStat
}

func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) string {
Expand Down Expand Up @@ -752,36 +743,43 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: maxFiles,
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
FileOnly: fileOnly,
}

if !willShowSpecifiedCommit {
diffOptions.BeforeCommitID = startCommitID
}

var methodWithError string
var diff *gitdiff.Diff
shouldGetUserSpecificDiff := false
diff, err := gitdiff.GetDiff(ctx, gitRepo, diffOptions, files...)
if err != nil {
ctx.ServerError("GetDiff", err)
return
}

// if we're not logged in or only a single commit (or commit range) is shown we
// have to load only the diff and not get the viewed information
// as the viewed information is designed to be loaded only on latest PR
// diff and if you're signed in.
shouldGetUserSpecificDiff := false
if !ctx.IsSigned || willShowSpecifiedCommit || willShowSpecifiedCommitRange {
diff, err = gitdiff.GetDiff(ctx, gitRepo, diffOptions, files...)
methodWithError = "GetDiff"
// do nothing
} else {
diff, err = gitdiff.SyncAndGetUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diffOptions, files...)
methodWithError = "SyncAndGetUserSpecificDiff"
shouldGetUserSpecificDiff = true
err = gitdiff.SyncUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diff, diffOptions, files...)
if err != nil {
ctx.ServerError("SyncUserSpecificDiff", err)
return
}
}

diffShortStat, err := gitdiff.GetDiffShortStat(ctx.Repo.GitRepo, startCommitID, endCommitID)
if err != nil {
ctx.ServerError(methodWithError, err)
ctx.ServerError("GetDiffShortStat", err)
return
}
ctx.Data["DiffShortStat"] = diffShortStat

ctx.PageData["prReview"] = map[string]any{
"numberOfFiles": diff.NumFiles,
"numberOfFiles": diffShortStat.NumFiles,
"numberOfViewedFiles": diff.NumViewedFiles,
}

Expand Down Expand Up @@ -840,7 +838,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
}

ctx.Data["Diff"] = diff
ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0

baseCommit, err := ctx.Repo.GitRepo.GetCommit(startCommitID)
if err != nil {
Expand Down
10 changes: 4 additions & 6 deletions services/convert/git_commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,17 +210,15 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep

// Get diff stats for commit
if opts.Stat {
diff, err := gitdiff.GetDiff(ctx, gitRepo, &gitdiff.DiffOptions{
AfterCommitID: commit.ID.String(),
})
diffShortStat, err := gitdiff.GetDiffShortStat(gitRepo, "", commit.ID.String())
if err != nil {
return nil, err
}

res.Stats = &api.CommitStats{
Total: diff.TotalAddition + diff.TotalDeletion,
Additions: diff.TotalAddition,
Deletions: diff.TotalDeletion,
Total: diffShortStat.TotalAddition + diffShortStat.TotalDeletion,
Additions: diffShortStat.TotalAddition,
Deletions: diffShortStat.TotalDeletion,
}
}

Expand Down
30 changes: 13 additions & 17 deletions services/convert/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff"
)

// ToAPIPullRequest assumes following fields have been assigned with valid values:
Expand Down Expand Up @@ -239,9 +241,13 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
// Calculate diff
startCommitID = pr.MergeBase

apiPullRequest.ChangedFiles, apiPullRequest.Additions, apiPullRequest.Deletions, err = gitRepo.GetDiffShortStat(startCommitID, endCommitID)
diffShortStats, err := gitdiff.GetDiffShortStat(gitRepo, startCommitID, endCommitID)
if err != nil {
log.Error("GetDiffShortStat: %v", err)
} else {
apiPullRequest.ChangedFiles = &diffShortStats.NumFiles
apiPullRequest.Additions = &diffShortStats.TotalAddition
apiPullRequest.Deletions = &diffShortStats.TotalDeletion
}
}

Expand Down Expand Up @@ -462,12 +468,6 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
return nil, err
}

// Outer scope variables to be used in diff calculation
var (
startCommitID string
endCommitID string
)

if git.IsErrBranchNotExist(err) {
headCommitID, err := headGitRepo.GetRefCommitID(apiPullRequest.Head.Ref)
if err != nil && !git.IsErrNotExist(err) {
Expand All @@ -476,7 +476,6 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
}
if err == nil {
apiPullRequest.Head.Sha = headCommitID
endCommitID = headCommitID
}
} else {
commit, err := headBranch.GetCommit()
Expand All @@ -487,17 +486,8 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
if err == nil {
apiPullRequest.Head.Ref = pr.HeadBranch
apiPullRequest.Head.Sha = commit.ID.String()
endCommitID = commit.ID.String()
}
}

// Calculate diff
startCommitID = pr.MergeBase

apiPullRequest.ChangedFiles, apiPullRequest.Additions, apiPullRequest.Deletions, err = gitRepo.GetDiffShortStat(startCommitID, endCommitID)
if err != nil {
log.Error("GetDiffShortStat: %v", err)
}
}

if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 {
Expand All @@ -518,6 +508,12 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
apiPullRequest.MergedBy = ToUser(ctx, pr.Merger, nil)
}

// Do not provide "ChangeFiles/Additions/Deletions" for the PR list, because the "diff" is quite slow
// If callers are interested in these values, they should do a separate request to get the PR details
if apiPullRequest.ChangedFiles != nil || apiPullRequest.Additions != nil || apiPullRequest.Deletions != nil {
setting.PanicInDevOrTesting("ChangedFiles/Additions/Deletions should not be set in PR list")
}

apiPullRequests = append(apiPullRequests, apiPullRequest)
}

Expand Down
Loading

0 comments on commit 6422f05

Please sign in to comment.