]> git.ipfire.org Git - thirdparty/git.git/commitdiff
merge-ort: add implementation of both sides renaming differently
authorElijah Newren <newren@gmail.com>
Tue, 15 Dec 2020 18:28:02 +0000 (18:28 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 16 Dec 2020 01:18:32 +0000 (17:18 -0800)
Implement rename/rename(1to2) handling, i.e. both sides of history
renaming a file and rename it differently.  This code replaces the
following from merge-recurisve.c:

  * all the 1to2 code in process_renames()
  * the RENAME_ONE_FILE_TO_TWO case of process_entry()
  * handle_rename_rename_1to2()

Also, there is some shared code from merge-recursive.c for multiple
different rename cases which we will no longer need for this case (or
other rename cases):

  * handle_file_collision()
  * setup_rename_conflict_info()

The consolidation of five separate codepaths into one is made possible
by a change in design: process_renames() tweaks the conflict_info
entries within opt->priv->paths such that process_entry() can then
handle all the non-rename conflict types (directory/file, modify/delete,
etc.) orthogonally.  This means we're much less likely to miss special
implementation of some kind of combination of conflict types (see
commits brought in by 66c62eaec6 ("Merge branch 'en/merge-tests'",
2020-11-18), especially commit ef52778708 ("merge tests: expect improved
directory/file conflict handling in ort", 2020-10-26) for more details).
That, together with letting worktree/index updating be handled
orthogonally in the merge_switch_to_result() function, dramatically
simplifies the code for various special rename cases.

To be fair, there is a _slight_ tweak to process_entry() here to make
sure that the two different paths aren't marked as clean but are left in
a conflicted state.  So process_renames() and process_entry() aren't
quite entirely orthogonal, but they are pretty close.

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

index da3715baa63fcc5f9d1d9568493e58358b2acfa5..19477cfae60c08ca4c9e4a1be59e2c9b2595f4b6 100644 (file)
@@ -676,6 +676,7 @@ static int process_renames(struct merge_options *opt,
                        const char *pathnames[3];
                        struct version_info merged;
                        struct conflict_info *base, *side1, *side2;
+                       unsigned was_binary_blob = 0;
 
                        pathnames[0] = oldpath;
                        pathnames[1] = newpath;
@@ -706,7 +707,58 @@ static int process_renames(struct merge_options *opt,
                        }
 
                        /* This is a rename/rename(1to2) */
-                       die("Not yet implemented");
+                       clean_merge = handle_content_merge(opt,
+                                                          pair->one->path,
+                                                          &base->stages[0],
+                                                          &side1->stages[1],
+                                                          &side2->stages[2],
+                                                          pathnames,
+                                                          1 + 2 * opt->priv->call_depth,
+                                                          &merged);
+                       if (!clean_merge &&
+                           merged.mode == side1->stages[1].mode &&
+                           oideq(&merged.oid, &side1->stages[1].oid))
+                               was_binary_blob = 1;
+                       memcpy(&side1->stages[1], &merged, sizeof(merged));
+                       if (was_binary_blob) {
+                               /*
+                                * Getting here means we were attempting to
+                                * merge a binary blob.
+                                *
+                                * Since we can't merge binaries,
+                                * handle_content_merge() just takes one
+                                * side.  But we don't want to copy the
+                                * contents of one side to both paths.  We
+                                * used the contents of side1 above for
+                                * side1->stages, let's use the contents of
+                                * side2 for side2->stages below.
+                                */
+                               oidcpy(&merged.oid, &side2->stages[2].oid);
+                               merged.mode = side2->stages[2].mode;
+                       }
+                       memcpy(&side2->stages[2], &merged, sizeof(merged));
+
+                       side1->path_conflict = 1;
+                       side2->path_conflict = 1;
+                       /*
+                        * TODO: For renames we normally remove the path at the
+                        * old name.  It would thus seem consistent to do the
+                        * same for rename/rename(1to2) cases, but we haven't
+                        * done so traditionally and a number of the regression
+                        * tests now encode an expectation that the file is
+                        * left there at stage 1.  If we ever decide to change
+                        * this, add the following two lines here:
+                        *    base->merged.is_null = 1;
+                        *    base->merged.clean = 1;
+                        * and remove the setting of base->path_conflict to 1.
+                        */
+                       base->path_conflict = 1;
+                       path_msg(opt, oldpath, 0,
+                                _("CONFLICT (rename/rename): %s renamed to "
+                                  "%s in %s and to %s in %s."),
+                                pathnames[0],
+                                pathnames[1], opt->branch1,
+                                pathnames[2], opt->branch2);
 
                        i++; /* We handled both renames, i.e. i+1 handled */
                        continue;
@@ -1291,13 +1343,13 @@ static void process_entry(struct merge_options *opt,
                int side = (ci->filemask == 4) ? 2 : 1;
                ci->merged.result.mode = ci->stages[side].mode;
                oidcpy(&ci->merged.result.oid, &ci->stages[side].oid);
-               ci->merged.clean = !ci->df_conflict;
+               ci->merged.clean = !ci->df_conflict && !ci->path_conflict;
        } else if (ci->filemask == 1) {
                /* Deleted on both sides */
                ci->merged.is_null = 1;
                ci->merged.result.mode = 0;
                oidcpy(&ci->merged.result.oid, &null_oid);
-               ci->merged.clean = 1;
+               ci->merged.clean = !ci->path_conflict;
        }
 
        /*