merge: if DELETED_CHANGED and GET are in actions, choose DELETED_CHANGED
authorPulkit Goyal <7895pulkit@gmail.com>
Wed, 30 Sep 2020 15:46:54 +0530
changeset 45617 ad984583969a
parent 45616 64461b43a7bf
child 45618 29c1d2401823
merge: if DELETED_CHANGED and GET are in actions, choose DELETED_CHANGED ACTION_GET represents that either the file is created on remote or it's newer on the remote side. However, since we have a ACTION_DELETE_CHANGED too, it means the file is not present locally and ACTION_GET is representing that file was created on remote. Having both ACTION_GET and ACTION_DELETED_CHANGED is conflicting because one says that file was created on remote and other says file has delete-changed conflicts. Let's choose ACTION_DELETED_CHANGED which will result in conflicts and make user choose the right way forward.
mercurial/merge.py
tests/test-merge-criss-cross.t
--- a/mercurial/merge.py	Wed Sep 30 15:09:25 2020 +0530
+++ b/mercurial/merge.py	Wed Sep 30 15:46:54 2020 +0530
@@ -1220,6 +1220,21 @@
                 repo.ui.note(_(b" %s: picking 'keep new' action\n") % f)
                 mresult.addfile(f, *bids[mergestatemod.ACTION_KEEP_NEW][0])
                 continue
+            # ACTION_GET and ACTION_DELETE_CHANGED are conflicting actions as
+            # one action states the file is newer/created on remote side and
+            # other states that file is deleted locally and changed on remote
+            # side. Let's fallback and rely on a conflicting action to let user
+            # do the right thing
+            if (
+                mergestatemod.ACTION_DELETED_CHANGED in bids
+                and mergestatemod.ACTION_GET in bids
+                and len(bids) == 2
+            ):
+                repo.ui.note(_(b" %s: picking 'delete/changed' action\n") % f)
+                mresult.addfile(
+                    f, *bids[mergestatemod.ACTION_DELETED_CHANGED][0]
+                )
+                continue
             # If there are gets and they all agree [how could they not?], do it.
             if mergestatemod.ACTION_GET in bids:
                 ga0 = bids[mergestatemod.ACTION_GET][0]
--- a/tests/test-merge-criss-cross.t	Wed Sep 30 15:09:25 2020 +0530
+++ b/tests/test-merge-criss-cross.t	Wed Sep 30 15:46:54 2020 +0530
@@ -663,14 +663,24 @@
   no merge state found
 
 (merging a deletion with keeping → conflict)
-BROKEN: this should result in conflict
 
   $ hg update --clean 'desc("merge-deleting-the-file-from-deleted")'
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+#if newfilenode
   $ hg merge          'desc("merge-keeping-the-file-from-deleted")'
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved (newfilenode !)
+  file 'the-file' was deleted in local [working copy] but was modified in other [merge rev].
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved (old !)
+  You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
+  What do you want to do? u
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
+  [1]
+#else
+  $ hg merge          'desc("merge-keeping-the-file-from-deleted")'
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
+#endif
   $ ls -1
   other-file
   the-file (newfilenode !)
@@ -679,7 +689,12 @@
   $ hg debugmergestate
   local (working copy): adfd88e5d7d3d3e22bdd26512991ee64d59c1d8f
   other (merge rev): 38a4c3e7cac8c294ecb0a7a85a05464e9836ca78
-  extra: the-file (merge-removal-candidate = yes)
+  file: the-file (state "u")
+    local path: the-file (hash 0000000000000000000000000000000000000000, flags "")
+    ancestor path: the-file (node 59e363a07dc876278f0e41756236f30213b6b460)
+    other path: the-file (node 885af55420b35d7bf3bbd6f546615295bfe6544a)
+    extra: ancestorlinknode = 9b610631ab29024c5f44af7d2c19658ef8f8f071
+    extra: merge-removal-candidate = yes
 #else
   $ hg debugmergestate
   local (working copy): adfd88e5d7d3d3e22bdd26512991ee64d59c1d8f
@@ -716,14 +731,25 @@
   no merge state found
 
 (merging a deletion with keeping → conflict)
-BROKEN: this should result in conflict
 
   $ hg update --clean 'desc("merge-deleting-the-file-from-updated")'
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+#if newfilenode
   $ hg merge          'desc("merge-keeping-the-file-from-deleted")'
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved (newfilenode !)
+  file 'the-file' was deleted in local [working copy] but was modified in other [merge rev].
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved (old !)
+  You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
+  What do you want to do? u
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
+  [1]
+#else
+  $ hg merge          'desc("merge-keeping-the-file-from-deleted")'
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
+#endif
+
   $ ls -1
   other-file
   the-file (newfilenode !)
@@ -731,7 +757,12 @@
   $ hg debugmergestate
   local (working copy): a4e0e44229dc130be2915b92c957c093f8c7ee3e
   other (merge rev): 38a4c3e7cac8c294ecb0a7a85a05464e9836ca78
-  extra: the-file (merge-removal-candidate = yes)
+  file: the-file (state "u")
+    local path: the-file (hash 0000000000000000000000000000000000000000, flags "")
+    ancestor path: the-file (node 59e363a07dc876278f0e41756236f30213b6b460)
+    other path: the-file (node 885af55420b35d7bf3bbd6f546615295bfe6544a)
+    extra: ancestorlinknode = 9b610631ab29024c5f44af7d2c19658ef8f8f071
+    extra: merge-removal-candidate = yes
 #else
   $ hg debugmergestate
   local (working copy): a4e0e44229dc130be2915b92c957c093f8c7ee3e