33 */
44#include "cache.h"
55#include "diff.h"
6+ #include "diffcore.h"
67#include "tree.h"
78
89static 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
290291 return 0 ;
291292}
292293
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+
293366int diff_tree_sha1 (const unsigned char * old , const unsigned char * new , const char * base , struct diff_options * opt )
294367{
295368 void * tree1 , * tree2 ;
@@ -306,6 +379,11 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
306379 init_tree_desc (& t1 , tree1 , size1 );
307380 init_tree_desc (& t2 , tree2 , size2 );
308381 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+ }
309387 free (tree1 );
310388 free (tree2 );
311389 return retval ;
0 commit comments