From 7f7dc2d16c5bece927af0113f072ec84e0ed0078 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 3 Aug 2026 01:34:26 +0800 Subject: [PATCH] chore: fix git diff render (#38746) Fix a regression from #38517 (the tail section index is not correctly assigned). And add a test to cover GetDiffForRender. --- modules/git/fastimport.go | 7 +- services/gitdiff/gitdiff.go | 7 +- services/gitdiff/gitdiff_render_test.go | 118 ++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 services/gitdiff/gitdiff_render_test.go diff --git a/modules/git/fastimport.go b/modules/git/fastimport.go index 7411877470..b0fab69690 100644 --- a/modules/git/fastimport.go +++ b/modules/git/fastimport.go @@ -9,6 +9,7 @@ import ( "fmt" "gitea.dev/modules/git/gitcmd" + "gitea.dev/modules/util" ) type FastImportFile struct { @@ -27,11 +28,13 @@ type FastImportCommit struct { func ForceFastImport(ctx context.Context, repo RepositoryFacade, commits []FastImportCommit) error { var buf bytes.Buffer for i, c := range commits { + msg := util.IfZero(c.Message, fmt.Sprintf("commit %d", i+1)) _, _ = fmt.Fprintf(&buf, "reset %s\n", c.Ref) _, _ = fmt.Fprintf(&buf, "commit %s\nmark :%d\ncommitter Gitea 1500000000 +0000\n", c.Ref, i+1) - _, _ = fmt.Fprintf(&buf, "data %d\n%s\n", len(c.Message), c.Message) + _, _ = fmt.Fprintf(&buf, "data %d\n%s\n", len(msg), msg) for _, f := range c.Files { - _, _ = fmt.Fprintf(&buf, "M %s inline %s\ndata %d\n%s\n", f.Mode.String(), f.Path, len(f.Content), f.Content) + mode := util.IfZero(f.Mode, EntryModeBlob) + _, _ = fmt.Fprintf(&buf, "M %s inline %s\ndata %d\n%s\n", mode.String(), f.Path, len(f.Content), f.Content) } } buf.WriteString("done\n") diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index f524afc465..1b17aec973 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -503,16 +503,15 @@ func (diffFile *DiffFile) prepareDiffRenderDetail(ctx context.Context, gitRepo * // * for "bin" type: need the pre-fetched buffer to detect content type (e.g.: help to render image diff) // * for "text" type: need to read up to "highlight limit size" to do full-file-highlighting contentLimit := util.Iif(diffFile.IsBin, typesniffer.SniffContentSize, MaxFullFileHighlightSizeLimit) - var leftLineCount, rightLineCount int var leftBlobType, rightBlobType typesniffer.SniffedType if (diffFile.Type == DiffFileDel || diffFile.Type == DiffFileChange) && leftCommit != nil { c := getCommitFileBlobAndLimitedContent(ctx, gitRepo, leftCommit, diffFile.OldName, contentLimit) - diffFile.LeftBlob, diffFile.LeftBlobSize, leftLineCount, ret.leftContent = c.gitBlob, c.blobSize, c.lineCount, c.limitedContent + diffFile.LeftBlob, diffFile.LeftBlobSize, ret.leftLineCount, ret.leftContent = c.gitBlob, c.blobSize, c.lineCount, c.limitedContent leftBlobType = typesniffer.DetectContentType(ret.leftContent.buf.Bytes()) } if (diffFile.Type == DiffFileAdd || diffFile.Type == DiffFileChange) && rightCommit != nil { c := getCommitFileBlobAndLimitedContent(ctx, gitRepo, rightCommit, diffFile.OldName, contentLimit) - diffFile.RightBlob, diffFile.RightBlobSize, rightLineCount, ret.rightContent = c.gitBlob, c.blobSize, c.lineCount, c.limitedContent + diffFile.RightBlob, diffFile.RightBlobSize, ret.rightLineCount, ret.rightContent = c.gitBlob, c.blobSize, c.lineCount, c.limitedContent rightBlobType = typesniffer.DetectContentType(ret.rightContent.buf.Bytes()) } @@ -534,7 +533,7 @@ func (diffFile *DiffFile) prepareDiffRenderDetail(ctx context.Context, gitRepo * // check whether the text file diff needs a tail section lastSection := diffFile.Sections[len(diffFile.Sections)-1] lastLine := lastSection.Lines[len(lastSection.Lines)-1] - if leftLineCount <= lastLine.LeftIdx || rightLineCount <= lastLine.RightIdx { + if ret.leftLineCount <= lastLine.LeftIdx || ret.rightLineCount <= lastLine.RightIdx { return ret } ret.needTailSection = true diff --git a/services/gitdiff/gitdiff_render_test.go b/services/gitdiff/gitdiff_render_test.go new file mode 100644 index 0000000000..8146f725ca --- /dev/null +++ b/services/gitdiff/gitdiff_render_test.go @@ -0,0 +1,118 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitdiff + +import ( + "path/filepath" + "strings" + "testing" + + "gitea.dev/modules/git" + "gitea.dev/modules/setting" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetDiffForRender(t *testing.T) { + repoDir := filepath.Join(t.TempDir(), "temp-repo") + require.NoError(t, git.InitRepositoryLocal(t.Context(), repoDir, false, git.Sha1ObjectFormat.Name())) + + contentLeft := strings.Repeat("a\n", 20) + + "mark1\n" + + strings.Repeat("b\n", 10) + + "mark2\n" + + strings.Repeat("c\n", 40) + + "mark3\n" + + strings.Repeat("a\n", 30) + contentRight := strings.Repeat("a\n", 20-1) + + "mark1-x\n" + + strings.Repeat("b\n", 10+3) + + "mark2-x\n" + + strings.Repeat("c\n", 40-5) + + "mark3-x\n" + + strings.Repeat("a\n", 30) + + gitRepo, err := git.OpenRepositoryLocal(t.Context(), repoDir) + require.NoError(t, err) + defer gitRepo.Close() + + require.NoError(t, git.ForceFastImport(t.Context(), gitRepo, []git.FastImportCommit{ + {Ref: "refs/heads/b1", Files: []git.FastImportFile{{Path: "foo.txt", Content: contentLeft}}}, + {Ref: "refs/heads/b2", Files: []git.FastImportFile{{Path: "foo.txt", Content: contentRight}}}, + })) + + beforeCommit, err := gitRepo.GetBranchCommit(t.Context(), "b1") + require.NoError(t, err) + afterCommit, err := gitRepo.GetBranchCommit(t.Context(), "b2") + require.NoError(t, err) + + diff, err := GetDiffForRender(t.Context(), "/any/repo-link", gitRepo, &DiffOptions{ + BeforeCommitID: beforeCommit.ID.String(), + AfterCommitID: afterCommit.ID.String(), + MaxLines: setting.Git.MaxGitDiffLines, + MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, + MaxFiles: setting.Git.MaxGitDiffFiles, + }) + require.NoError(t, err) + require.Len(t, diff.Files, 1) + + type section struct { + ExpandDirection string + + LastLeftIdx int + LastRightIdx int + LeftIdx int + RightIdx int + LeftHunkSize int + RightHunkSize int + } + expectedSections := []section{ + { + ExpandDirection: "up", + LeftIdx: 17, + RightIdx: 17, + LeftHunkSize: 8, + RightHunkSize: 7, + }, + { + ExpandDirection: "single", + LastLeftIdx: 24, + LastRightIdx: 23, + LeftIdx: 29, + RightIdx: 28, + LeftHunkSize: 7, + RightHunkSize: 10, + }, + { + ExpandDirection: "updown", + LastLeftIdx: 35, + LastRightIdx: 37, + LeftIdx: 65, + RightIdx: 67, + LeftHunkSize: 12, + RightHunkSize: 7, + }, + { + ExpandDirection: "down", + LastLeftIdx: 76, + LastRightIdx: 73, + LeftIdx: 104, + RightIdx: 101, + }, + } + for idx, exp := range expectedSections { + secLine := diff.Files[0].Sections[idx].Lines[0] // line 0 should be the "section info" + actual := section{ + ExpandDirection: secLine.GetExpandDirection(), + LastLeftIdx: secLine.SectionInfo.LastLeftIdx, + LastRightIdx: secLine.SectionInfo.LastRightIdx, + LeftIdx: secLine.SectionInfo.LeftIdx, + RightIdx: secLine.SectionInfo.RightIdx, + LeftHunkSize: secLine.SectionInfo.LeftHunkSize, + RightHunkSize: secLine.SectionInfo.RightHunkSize, + } + assert.Equal(t, exp, actual, "idx=%d", idx) + } +}