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;