@@ -380,6 +380,129 @@ static char *get_dirname(const char *filename)
380
380
return slash ? xstrndup (filename , slash - filename ) : xstrdup ("" );
381
381
}
382
382
383
+ static void dirname_munge (char * filename )
384
+ {
385
+ char * slash = strrchr (filename , '/' );
386
+ if (!slash )
387
+ slash = filename ;
388
+ * slash = '\0' ;
389
+ }
390
+
391
+ static void increment_count (struct strmap * dir_rename_count ,
392
+ char * old_dir ,
393
+ char * new_dir )
394
+ {
395
+ struct strintmap * counts ;
396
+ struct strmap_entry * e ;
397
+
398
+ /* Get the {new_dirs -> counts} mapping using old_dir */
399
+ e = strmap_get_entry (dir_rename_count , old_dir );
400
+ if (e ) {
401
+ counts = e -> value ;
402
+ } else {
403
+ counts = xmalloc (sizeof (* counts ));
404
+ strintmap_init_with_options (counts , 0 , NULL , 1 );
405
+ strmap_put (dir_rename_count , old_dir , counts );
406
+ }
407
+
408
+ /* Increment the count for new_dir */
409
+ strintmap_incr (counts , new_dir , 1 );
410
+ }
411
+
412
+ static void update_dir_rename_counts (struct strmap * dir_rename_count ,
413
+ struct strset * dirs_removed ,
414
+ const char * oldname ,
415
+ const char * newname )
416
+ {
417
+ char * old_dir = xstrdup (oldname );
418
+ char * new_dir = xstrdup (newname );
419
+ char new_dir_first_char = new_dir [0 ];
420
+ int first_time_in_loop = 1 ;
421
+
422
+ while (1 ) {
423
+ dirname_munge (old_dir );
424
+ dirname_munge (new_dir );
425
+
426
+ /*
427
+ * When renaming
428
+ * "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
429
+ * then this suggests that both
430
+ * a/b/c/d/e/ => a/b/some/thing/else/e/
431
+ * a/b/c/d/ => a/b/some/thing/else/
432
+ * so we want to increment counters for both. We do NOT,
433
+ * however, also want to suggest that there was the following
434
+ * rename:
435
+ * a/b/c/ => a/b/some/thing/
436
+ * so we need to quit at that point.
437
+ *
438
+ * Note the when first_time_in_loop, we only strip off the
439
+ * basename, and we don't care if that's different.
440
+ */
441
+ if (!first_time_in_loop ) {
442
+ char * old_sub_dir = strchr (old_dir , '\0' )+ 1 ;
443
+ char * new_sub_dir = strchr (new_dir , '\0' )+ 1 ;
444
+ if (!* new_dir ) {
445
+ /*
446
+ * Special case when renaming to root directory,
447
+ * i.e. when new_dir == "". In this case, we had
448
+ * something like
449
+ * a/b/subdir => subdir
450
+ * and so dirname_munge() sets things up so that
451
+ * old_dir = "a/b\0subdir\0"
452
+ * new_dir = "\0ubdir\0"
453
+ * We didn't have a '/' to overwrite a '\0' onto
454
+ * in new_dir, so we have to compare differently.
455
+ */
456
+ if (new_dir_first_char != old_sub_dir [0 ] ||
457
+ strcmp (old_sub_dir + 1 , new_sub_dir ))
458
+ break ;
459
+ } else {
460
+ if (strcmp (old_sub_dir , new_sub_dir ))
461
+ break ;
462
+ }
463
+ }
464
+
465
+ if (strset_contains (dirs_removed , old_dir ))
466
+ increment_count (dir_rename_count , old_dir , new_dir );
467
+ else
468
+ break ;
469
+
470
+ /* If we hit toplevel directory ("") for old or new dir, quit */
471
+ if (!* old_dir || !* new_dir )
472
+ break ;
473
+
474
+ first_time_in_loop = 0 ;
475
+ }
476
+
477
+ /* Free resources we don't need anymore */
478
+ free (old_dir );
479
+ free (new_dir );
480
+ }
481
+
482
+ static void compute_dir_rename_counts (struct strmap * dir_rename_count ,
483
+ struct strset * dirs_removed )
484
+ {
485
+ int i ;
486
+
487
+ /* Set up dir_rename_count */
488
+ for (i = 0 ; i < rename_dst_nr ; ++ i ) {
489
+ /* File not part of directory rename counts if not a rename */
490
+ if (!rename_dst [i ].is_rename )
491
+ continue ;
492
+
493
+ /*
494
+ * Make dir_rename_count contain a map of a map:
495
+ * old_directory -> {new_directory -> count}
496
+ * In other words, for every pair look at the directories for
497
+ * the old filename and the new filename and count how many
498
+ * times that pairing occurs.
499
+ */
500
+ update_dir_rename_counts (dir_rename_count , dirs_removed ,
501
+ rename_dst [i ].p -> one -> path ,
502
+ rename_dst [i ].p -> two -> path );
503
+ }
504
+ }
505
+
383
506
static void initialize_dir_rename_info (struct dir_rename_info * info )
384
507
{
385
508
int i ;
@@ -790,7 +913,9 @@ static void remove_unneeded_paths_from_src(int detecting_copies)
790
913
rename_src_nr = new_num_src ;
791
914
}
792
915
793
- void diffcore_rename (struct diff_options * options )
916
+ void diffcore_rename_extended (struct diff_options * options ,
917
+ struct strset * dirs_removed ,
918
+ struct strmap * dir_rename_count )
794
919
{
795
920
int detect_rename = options -> detect_rename ;
796
921
int minimum_score = options -> rename_score ;
@@ -805,6 +930,7 @@ void diffcore_rename(struct diff_options *options)
805
930
806
931
trace2_region_enter ("diff" , "setup" , options -> repo );
807
932
info .setup = 0 ;
933
+ assert (!dir_rename_count || strmap_empty (dir_rename_count ));
808
934
want_copies = (detect_rename == DIFF_DETECT_COPY );
809
935
if (!minimum_score )
810
936
minimum_score = DEFAULT_RENAME_SCORE ;
@@ -999,6 +1125,11 @@ void diffcore_rename(struct diff_options *options)
999
1125
trace2_region_leave ("diff" , "inexact renames" , options -> repo );
1000
1126
1001
1127
cleanup :
1128
+ /*
1129
+ * Now that renames have been computed, compute dir_rename_count */
1130
+ if (dirs_removed && dir_rename_count )
1131
+ compute_dir_rename_counts (dir_rename_count , dirs_removed );
1132
+
1002
1133
/* At this point, we have found some renames and copies and they
1003
1134
* are recorded in rename_dst. The original list is still in *q.
1004
1135
*/
@@ -1082,3 +1213,8 @@ void diffcore_rename(struct diff_options *options)
1082
1213
trace2_region_leave ("diff" , "write back to queue" , options -> repo );
1083
1214
return ;
1084
1215
}
1216
+
1217
+ void diffcore_rename (struct diff_options * options )
1218
+ {
1219
+ diffcore_rename_extended (options , NULL , NULL );
1220
+ }
0 commit comments