Skip to content

Commit d3cd05b

Browse files
committed
Enable show more files in diff for git <2.31
Unfortunately due to a misread on my behalf I missed that git diff only learned --skip-to in version 2.31.0. Thus this functionality was not working on older versions of git. This PR adds a handler that simply allows for us to skip reading the diffs until we find the correct file to skip to. Fix go-gitea#17731 Signed-off-by: Andrew Thornton <[email protected]>
1 parent a8fd765 commit d3cd05b

File tree

4 files changed

+237
-17
lines changed

4 files changed

+237
-17
lines changed

modules/repofiles/temp_repo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
302302
if err := git.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD").
303303
RunInDirTimeoutEnvFullPipelineFunc(nil, 30*time.Second, t.basePath, stdoutWriter, stderr, nil, func(ctx context.Context, cancel context.CancelFunc) error {
304304
_ = stdoutWriter.Close()
305-
diff, finalErr = gitdiff.ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader)
305+
diff, finalErr = gitdiff.ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "")
306306
if finalErr != nil {
307307
log.Error("ParsePatch: %v", finalErr)
308308
cancel()

services/gitdiff/csv_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ c,d,e`,
188188
}
189189

190190
for n, c := range cases {
191-
diff, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(c.diff))
191+
diff, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(c.diff), "")
192192
if err != nil {
193193
t.Errorf("ParsePatch failed: %s", err)
194194
}

services/gitdiff/gitdiff.go

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -694,9 +694,11 @@ func (diff *Diff) LoadComments(issue *models.Issue, currentUser *models.User) er
694694
const cmdDiffHead = "diff --git "
695695

696696
// ParsePatch builds a Diff object from a io.Reader and some parameters.
697-
func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*Diff, error) {
697+
func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader, skipTo string) (*Diff, error) {
698698
var curFile *DiffFile
699699

700+
skipping := skipTo != ""
701+
700702
diff := &Diff{Files: make([]*DiffFile, 0)}
701703

702704
sb := strings.Builder{}
@@ -739,6 +741,20 @@ parsingLoop:
739741
}
740742

741743
curFile = createDiffFile(diff, line)
744+
if skipping {
745+
if curFile.Name != skipTo {
746+
line, err = skipToNextDiffHead(input)
747+
if err != nil {
748+
if err == io.EOF {
749+
return diff, nil
750+
}
751+
return diff, err
752+
}
753+
continue
754+
}
755+
skipping = false
756+
}
757+
742758
diff.Files = append(diff.Files, curFile)
743759

744760
// 2. It is followed by one or more extended header lines:
@@ -955,6 +971,36 @@ parsingLoop:
955971
return diff, nil
956972
}
957973

974+
func skipToNextDiffHead(input *bufio.Reader) (line string, err error) {
975+
// need to skip until the next cmdDiffHead
976+
isFragment, wasFragment := false, false
977+
var lineBytes []byte
978+
for {
979+
lineBytes, isFragment, err = input.ReadLine()
980+
if err != nil {
981+
return
982+
}
983+
if wasFragment {
984+
wasFragment = isFragment
985+
continue
986+
}
987+
if bytes.HasPrefix(lineBytes, []byte(cmdDiffHead)) {
988+
break
989+
}
990+
wasFragment = isFragment
991+
}
992+
line = string(lineBytes)
993+
if isFragment {
994+
var tail string
995+
tail, err = input.ReadString('\n')
996+
if err != nil {
997+
return
998+
}
999+
line += tail
1000+
}
1001+
return
1002+
}
1003+
9581004
func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio.Reader) (lineBytes []byte, isFragment bool, err error) {
9591005
sb := strings.Builder{}
9601006

@@ -1279,8 +1325,11 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
12791325
diffArgs = append(diffArgs, afterCommitID)
12801326
beforeCommitID = actualBeforeCommitID
12811327
}
1328+
12821329
if skipTo != "" {
1283-
diffArgs = append(diffArgs, "--skip-to="+skipTo)
1330+
if git.CheckGitVersionAtLeast("2.31") == nil {
1331+
diffArgs = append(diffArgs, "--skip-to="+skipTo)
1332+
}
12841333
}
12851334
cmd := exec.CommandContext(ctx, git.GitExecutable, diffArgs...)
12861335

@@ -1299,7 +1348,7 @@ func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID,
12991348
pid := process.GetManager().Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cancel)
13001349
defer process.GetManager().Remove(pid)
13011350

1302-
diff, err := ParsePatch(maxLines, maxLineCharacters, maxFiles, stdout)
1351+
diff, err := ParsePatch(maxLines, maxLineCharacters, maxFiles, stdout, skipTo)
13031352
if err != nil {
13041353
return nil, fmt.Errorf("ParsePatch: %v", err)
13051354
}
@@ -1418,7 +1467,7 @@ func GetDiffCommitWithWhitespaceBehavior(gitRepo *git.Repository, commitID, skip
14181467
// CommentAsDiff returns c.Patch as *Diff
14191468
func CommentAsDiff(c *models.Comment) (*Diff, error) {
14201469
diff, err := ParsePatch(setting.Git.MaxGitDiffLines,
1421-
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(c.Patch))
1470+
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(c.Patch), "")
14221471
if err != nil {
14231472
log.Error("Unable to parse patch: %v", err)
14241473
return nil, err

services/gitdiff/gitdiff_test.go

Lines changed: 182 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,177 @@ func TestDiffToHTML(t *testing.T) {
104104
}, DiffLineAdd))
105105
}
106106

107+
func TestParsePatch_skipTo(t *testing.T) {
108+
type testcase struct {
109+
name string
110+
gitdiff string
111+
wantErr bool
112+
addition int
113+
deletion int
114+
oldFilename string
115+
filename string
116+
skipTo string
117+
}
118+
tests := []testcase{
119+
{
120+
name: "readme.md2readme.md",
121+
gitdiff: `diff --git "a/A \\ B" "b/A \\ B"
122+
--- "a/A \\ B"
123+
+++ "b/A \\ B"
124+
@@ -1,3 +1,6 @@
125+
# gitea-github-migrator
126+
+
127+
+ Build Status
128+
- Latest Release
129+
Docker Pulls
130+
+ cut off
131+
+ cut off
132+
diff --git "\\a/README.md" "\\b/README.md"
133+
--- "\\a/README.md"
134+
+++ "\\b/README.md"
135+
@@ -1,3 +1,6 @@
136+
# gitea-github-migrator
137+
+
138+
+ Build Status
139+
- Latest Release
140+
Docker Pulls
141+
+ cut off
142+
+ cut off
143+
`,
144+
addition: 4,
145+
deletion: 1,
146+
filename: "README.md",
147+
oldFilename: "README.md",
148+
skipTo: "README.md",
149+
},
150+
{
151+
name: "A \\ B",
152+
gitdiff: `diff --git "a/A \\ B" "b/A \\ B"
153+
--- "a/A \\ B"
154+
+++ "b/A \\ B"
155+
@@ -1,3 +1,6 @@
156+
# gitea-github-migrator
157+
+
158+
+ Build Status
159+
- Latest Release
160+
Docker Pulls
161+
+ cut off
162+
+ cut off`,
163+
addition: 4,
164+
deletion: 1,
165+
filename: "A \\ B",
166+
oldFilename: "A \\ B",
167+
skipTo: "A \\ B",
168+
},
169+
{
170+
name: "A \\ B",
171+
gitdiff: `diff --git "\\a/README.md" "\\b/README.md"
172+
--- "\\a/README.md"
173+
+++ "\\b/README.md"
174+
@@ -1,3 +1,6 @@
175+
# gitea-github-migrator
176+
+
177+
+ Build Status
178+
- Latest Release
179+
Docker Pulls
180+
+ cut off
181+
+ cut off
182+
diff --git "a/A \\ B" "b/A \\ B"
183+
--- "a/A \\ B"
184+
+++ "b/A \\ B"
185+
@@ -1,3 +1,6 @@
186+
# gitea-github-migrator
187+
+
188+
+ Build Status
189+
- Latest Release
190+
Docker Pulls
191+
+ cut off
192+
+ cut off`,
193+
addition: 4,
194+
deletion: 1,
195+
filename: "A \\ B",
196+
oldFilename: "A \\ B",
197+
skipTo: "A \\ B",
198+
},
199+
{
200+
name: "readme.md2readme.md",
201+
gitdiff: `diff --git "a/A \\ B" "b/A \\ B"
202+
--- "a/A \\ B"
203+
+++ "b/A \\ B"
204+
@@ -1,3 +1,6 @@
205+
# gitea-github-migrator
206+
+
207+
+ Build Status
208+
- Latest Release
209+
Docker Pulls
210+
+ cut off
211+
+ cut off
212+
diff --git "a/A \\ B" "b/A \\ B"
213+
--- "a/A \\ B"
214+
+++ "b/A \\ B"
215+
@@ -1,3 +1,6 @@
216+
# gitea-github-migrator
217+
+
218+
+ Build Status
219+
- Latest Release
220+
Docker Pulls
221+
+ cut off
222+
+ cut off
223+
diff --git "\\a/README.md" "\\b/README.md"
224+
--- "\\a/README.md"
225+
+++ "\\b/README.md"
226+
@@ -1,3 +1,6 @@
227+
# gitea-github-migrator
228+
+
229+
+ Build Status
230+
- Latest Release
231+
Docker Pulls
232+
+ cut off
233+
+ cut off
234+
`,
235+
addition: 4,
236+
deletion: 1,
237+
filename: "README.md",
238+
oldFilename: "README.md",
239+
skipTo: "README.md",
240+
},
241+
}
242+
for _, testcase := range tests {
243+
t.Run(testcase.name, func(t *testing.T) {
244+
got, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff), testcase.skipTo)
245+
if (err != nil) != testcase.wantErr {
246+
t.Errorf("ParsePatch(%q) error = %v, wantErr %v", testcase.name, err, testcase.wantErr)
247+
return
248+
}
249+
250+
gotMarshaled, _ := json.MarshalIndent(got, "", " ")
251+
if got.NumFiles != 1 {
252+
t.Errorf("ParsePath(%q) did not receive 1 file:\n%s", testcase.name, string(gotMarshaled))
253+
return
254+
}
255+
if got.TotalAddition != testcase.addition {
256+
t.Errorf("ParsePath(%q) does not have correct totalAddition %d, wanted %d", testcase.name, got.TotalAddition, testcase.addition)
257+
}
258+
if got.TotalDeletion != testcase.deletion {
259+
t.Errorf("ParsePath(%q) did not have correct totalDeletion %d, wanted %d", testcase.name, got.TotalDeletion, testcase.deletion)
260+
}
261+
file := got.Files[0]
262+
if file.Addition != testcase.addition {
263+
t.Errorf("ParsePath(%q) does not have correct file addition %d, wanted %d", testcase.name, file.Addition, testcase.addition)
264+
}
265+
if file.Deletion != testcase.deletion {
266+
t.Errorf("ParsePath(%q) did not have correct file deletion %d, wanted %d", testcase.name, file.Deletion, testcase.deletion)
267+
}
268+
if file.OldName != testcase.oldFilename {
269+
t.Errorf("ParsePath(%q) did not have correct OldName %q, wanted %q", testcase.name, file.OldName, testcase.oldFilename)
270+
}
271+
if file.Name != testcase.filename {
272+
t.Errorf("ParsePath(%q) did not have correct Name %q, wanted %q", testcase.name, file.Name, testcase.filename)
273+
}
274+
})
275+
}
276+
}
277+
107278
func TestParsePatch_singlefile(t *testing.T) {
108279
type testcase struct {
109280
name string
@@ -295,7 +466,7 @@ index 6961180..9ba1a00 100644
295466

296467
for _, testcase := range tests {
297468
t.Run(testcase.name, func(t *testing.T) {
298-
got, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff))
469+
got, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff), "")
299470
if (err != nil) != testcase.wantErr {
300471
t.Errorf("ParsePatch(%q) error = %v, wantErr %v", testcase.name, err, testcase.wantErr)
301472
return
@@ -344,21 +515,21 @@ index 0000000..6bb8f39
344515
diffBuilder.WriteString("+line" + strconv.Itoa(i) + "\n")
345516
}
346517
diff = diffBuilder.String()
347-
result, err := ParsePatch(20, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
518+
result, err := ParsePatch(20, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff), "")
348519
if err != nil {
349520
t.Errorf("There should not be an error: %v", err)
350521
}
351522
if !result.Files[0].IsIncomplete {
352523
t.Errorf("Files should be incomplete! %v", result.Files[0])
353524
}
354-
result, err = ParsePatch(40, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
525+
result, err = ParsePatch(40, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff), "")
355526
if err != nil {
356527
t.Errorf("There should not be an error: %v", err)
357528
}
358529
if result.Files[0].IsIncomplete {
359530
t.Errorf("Files should not be incomplete! %v", result.Files[0])
360531
}
361-
result, err = ParsePatch(40, 5, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
532+
result, err = ParsePatch(40, 5, setting.Git.MaxGitDiffFiles, strings.NewReader(diff), "")
362533
if err != nil {
363534
t.Errorf("There should not be an error: %v", err)
364535
}
@@ -389,14 +560,14 @@ index 0000000..6bb8f39
389560
diffBuilder.WriteString("+line" + strconv.Itoa(35) + "\n")
390561
diff = diffBuilder.String()
391562

392-
result, err = ParsePatch(20, 4096, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
563+
result, err = ParsePatch(20, 4096, setting.Git.MaxGitDiffFiles, strings.NewReader(diff), "")
393564
if err != nil {
394565
t.Errorf("There should not be an error: %v", err)
395566
}
396567
if !result.Files[0].IsIncomplete {
397568
t.Errorf("Files should be incomplete! %v", result.Files[0])
398569
}
399-
result, err = ParsePatch(40, 4096, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
570+
result, err = ParsePatch(40, 4096, setting.Git.MaxGitDiffFiles, strings.NewReader(diff), "")
400571
if err != nil {
401572
t.Errorf("There should not be an error: %v", err)
402573
}
@@ -415,7 +586,7 @@ index 0000000..6bb8f39
415586
Docker Pulls
416587
+ cut off
417588
+ cut off`
418-
result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
589+
result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff), "")
419590
if err != nil {
420591
t.Errorf("ParsePatch failed: %s", err)
421592
}
@@ -432,7 +603,7 @@ index 0000000..6bb8f39
432603
Docker Pulls
433604
+ cut off
434605
+ cut off`
435-
result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff2))
606+
result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff2), "")
436607
if err != nil {
437608
t.Errorf("ParsePatch failed: %s", err)
438609
}
@@ -449,7 +620,7 @@ index 0000000..6bb8f39
449620
Docker Pulls
450621
+ cut off
451622
+ cut off`
452-
result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff2a))
623+
result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff2a), "")
453624
if err != nil {
454625
t.Errorf("ParsePatch failed: %s", err)
455626
}
@@ -466,7 +637,7 @@ index 0000000..6bb8f39
466637
Docker Pulls
467638
+ cut off
468639
+ cut off`
469-
result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff3))
640+
result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff3), "")
470641
if err != nil {
471642
t.Errorf("ParsePatch failed: %s", err)
472643
}
@@ -557,6 +728,6 @@ func TestNoCrashes(t *testing.T) {
557728
}
558729
for _, testcase := range tests {
559730
// It shouldn't crash, so don't care about the output.
560-
ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff))
731+
ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff), "")
561732
}
562733
}

0 commit comments

Comments
 (0)