Patchwork [4,of,4,mergedriver] merge: move almost all change/delete conflicts to resolve phase (BC) (API)

login
register
mail settings
Submitter Siddharth Agarwal
Date Nov. 30, 2015, 10:25 p.m.
Message ID <2d7ab840e26d1a5dfc14.1448922309@dev666.prn1.facebook.com>
Download mbox | patch
Permalink /patch/11690/
State Accepted
Headers show

Comments

Siddharth Agarwal - Nov. 30, 2015, 10:25 p.m.
# HG changeset patch
# User Siddharth Agarwal <sid0@fb.com>
# Date 1448490333 28800
#      Wed Nov 25 14:25:33 2015 -0800
# Node ID 2d7ab840e26d1a5dfc1483c21e8bf0cbb1babafa
# Parent  e4745330dbb979cc776cf909c4355425f145ebaa
# Available At http://42.netv6.net/sid0-wip/hg/
#              hg pull http://42.netv6.net/sid0-wip/hg/ -r 2d7ab840e26d
merge: move almost all change/delete conflicts to resolve phase (BC) (API)

We have finally laid all the groundwork to make this happen.

The only change/delete conflicts that haven't been moved are .hgsubstate
conflicts. Those are trickier to deal with and well outside the scope of this
series.

We add comprehensive testing not just for the initial selections but also for
re-resolves and all possible dirstate transitions caused by merge tools. That
testing managed to shake out several bugs in the way we were handling dirstate
transitions.

The other test changes are because we now treat change/delete conflicts as
proper merges, and increment the 'merged' counter rather than the 'updated'
counter. I believe this is the right approach here.

For third-party extensions, if they're interacting with filemerge code they
might have to deal with an absentfilectx rather than a regular filectx.

Still to come:
- add a 'leave unresolved' option to merges
- change the default for non-interactive change/delete conflicts to be 'leave
  unresolved'
- add debug output to go alongside debug outputs for binary and symlink file
  merges
Martin von Zweigbergk - Nov. 30, 2015, 10:56 p.m.
On Mon, Nov 30, 2015 at 2:30 PM Siddharth Agarwal <sid0@fb.com> wrote:

> # HG changeset patch
> # User Siddharth Agarwal <sid0@fb.com>
> # Date 1448490333 28800
> #      Wed Nov 25 14:25:33 2015 -0800
> # Node ID 2d7ab840e26d1a5dfc1483c21e8bf0cbb1babafa
> # Parent  e4745330dbb979cc776cf909c4355425f145ebaa
> # Available At http://42.netv6.net/sid0-wip/hg/
> #              hg pull http://42.netv6.net/sid0-wip/hg/ -r 2d7ab840e26d
> merge: move almost all change/delete conflicts to resolve phase (BC) (API)
>

Hooray! I've pushed this to the clowncopter, thanks!

I didn't review the added tests, but I have checked that they pass. There
were just too many tests for me to review.

