3
3
*/
4
4
#include "cache.h"
5
5
#include "diff.h"
6
+ #include "diffcore.h"
6
7
#include "tree.h"
7
8
8
9
static char * malloc_base (const char * base , int baselen , const char * path , int pathlen )
@@ -290,6 +291,78 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru
290
291
return 0 ;
291
292
}
292
293
294
+ /*
295
+ * Does it look like the resulting diff might be due to a rename?
296
+ * - single entry
297
+ * - not a valid previous file
298
+ */
299
+ static inline int diff_might_be_rename (void )
300
+ {
301
+ return diff_queued_diff .nr == 1 &&
302
+ !DIFF_FILE_VALID (diff_queued_diff .queue [0 ]-> one );
303
+ }
304
+
305
+ static void try_to_follow_renames (struct tree_desc * t1 , struct tree_desc * t2 , const char * base , struct diff_options * opt )
306
+ {
307
+ struct diff_options diff_opts ;
308
+ struct diff_queue_struct * q = & diff_queued_diff ;
309
+ struct diff_filepair * choice ;
310
+ const char * paths [1 ];
311
+ int i ;
312
+
313
+ /* Remove the file creation entry from the diff queue, and remember it */
314
+ choice = q -> queue [0 ];
315
+ q -> nr = 0 ;
316
+
317
+ diff_setup (& diff_opts );
318
+ diff_opts .recursive = 1 ;
319
+ diff_opts .detect_rename = DIFF_DETECT_RENAME ;
320
+ diff_opts .output_format = DIFF_FORMAT_NO_OUTPUT ;
321
+ diff_opts .single_follow = opt -> paths [0 ];
322
+ paths [0 ] = NULL ;
323
+ diff_tree_setup_paths (paths , & diff_opts );
324
+ if (diff_setup_done (& diff_opts ) < 0 )
325
+ die ("unable to set up diff options to follow renames" );
326
+ diff_tree (t1 , t2 , base , & diff_opts );
327
+ diffcore_std (& diff_opts );
328
+
329
+ /* Go through the new set of filepairing, and see if we find a more interesting one */
330
+ for (i = 0 ; i < q -> nr ; i ++ ) {
331
+ struct diff_filepair * p = q -> queue [i ];
332
+
333
+ /*
334
+ * Found a source? Not only do we use that for the new
335
+ * diff_queued_diff, we will also use that as the path in
336
+ * the future!
337
+ */
338
+ if ((p -> status == 'R' || p -> status == 'C' ) && !strcmp (p -> two -> path , opt -> paths [0 ])) {
339
+ /* Switch the file-pairs around */
340
+ q -> queue [i ] = choice ;
341
+ choice = p ;
342
+
343
+ /* Update the path we use from now on.. */
344
+ opt -> paths [0 ] = xstrdup (p -> one -> path );
345
+ diff_tree_setup_paths (opt -> paths , opt );
346
+ break ;
347
+ }
348
+ }
349
+
350
+ /*
351
+ * Then, discard all the non-relevane file pairs...
352
+ */
353
+ for (i = 0 ; i < q -> nr ; i ++ ) {
354
+ struct diff_filepair * p = q -> queue [i ];
355
+ diff_free_filepair (p );
356
+ }
357
+
358
+ /*
359
+ * .. and re-instate the one we want (which might be either the
360
+ * original one, or the rename/copy we found)
361
+ */
362
+ q -> queue [0 ] = choice ;
363
+ q -> nr = 1 ;
364
+ }
365
+
293
366
int diff_tree_sha1 (const unsigned char * old , const unsigned char * new , const char * base , struct diff_options * opt )
294
367
{
295
368
void * tree1 , * tree2 ;
@@ -306,6 +379,11 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
306
379
init_tree_desc (& t1 , tree1 , size1 );
307
380
init_tree_desc (& t2 , tree2 , size2 );
308
381
retval = diff_tree (& t1 , & t2 , base , opt );
382
+ if (opt -> follow_renames && diff_might_be_rename ()) {
383
+ init_tree_desc (& t1 , tree1 , size1 );
384
+ init_tree_desc (& t2 , tree2 , size2 );
385
+ try_to_follow_renames (& t1 , & t2 , base , opt );
386
+ }
309
387
free (tree1 );
310
388
free (tree2 );
311
389
return retval ;
0 commit comments