@@ -583,6 +583,7 @@ type DiffFile struct {
583
583
IsBin bool
584
584
IsLFSFile bool
585
585
IsRenamed bool
586
+ IsAmbiguous bool
586
587
IsSubmodule bool
587
588
Sections []* DiffSection
588
589
IsIncomplete bool
@@ -776,12 +777,32 @@ parsingLoop:
776
777
if strings .HasSuffix (line , " 160000\n " ) {
777
778
curFile .IsSubmodule = true
778
779
}
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
+ }
779
793
case strings .HasPrefix (line , "copy from " ):
780
794
curFile .IsRenamed = true
781
795
curFile .Type = DiffFileCopy
796
+ if curFile .IsAmbiguous {
797
+ curFile .OldName = line [len ("copy from " ) : len (line )- 1 ]
798
+ }
782
799
case strings .HasPrefix (line , "copy to " ):
783
800
curFile .IsRenamed = true
784
801
curFile .Type = DiffFileCopy
802
+ if curFile .IsAmbiguous {
803
+ curFile .Name = line [len ("copy to " ) : len (line )- 1 ]
804
+ curFile .IsAmbiguous = false
805
+ }
785
806
case strings .HasPrefix (line , "new file" ):
786
807
curFile .Type = DiffFileAdd
787
808
curFile .IsCreated = true
@@ -803,9 +824,35 @@ parsingLoop:
803
824
case strings .HasPrefix (line , "Binary" ):
804
825
curFile .IsBin = true
805
826
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
807
839
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
809
856
lineBytes , isFragment , err := parseHunks (curFile , maxLines , maxLineCharacters , input )
810
857
diff .TotalAddition += curFile .Addition
811
858
diff .TotalDeletion += curFile .Deletion
@@ -1056,13 +1103,33 @@ func createDiffFile(diff *Diff, line string) *DiffFile {
1056
1103
1057
1104
rd := strings .NewReader (line [len (cmdDiffHead ):] + " " )
1058
1105
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
+
1061
1127
curFile .IsRenamed = curFile .Name != curFile .OldName
1062
1128
return curFile
1063
1129
}
1064
1130
1065
- func readFileName (rd * strings.Reader ) string {
1131
+ func readFileName (rd * strings.Reader ) (string , bool ) {
1132
+ ambiguity := false
1066
1133
var name string
1067
1134
char , _ := rd .ReadByte ()
1068
1135
_ = rd .UnreadByte ()
@@ -1072,9 +1139,24 @@ func readFileName(rd *strings.Reader) string {
1072
1139
name = name [1 :]
1073
1140
}
1074
1141
} 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
1075
1144
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
1076
1158
}
1077
- return name [2 :]
1159
+ return name [2 :], ambiguity
1078
1160
}
1079
1161
1080
1162
// GetDiffRange builds a Diff between two commits of a repository.
@@ -1191,6 +1273,7 @@ func CommentAsDiff(c *models.Comment) (*Diff, error) {
1191
1273
diff , err := ParsePatch (setting .Git .MaxGitDiffLines ,
1192
1274
setting .Git .MaxGitDiffLineCharacters , setting .Git .MaxGitDiffFiles , strings .NewReader (c .Patch ))
1193
1275
if err != nil {
1276
+ log .Error ("Unable to parse patch: %v" , err )
1194
1277
return nil , err
1195
1278
}
1196
1279
if len (diff .Files ) == 0 {
0 commit comments