Patch

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -997,8 +997,11 @@  def applyupdates(repo, actions, wctx, mc
     for m, l in actions.items():
         l.sort()
 
-    # prescan for merges
-    for f, args, msg in actions['m']:
+    # 'cd' and 'dc' actions are treated like other merge conflicts
+    mergeactions = sorted(actions['cd'])
+    mergeactions.extend(sorted(actions['dc']))
+    mergeactions.extend(actions['m'])
+    for f, args, msg in mergeactions:
         f1, f2, fa, move, anc = args
         if f == '.hgsubstate': # merged internally
             continue
@@ -1111,7 +1114,6 @@  def applyupdates(repo, actions, wctx, mc
         util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
         updated += 1
 
-    mergeactions = actions['m']
     # the ordering is important here -- ms.mergedriver will raise if the merge
     # driver has changed, and we want to be able to bypass it when overwrite is
     # True
@@ -1419,8 +1421,12 @@  def update(repo, node, branchmerge, forc
             else:
                 _checkcollision(repo, wc.manifest(), actions)
 
-        # Prompt and create actions. TODO: Move this towards resolve phase.
+        # Prompt and create actions. Most of this is in the resolve phase
+        # already, but we can't handle .hgsubstate in filemerge or
+        # subrepo.submerge yet so we have to keep prompting for it.
         for f, args, msg in sorted(actions['cd']):
+            if f != '.hgsubstate':
+                continue
             if repo.ui.promptchoice(
                 _("local changed %s which remote deleted\n"
                   "use (c)hanged version or (d)elete?"
@@ -1432,6 +1438,8 @@  def update(repo, node, branchmerge, forc
                 actions['a'].append((f, None, "prompt keep"))
 
         for f, args, msg in sorted(actions['dc']):
+            if f != '.hgsubstate':
+                continue
             f1, f2, fa, move, anc = args
             flags = p2[f2].flags()
             if repo.ui.promptchoice(
diff --git a/tests/test-merge-changedelete.t b/tests/test-merge-changedelete.t
--- a/tests/test-merge-changedelete.t
+++ b/tests/test-merge-changedelete.t
@@ -1,4 +1,4 @@ 
-Test for
+Tests for change/delete conflicts, including:
 b5605d88dc27: Make ui.prompt repeat on "unrecognized response" again
  (issue897)
 
@@ -25,7 +25,8 @@  Make sure HGMERGE doesn't interfere with
   >     done
   > }
 
-  $ hg init
+  $ hg init repo
+  $ cd repo
 
   $ echo 1 > file1
   $ echo 2 > file2
@@ -59,7 +60,7 @@  Non-interactive merge:
   use (c)hanged version or leave (d)eleted? c
   merging file3
   warning: conflicts while merging file3! (edit, then use 'hg resolve --mark')
-  1 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  0 files updated, 2 files merged, 0 files removed, 1 files unresolved
   use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
   [1]
 
@@ -69,11 +70,21 @@  Non-interactive merge:
   M file3
   C file1
   --- resolve --list ---
+  R file1
+  R file2
   U file3
   --- debugmergestate ---
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -108,7 +119,7 @@  Interactive merge:
   use (c)hanged version or leave (d)eleted? d
   merging file3
   warning: conflicts while merging file3! (edit, then use 'hg resolve --mark')
-  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  0 files updated, 2 files merged, 0 files removed, 1 files unresolved
   use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
   [1]
 
@@ -118,11 +129,21 @@  Interactive merge:
   M file3
   C file1
   --- resolve --list ---
+  R file1
+  R file2
   U file3
   --- debugmergestate ---
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -167,7 +188,7 @@  Interactive merge with bad input:
   use (c)hanged version or leave (d)eleted? c
   merging file3
   warning: conflicts while merging file3! (edit, then use 'hg resolve --mark')
-  1 files updated, 0 files merged, 1 files removed, 1 files unresolved
+  0 files updated, 1 files merged, 1 files removed, 1 files unresolved
   use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
   [1]
 
@@ -177,11 +198,21 @@  Interactive merge with bad input:
   M file3
   R file1
   --- resolve --list ---
+  R file1
+  R file2
   U file3
   --- debugmergestate ---
   * version 2 records
   local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
   other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
   file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
     local path: file3 (flags "")
     ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
@@ -210,17 +241,83 @@  Interactive merge with not enough input:
   local changed file1 which remote deleted
   use (c)hanged version or (d)elete? d
   remote changed file2 which local deleted
-  use (c)hanged version or leave (d)eleted? abort: response expected
-  [255]
+  use (c)hanged version or leave (d)eleted? 
+  merging file3
+  warning: conflicts while merging file3! (edit, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 1 files removed, 2 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
 
   $ status
   --- status ---
+  M file2
+  M file3
+  R file1
+  --- resolve --list ---
+  R file1
+  U file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "u", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  *** file1 does not exist
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3
+  changed2
+  =======
+  changed1
+  >>>>>>> other: 10f9a0a634e8  - test: removed file1, changed file2, changed file3
+
+Choose local versions of files
+
+  $ hg co -C
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --tool :local
+  0 files updated, 3 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ status 2>&1 | tee $TESTTMP/local.status
+  --- status ---
   file2: * (glob)
+  M file3
   C file1
-  C file3
   --- resolve --list ---
+  R file1
+  R file2
+  R file3
   --- debugmergestate ---
-  no merge state found
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "r", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
   --- file1 ---
   1
   changed
@@ -229,6 +326,350 @@  Interactive merge with not enough input:
   3
   changed2
 
+Choose other versions of files
+
+  $ hg co -C
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg merge --tool :other
+  0 files updated, 2 files merged, 1 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ status 2>&1 | tee $TESTTMP/other.status
+  --- status ---
+  M file2
+  M file3
+  R file1
+  --- resolve --list ---
+  R file1
+  R file2
+  R file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "r", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  *** file1 does not exist
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+
+Fail
+
+  $ hg co -C
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --tool :fail
+  0 files updated, 0 files merged, 0 files removed, 3 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ status 2>&1 | tee $TESTTMP/fail.status
+  --- status ---
+  M file2
+  M file3
+  C file1
+  --- resolve --list ---
+  U file1
+  U file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "u", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed2
+
+Force prompts with no input (should be similar to :fail)
+
+  $ hg co -C
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --config ui.interactive=True --tool :prompt
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? 
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? 
+  no tool found to merge file3
+  keep (l)ocal or take (o)ther? 
+  0 files updated, 0 files merged, 0 files removed, 3 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ status 2>&1 | tee $TESTTMP/prompt.status
+  --- status ---
+  M file2
+  M file3
+  C file1
+  --- resolve --list ---
+  U file1
+  U file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "u", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed2
+  $ diff -U8 $TESTTMP/fail.status $TESTTMP/prompt.status
+
+
+Force prompts
+
+  $ hg co -C
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --tool :prompt
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? c
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  no tool found to merge file3
+  keep (l)ocal or take (o)ther? l
+  0 files updated, 3 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ status
+  --- status ---
+  M file2
+  M file3
+  C file1
+  --- resolve --list ---
+  R file1
+  R file2
+  R file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "r", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed2
+
+Choose to merge all files
+
+  $ hg co -C
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+  $ hg merge --tool :merge3
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? c
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  merging file3
+  warning: conflicts while merging file3! (edit, then use 'hg resolve --mark')
+  0 files updated, 2 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ status
+  --- status ---
+  M file2
+  M file3
+  C file1
+  --- resolve --list ---
+  R file1
+  R file2
+  U file3
+  --- debugmergestate ---
+  * version 2 records
+  local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  file: file3 (record type "F", state "u", hash d5b0a58bc47161b1b8a831084b366f757c4f0b11)
+    local path: file3 (flags "")
+    ancestor path: file3 (node 2661d26c649684b482d10f91960cc3db683c38b4)
+    other path: file3 (node a2644c43e210356772c7772a8674544a62e06beb)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3
+  changed2
+  ||||||| base
+  =======
+  changed1
+  >>>>>>> other: 10f9a0a634e8  - test: removed file1, changed file2, changed file3
+
+Exercise transitions between local, other, fail and prompt, and make sure the
+dirstate stays consistent. (Compare with each other and to the above
+invocations.)
+
+  $ testtransitions() {
+  >     # this traversal order covers every transition
+  >     tools="local other prompt local fail other local prompt other fail prompt fail local"
+  >     lasttool="merge3"
+  >     for tool in $tools; do
+  >         echo "=== :$lasttool -> :$tool ==="
+  >         ref="$TESTTMP/$tool.status"
+  >         hg resolve --unmark --all
+  >         hg resolve --tool ":$tool" --all --config ui.interactive=True
+  >         status > "$TESTTMP/compare.status" 2>&1
+  >         echo '--- diff of status ---'
+  >         if diff -U8 "$TESTTMP/$tool.status" "$TESTTMP/compare.status"; then
+  >             echo '(status identical)'
+  >         fi
+  >         lasttool="$tool"
+  >         echo
+  >     done
+  > }
+
+  $ testtransitions
+  === :merge3 -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? 
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? 
+  no tool found to merge file3
+  keep (l)ocal or take (o)ther? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? 
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? 
+  no tool found to merge file3
+  keep (l)ocal or take (o)ther? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? 
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? 
+  no tool found to merge file3
+  keep (l)ocal or take (o)ther? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+
+
 Non-interactive linear update
 
   $ hg co -C 0
@@ -240,15 +681,27 @@  Non-interactive linear update
   use (c)hanged version or (d)elete? c
   remote changed file2 which local deleted
   use (c)hanged version or leave (d)eleted? c
-  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 2 files merged, 0 files removed, 0 files unresolved
   $ status
   --- status ---
   A file1
   C file2
   C file3
   --- resolve --list ---
+  R file1
+  R file2
   --- debugmergestate ---
-  no merge state found
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
   --- file1 ---
   1
   changed
@@ -258,3 +711,277 @@  Non-interactive linear update
   --- file3 ---
   3
   changed1
+
+Choose local versions of files
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 --tool :local
+  1 files updated, 2 files merged, 0 files removed, 0 files unresolved
+  $ status 2>&1 | tee $TESTTMP/local.status
+  --- status ---
+  file2: * (glob)
+  A file1
+  C file3
+  --- resolve --list ---
+  R file1
+  R file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  --- file1 ---
+  1
+  changed
+  *** file2 does not exist
+  --- file3 ---
+  3
+  changed1
+
+Choose other versions of files
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 --tool :other
+  1 files updated, 1 files merged, 1 files removed, 0 files unresolved
+  $ status 2>&1 | tee $TESTTMP/other.status
+  --- status ---
+  file1: * (glob)
+  C file2
+  C file3
+  --- resolve --list ---
+  R file1
+  R file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  *** file1 does not exist
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+
+Fail
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 --tool :fail
+  1 files updated, 0 files merged, 0 files removed, 2 files unresolved
+  use 'hg resolve' to retry unresolved file merges
+  [1]
+  $ status 2>&1 | tee $TESTTMP/fail.status
+  --- status ---
+  A file1
+  C file2
+  C file3
+  --- resolve --list ---
+  U file1
+  U file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "u", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+
+Force prompts with no input
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 --config ui.interactive=True --tool :prompt
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? 
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? 
+  1 files updated, 0 files merged, 0 files removed, 2 files unresolved
+  use 'hg resolve' to retry unresolved file merges
+  [1]
+  $ status 2>&1 | tee $TESTTMP/prompt.status
+  --- status ---
+  A file1
+  C file2
+  C file3
+  --- resolve --list ---
+  U file1
+  U file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "u", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+  $ diff -U8 $TESTTMP/fail.status $TESTTMP/prompt.status
+
+Choose to merge all files
+
+  $ hg co -C 0
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo changed >> file1
+  $ hg rm file2
+  $ hg update 1 --tool :merge3
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? c
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  1 files updated, 2 files merged, 0 files removed, 0 files unresolved
+  $ status
+  --- status ---
+  A file1
+  C file2
+  C file3
+  --- resolve --list ---
+  R file1
+  R file2
+  --- debugmergestate ---
+  * version 2 records
+  local: ab57bf49aa276a22d35a473592d4c34b5abc3eff
+  other: 10f9a0a634e82080907e62f075ab119cbc565ea6
+  file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390)
+    local path: file1 (flags "")
+    ancestor path: file1 (node b8e02f6433738021a065f94175c7cd23db5f05be)
+    other path: file1 (node null)
+  file: file2 (record type "C", state "r", hash null)
+    local path: file2 (flags "")
+    ancestor path: file2 (node 5d9299349fc01ddd25d0070d149b124d8f10411e)
+    other path: file2 (node e7c1328648519852e723de86c0c0525acd779257)
+  --- file1 ---
+  1
+  changed
+  --- file2 ---
+  2
+  changed
+  --- file3 ---
+  3
+  changed1
+
+Test transitions between different merge tools
+
+  $ testtransitions
+  === :merge3 -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? 
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :local -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? 
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :other ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
+  === :other -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :prompt ===
+  local changed file1 which remote deleted
+  use (c)hanged version or (d)elete? 
+  remote changed file2 which local deleted
+  use (c)hanged version or leave (d)eleted? 
+  --- diff of status ---
+  (status identical)
+  
+  === :prompt -> :fail ===
+  --- diff of status ---
+  (status identical)
+  
+  === :fail -> :local ===
+  (no more unresolved files)
+  --- diff of status ---
+  (status identical)
+  
diff --git a/tests/test-merge-force.t b/tests/test-merge-force.t
--- a/tests/test-merge-force.t
+++ b/tests/test-merge-force.t
@@ -5,7 +5,8 @@  file that was modified in the remote cha
 local changeset, and then modified in the working copy to match the
 remote content, then finally forgotten.
 
-  $ hg init
+  $ hg init repo
+  $ cd repo
 
 Create base changeset
 
@@ -214,7 +215,7 @@  Merge with remote
   warning: conflicts while merging missing_content2_content3_content4-tracked! (edit, then use 'hg resolve --mark')
   warning: conflicts while merging missing_content2_missing_content4-tracked! (edit, then use 'hg resolve --mark')
   warning: conflicts while merging missing_content2_missing_content4-untracked! (edit, then use 'hg resolve --mark')
-  39 files updated, 3 files merged, 8 files removed, 10 files unresolved
+  18 files updated, 28 files merged, 8 files removed, 10 files unresolved
   use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
   [1]
 
@@ -226,14 +227,39 @@  Interestingly, one untracked file got me
 odd 'if force and branchmerge and different' case in manifestmerge().
 
   $ hg resolve -l
+  R content1_content2_content1_content1-untracked
+  R content1_content2_content1_content2-untracked
   U content1_content2_content1_content4-tracked
+  R content1_content2_content1_content4-untracked
+  R content1_content2_content1_missing-tracked
+  R content1_content2_content1_missing-untracked
   R content1_content2_content2_content1-tracked
+  R content1_content2_content2_content1-untracked
+  R content1_content2_content2_content2-untracked
   U content1_content2_content2_content4-tracked
+  R content1_content2_content2_content4-untracked
+  R content1_content2_content2_missing-tracked
+  R content1_content2_content2_missing-untracked
   R content1_content2_content3_content1-tracked
+  R content1_content2_content3_content1-untracked
+  R content1_content2_content3_content2-untracked
   U content1_content2_content3_content3-tracked
+  R content1_content2_content3_content3-untracked
   U content1_content2_content3_content4-tracked
+  R content1_content2_content3_content4-untracked
+  R content1_content2_content3_missing-tracked
+  R content1_content2_content3_missing-untracked
   R content1_content2_missing_content1-tracked
+  R content1_content2_missing_content1-untracked
+  R content1_content2_missing_content2-untracked
   U content1_content2_missing_content4-tracked
+  R content1_content2_missing_content4-untracked
+  R content1_content2_missing_missing-tracked
+  R content1_content2_missing_missing-untracked
+  R content1_missing_content1_content4-tracked
+  R content1_missing_content3_content3-tracked
+  R content1_missing_content3_content4-tracked
+  R content1_missing_missing_content4-tracked
   U missing_content2_content2_content4-tracked
   U missing_content2_content3_content3-tracked
   U missing_content2_content3_content4-tracked
@@ -260,17 +286,20 @@  almost as strange.
 missing_missing_content3_missing-tracked becomes removed ('R'), even though
 the remote side did not touch the file
 
-  $ for f in `python $TESTDIR/generate-working-copy-states.py filelist 3`
-  > do
-  >   echo
-  >   hg status -A $f
-  >   if test -f $f
-  >   then
-  >     cat $f
-  >   else
-  >     echo '<missing>'
-  >   fi
-  > done
+  $ checkstatus() {
+  >   for f in `python $TESTDIR/generate-working-copy-states.py filelist 3`
+  >   do
+  >     echo
+  >     hg status -A $f
+  >     if test -f $f
+  >     then
+  >       cat $f
+  >     else
+  >       echo '<missing>'
+  >     fi
+  >   done
+  > }
+  $ checkstatus 2>&1 | tee $TESTTMP/status1
   
   C content1_content1_content1_content1-tracked
   content1
@@ -667,3 +696,87 @@  the remote side did not touch the file
   content4
   missing_content2_missing_content4-untracked.orig:
   content4
+
+Re-resolve and check status
+
+  $ hg resolve --unmark --all
+  $ hg resolve --all --tool :local
+  (no more unresolved files)
+  $ hg resolve --unmark --all
+  $ hg resolve --all --tool internal:merge3
+  remote changed content1_content2_content1_content1-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_content1_content2-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  merging content1_content2_content1_content4-tracked
+  remote changed content1_content2_content1_content4-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_content1_missing-tracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_content1_missing-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  merging content1_content2_content2_content1-tracked
+  remote changed content1_content2_content2_content1-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_content2_content2-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  merging content1_content2_content2_content4-tracked
+  remote changed content1_content2_content2_content4-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_content2_missing-tracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_content2_missing-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  merging content1_content2_content3_content1-tracked
+  remote changed content1_content2_content3_content1-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_content3_content2-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  merging content1_content2_content3_content3-tracked
+  remote changed content1_content2_content3_content3-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  merging content1_content2_content3_content4-tracked
+  remote changed content1_content2_content3_content4-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_content3_missing-tracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_content3_missing-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  merging content1_content2_missing_content1-tracked
+  remote changed content1_content2_missing_content1-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_missing_content2-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  merging content1_content2_missing_content4-tracked
+  remote changed content1_content2_missing_content4-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_missing_missing-tracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  remote changed content1_content2_missing_missing-untracked which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  local changed content1_missing_content1_content4-tracked which remote deleted
+  use (c)hanged version or (d)elete? c
+  local changed content1_missing_content3_content3-tracked which remote deleted
+  use (c)hanged version or (d)elete? c
+  local changed content1_missing_content3_content4-tracked which remote deleted
+  use (c)hanged version or (d)elete? c
+  local changed content1_missing_missing_content4-tracked which remote deleted
+  use (c)hanged version or (d)elete? c
+  merging missing_content2_content2_content4-tracked
+  merging missing_content2_content3_content3-tracked
+  merging missing_content2_content3_content4-tracked
+  merging missing_content2_missing_content4-tracked
+  merging missing_content2_missing_content4-untracked
+  warning: conflicts while merging content1_content2_content1_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging content1_content2_content2_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging content1_content2_content3_content3-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging content1_content2_content3_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging content1_content2_missing_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging missing_content2_content2_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging missing_content2_content3_content3-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging missing_content2_content3_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging missing_content2_missing_content4-tracked! (edit, then use 'hg resolve --mark')
+  warning: conflicts while merging missing_content2_missing_content4-untracked! (edit, then use 'hg resolve --mark')
+  [1]
+  $ checkstatus > $TESTTMP/status2 2>&1
+  $ diff -U8 $TESTTMP/status1 $TESTTMP/status2
diff --git a/tests/test-merge-remove.t b/tests/test-merge-remove.t
--- a/tests/test-merge-remove.t
+++ b/tests/test-merge-remove.t
@@ -105,7 +105,7 @@  Those who use force will lose
   remote changed bar which local deleted
   use (c)hanged version or leave (d)eleted? c
   merging foo1 and foo to foo1
-  1 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 2 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ cat bar
   bleh
diff --git a/tests/test-rename-merge2.t b/tests/test-rename-merge2.t
--- a/tests/test-rename-merge2.t
+++ b/tests/test-rename-merge2.t
@@ -679,12 +679,12 @@  m "um a c" "um x c" "      " "10 do merg
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a
+   preserving b for resolve of b
+   preserving rev for resolve of rev
+   a: prompt deleted/changed -> m (premerge)
+  picked tool ':prompt' for a (binary False symlink False)
   remote changed a which local deleted
   use (c)hanged version or leave (d)eleted? c
-   preserving b for resolve of b
-   preserving rev for resolve of rev
-   a: prompt recreating -> g
-  getting a
    b: both created -> m (premerge)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
@@ -703,7 +703,7 @@  m "um a c" "um x c" "      " "10 do merg
   my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
-  1 files updated, 2 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 3 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   --------------
   M a
@@ -721,11 +721,13 @@  m "um a c" "um x c" "      " "10 do merg
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a
+   preserving a for resolve of a
+   preserving b for resolve of b
+   preserving rev for resolve of rev
+   a: prompt changed/deleted -> m (premerge)
+  picked tool ':prompt' for a (binary False symlink False)
   local changed a which remote deleted
   use (c)hanged version or (d)elete? c
-   preserving b for resolve of b
-   preserving rev for resolve of rev
-   a: prompt keep -> am
    b: both created -> m (premerge)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
@@ -744,7 +746,7 @@  m "um a c" "um x c" "      " "10 do merg
   my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
   launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
   merge tool returned: 0
-  0 files updated, 2 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 3 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   --------------
   M b