Patchwork D8988: merge: store commitinfo if mergetool resolved a dc or cd conflict

login
register
mail settings
Submitter phabricator
Date Sept. 5, 2020, 7:11 a.m.
Message ID <differential-rev-PHID-DREV-jb2bs4fi22nk5c2l5pop-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/47097/
State Superseded
Headers show

Comments

phabricator - Sept. 5, 2020, 7:11 a.m.
pulkit created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  delete-changed or changed-delete conflicts can be resolved by mergetool, either
  if some tool is passed and using or by user choose something on prompt.
  
  If the user decides to keep the changed side, on commit we just reuse the parent
  filenode. This is mostly fine unless we are in a distributed environment and
  people are doing criss-cross merges.
  
  Since, we don't have recursive merges or any other way of describing the end
  result of the merge was an explicit choice and it should be differentiated from
  it's ancestors, merge algo during criss-cross merges fails to take in account
  the explicit choice made by user and end up with a what-can-be-said-wrong-merge.
  
  The solution which we are trying to fix this is by creating a filenode on commit
  instead of reusing the parent filenode. This helps differentiate between
  pre-merged filenode and post-merge filenode and kind of tells about the choice
  user made.
  
  To implement creating new filenode functionality, we store info about these
  files in mergestate so that we can read them on commit and force create a new
  filenode.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D8988

AFFECTED FILES
  mercurial/merge.py
  tests/test-merge-criss-cross.t

CHANGE DETAILS




To: pulkit, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/tests/test-merge-criss-cross.t b/tests/test-merge-criss-cross.t
--- a/tests/test-merge-criss-cross.t
+++ b/tests/test-merge-criss-cross.t
@@ -537,6 +537,16 @@ 
   $ hg merge 'desc("updating-both-file")' -t :local
   1 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
+  $ hg debugmergestate
+  local (working copy): 7801bc9b9899de5e304bd162cafde9b78e10ab9b
+  other (merge rev): 9b610631ab29024c5f44af7d2c19658ef8f8f071
+  file: the-file (state "r")
+    local path: the-file (hash 0000000000000000000000000000000000000000, flags "")
+    ancestor path: the-file (node 4b69178b9bdae28b651393b46e631427a72f217a)
+    other path: the-file (node 59e363a07dc876278f0e41756236f30213b6b460)
+    extra: FORCE_NEW_FILENODE = yes
+    extra: ancestorlinknode = 955800955977bd6c103836ee3e437276e940a589
+  extra: other-file (filenode-source = other)
   $ hg ci -m "merge-deleting-the-file-from-deleted"
   $ hg manifest
   other-file
@@ -550,6 +560,15 @@ 
   $ hg merge 'desc("delete-the-file")' -t :other
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
+  $ hg debugmergestate
+  local (working copy): 9b610631ab29024c5f44af7d2c19658ef8f8f071
+  other (merge rev): 7801bc9b9899de5e304bd162cafde9b78e10ab9b
+  file: the-file (state "r")
+    local path: the-file (hash 6d2e02da5a9fe0691363dc6b573845fa271eaa35, flags "")
+    ancestor path: the-file (node 4b69178b9bdae28b651393b46e631427a72f217a)
+    other path: the-file (node 0000000000000000000000000000000000000000)
+    extra: FORCE_NEW_FILENODE = yes
+    extra: ancestorlinknode = 955800955977bd6c103836ee3e437276e940a589
   $ hg ci -m "merge-deleting-the-file-from-updated"
   created new head
   $ hg manifest
@@ -564,6 +583,16 @@ 
   $ hg merge 'desc("updating-both-file")' -t :other
   1 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
+  $ hg debugmergestate
+  local (working copy): 7801bc9b9899de5e304bd162cafde9b78e10ab9b
+  other (merge rev): 9b610631ab29024c5f44af7d2c19658ef8f8f071
+  file: the-file (state "r")
+    local path: the-file (hash 0000000000000000000000000000000000000000, flags "")
+    ancestor path: the-file (node 4b69178b9bdae28b651393b46e631427a72f217a)
+    other path: the-file (node 59e363a07dc876278f0e41756236f30213b6b460)
+    extra: FORCE_NEW_FILENODE = yes
+    extra: ancestorlinknode = 955800955977bd6c103836ee3e437276e940a589
+  extra: other-file (filenode-source = other)
   $ hg ci -m "merge-keeping-the-file-from-deleted"
   created new head
   $ hg manifest
@@ -581,6 +610,15 @@ 
   $ hg merge 'desc("delete-the-file")' -t :local
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
+  $ hg debugmergestate
+  local (working copy): 9b610631ab29024c5f44af7d2c19658ef8f8f071
+  other (merge rev): 7801bc9b9899de5e304bd162cafde9b78e10ab9b
+  file: the-file (state "r")
+    local path: the-file (hash 6d2e02da5a9fe0691363dc6b573845fa271eaa35, flags "")
+    ancestor path: the-file (node 4b69178b9bdae28b651393b46e631427a72f217a)
+    other path: the-file (node 0000000000000000000000000000000000000000)
+    extra: FORCE_NEW_FILENODE = yes
+    extra: ancestorlinknode = 955800955977bd6c103836ee3e437276e940a589
   $ hg ci -m "merge-keeping-the-file-from-updated"
   created new head
   $ hg manifest
diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -1690,6 +1690,27 @@ 
             if not complete:
                 numupdates += 1
                 tocomplete.append((f, args, msg))
+            else:
+                action = mresult.getfile(f)[0]
+                if action in (
+                    mergestatemod.ACTION_DELETED_CHANGED,
+                    mergestatemod.ACTION_CHANGED_DELETED,
+                ):
+                    # the merge was conflicting and either the mergetool
+                    # choosed by the user or the user themselves on the prompt
+                    # made a choice.
+                    # If the changed file was kept, at commit we end up using
+                    # the same filelog. This is not completely correct behavior
+                    # as the new file post merge also represents that a
+                    # conflicting change-delete merge was resolved in it's
+                    # favor. Hence we will like to create a new filenode for
+                    # that. Let's store this in mergestate extras
+                    repo.ui.debug(
+                        b"file %s had %s conflicts and mergetool resolved it, storing info in commitinfo\n"
+                        % (f, action)
+                    )
+
+                    ms.addcommitinfo(f, {b'FORCE_NEW_FILENODE': b'yes'})
 
         # merge
         for f, args, msg in tocomplete: