@@ -167,7 +167,7 @@ class DiffIndex(list):
167167 # R = Renamed
168168 # M = Modified
169169 # T = Changed in the type
170- change_type = ("A" , "D" , "R" , "M" , "T" )
170+ change_type = ("A" , "C" , " D" , "R" , "M" , "T" )
171171
172172 def iter_change_type (self , change_type ):
173173 """
@@ -193,6 +193,8 @@ def iter_change_type(self, change_type):
193193 yield diff
194194 elif change_type == "D" and diff .deleted_file :
195195 yield diff
196+ elif change_type == "C" and diff .copied_file :
197+ yield diff
196198 elif change_type == "R" and diff .renamed :
197199 yield diff
198200 elif change_type == "M" and diff .a_blob and diff .b_blob and diff .a_blob != diff .b_blob :
@@ -235,14 +237,17 @@ class Diff(object):
235237 # precompiled regex
236238 re_header = re .compile (br"""
237239 ^diff[ ]--git
238- [ ](?P<a_path_fallback>"?a /.+?"?)[ ](?P<b_path_fallback>"?b /.+?"?)\n
240+ [ ](?P<a_path_fallback>"?[ab] /.+?"?)[ ](?P<b_path_fallback>"?[ab] /.+?"?)\n
239241 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
240242 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
241243 (?:^similarity[ ]index[ ]\d+%\n
242244 ^rename[ ]from[ ](?P<rename_from>.*)\n
243245 ^rename[ ]to[ ](?P<rename_to>.*)(?:\n|$))?
244246 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
245247 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
248+ (?:^similarity[ ]index[ ]\d+%\n
249+ ^copy[ ]from[ ].*\n
250+ ^copy[ ]to[ ](?P<copied_file_name>.*)(?:\n|$))?
246251 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
247252 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
248253 (?:^---[ ](?P<a_path>[^\t\n\r\f\v]*)[\t\r\f\v]*(?:\n|$))?
@@ -253,11 +258,11 @@ class Diff(object):
253258 NULL_BIN_SHA = b"\0 " * 20
254259
255260 __slots__ = ("a_blob" , "b_blob" , "a_mode" , "b_mode" , "a_rawpath" , "b_rawpath" ,
256- "new_file" , "deleted_file" , "raw_rename_from " , "raw_rename_to " ,
257- "diff" , "change_type" , "score" )
261+ "new_file" , "deleted_file" , "copied_file " , "raw_rename_from " ,
262+ "raw_rename_to" , " diff" , "change_type" , "score" )
258263
259264 def __init__ (self , repo , a_rawpath , b_rawpath , a_blob_id , b_blob_id , a_mode ,
260- b_mode , new_file , deleted_file , raw_rename_from ,
265+ b_mode , new_file , deleted_file , copied_file , raw_rename_from ,
261266 raw_rename_to , diff , change_type , score ):
262267
263268 self .a_mode = a_mode
@@ -273,6 +278,14 @@ def __init__(self, repo, a_rawpath, b_rawpath, a_blob_id, b_blob_id, a_mode,
273278 if self .b_mode :
274279 self .b_mode = mode_str_to_int (self .b_mode )
275280
281+ # Determine whether this diff references a submodule, if it does then
282+ # we need to overwrite "repo" to the corresponding submodule's repo instead
283+ if repo and a_rawpath :
284+ for submodule in repo .submodules :
285+ if submodule .path == a_rawpath .decode ("utf-8" ):
286+ repo = submodule .module ()
287+ break
288+
276289 if a_blob_id is None or a_blob_id == self .NULL_HEX_SHA :
277290 self .a_blob = None
278291 else :
@@ -285,6 +298,7 @@ def __init__(self, repo, a_rawpath, b_rawpath, a_blob_id, b_blob_id, a_mode,
285298
286299 self .new_file = new_file
287300 self .deleted_file = deleted_file
301+ self .copied_file = copied_file
288302
289303 # be clear and use None instead of empty strings
290304 assert raw_rename_from is None or isinstance (raw_rename_from , binary_type )
@@ -336,6 +350,8 @@ def __str__(self):
336350 msg += '\n file deleted in rhs'
337351 if self .new_file :
338352 msg += '\n file added in rhs'
353+ if self .copied_file :
354+ msg += '\n file %r copied from %r' % (self .b_path , self .a_path )
339355 if self .rename_from :
340356 msg += '\n file renamed from %r' % self .rename_from
341357 if self .rename_to :
@@ -420,11 +436,12 @@ def _index_from_patch_format(cls, repo, proc):
420436 a_path_fallback , b_path_fallback , \
421437 old_mode , new_mode , \
422438 rename_from , rename_to , \
423- new_file_mode , deleted_file_mode , \
439+ new_file_mode , deleted_file_mode , copied_file_name , \
424440 a_blob_id , b_blob_id , b_mode , \
425441 a_path , b_path = _header .groups ()
426442
427- new_file , deleted_file = bool (new_file_mode ), bool (deleted_file_mode )
443+ new_file , deleted_file , copied_file = \
444+ bool (new_file_mode ), bool (deleted_file_mode ), bool (copied_file_name )
428445
429446 a_path = cls ._pick_best_path (a_path , rename_from , a_path_fallback )
430447 b_path = cls ._pick_best_path (b_path , rename_to , b_path_fallback )
@@ -446,7 +463,7 @@ def _index_from_patch_format(cls, repo, proc):
446463 b_blob_id and b_blob_id .decode (defenc ),
447464 a_mode and a_mode .decode (defenc ),
448465 b_mode and b_mode .decode (defenc ),
449- new_file , deleted_file ,
466+ new_file , deleted_file , copied_file ,
450467 rename_from ,
451468 rename_to ,
452469 None , None , None ))
@@ -487,6 +504,7 @@ def handle_diff_line(line):
487504 b_path = path .encode (defenc )
488505 deleted_file = False
489506 new_file = False
507+ copied_file = False
490508 rename_from = None
491509 rename_to = None
492510
@@ -498,6 +516,11 @@ def handle_diff_line(line):
498516 elif change_type == 'A' :
499517 a_blob_id = None
500518 new_file = True
519+ elif change_type == 'C' :
520+ copied_file = True
521+ a_path , b_path = path .split ('\t ' , 1 )
522+ a_path = a_path .encode (defenc )
523+ b_path = b_path .encode (defenc )
501524 elif change_type == 'R' :
502525 a_path , b_path = path .split ('\t ' , 1 )
503526 a_path = a_path .encode (defenc )
@@ -509,8 +532,8 @@ def handle_diff_line(line):
509532 # END add/remove handling
510533
511534 diff = Diff (repo , a_path , b_path , a_blob_id , b_blob_id , old_mode , new_mode ,
512- new_file , deleted_file , rename_from , rename_to , '' ,
513- change_type , score )
535+ new_file , deleted_file , copied_file , rename_from , rename_to ,
536+ '' , change_type , score )
514537 index .append (diff )
515538
516539 handle_process_output (proc , handle_diff_line , None , finalize_process , decode_streams = False )
0 commit comments