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