Skip to content

Commit bc2d0f3

Browse files
committed
Handle unquoted comment patch files
When making comment patches unfortunately the patch does not always quote the filename This makes the diff --git header ambiguous again. This PR finally adds handling for ambiguity in to parse patch Fix #14812 Signed-off-by: Andrew Thornton <[email protected]>
1 parent a18cdea commit bc2d0f3

File tree

2 files changed

+157
-14
lines changed

2 files changed

+157
-14
lines changed

services/gitdiff/gitdiff.go

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ type DiffFile struct {
583583
IsBin bool
584584
IsLFSFile bool
585585
IsRenamed bool
586+
IsAmbiguous bool
586587
IsSubmodule bool
587588
Sections []*DiffSection
588589
IsIncomplete bool
@@ -776,12 +777,32 @@ parsingLoop:
776777
if strings.HasSuffix(line, " 160000\n") {
777778
curFile.IsSubmodule = true
778779
}
780+
case strings.HasPrefix(line, "rename from "):
781+
curFile.IsRenamed = true
782+
curFile.Type = DiffFileRename
783+
if curFile.IsAmbiguous {
784+
curFile.OldName = line[len("rename from ") : len(line)-1]
785+
}
786+
case strings.HasPrefix(line, "rename to "):
787+
curFile.IsRenamed = true
788+
curFile.Type = DiffFileRename
789+
if curFile.IsAmbiguous {
790+
curFile.Name = line[len("rename to ") : len(line)-1]
791+
curFile.IsAmbiguous = false
792+
}
779793
case strings.HasPrefix(line, "copy from "):
780794
curFile.IsRenamed = true
781795
curFile.Type = DiffFileCopy
796+
if curFile.IsAmbiguous {
797+
curFile.OldName = line[len("copy from ") : len(line)-1]
798+
}
782799
case strings.HasPrefix(line, "copy to "):
783800
curFile.IsRenamed = true
784801
curFile.Type = DiffFileCopy
802+
if curFile.IsAmbiguous {
803+
curFile.Name = line[len("copy to ") : len(line)-1]
804+
curFile.IsAmbiguous = false
805+
}
785806
case strings.HasPrefix(line, "new file"):
786807
curFile.Type = DiffFileAdd
787808
curFile.IsCreated = true
@@ -803,9 +824,35 @@ parsingLoop:
803824
case strings.HasPrefix(line, "Binary"):
804825
curFile.IsBin = true
805826
case strings.HasPrefix(line, "--- "):
806-
// Do nothing with this line
827+
// Handle ambiguous filenames
828+
if curFile.IsAmbiguous {
829+
if len(line) > 6 && line[4] == 'a' {
830+
curFile.OldName = line[6 : len(line)-1]
831+
if line[len(line)-2] == '\t' {
832+
curFile.OldName = curFile.OldName[:len(curFile.OldName)-1]
833+
}
834+
} else {
835+
curFile.OldName = ""
836+
}
837+
}
838+
// Otherwise do nothing with this line
807839
case strings.HasPrefix(line, "+++ "):
808-
// Do nothing with this line
840+
// Handle ambiguous filenames
841+
if curFile.IsAmbiguous {
842+
if len(line) > 6 && line[4] == 'b' {
843+
curFile.Name = line[6 : len(line)-1]
844+
if line[len(line)-2] == '\t' {
845+
curFile.Name = curFile.Name[:len(curFile.Name)-1]
846+
}
847+
if curFile.OldName == "" {
848+
curFile.OldName = curFile.Name
849+
}
850+
} else {
851+
curFile.Name = curFile.OldName
852+
}
853+
curFile.IsAmbiguous = false
854+
}
855+
// Otherwise do nothing with this line, but now switch to parsing hunks
809856
lineBytes, isFragment, err := parseHunks(curFile, maxLines, maxLineCharacters, input)
810857
diff.TotalAddition += curFile.Addition
811858
diff.TotalDeletion += curFile.Deletion
@@ -1056,13 +1103,33 @@ func createDiffFile(diff *Diff, line string) *DiffFile {
10561103

10571104
rd := strings.NewReader(line[len(cmdDiffHead):] + " ")
10581105
curFile.Type = DiffFileChange
1059-
curFile.OldName = readFileName(rd)
1060-
curFile.Name = readFileName(rd)
1106+
oldNameAmbiguity := false
1107+
newNameAmbiguity := false
1108+
1109+
curFile.OldName, oldNameAmbiguity = readFileName(rd)
1110+
curFile.Name, newNameAmbiguity = readFileName(rd)
1111+
if oldNameAmbiguity && newNameAmbiguity {
1112+
curFile.IsAmbiguous = true
1113+
// OK we should bet that the oldName and the newName are the same if they can be made to be same
1114+
// So we need to start again ...
1115+
if (len(line)-len(cmdDiffHead)-1)%2 == 0 {
1116+
// diff --git a/b b/b b/b b/b b/b b/b
1117+
//
1118+
midpoint := (len(line) + len(cmdDiffHead) - 1) / 2
1119+
new, old := line[len(cmdDiffHead):midpoint], line[midpoint+1:]
1120+
if len(new) > 2 && len(old) > 2 && new[2:] == old[2:] {
1121+
curFile.OldName = old[2:]
1122+
curFile.Name = old[2:]
1123+
}
1124+
}
1125+
}
1126+
10611127
curFile.IsRenamed = curFile.Name != curFile.OldName
10621128
return curFile
10631129
}
10641130

1065-
func readFileName(rd *strings.Reader) string {
1131+
func readFileName(rd *strings.Reader) (string, bool) {
1132+
ambiguity := false
10661133
var name string
10671134
char, _ := rd.ReadByte()
10681135
_ = rd.UnreadByte()
@@ -1072,9 +1139,24 @@ func readFileName(rd *strings.Reader) string {
10721139
name = name[1:]
10731140
}
10741141
} else {
1142+
// This technique is potentially ambiguous it may not be possible to uniquely identify the filenames from the diff line alone
1143+
ambiguity = true
10751144
fmt.Fscanf(rd, "%s ", &name)
1145+
char, _ := rd.ReadByte()
1146+
_ = rd.UnreadByte()
1147+
for !(char == 0 || char == '"' || char == 'b') {
1148+
var suffix string
1149+
fmt.Fscanf(rd, "%s ", &suffix)
1150+
name += " " + suffix
1151+
char, _ = rd.ReadByte()
1152+
_ = rd.UnreadByte()
1153+
}
1154+
}
1155+
if len(name) < 2 {
1156+
log.Error("Unable to determine name from reader: %v", rd)
1157+
return "", true
10761158
}
1077-
return name[2:]
1159+
return name[2:], ambiguity
10781160
}
10791161

10801162
// GetDiffRange builds a Diff between two commits of a repository.
@@ -1191,6 +1273,7 @@ func CommentAsDiff(c *models.Comment) (*Diff, error) {
11911273
diff, err := ParsePatch(setting.Git.MaxGitDiffLines,
11921274
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(c.Patch))
11931275
if err != nil {
1276+
log.Error("Unable to parse patch: %v", err)
11941277
return nil, err
11951278
}
11961279
if len(diff.Files) == 0 {

services/gitdiff/gitdiff_test.go

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,66 @@ rename to a b/a a/file b/b file
208208
oldFilename: "a b/file b/a a/file",
209209
filename: "a b/a a/file b/b file",
210210
},
211+
{
212+
name: "ambiguous deleted",
213+
gitdiff: `diff --git a/b b/b b/b b/b
214+
deleted file mode 100644
215+
index 92e798b..0000000
216+
--- a/b b/b` + "\t" + `
217+
+++ /dev/null
218+
@@ -1 +0,0 @@
219+
-b b/b
220+
`,
221+
oldFilename: "b b/b",
222+
filename: "b b/b",
223+
addition: 0,
224+
deletion: 1,
225+
},
226+
{
227+
name: "ambiguous addition",
228+
gitdiff: `diff --git a/b b/b b/b b/b
229+
new file mode 100644
230+
index 0000000..92e798b
231+
--- /dev/null
232+
+++ b/b b/b` + "\t" + `
233+
@@ -0,0 +1 @@
234+
+b b/b
235+
`,
236+
oldFilename: "b b/b",
237+
filename: "b b/b",
238+
addition: 1,
239+
deletion: 0,
240+
},
241+
{
242+
name: "rename",
243+
gitdiff: `diff --git a/b b/b b/b b/b b/b b/b
244+
similarity index 100%
245+
rename from b b/b b/b b/b b/b
246+
rename to b
247+
`,
248+
oldFilename: "b b/b b/b b/b b/b",
249+
filename: "b",
250+
},
251+
{
252+
name: "ambiguous 1",
253+
gitdiff: `diff --git a/b b/b b/b b/b b/b b/b
254+
similarity index 100%
255+
rename from b b/b b/b b/b b/b
256+
rename to b
257+
`,
258+
oldFilename: "b b/b b/b b/b b/b",
259+
filename: "b",
260+
},
261+
{
262+
name: "ambiguous 2",
263+
gitdiff: `diff --git a/b b/b b/b b/b b/b b/b
264+
similarity index 100%
265+
rename from b b/b b/b b/b
266+
rename to b b/b
267+
`,
268+
oldFilename: "b b/b b/b b/b",
269+
filename: "b b/b",
270+
},
211271
{
212272
name: "minuses-and-pluses",
213273
gitdiff: `diff --git a/minuses-and-pluses b/minuses-and-pluses
@@ -235,32 +295,32 @@ index 6961180..9ba1a00 100644
235295
t.Run(testcase.name, func(t *testing.T) {
236296
got, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff))
237297
if (err != nil) != testcase.wantErr {
238-
t.Errorf("ParsePatch() error = %v, wantErr %v", err, testcase.wantErr)
298+
t.Errorf("ParsePatch(%q) error = %v, wantErr %v", testcase.name, err, testcase.wantErr)
239299
return
240300
}
241301
gotMarshaled, _ := json.MarshalIndent(got, " ", " ")
242302
if got.NumFiles != 1 {
243-
t.Errorf("ParsePath() did not receive 1 file:\n%s", string(gotMarshaled))
303+
t.Errorf("ParsePath(%q) did not receive 1 file:\n%s", testcase.name, string(gotMarshaled))
244304
return
245305
}
246306
if got.TotalAddition != testcase.addition {
247-
t.Errorf("ParsePath() does not have correct totalAddition %d, wanted %d", got.TotalAddition, testcase.addition)
307+
t.Errorf("ParsePath(%q) does not have correct totalAddition %d, wanted %d", testcase.name, got.TotalAddition, testcase.addition)
248308
}
249309
if got.TotalDeletion != testcase.deletion {
250-
t.Errorf("ParsePath() did not have correct totalDeletion %d, wanted %d", got.TotalDeletion, testcase.deletion)
310+
t.Errorf("ParsePath(%q) did not have correct totalDeletion %d, wanted %d", testcase.name, got.TotalDeletion, testcase.deletion)
251311
}
252312
file := got.Files[0]
253313
if file.Addition != testcase.addition {
254-
t.Errorf("ParsePath() does not have correct file addition %d, wanted %d", file.Addition, testcase.addition)
314+
t.Errorf("ParsePath(%q) does not have correct file addition %d, wanted %d", testcase.name, file.Addition, testcase.addition)
255315
}
256316
if file.Deletion != testcase.deletion {
257-
t.Errorf("ParsePath() did not have correct file deletion %d, wanted %d", file.Deletion, testcase.deletion)
317+
t.Errorf("ParsePath(%q) did not have correct file deletion %d, wanted %d", testcase.name, file.Deletion, testcase.deletion)
258318
}
259319
if file.OldName != testcase.oldFilename {
260-
t.Errorf("ParsePath() did not have correct OldName %s, wanted %s", file.OldName, testcase.oldFilename)
320+
t.Errorf("ParsePath(%q) did not have correct OldName %q, wanted %q", testcase.name, file.OldName, testcase.oldFilename)
261321
}
262322
if file.Name != testcase.filename {
263-
t.Errorf("ParsePath() did not have correct Name %s, wanted %s", file.Name, testcase.filename)
323+
t.Errorf("ParsePath(%q) did not have correct Name %q, wanted %q", testcase.name, file.Name, testcase.filename)
264324
}
265325
})
266326
}

0 commit comments

Comments
 (0)