Skip to content

Decouple diff stats query from actual diffing #33810

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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