From 79535f4e012bf5d93d757c72e6e7cf66df73060b Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Sun, 9 Aug 2026 13:13:22 +0200 Subject: [PATCH] fix(gitdiff): render exact-limit diffs and zero-limit comments (#38838) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes the treatment of various edge cases related to diff limits. - Exact-limit diffs: a file containing exactly the configured maximum was incorrectly treated as too large and hidden behind “Load diff.” It now renders normally. Co-authored-by: wxiaoguang --- services/gitdiff/gitdiff.go | 36 +++++++++++++++--------------- services/gitdiff/gitdiff_test.go | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 6fde7daf68..04fdf46a5e 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -1010,15 +1010,17 @@ func newDiffSectionForDiffFile(curFile *DiffFile) *DiffSection { func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio.Reader) (lineBytes []byte, isFragment bool, err error) { sb := strings.Builder{} - var ( - curSection *DiffSection - curFileLinesCount int - curFileLFSPrefix bool - ) + var curSection *DiffSection + curFileLFSPrefix := false lastLeftIdx := -1 leftLine, rightLine := 1, 1 + curFileLinesCount := 0 + curFileLineReachesLimit := func() bool { + return maxLines > -1 && curFileLinesCount >= maxLines + } + for { for isFragment { curFile.IsIncomplete = true @@ -1045,7 +1047,7 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact switch lineBytes[0] { case '@': - if maxLines > -1 && curFileLinesCount >= maxLines { + if curFileLineReachesLimit() { curFile.IsIncomplete = true continue } @@ -1068,8 +1070,8 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact lastLeftIdx = -1 curFile.Sections = append(curFile.Sections, curSection) - // FIXME: the "-1" can't be right, these "line idx" are all 1-based, maybe there are other bugs that covers this bug. - lineSectionInfo := newDiffLineSectionInfo(curFile, line, leftLine-1, rightLine-1) + // use "idx-1" as "last idx" (the last line before this hunk) + lineSectionInfo := newDiffLineSectionInfo(curFile, line, leftLine-1 /*lastLeftIdx*/, rightLine-1 /*lastRightIdx*/) diffLine := &DiffLine{ Type: DiffLineSection, Content: line, @@ -1082,10 +1084,6 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact rightLine = lineSectionInfo.RightIdx continue case '\\': - if maxLines > -1 && curFileLinesCount >= maxLines { - curFile.IsIncomplete = true - continue - } // This is used only to indicate that the current file does not have a terminal newline if !bytes.Equal(lineBytes, []byte("\\ No newline at end of file")) { return nil, false, fmt.Errorf("unexpected line in hunk: %s", string(lineBytes)) @@ -1094,12 +1092,13 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact // FIXME: we should be putting a marker at the end of the file if there is no terminal new line continue case '+': - curFileLinesCount++ curFile.Addition++ - if maxLines > -1 && curFileLinesCount >= maxLines { + if curFileLineReachesLimit() { curFile.IsIncomplete = true continue } + curFileLinesCount++ + diffLine := &DiffLine{Type: DiffLineAdd, RightIdx: rightLine, Match: -1} rightLine++ if curSection == nil { @@ -1125,12 +1124,13 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact } } case '-': - curFileLinesCount++ curFile.Deletion++ - if maxLines > -1 && curFileLinesCount >= maxLines { + if curFileLineReachesLimit() { curFile.IsIncomplete = true continue } + curFileLinesCount++ + diffLine := &DiffLine{Type: DiffLineDel, LeftIdx: leftLine, Match: -1} if leftLine > 0 { leftLine++ @@ -1153,11 +1153,11 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact } } case ' ': - curFileLinesCount++ - if maxLines > -1 && curFileLinesCount >= maxLines { + if curFileLineReachesLimit() { curFile.IsIncomplete = true continue } + curFileLinesCount++ diffLine := &DiffLine{Type: DiffLinePlain, LeftIdx: leftLine, RightIdx: rightLine} leftLine++ rightLine++ diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index d483ac60a7..aaf8135e39 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -19,6 +19,7 @@ import ( "gitea.dev/modules/json" "gitea.dev/modules/setting" "gitea.dev/modules/translation" + "gitea.dev/modules/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -548,6 +549,43 @@ index 0000000..6bb8f39 } } +func TestParsePatchExactLineLimit(t *testing.T) { + for _, test := range []struct { + name, hunk string + limit, lines int + incomplete bool + }{ + {name: "zero", limit: 0, hunk: "@@ -1,3 +1,3 @@\n one\n two\n three\n", incomplete: true}, + {name: "one", limit: 1, lines: 1, hunk: "@@ -1,3 +1,3 @@\n one\n two\n three\n", incomplete: true}, + {name: "N plus one", limit: 2, lines: 2, hunk: "@@ -1,3 +1,3 @@\n one\n two\n three\n", incomplete: true}, + {name: "N", limit: 3, lines: 3, hunk: "@@ -1,3 +1,3 @@\n one\n two\n three\n"}, + {name: "addition", limit: 1, lines: 1, hunk: "@@ -0,0 +1 @@\n+one\n"}, + {name: "deletion", limit: 1, lines: 1, hunk: "@@ -1 +0,0 @@\n-one\n"}, + {name: "marker has no cost", limit: 1, lines: 1, hunk: "@@ -1 +1 @@\n line\n\\ No newline at end of file\n"}, + {name: "hunk at capacity", limit: 1, lines: 1, hunk: "@@ -1 +1 @@\n one\n@@ -3 +3 @@\n three\n", incomplete: true}, + } { + t.Run(test.name, func(t *testing.T) { + patch := "diff --git a/file b/file\n--- a/file\n+++ b/file\n" + test.hunk + diff, err := ParsePatch(t.Context(), test.limit, 5000, 10, strings.NewReader(patch), "") + require.NoError(t, err) + require.Len(t, diff.Files, 1) + diffFile := diff.Files[0] + if test.limit == 0 { + require.Len(t, diffFile.Sections, 0) + } else { + require.Len(t, diffFile.Sections, 1) + diffSection := diffFile.Sections[0] + lineSecCount := 0 + for _, line := range diffSection.Lines { + lineSecCount += util.Iif(line.Type == DiffLineSection, 1, 0) + } + assert.Equal(t, test.lines, len(diffSection.Lines)-lineSecCount) // actual diff lines + assert.Equal(t, test.incomplete, diffFile.IsIncomplete) + } + }) + } +} + func setupDefaultDiff() *Diff { return &Diff{ Files: []*DiffFile{