merge-ort: handle directory/file conflicts that remain

When a directory/file conflict remains, we can leave the directory where it is, but need to move the information about the file to a different pathname. After moving the file to a different pathname, we allow subsequent process_entry() logic to handle any additional details that might be relevant. This depends on a new helper function, unique_path(), that dies with an unimplemented error currently but will be implemented in a subsequent commit. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Jan 1, 2021 at 02:34 UTC 23366d2aa97544fe76bc86964f4493b7b89e7b22
1 file changed +84 -2
merge-ort.c
+84 -2
@@ -343,6 +343,13 @@ static void path_msg(struct merge_options *opt,
343 strbuf_addch(sb, '\n');
344 }
345
346 +static char *unique_path(struct strmap *existing_paths,
347 + const char *path,
348 + const char *branch)
349 +{
350 + die("Not yet implemented.");
351 +}
352 +
353 /*** Function Grouping: functions related to collect_merge_info() ***/
354
355 static void setup_path_info(struct merge_options *opt,
@@ -962,6 +969,8 @@ static void process_entry(struct merge_options *opt,
969 struct conflict_info *ci,
970 struct directory_versions *dir_metadata)
971 {
972 + int df_file_index = 0;
973 +
974 VERIFY_CI(ci);
975 assert(ci->filemask >= 0 && ci->filemask <= 7);
976 /* ci->match_mask == 7 was handled in collect_merge_info_callback() */
@@ -998,13 +1007,86 @@ static void process_entry(struct merge_options *opt,
1007 oidcpy(&ci->stages[i].oid, &null_oid);
1008 }
1009 } else if (ci->df_conflict && ci->merged.result.mode != 0) {
1001 - die("Not yet implemented.");
1010 + /*
1011 + * This started out as a D/F conflict, and the entries in
1012 + * the competing directory were not removed by the merge as
1013 + * evidenced by write_completed_directory() writing a value
1014 + * to ci->merged.result.mode.
1015 + */
1016 + struct conflict_info *new_ci;
1017 + const char *branch;
1018 + const char *old_path = path;
1019 + int i;
1020 +
1021 + assert(ci->merged.result.mode == S_IFDIR);
1022 +
1023 + /*
1024 + * If filemask is 1, we can just ignore the file as having
1025 + * been deleted on both sides. We do not want to overwrite
1026 + * ci->merged.result, since it stores the tree for all the
1027 + * files under it.
1028 + */
1029 + if (ci->filemask == 1) {
1030 + ci->filemask = 0;
1031 + return;
1032 + }
1033 +
1034 + /*
1035 + * This file still exists on at least one side, and we want
1036 + * the directory to remain here, so we need to move this
1037 + * path to some new location.
1038 + */
1039 + new_ci = xcalloc(1, sizeof(*new_ci));
1040 + /* We don't really want new_ci->merged.result copied, but it'll
1041 + * be overwritten below so it doesn't matter. We also don't
1042 + * want any directory mode/oid values copied, but we'll zero
1043 + * those out immediately. We do want the rest of ci copied.
1044 + */
1045 + memcpy(new_ci, ci, sizeof(*ci));
1046 + new_ci->match_mask = (new_ci->match_mask & ~new_ci->dirmask);
1047 + new_ci->dirmask = 0;
1048 + for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
1049 + if (new_ci->filemask & (1 << i))
1050 + continue;
1051 + /* zero out any entries related to directories */
1052 + new_ci->stages[i].mode = 0;
1053 + oidcpy(&new_ci->stages[i].oid, &null_oid);
1054 + }
1055 +
1056 + /*
1057 + * Find out which side this file came from; note that we
1058 + * cannot just use ci->filemask, because renames could cause
1059 + * the filemask to go back to 7. So we use dirmask, then
1060 + * pick the opposite side's index.
1061 + */
1062 + df_file_index = (ci->dirmask & (1 << 1)) ? 2 : 1;
1063 + branch = (df_file_index == 1) ? opt->branch1 : opt->branch2;
1064 + path = unique_path(&opt->priv->paths, path, branch);
1065 + strmap_put(&opt->priv->paths, path, new_ci);
1066 +
1067 + path_msg(opt, path, 0,
1068 + _("CONFLICT (file/directory): directory in the way "
1069 + "of %s from %s; moving it to %s instead."),
1070 + old_path, branch, path);
1071 +
1072 + /*
1073 + * Zero out the filemask for the old ci. At this point, ci
1074 + * was just an entry for a directory, so we don't need to
1075 + * do anything more with it.
1076 + */
1077 + ci->filemask = 0;
1078 +
1079 + /*
1080 + * Now note that we're working on the new entry (path was
1081 + * updated above.
1082 + */
1083 + ci = new_ci;
1084 }
1085
1086 /*
1087 * NOTE: Below there is a long switch-like if-elseif-elseif... block
1088 * which the code goes through even for the df_conflict cases
1007 - * above. Well, it will once we don't die-not-implemented above.
1089 + * above.
1090 */
1091 if (ci->match_mask) {
1092 ci->merged.clean = 1;