Move computation of dir_rename_count from merge-ort to diffcore-rename

Move the computation of dir_rename_count from merge-ort.c to diffcore-rename.c, making slight adjustments to the data structures based on the move. While the diffstat looks large, viewing this commit with --color-moved makes it clear that only about 20 lines changed. With this patch, the computation of dir_rename_count is still only done after inexact rename detection, but subsequent commits will add a preliminary computation of dir_rename_count after exact rename detection, followed by some updates after inexact rename detection. Reviewed-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Feb 27, 2021 at 00:30 UTC 0c4fd732f043be570e08b51c475ff9f2e2066912
3 files changed +145 -130
diffcore-rename.c
+137 -1
@@ -380,6 +380,129 @@ static char *get_dirname(const char *filename)
380 return slash ? xstrndup(filename, slash - filename) : xstrdup("");
381 }
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 +
506 static void initialize_dir_rename_info(struct dir_rename_info *info)
507 {
508 int i;
@@ -790,7 +913,9 @@ static void remove_unneeded_paths_from_src(int detecting_copies)
913 rename_src_nr = new_num_src;
914 }
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)
919 {
920 int detect_rename = options->detect_rename;
921 int minimum_score = options->rename_score;
@@ -805,6 +930,7 @@ void diffcore_rename(struct diff_options *options)
930
931 trace2_region_enter("diff", "setup", options->repo);
932 info.setup = 0;
933 + assert(!dir_rename_count || strmap_empty(dir_rename_count));
934 want_copies = (detect_rename == DIFF_DETECT_COPY);
935 if (!minimum_score)
936 minimum_score = DEFAULT_RENAME_SCORE;
@@ -999,6 +1125,11 @@ void diffcore_rename(struct diff_options *options)
1125 trace2_region_leave("diff", "inexact renames", options->repo);
1126
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 +
1133 /* At this point, we have found some renames and copies and they
1134 * are recorded in rename_dst. The original list is still in *q.
1135 */
@@ -1082,3 +1213,8 @@ void diffcore_rename(struct diff_options *options)
1213 trace2_region_leave("diff", "write back to queue", options->repo);
1214 return;
1215 }
1216 +
1217 +void diffcore_rename(struct diff_options *options)
1218 +{
1219 + diffcore_rename_extended(options, NULL, NULL);
1220 +}
diffcore.h
+5
@@ -8,6 +8,8 @@
8
9 struct diff_options;
10 struct repository;
11 +struct strmap;
12 +struct strset;
13 struct userdiff_driver;
14
15 /* This header file is internal between diff.c and its diff transformers
@@ -161,6 +163,9 @@ void diff_q(struct diff_queue_struct *, struct diff_filepair *);
163
164 void diffcore_break(struct repository *, int);
165 void diffcore_rename(struct diff_options *);
166 +void diffcore_rename_extended(struct diff_options *options,
167 + struct strset *dirs_removed,
168 + struct strmap *dir_rename_count);
169 void diffcore_merge_broken(void);
170 void diffcore_pickaxe(struct diff_options *);
171 void diffcore_order(const char *orderfile);
merge-ort.c
+3 -129
@@ -1302,131 +1302,6 @@ static char *handle_path_level_conflicts(struct merge_options *opt,
1302 return new_path;
1303 }
1304
1305 -static void dirname_munge(char *filename)
1306 -{
1307 - char *slash = strrchr(filename, '/');
1308 - if (!slash)
1309 - slash = filename;
1310 - *slash = '\0';
1311 -}
1312 -
1313 -static void increment_count(struct strmap *dir_rename_count,
1314 - char *old_dir,
1315 - char *new_dir)
1316 -{
1317 - struct strintmap *counts;
1318 - struct strmap_entry *e;
1319 -
1320 - /* Get the {new_dirs -> counts} mapping using old_dir */
1321 - e = strmap_get_entry(dir_rename_count, old_dir);
1322 - if (e) {
1323 - counts = e->value;
1324 - } else {
1325 - counts = xmalloc(sizeof(*counts));
1326 - strintmap_init_with_options(counts, 0, NULL, 1);
1327 - strmap_put(dir_rename_count, old_dir, counts);
1328 - }
1329 -
1330 - /* Increment the count for new_dir */
1331 - strintmap_incr(counts, new_dir, 1);
1332 -}
1333 -
1334 -static void update_dir_rename_counts(struct strmap *dir_rename_count,
1335 - struct strset *dirs_removed,
1336 - const char *oldname,
1337 - const char *newname)
1338 -{
1339 - char *old_dir = xstrdup(oldname);
1340 - char *new_dir = xstrdup(newname);
1341 - char new_dir_first_char = new_dir[0];
1342 - int first_time_in_loop = 1;
1343 -
1344 - while (1) {
1345 - dirname_munge(old_dir);
1346 - dirname_munge(new_dir);
1347 -
1348 - /*
1349 - * When renaming
1350 - * "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
1351 - * then this suggests that both
1352 - * a/b/c/d/e/ => a/b/some/thing/else/e/
1353 - * a/b/c/d/ => a/b/some/thing/else/
1354 - * so we want to increment counters for both. We do NOT,
1355 - * however, also want to suggest that there was the following
1356 - * rename:
1357 - * a/b/c/ => a/b/some/thing/
1358 - * so we need to quit at that point.
1359 - *
1360 - * Note the when first_time_in_loop, we only strip off the
1361 - * basename, and we don't care if that's different.
1362 - */
1363 - if (!first_time_in_loop) {
1364 - char *old_sub_dir = strchr(old_dir, '\0')+1;
1365 - char *new_sub_dir = strchr(new_dir, '\0')+1;
1366 - if (!*new_dir) {
1367 - /*
1368 - * Special case when renaming to root directory,
1369 - * i.e. when new_dir == "". In this case, we had
1370 - * something like
1371 - * a/b/subdir => subdir
1372 - * and so dirname_munge() sets things up so that
1373 - * old_dir = "a/b\0subdir\0"
1374 - * new_dir = "\0ubdir\0"
1375 - * We didn't have a '/' to overwrite a '\0' onto
1376 - * in new_dir, so we have to compare differently.
1377 - */
1378 - if (new_dir_first_char != old_sub_dir[0] ||
1379 - strcmp(old_sub_dir+1, new_sub_dir))
1380 - break;
1381 - } else {
1382 - if (strcmp(old_sub_dir, new_sub_dir))
1383 - break;
1384 - }
1385 - }
1386 -
1387 - if (strset_contains(dirs_removed, old_dir))
1388 - increment_count(dir_rename_count, old_dir, new_dir);
1389 - else
1390 - break;
1391 -
1392 - /* If we hit toplevel directory ("") for old or new dir, quit */
1393 - if (!*old_dir || !*new_dir)
1394 - break;
1395 -
1396 - first_time_in_loop = 0;
1397 - }
1398 -
1399 - /* Free resources we don't need anymore */
1400 - free(old_dir);
1401 - free(new_dir);
1402 -}
1403 -
1404 -static void compute_rename_counts(struct diff_queue_struct *pairs,
1405 - struct strmap *dir_rename_count,
1406 - struct strset *dirs_removed)
1407 -{
1408 - int i;
1409 -
1410 - for (i = 0; i < pairs->nr; ++i) {
1411 - struct diff_filepair *pair = pairs->queue[i];
1412 -
1413 - /* File not part of directory rename if it wasn't renamed */
1414 - if (pair->status != 'R')
1415 - continue;
1416 -
1417 - /*
1418 - * Make dir_rename_count contain a map of a map:
1419 - * old_directory -> {new_directory -> count}
1420 - * In other words, for every pair look at the directories for
1421 - * the old filename and the new filename and count how many
1422 - * times that pairing occurs.
1423 - */
1424 - update_dir_rename_counts(dir_rename_count, dirs_removed,
1425 - pair->one->path,
1426 - pair->two->path);
1427 - }
1428 -}
1429 -
1305 static void get_provisional_directory_renames(struct merge_options *opt,
1306 unsigned side,
1307 int *clean)
@@ -1435,9 +1310,6 @@ static void get_provisional_directory_renames(struct merge_options *opt,
1310 struct strmap_entry *entry;
1311 struct rename_info *renames = &opt->priv->renames;
1312
1438 - compute_rename_counts(&renames->pairs[side],
1439 - &renames->dir_rename_count[side],
1440 - &renames->dirs_removed[side]);
1313 /*
1314 * Collapse
1315 * dir_rename_count: old_directory -> {new_directory -> count}
@@ -2162,7 +2034,9 @@ static void detect_regular_renames(struct merge_options *opt,
2034
2035 diff_queued_diff = renames->pairs[side_index];
2036 trace2_region_enter("diff", "diffcore_rename", opt->repo);
2165 - diffcore_rename(&diff_opts);
2037 + diffcore_rename_extended(&diff_opts,
2038 + &renames->dirs_removed[side_index],
2039 + &renames->dir_rename_count[side_index]);
2040 trace2_region_leave("diff", "diffcore_rename", opt->repo);
2041 resolve_diffpair_statuses(&diff_queued_diff);
2042