Skip to content

Commit a80e38f

Browse files
committed
diffcore-rename: add computation of number of unknown renames
In directory rename detection (when a directory is removed on one side of history and the other side adds new files to that directory), we work to find where the greatest number of files within that directory were renamed to so that the new files can be moved with the majority of the files. Naively, we can just do this by detecting renames for *all* files within the removed/renamed directory, looking at all the destination directories where files within that directory were moved, and if there is more than one such directory then taking the one with the greatest number of files as the directory where the old directory was renamed to. However, sometimes there are enough renames from exact rename detection or basename-guided rename detection that we have enough information to determine the majority winner already. The only way to know if we have enough for a majority, though, is if we compute the number of unpaired files for each directory. Add a function which computes these values. A subsequent commit will make use of these. Note that this change means dir_rename_count might have a directory whose only entry (for UNKNOWN_DIR) was removed by the time merge-ort goes to check it. To account for this, merge-ort needs to check for the case where the max count is 0. Signed-off-by: Elijah Newren <[email protected]>
1 parent e242e95 commit a80e38f

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

diffcore-rename.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,8 @@ static const char *get_highest_rename_path(struct strintmap *counts)
407407
return highest_destination_dir;
408408
}
409409

410+
static char *UNKNOWN_DIR = "/"; /* placeholder -- short, illegal directory */
411+
410412
static void increment_count(struct dir_rename_info *info,
411413
char *old_dir,
412414
char *new_dir)
@@ -660,7 +662,8 @@ static void cleanup_dir_rename_info(struct dir_rename_info *info,
660662
/*
661663
* Although dir_rename_count was passed in
662664
* diffcore_rename_extended() and we want to keep it around and
663-
* return it to that caller, we first want to remove any data
665+
* return it to that caller, we first want to remove any counts in
666+
* the maps associated with UNKNOWN_DIR entries and any data
664667
* associated with directories that weren't renamed.
665668
*/
666669
strmap_for_each_entry(info->dir_rename_count, &iter, entry) {
@@ -672,6 +675,9 @@ static void cleanup_dir_rename_info(struct dir_rename_info *info,
672675
strintmap_clear(counts);
673676
continue;
674677
}
678+
679+
if (strintmap_contains(counts, UNKNOWN_DIR))
680+
strintmap_remove(counts, UNKNOWN_DIR);
675681
}
676682
for (i = 0; i < to_remove.nr; ++i)
677683
strmap_remove(info->dir_rename_count,
@@ -1074,6 +1080,49 @@ static void remove_unneeded_paths_from_src(int detecting_copies,
10741080
rename_src_nr = new_num_src;
10751081
}
10761082

1083+
MAYBE_UNUSED
1084+
static void handle_early_known_dir_renames(struct dir_rename_info *info,
1085+
struct strintmap *relevant_sources,
1086+
struct strintmap *dirs_removed)
1087+
{
1088+
int i;
1089+
1090+
if (!dirs_removed || !relevant_sources)
1091+
return; /* nothing to cull */
1092+
if (break_idx)
1093+
return; /* culling incompatbile with break detection */
1094+
1095+
for (i = 0; i < rename_src_nr; i++) {
1096+
char *old_dir;
1097+
struct diff_filespec *one = rename_src[i].p->one;
1098+
1099+
/*
1100+
* sources that are part of a rename will have already been
1101+
* removed by a prior call to remove_unneeded_paths_from_src()
1102+
*/
1103+
assert(!one->rename_used);
1104+
1105+
old_dir = get_dirname(one->path);
1106+
while (*old_dir != '\0' &&
1107+
NOT_RELEVANT != strintmap_get(dirs_removed, old_dir)) {
1108+
char *freeme = old_dir;
1109+
1110+
increment_count(info, old_dir, UNKNOWN_DIR);
1111+
old_dir = get_dirname(old_dir);
1112+
1113+
/* Free resources we don't need anymore */
1114+
free(freeme);
1115+
}
1116+
/*
1117+
* old_dir and new_dir free'd in increment_count, but
1118+
* get_dirname() gives us a new pointer we need to free for
1119+
* old_dir. Also, if the loop runs 0 times we need old_dir
1120+
* to be freed.
1121+
*/
1122+
free(old_dir);
1123+
}
1124+
}
1125+
10771126
void diffcore_rename_extended(struct diff_options *options,
10781127
struct strintmap *relevant_sources,
10791128
struct strintmap *dirs_removed,

merge-ort.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,9 @@ static void get_provisional_directory_renames(struct merge_options *opt,
15521552
}
15531553
}
15541554

1555+
if (max == 0)
1556+
continue;
1557+
15551558
if (bad_max == max) {
15561559
path_msg(opt, source_dir, 0,
15571560
_("CONFLICT (directory rename split): "

0 commit comments

Comments
 (0)