changeset 27131:d837da26155e

merge: add a new action type representing files to add/mark as modified This is somewhat different from the currently existing 'a' action, for the following case: - dirty working copy, with file 'fa' added and 'fm' modified - hg merge --force with a rev that neither has 'fa' nor 'fm' - for the change/delete conflicts we pick 'changed' for both 'fa' and 'fm'. In this case 'branchmerge' is true, but we need to distinguish between 'fa', which should ultimately be marked added, and 'fm', which should be marked modified. Our current strategy is to just not touch the dirstate at all. That works for now, but won't work once we move change/delete conflicts to the resolve phase. In that case we may perform repeated re-resolves, some of which might mark the file removed or remove the file from the dirstate. We'll need to re-add the file to the dirstate, and we need to be able to figure out whether we mark the file added or modified. That is what the new 'am' action lets us do.
author Siddharth Agarwal <sid0@fb.com>
date Mon, 30 Nov 2015 10:19:39 -0800
parents 6f045b563fa5
children baa7571f40c5
files mercurial/merge.py tests/test-rename-merge2.t
diffstat 2 files changed, 23 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/merge.py	Mon Nov 30 10:03:21 2015 -0800
+++ b/mercurial/merge.py	Mon Nov 30 10:19:39 2015 -0800
@@ -479,7 +479,10 @@
                 if fcd.isabsent(): # dc: remote picked
                     action = 'g'
                 elif fco.isabsent(): # cd: local picked
-                    action = 'a'
+                    if dfile in self.localctx:
+                        action = 'am'
+                    else:
+                        action = 'a'
                 # else: regular merges (no action necessary)
             self._results[dfile] = r, action
 
@@ -524,7 +527,7 @@
 
     def actions(self):
         """return lists of actions to perform on the dirstate"""
-        actions = {'r': [], 'f': [], 'a': [], 'g': []}
+        actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
         for f, (r, action) in self._results.iteritems():
             if action is not None:
                 actions[action].append((f, None, "merge result"))
@@ -631,7 +634,7 @@
 
     if actions:
         # k, dr, e and rd are no-op
-        for m in 'a', 'f', 'g', 'cd', 'dc':
+        for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
             for f, args, msg in actions[m]:
                 pmmf.add(f)
         for f, args, msg in actions['r']:
@@ -1065,6 +1068,12 @@
         z += 1
         progress(_updating, z, item=f, total=numupdates, unit=_files)
 
+    # re-add/mark as modified (manifest only, just log it)
+    for f, args, msg in actions['am']:
+        repo.ui.debug(" %s: %s -> am\n" % (f, msg))
+        z += 1
+        progress(_updating, z, item=f, total=numupdates, unit=_files)
+
     # keep (noop, just log it)
     for f, args, msg in actions['k']:
         repo.ui.debug(" %s: %s -> k\n" % (f, msg))
@@ -1189,6 +1198,13 @@
         if not branchmerge:
             repo.dirstate.add(f)
 
+    # re-add/mark as modified
+    for f, args, msg in actions.get('am', []):
+        if branchmerge:
+            repo.dirstate.normallookup(f)
+        else:
+            repo.dirstate.add(f)
+
     # exec change
     for f, args, msg in actions.get('e', []):
         repo.dirstate.normallookup(f)
@@ -1390,7 +1406,7 @@
             repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
             followcopies)
         # Convert to dictionary-of-lists format
-        actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
+        actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
         for f, (m, args, msg) in actionbyfile.iteritems():
             if m not in actions:
                 actions[m] = []
@@ -1411,6 +1427,8 @@
                   "use (c)hanged version or (d)elete?"
                   "$$ &Changed $$ &Delete") % f, 0):
                 actions['r'].append((f, None, "prompt delete"))
+            elif f in p1:
+                actions['am'].append((f, None, "prompt keep"))
             else:
                 actions['a'].append((f, None, "prompt keep"))
 
--- a/tests/test-rename-merge2.t	Mon Nov 30 10:03:21 2015 -0800
+++ b/tests/test-rename-merge2.t	Mon Nov 30 10:19:39 2015 -0800
@@ -725,7 +725,7 @@
   use (c)hanged version or (d)elete? c
    preserving b for resolve of b
    preserving rev for resolve of rev
-   a: prompt keep -> a
+   a: prompt keep -> am
    b: both created -> m (premerge)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b