From 3bd7ea4c9fb3733ec97562ad24896c3cf74de3cd Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 6 Sep 2026 13:20:42 +0800 Subject: [PATCH] refactor: GetDiffShortStat and fix panic caused by inconsistent "changed file number" (#39248) --- modules/git/diff.go | 8 ++++ modules/git/diff2.go | 71 ----------------------------- modules/git/fastimport.go | 3 +- modules/git/object_format.go | 6 +-- routers/api/v1/repo/pull.go | 25 +++++----- routers/web/repo/commit.go | 17 ++++--- routers/web/repo/compare.go | 21 +++++---- routers/web/repo/pull.go | 17 ++++--- services/convert/git_commit.go | 2 +- services/convert/pull.go | 2 +- services/gitdiff/gitdiff.go | 78 ++++++++++++++++++++------------ services/gitdiff/gitdiff_test.go | 34 ++++++++++++++ services/pull/merge_test.go | 5 +- 13 files changed, 143 insertions(+), 146 deletions(-) delete mode 100644 modules/git/diff2.go diff --git a/modules/git/diff.go b/modules/git/diff.go index 442cf742acc..86b25cc8860 100644 --- a/modules/git/diff.go +++ b/modules/git/diff.go @@ -330,3 +330,11 @@ func GetAffectedFiles(ctx context.Context, repo *Repository, branchName, oldComm return affectedFiles, err } + +// GetReverseRawDiff dumps the reverse diff results of repository in given commit ID to io.Writer. +func GetReverseRawDiff(ctx context.Context, repo RepositoryFacade, commitID string, writer io.Writer) error { + return gitcmd.NewCommand("show", "--pretty=format:revert %H%n", "-R"). + AddDynamicArguments(commitID). + WithStdoutCopy(writer). + WithRepo(repo).RunWithStderr(ctx) +} diff --git a/modules/git/diff2.go b/modules/git/diff2.go deleted file mode 100644 index d7ce47c2124..00000000000 --- a/modules/git/diff2.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2025 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package git - -import ( - "context" - "fmt" - "io" - "regexp" - "strconv" - - "gitea.dev/modules/git/gitcmd" -) - -// 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, repo RepositoryFacade, trustedArgs gitcmd.TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) { - // Now if we call: - // $ git diff --shortstat 1ebb35b98889ff77299f24d82da426b434b0cca0...788b8b1440462d477f45b0088875 - // we get: - // " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n" - cmd := gitcmd.NewCommand("diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...) - stdout, _, err := cmd.WithRepo(repo).RunStdString(ctx) - if err != nil { - return 0, 0, 0, err - } - - return parseDiffStat(stdout) -} - -var shortStatFormat = regexp.MustCompile( - `\s*(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?`) - -func parseDiffStat(stdout string) (numFiles, totalAdditions, totalDeletions int, err error) { - if len(stdout) == 0 || stdout == "\n" { - return 0, 0, 0, nil - } - groups := shortStatFormat.FindStringSubmatch(stdout) - if len(groups) != 4 { - return 0, 0, 0, fmt.Errorf("unable to parse shortstat: %s groups: %s", stdout, groups) - } - - numFiles, err = strconv.Atoi(groups[1]) - if err != nil { - return 0, 0, 0, fmt.Errorf("unable to parse shortstat: %s. Error parsing NumFiles %w", stdout, err) - } - - if len(groups[2]) != 0 { - totalAdditions, err = strconv.Atoi(groups[2]) - if err != nil { - return 0, 0, 0, fmt.Errorf("unable to parse shortstat: %s. Error parsing NumAdditions %w", stdout, err) - } - } - - if len(groups[3]) != 0 { - totalDeletions, err = strconv.Atoi(groups[3]) - if err != nil { - return 0, 0, 0, fmt.Errorf("unable to parse shortstat: %s. Error parsing NumDeletions %w", stdout, err) - } - } - return numFiles, totalAdditions, totalDeletions, err -} - -// GetReverseRawDiff dumps the reverse diff results of repository in given commit ID to io.Writer. -func GetReverseRawDiff(ctx context.Context, repo RepositoryFacade, commitID string, writer io.Writer) error { - return gitcmd.NewCommand("show", "--pretty=format:revert %H%n", "-R"). - AddDynamicArguments(commitID). - WithStdoutCopy(writer). - WithRepo(repo).RunWithStderr(ctx) -} diff --git a/modules/git/fastimport.go b/modules/git/fastimport.go index 2fbdafb1f03..048bd4a3e56 100644 --- a/modules/git/fastimport.go +++ b/modules/git/fastimport.go @@ -35,7 +35,8 @@ type FastImportCommit struct { func ForceFastImportWithInit(ctx context.Context, repoLocalPath string, commits []FastImportCommit, initOpts ...FastImportInit) (RepositoryFacade, error) { repo := gitrepo.RepositoryUnmanaged(repoLocalPath) initOpt := util.OptionalArg(initOpts, FastImportInit{Bare: true}) - if exist, _ := IsRepositoryExist(ctx, repo); !exist { + dirEntries, err := os.ReadDir(repoLocalPath) + if os.IsNotExist(err) || (err == nil && len(dirEntries) == 0) { _ = os.MkdirAll(repoLocalPath, 0o755) err := InitRepositoryLocal(ctx, repoLocalPath, initOpt.Bare, util.IfZero(initOpt.ObjectFormat, "sha1")) if err != nil { diff --git a/modules/git/object_format.go b/modules/git/object_format.go index 9e0ab9ec6a9..081dc5a2127 100644 --- a/modules/git/object_format.go +++ b/modules/git/object_format.go @@ -37,10 +37,8 @@ type Sha1ObjectFormatImpl struct{} var ( emptySha1ObjectID = &Sha1Hash{} - emptySha1Tree = &Sha1Hash{ - 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, - 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04, - } + // emptySha1Tree: 4b825dc642cb6eb9a060e54bf8d69288fbee4904 + emptySha1Tree = &Sha1Hash{0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04} ) func (Sha1ObjectFormatImpl) Name() string { return "sha1" } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index d5c2476a324..94cc9c3b9ba 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1580,39 +1580,40 @@ func GetPullRequestFiles(ctx *context.APIContext) { maxLines := setting.Git.MaxGitDiffLines // FIXME: If there are too many files in the repo, may cause some unpredictable issues. + diffCommonOptions := gitdiff.DiffCommonOptions{ + BeforeCommitID: startCommitID, + AfterCommitID: endCommitID, + WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.FormString("whitespace")), + } diff, err := gitdiff.GetDiffForAPI(ctx, baseGitRepo, &gitdiff.DiffOptions{ - BeforeCommitID: startCommitID, - AfterCommitID: endCommitID, - SkipTo: ctx.FormString("skip-to"), - MaxLines: maxLines, - MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, - MaxFiles: -1, // GetDiff() will return all files - WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.FormString("whitespace")), + DiffCommonOptions: diffCommonOptions, + SkipTo: ctx.FormString("skip-to"), + MaxLines: maxLines, + MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, + MaxFiles: -1, // GetDiff() will return all files }) if err != nil { ctx.APIErrorInternal(err) return } - diffShortStat, err := gitdiff.GetDiffShortStat(ctx, baseGitRepo, startCommitID, endCommitID) + diffShortStat, err := gitdiff.GetDiffShortStat(ctx, baseGitRepo, &diffCommonOptions) if err != nil { ctx.APIErrorInternal(err) return } - listOptions := utils.GetListOptions(ctx) + listOptions := utils.GetListOptions(ctx) totalNumberOfFiles := diffShortStat.NumFiles totalNumberOfPages := int(math.Ceil(float64(totalNumberOfFiles) / float64(listOptions.PageSize))) start, limit := listOptions.GetSkipTake() - limit = min(limit, totalNumberOfFiles-start) - limit = max(limit, 0) apiFiles := make([]*api.ChangedFile, 0, limit) - for i := start; i < start+limit; i++ { + for i := start; i < start+limit && i < len(diff.Files); i++ { // refs/pull/1/head stores the HEAD commit ID, allowing all related commits to be found in the base repository. // The head repository might have been deleted, so we should not rely on it here. apiFiles = append(apiFiles, convert.ToChangedFile(diff.Files[i], pr.BaseRepo, endCommitID)) diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 2b5978defad..613a05fd778 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -318,19 +318,22 @@ func Diff(ctx *context.Context) { maxLines, maxFiles = -1, -1 } - diff, err := gitdiff.GetDiffForRender(ctx, ctx.Repo.RepoLink, gitRepo, &gitdiff.DiffOptions{ - AfterCommitID: commitID, - SkipTo: ctx.FormString("skip-to"), - MaxLines: maxLines, - MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, - MaxFiles: maxFiles, + diffCommonOptions := gitdiff.DiffCommonOptions{ WhitespaceBehavior: gitdiff.GetWhitespaceFlag(GetWhitespaceBehavior(ctx)), + AfterCommitID: commitID, + } + diff, err := gitdiff.GetDiffForRender(ctx, ctx.Repo.RepoLink, gitRepo, &gitdiff.DiffOptions{ + DiffCommonOptions: diffCommonOptions, + SkipTo: ctx.FormString("skip-to"), + MaxLines: maxLines, + MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, + MaxFiles: maxFiles, }, files...) if err != nil { ctx.NotFound(err) return } - diffShortStat, err := gitdiff.GetDiffShortStat(ctx, gitRepo, "", commitID) + diffShortStat, err := gitdiff.GetDiffShortStat(ctx, gitRepo, &diffCommonOptions) if err != nil { ctx.ServerError("GetDiffShortStat", err) return diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index f68e3d2c332..4781c5a68b5 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -409,23 +409,24 @@ func (cpi *comparePageInfoType) prepareCompareDiff(ctx *context.Context, whitesp } fileOnly := ctx.FormBool("file-only") - + diffCommonOptions := gitdiff.DiffCommonOptions{ + BeforeCommitID: beforeCommitID, + AfterCommitID: headCommitID, + WhitespaceBehavior: whitespaceBehavior, + } diff, err := gitdiff.GetDiffForRender(ctx, ci.HeadRepo.Link(), ci.HeadGitRepo, &gitdiff.DiffOptions{ - BeforeCommitID: beforeCommitID, - AfterCommitID: headCommitID, - SkipTo: ctx.FormString("skip-to"), - MaxLines: maxLines, - MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, - MaxFiles: maxFiles, - WhitespaceBehavior: whitespaceBehavior, - DirectComparison: ci.DirectComparison(), + DiffCommonOptions: diffCommonOptions, + SkipTo: ctx.FormString("skip-to"), + MaxLines: maxLines, + MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, + MaxFiles: maxFiles, }, ctx.FormStrings("files")...) if err != nil { ctx.ServerError("GetDiff", err) return } - diffShortStat, err := gitdiff.GetDiffShortStat(ctx, ci.HeadGitRepo, beforeCommitID, headCommitID) + diffShortStat, err := gitdiff.GetDiffShortStat(ctx, ci.HeadGitRepo, &diffCommonOptions) if err != nil { ctx.ServerError("GetDiffShortStat", err) return diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index ba92e5a8a7f..a0cf6140332 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -203,7 +203,7 @@ func GetPullDiffStats(ctx *context.Context) { log.Error("Failed to GetRefCommitID: %v, repo: %v", err, ctx.Repo.Repository.FullName()) return } - diffShortStat, err := gitdiff.GetDiffShortStat(ctx, ctx.Repo.GitRepo, mergeBaseCommitID, headCommitID) + diffShortStat, err := gitdiff.GetDiffShortStat(ctx, ctx.Repo.GitRepo, &gitdiff.DiffCommonOptions{BeforeCommitID: mergeBaseCommitID, AfterCommitID: headCommitID}) if err != nil { log.Error("Failed to GetDiffShortStat: %v, repo: %v", err, ctx.Repo.Repository.FullName()) return @@ -770,15 +770,18 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) { maxLines, maxFiles = -1, -1 } - diffOptions := &gitdiff.DiffOptions{ + diffCommonOptions := gitdiff.DiffCommonOptions{ BeforeCommitID: beforeCommitID, AfterCommitID: afterCommitID, - SkipTo: ctx.FormString("skip-to"), - MaxLines: maxLines, - MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, - MaxFiles: maxFiles, WhitespaceBehavior: gitdiff.GetWhitespaceFlag(GetWhitespaceBehavior(ctx)), } + diffOptions := &gitdiff.DiffOptions{ + DiffCommonOptions: diffCommonOptions, + SkipTo: ctx.FormString("skip-to"), + MaxLines: maxLines, + MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, + MaxFiles: maxFiles, + } diff, err := gitdiff.GetDiffForRender(ctx, ctx.Repo.RepoLink, gitRepo, diffOptions, files...) if err != nil { @@ -803,7 +806,7 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) { } } - diffShortStat, err := gitdiff.GetDiffShortStat(ctx, ctx.Repo.GitRepo, beforeCommitID, afterCommitID) + diffShortStat, err := gitdiff.GetDiffShortStat(ctx, ctx.Repo.GitRepo, &diffCommonOptions) if err != nil { ctx.ServerError("GetDiffShortStat", err) return diff --git a/services/convert/git_commit.go b/services/convert/git_commit.go index 745b5ef9d6b..04bd8ec6d12 100644 --- a/services/convert/git_commit.go +++ b/services/convert/git_commit.go @@ -209,7 +209,7 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep // Get diff stats for commit if opts.Stat { - diffShortStat, err := gitdiff.GetDiffShortStat(ctx, gitRepo, "", commit.ID.String()) + diffShortStat, err := gitdiff.GetDiffShortStat(ctx, gitRepo, &gitdiff.DiffCommonOptions{AfterCommitID: commit.ID.String()}) if err != nil { return nil, err } diff --git a/services/convert/pull.go b/services/convert/pull.go index ba564474f37..c8d8807cb45 100644 --- a/services/convert/pull.go +++ b/services/convert/pull.go @@ -229,7 +229,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u // Calculate diff startCommitID = pr.MergeBase - diffShortStats, err := gitdiff.GetDiffShortStat(ctx, gitRepo, startCommitID, endCommitID) + diffShortStats, err := gitdiff.GetDiffShortStat(ctx, gitRepo, &gitdiff.DiffCommonOptions{BeforeCommitID: startCommitID, AfterCommitID: endCommitID}) if err != nil { log.Error("GetDiffShortStat: %v", err) } else { diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 1febdbaa938..d0c80c78c3f 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -15,6 +15,7 @@ import ( "net/url" "path" "sort" + "strconv" "strings" "time" @@ -1293,36 +1294,45 @@ func readFileName(rd *strings.Reader) (string, bool) { return name[2:], ambiguity } -// DiffOptions represents the options for a DiffRange -type DiffOptions struct { +type DiffCommonOptions struct { BeforeCommitID string AfterCommitID string - SkipTo string - MaxLines int - MaxLineCharacters int - MaxFiles int WhitespaceBehavior gitcmd.TrustedCmdArgs - DirectComparison bool } -func guessBeforeCommitForDiff(ctx context.Context, gitRepo *git.Repository, beforeCommitID string, afterCommit *git.Commit) (actualBeforeCommit *git.Commit, actualBeforeCommitID git.ObjectID, err error) { - commitObjectFormat := afterCommit.ID.Type() - isBeforeCommitIDEmpty := beforeCommitID == "" || beforeCommitID == commitObjectFormat.EmptyObjectID().String() +type DiffOptions struct { + DiffCommonOptions + SkipTo string + MaxLines int + MaxLineCharacters int + MaxFiles int +} +// prepareDiffCommits prepares the before and after commits for a diff operation based on the provided options. +// The "before commit" can be nil (the empty tree ID is used) if there is no "before commit" can be determined. +func prepareDiffCommits(ctx context.Context, gitRepo *git.Repository, opts *DiffCommonOptions) (actualBeforeCommit *git.Commit, actualBeforeCommitID git.ObjectID, afterCommit *git.Commit, err error) { + afterCommit, err = gitRepo.GetCommit(ctx, opts.AfterCommitID) + if err != nil { + return nil, nil, nil, err + } + + commitObjectFormat := afterCommit.ID.Type() + isBeforeCommitIDEmpty := opts.BeforeCommitID == "" || opts.BeforeCommitID == commitObjectFormat.EmptyObjectID().String() if isBeforeCommitIDEmpty && afterCommit.ParentCount() == 0 { + // "git diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 after-commit" can work with tree ID as before commit ID actualBeforeCommitID = commitObjectFormat.EmptyTree() } else { if isBeforeCommitIDEmpty { actualBeforeCommit, err = afterCommit.Parent(ctx, gitRepo, 0) } else { - actualBeforeCommit, err = gitRepo.GetCommit(ctx, beforeCommitID) + actualBeforeCommit, err = gitRepo.GetCommit(ctx, opts.BeforeCommitID) } if err != nil { - return nil, nil, err + return nil, nil, nil, err } actualBeforeCommitID = actualBeforeCommit.ID } - return actualBeforeCommit, actualBeforeCommitID, nil + return actualBeforeCommit, actualBeforeCommitID, afterCommit, nil } // getDiffBasic builds a Diff between two commits of a repository. @@ -1330,12 +1340,7 @@ func guessBeforeCommitForDiff(ctx context.Context, gitRepo *git.Repository, befo // The whitespaceBehavior is either an empty string or a git flag // Returned beforeCommit could be nil if the afterCommit doesn't have parent commit func getDiffBasic(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, files ...string) (_ *Diff, beforeCommit, afterCommit *git.Commit, err error) { - afterCommit, err = gitRepo.GetCommit(ctx, opts.AfterCommitID) - if err != nil { - return nil, nil, nil, err - } - - beforeCommit, beforeCommitID, err := guessBeforeCommitForDiff(ctx, gitRepo, opts.BeforeCommitID, afterCommit) + beforeCommit, beforeCommitID, afterCommit, err := prepareDiffCommits(ctx, gitRepo, &opts.DiffCommonOptions) if err != nil { return nil, nil, nil, err } @@ -1494,26 +1499,41 @@ func highlightCodeLines(name, lang string, sections []*DiffSection, isLeft bool, } type DiffShortStat struct { - NumFiles, TotalAddition, TotalDeletion int + NumFiles, TotalAddition, TotalDeletion int // these fields are used in templates directly } -func GetDiffShortStat(ctx context.Context, gitRepo *git.Repository, beforeCommitID, afterCommitID string) (*DiffShortStat, error) { - afterCommit, err := gitRepo.GetCommit(ctx, afterCommitID) +func GetDiffShortStat(ctx context.Context, gitRepo *git.Repository, opts *DiffCommonOptions) (*DiffShortStat, error) { + _, actualBeforeCommitID, afterCommit, err := prepareDiffCommits(ctx, gitRepo, opts) if err != nil { return nil, err } - _, actualBeforeCommitID, err := guessBeforeCommitForDiff(ctx, gitRepo, beforeCommitID, afterCommit) + stat := &DiffShortStat{} + cmd := gitcmd.NewCommand("diff", "--shortstat"). + AddArguments(opts.WhitespaceBehavior...). + AddOptionFormat("--find-renames=%s", setting.Git.DiffRenameSimilarityThreshold). + AddDynamicArguments(actualBeforeCommitID.String(), afterCommit.ID.String()) + // output: " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n" + stdout, _, err := cmd.WithRepo(gitRepo).RunStdString(ctx) if err != nil { return nil, err } - - diff := &DiffShortStat{} - diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStatByCmdArgs(ctx, gitRepo, nil, actualBeforeCommitID.String(), afterCommitID) - if err != nil { - return nil, err + stdout = strings.TrimSpace(stdout) + for field := range strings.SplitSeq(stdout, ",") { + field = strings.TrimSpace(field) + num, suffix, ok := strings.Cut(field, " ") + switch { + case strings.Contains(suffix, "file") && strings.Contains(suffix, "change"): + stat.NumFiles, _ = strconv.Atoi(num) + case strings.Contains(suffix, "insertion"): + stat.TotalAddition, _ = strconv.Atoi(num) + case strings.Contains(suffix, "deletion"): + stat.TotalDeletion, _ = strconv.Atoi(num) + case ok: + setting.PanicInDevOrTesting("unexpected diff shortstat output: %s", stdout) + } } - return diff, nil + return stat, nil } // SyncUserSpecificDiff inserts user-specific data such as which files the user has already viewed on the given diff diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index aaf8135e397..c1dd5615823 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -1308,3 +1308,37 @@ D test10.txt` assert.Equal(t, thirdReviewUpdatedFiles, thirdReview.UpdatedFiles) assert.Equal(t, 1, thirdReview.GetViewedFileCount()) } + +func TestGetDiffShortStatWithOptions(t *testing.T) { + repo, err := git.ForceFastImportWithInit(t.Context(), t.TempDir(), []git.FastImportCommit{ + {Ref: "refs/heads/base", Files: []git.FastImportFile{ + {Path: "real.txt", Content: "a1\na2\n"}, + {Path: "whitespace.txt", Content: "b\n"}, + }}, + {Ref: "refs/heads/head", Files: []git.FastImportFile{ + {Path: "real.txt", Content: "A1\nA2\nA3\n"}, + {Path: "whitespace.txt", Content: "b \n"}, + }}, + }) + require.NoError(t, err) + gitRepo, err := git.OpenRepository(t.Context(), repo) + require.NoError(t, err) + defer gitRepo.Close() + t.Run("NoParent", func(t *testing.T) { + stat, err := GetDiffShortStat(t.Context(), gitRepo, &DiffCommonOptions{AfterCommitID: "refs/heads/head"}) + require.NoError(t, err) + assert.Equal(t, &DiffShortStat{NumFiles: 2, TotalAddition: 4, TotalDeletion: 0}, stat) + }) + diffOptions := DiffCommonOptions{BeforeCommitID: "refs/heads/base", AfterCommitID: "refs/heads/head"} + t.Run("NormalDiff", func(t *testing.T) { + stat, err := GetDiffShortStat(t.Context(), gitRepo, &diffOptions) + require.NoError(t, err) + assert.Equal(t, &DiffShortStat{NumFiles: 2, TotalAddition: 4, TotalDeletion: 3}, stat) + }) + t.Run("IgnoreSpace", func(t *testing.T) { + diffOptions.WhitespaceBehavior = GetWhitespaceFlag("ignore-all") + stat, err := GetDiffShortStat(t.Context(), gitRepo, &diffOptions) + require.NoError(t, err) + assert.Equal(t, &DiffShortStat{NumFiles: 1, TotalAddition: 3, TotalDeletion: 2}, stat) + }) +} diff --git a/services/pull/merge_test.go b/services/pull/merge_test.go index 78104b91ac3..48c042c64e3 100644 --- a/services/pull/merge_test.go +++ b/services/pull/merge_test.go @@ -4,7 +4,6 @@ package pull import ( - "path/filepath" "testing" "gitea.dev/modules/git" @@ -97,7 +96,7 @@ func TestAddCommitMessageTailer(t *testing.T) { func TestResolveMergeMessageTemplate(t *testing.T) { t.Run("NoDefault", func(t *testing.T) { - repo, err := git.ForceFastImportWithInit(t.Context(), filepath.Join(t.TempDir(), "test-repo"), []git.FastImportCommit{ + repo, err := git.ForceFastImportWithInit(t.Context(), t.TempDir(), []git.FastImportCommit{ {Ref: "refs/heads/master", Files: []git.FastImportFile{ {Path: ".gitea/default_merge_message/REBASE_TEMPLATE.md", Content: "rebase template"}, }}, @@ -117,7 +116,7 @@ func TestResolveMergeMessageTemplate(t *testing.T) { assert.Equal(t, "rebase template", tmpl) }) t.Run("WithDefault", func(t *testing.T) { - repo, err := git.ForceFastImportWithInit(t.Context(), filepath.Join(t.TempDir(), "test-repo"), []git.FastImportCommit{ + repo, err := git.ForceFastImportWithInit(t.Context(), t.TempDir(), []git.FastImportCommit{ {Ref: "refs/heads/master", Files: []git.FastImportFile{ {Path: ".gitea/default_merge_message/DEFAULT_TEMPLATE.md", Content: "default template"}, {Path: ".gitea/default_merge_message/REBASE_TEMPLATE.md", Content: "rebase template"},