merge-ort: implement compute_collisions()

This is nearly a wholesale copy of compute_collisions() from merge-recursive.c, and the logic remains the same, but it has been tweaked slightly due to: * using strmap.h API (instead of direct hashmaps) * allocation/freeing of data structures were done separately in merge_start() and clear_or_reinit_internal_opts() in an earlier patch in this series * there is no non_unique_new_dir data field in merge-ort; that will be handled a different way It does depend on two new functions, apply_dir_rename() and check_dir_renamed() which were introduced with simple die-not-yet-implemented shells and will be implemented in subsequent patches. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Jan 19, 2021 at 19:53 UTC d9d015df4a3d794ec889dc70902d28f3ca87d817
1 file changed +67 -1
merge-ort.c
+67 -1
@@ -726,6 +726,19 @@ struct collision_info {
726 unsigned reported_already:1;
727 };
728
729 +/*
730 + * Return a new string that replaces the beginning portion (which matches
731 + * rename_info->key), with rename_info->util.new_dir. In perl-speak:
732 + * new_path_name = (old_path =~ s/rename_info->key/rename_info->value/);
733 + * NOTE:
734 + * Caller must ensure that old_path starts with rename_info->key + '/'.
735 + */
736 +static char *apply_dir_rename(struct strmap_entry *rename_info,
737 + const char *old_path)
738 +{
739 + die("Not yet implemented!");
740 +}
741 +
742 static void get_renamed_dir_portion(const char *old_path, const char *new_path,
743 char **old_dir, char **new_dir)
744 {
@@ -964,11 +977,64 @@ static void handle_directory_level_conflicts(struct merge_options *opt)
977 string_list_clear(&duplicated, 0);
978 }
979
980 +static struct strmap_entry *check_dir_renamed(const char *path,
981 + struct strmap *dir_renames)
982 +{
983 + die("Not yet implemented!");
984 +}
985 +
986 static void compute_collisions(struct strmap *collisions,
987 struct strmap *dir_renames,
988 struct diff_queue_struct *pairs)
989 {
971 - die("Not yet implemented.");
990 + int i;
991 +
992 + strmap_init_with_options(collisions, NULL, 0);
993 + if (strmap_empty(dir_renames))
994 + return;
995 +
996 + /*
997 + * Multiple files can be mapped to the same path due to directory
998 + * renames done by the other side of history. Since that other
999 + * side of history could have merged multiple directories into one,
1000 + * if our side of history added the same file basename to each of
1001 + * those directories, then all N of them would get implicitly
1002 + * renamed by the directory rename detection into the same path,
1003 + * and we'd get an add/add/.../add conflict, and all those adds
1004 + * from *this* side of history. This is not representable in the
1005 + * index, and users aren't going to easily be able to make sense of
1006 + * it. So we need to provide a good warning about what's
1007 + * happening, and fall back to no-directory-rename detection
1008 + * behavior for those paths.
1009 + *
1010 + * See testcases 9e and all of section 5 from t6043 for examples.
1011 + */
1012 + for (i = 0; i < pairs->nr; ++i) {
1013 + struct strmap_entry *rename_info;
1014 + struct collision_info *collision_info;
1015 + char *new_path;
1016 + struct diff_filepair *pair = pairs->queue[i];
1017 +
1018 + if (pair->status != 'A' && pair->status != 'R')
1019 + continue;
1020 + rename_info = check_dir_renamed(pair->two->path, dir_renames);
1021 + if (!rename_info)
1022 + continue;
1023 +
1024 + new_path = apply_dir_rename(rename_info, pair->two->path);
1025 + assert(new_path);
1026 + collision_info = strmap_get(collisions, new_path);
1027 + if (collision_info) {
1028 + free(new_path);
1029 + } else {
1030 + collision_info = xcalloc(1,
1031 + sizeof(struct collision_info));
1032 + string_list_init(&collision_info->source_files, 0);
1033 + strmap_put(collisions, new_path, collision_info);
1034 + }
1035 + string_list_insert(&collision_info->source_files,
1036 + pair->two->path);
1037 + }
1038 }
1039
1040 static char *check_for_directory_rename(struct merge_options *opt,