]> git.ipfire.org Git - thirdparty/git.git/commitdiff
merge-ort: implement check_for_directory_rename()
authorElijah Newren <newren@gmail.com>
Tue, 19 Jan 2021 19:53:48 +0000 (19:53 +0000)
committerJunio C Hamano <gitster@pobox.com>
Thu, 21 Jan 2021 06:18:55 +0000 (22:18 -0800)
This is copied from merge-recursive.c, with minor tweaks due to using strmap
API and the fact that it can use opt->priv->paths to get all pathnames that
exist instead of taking a tree object.

This depends on a new function, handle_path_level_conflicts(), which
just has a placeholder die-not-yet-implemented implementation for now; a
subsequent patch will implement it.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
merge-ort.c

index db922272edf940e9c7181d032e437bcf48dee28a..86d2214006ef96f74a40992ee29ad8e3a44d91fb 100644 (file)
@@ -864,6 +864,21 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
        *new_dir = xstrndup(new_path, end_of_new - new_path);
 }
 
+/*
+ * See if there is a directory rename for path, and if there are any file
+ * level conflicts on the given side for the renamed location.  If there is
+ * a rename and there are no conflicts, return the new name.  Otherwise,
+ * return NULL.
+ */
+static char *handle_path_level_conflicts(struct merge_options *opt,
+                                        const char *path,
+                                        unsigned side_index,
+                                        struct strmap_entry *rename_info,
+                                        struct strmap *collisions)
+{
+       die("Not yet implemented");
+}
+
 static void increment_count(struct strmap *dir_rename_count,
                            char *old_dir,
                            char *new_dir)
@@ -1078,7 +1093,57 @@ static char *check_for_directory_rename(struct merge_options *opt,
                                        struct strmap *collisions,
                                        int *clean_merge)
 {
-       die("Not yet implemented.");
+       char *new_path = NULL;
+       struct strmap_entry *rename_info;
+       struct strmap_entry *otherinfo = NULL;
+       const char *new_dir;
+
+       if (strmap_empty(dir_renames))
+               return new_path;
+       rename_info = check_dir_renamed(path, dir_renames);
+       if (!rename_info)
+               return new_path;
+       /* old_dir = rename_info->key; */
+       new_dir = rename_info->value;
+
+       /*
+        * This next part is a little weird.  We do not want to do an
+        * implicit rename into a directory we renamed on our side, because
+        * that will result in a spurious rename/rename(1to2) conflict.  An
+        * example:
+        *   Base commit: dumbdir/afile, otherdir/bfile
+        *   Side 1:      smrtdir/afile, otherdir/bfile
+        *   Side 2:      dumbdir/afile, dumbdir/bfile
+        * Here, while working on Side 1, we could notice that otherdir was
+        * renamed/merged to dumbdir, and change the diff_filepair for
+        * otherdir/bfile into a rename into dumbdir/bfile.  However, Side
+        * 2 will notice the rename from dumbdir to smrtdir, and do the
+        * transitive rename to move it from dumbdir/bfile to
+        * smrtdir/bfile.  That gives us bfile in dumbdir vs being in
+        * smrtdir, a rename/rename(1to2) conflict.  We really just want
+        * the file to end up in smrtdir.  And the way to achieve that is
+        * to not let Side1 do the rename to dumbdir, since we know that is
+        * the source of one of our directory renames.
+        *
+        * That's why otherinfo and dir_rename_exclusions is here.
+        *
+        * As it turns out, this also prevents N-way transient rename
+        * confusion; See testcases 9c and 9d of t6043.
+        */
+       otherinfo = strmap_get_entry(dir_rename_exclusions, new_dir);
+       if (otherinfo) {
+               path_msg(opt, rename_info->key, 1,
+                        _("WARNING: Avoiding applying %s -> %s rename "
+                          "to %s, because %s itself was renamed."),
+                        rename_info->key, new_dir, path, new_dir);
+               return NULL;
+       }
+
+       new_path = handle_path_level_conflicts(opt, path, side_index,
+                                              rename_info, collisions);
+       *clean_merge &= (new_path != NULL);
+
+       return new_path;
 }
 
 static void apply_directory_rename_modifications(struct merge_options *opt,