]> git.ipfire.org Git - thirdparty/git.git/commitdiff
merge-ort: add implementation of type-changed rename handling
authorElijah Newren <newren@gmail.com>
Tue, 15 Dec 2020 18:28:06 +0000 (18:28 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 16 Dec 2020 01:18:32 +0000 (17:18 -0800)
Implement cases where renames are involved in type changes (i.e. the
side of history that didn't rename the file changed its type from a
regular file to a symlink or submodule).  There was some code to handle
this in merge-recursive but only in the special case when the renamed
file had no content changes.  The code here works differently -- it
knows process_entry() can handle mode conflicts, so it does a few
minimal tweaks to ensure process_entry() can just finish the job as
needed.

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

index 26f357e524f960e88caebc3fc974eb9b09eb74a3..677c6a878c5098558ff6f744dacf7a9ced027c17 100644 (file)
@@ -777,8 +777,33 @@ static int process_renames(struct merge_options *opt,
                        (S_ISREG(oldinfo->stages[other_source_index].mode) !=
                         S_ISREG(newinfo->stages[target_index].mode));
                if (type_changed && collision) {
-                       /* special handling so later blocks can handle this */
-                       die("Not yet implemented");
+                       /*
+                        * special handling so later blocks can handle this...
+                        *
+                        * if type_changed && collision are both true, then this
+                        * was really a double rename, but one side wasn't
+                        * detected due to lack of break detection.  I.e.
+                        * something like
+                        *    orig: has normal file 'foo'
+                        *    side1: renames 'foo' to 'bar', adds 'foo' symlink
+                        *    side2: renames 'foo' to 'bar'
+                        * In this case, the foo->bar rename on side1 won't be
+                        * detected because the new symlink named 'foo' is
+                        * there and we don't do break detection.  But we detect
+                        * this here because we don't want to merge the content
+                        * of the foo symlink with the foo->bar file, so we
+                        * have some logic to handle this special case.  The
+                        * easiest way to do that is make 'bar' on side1 not
+                        * be considered a colliding file but the other part
+                        * of a normal rename.  If the file is very different,
+                        * well we're going to get content merge conflicts
+                        * anyway so it doesn't hurt.  And if the colliding
+                        * file also has a different type, that'll be handled
+                        * by the content merge logic in process_entry() too.
+                        *
+                        * See also t6430, 'rename vs. rename/symlink'
+                        */
+                       collision = 0;
                }
                if (source_deleted) {
                        if (target_index == 1) {
@@ -859,7 +884,11 @@ static int process_renames(struct merge_options *opt,
                        newinfo->pathnames[0] = oldpath;
                        if (type_changed) {
                                /* rename vs. typechange */
-                               die("Not yet implemented");
+                               /* Mark the original as resolved by removal */
+                               memcpy(&oldinfo->stages[0].oid, &null_oid,
+                                      sizeof(oldinfo->stages[0].oid));
+                               oldinfo->stages[0].mode = 0;
+                               oldinfo->filemask &= 0x06;
                        } else if (source_deleted) {
                                /* rename/delete */
                                newinfo->path_conflict = 1;