]> git.ipfire.org Git - thirdparty/git.git/commitdiff
merge-ort: handle directory/file conflicts that remain
authorElijah Newren <newren@gmail.com>
Fri, 1 Jan 2021 02:34:40 +0000 (02:34 +0000)
committerJunio C Hamano <gitster@pobox.com>
Mon, 4 Jan 2021 18:40:45 +0000 (10:40 -0800)
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>
merge-ort.c

index dd90987ae3d86141c69e41452004cc46f6713e5a..d300a02810e36d50dd83244b9449490541b5f16a 100644 (file)
@@ -343,6 +343,13 @@ static void path_msg(struct merge_options *opt,
        strbuf_addch(sb, '\n');
 }
 
+static char *unique_path(struct strmap *existing_paths,
+                        const char *path,
+                        const char *branch)
+{
+       die("Not yet implemented.");
+}
+
 /*** Function Grouping: functions related to collect_merge_info() ***/
 
 static void setup_path_info(struct merge_options *opt,
@@ -962,6 +969,8 @@ static void process_entry(struct merge_options *opt,
                          struct conflict_info *ci,
                          struct directory_versions *dir_metadata)
 {
+       int df_file_index = 0;
+
        VERIFY_CI(ci);
        assert(ci->filemask >= 0 && ci->filemask <= 7);
        /* ci->match_mask == 7 was handled in collect_merge_info_callback() */
@@ -998,13 +1007,86 @@ static void process_entry(struct merge_options *opt,
                        oidcpy(&ci->stages[i].oid, &null_oid);
                }
        } else if (ci->df_conflict && ci->merged.result.mode != 0) {
-               die("Not yet implemented.");
+               /*
+                * This started out as a D/F conflict, and the entries in
+                * the competing directory were not removed by the merge as
+                * evidenced by write_completed_directory() writing a value
+                * to ci->merged.result.mode.
+                */
+               struct conflict_info *new_ci;
+               const char *branch;
+               const char *old_path = path;
+               int i;
+
+               assert(ci->merged.result.mode == S_IFDIR);
+
+               /*
+                * If filemask is 1, we can just ignore the file as having
+                * been deleted on both sides.  We do not want to overwrite
+                * ci->merged.result, since it stores the tree for all the
+                * files under it.
+                */
+               if (ci->filemask == 1) {
+                       ci->filemask = 0;
+                       return;
+               }
+
+               /*
+                * This file still exists on at least one side, and we want
+                * the directory to remain here, so we need to move this
+                * path to some new location.
+                */
+               new_ci = xcalloc(1, sizeof(*new_ci));
+               /* We don't really want new_ci->merged.result copied, but it'll
+                * be overwritten below so it doesn't matter.  We also don't
+                * want any directory mode/oid values copied, but we'll zero
+                * those out immediately.  We do want the rest of ci copied.
+                */
+               memcpy(new_ci, ci, sizeof(*ci));
+               new_ci->match_mask = (new_ci->match_mask & ~new_ci->dirmask);
+               new_ci->dirmask = 0;
+               for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
+                       if (new_ci->filemask & (1 << i))
+                               continue;
+                       /* zero out any entries related to directories */
+                       new_ci->stages[i].mode = 0;
+                       oidcpy(&new_ci->stages[i].oid, &null_oid);
+               }
+
+               /*
+                * Find out which side this file came from; note that we
+                * cannot just use ci->filemask, because renames could cause
+                * the filemask to go back to 7.  So we use dirmask, then
+                * pick the opposite side's index.
+                */
+               df_file_index = (ci->dirmask & (1 << 1)) ? 2 : 1;
+               branch = (df_file_index == 1) ? opt->branch1 : opt->branch2;
+               path = unique_path(&opt->priv->paths, path, branch);
+               strmap_put(&opt->priv->paths, path, new_ci);
+
+               path_msg(opt, path, 0,
+                        _("CONFLICT (file/directory): directory in the way "
+                          "of %s from %s; moving it to %s instead."),
+                        old_path, branch, path);
+
+               /*
+                * Zero out the filemask for the old ci.  At this point, ci
+                * was just an entry for a directory, so we don't need to
+                * do anything more with it.
+                */
+               ci->filemask = 0;
+
+               /*
+                * Now note that we're working on the new entry (path was
+                * updated above.
+                */
+               ci = new_ci;
        }
 
        /*
         * NOTE: Below there is a long switch-like if-elseif-elseif... block
         *       which the code goes through even for the df_conflict cases
-        *       above.  Well, it will once we don't die-not-implemented above.
+        *       above.
         */
        if (ci->match_mask) {
                ci->merged.clean = 1;