merge: allow smarter tool configuration
authorMatt Mackall <mpm@selenic.com>
Sun, 03 Feb 2008 19:29:05 -0600
changeset 6004 5af5f0f9d724
parent 6003 7855b88ba838
child 6005 3c33032d8906
merge: allow smarter tool configuration Add [merge-tool] hgrc section with: <tool>.executable = name or path (<tool>) <tool>.args = args with $local/base/other/output ($local $base $other) <tool>.priority = priority (default 0) <tool>.binary = handles binary (False) <tool>.symlink = handles symlinks (False) <tool>.checkconflict = check for conflict markers (False) <tool>.premerge = try internal simplemerge (True if not binary or symlink) Four built-in tools: internal:{merge,local,other,fail} Add [merge-patterns] section of the form: <pattern> = <tool> Priority of settings is: HGMERGE merge-patterns ui:merge merge-tools by priority hgmerge, if it can be found Changes: unsuccessful merges leave .orig files
mercurial/commands.py
mercurial/filemerge.py
tests/run-tests.py
tests/test-add.out
tests/test-conflict.out
tests/test-convert-svn-sink.out
tests/test-copy-move-merge.out
tests/test-double-merge.out
tests/test-install.out
tests/test-issue672.out
tests/test-merge-commit.out
tests/test-merge-local.out
tests/test-merge-revert2.out
tests/test-merge7.out
tests/test-merge9.out
tests/test-rename-merge1.out
tests/test-rename-merge2.out
tests/test-up-local-change.out
--- a/mercurial/commands.py	Sun Feb 03 19:29:05 2008 -0600
+++ b/mercurial/commands.py	Sun Feb 03 19:29:05 2008 -0600
@@ -813,40 +813,6 @@
     os.unlink(fa)
     os.unlink(fd)
 
-    # merge helper
-    ui.status(_("Checking merge helper...\n"))
-    cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
-           or "hgmerge")
-    cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
-    if not cmdpath:
-        if cmd == 'hgmerge':
-            ui.write(_(" No merge helper set and can't find default"
-                       " hgmerge script in PATH\n"))
-            ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
-        else:
-            ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
-            ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
-            problems += 1
-    else:
-        # actually attempt a patch here
-        fa = writetemp("1\n2\n3\n4\n")
-        fl = writetemp("1\n2\n3\ninsert\n4\n")
-        fr = writetemp("begin\n1\n2\n3\n4\n")
-        r = util.system('%s "%s" "%s" "%s"' % (cmd, fl, fa, fr))
-        if r:
-            ui.write(_(" Got unexpected merge error %d!\n") % r)
-            problems += 1
-        m = file(fl).read()
-        if m != "begin\n1\n2\n3\ninsert\n4\n":
-            ui.write(_(" Got unexpected merge results!\n"))
-            ui.write(_(" (your merge helper may have the"
-                       " wrong argument order)\n"))
-            ui.write(_(" Result: %r\n") % m)
-            problems += 1
-        os.unlink(fa)
-        os.unlink(fl)
-        os.unlink(fr)
-
     # editor
     ui.status(_("Checking commit editor...\n"))
     editor = ui.geteditor()
--- a/mercurial/filemerge.py	Sun Feb 03 19:29:05 2008 -0600
+++ b/mercurial/filemerge.py	Sun Feb 03 19:29:05 2008 -0600
@@ -7,7 +7,57 @@
 
 from node import *
 from i18n import _
-import util, os, tempfile, context
+import util, os, tempfile, context, simplemerge, re
+
+def _toolstr(ui, tool, part, default=None):
+    return ui.config("merge-tools", tool + "." + part, default)
+
+def _toolbool(ui, tool, part, default=False):
+    return ui.configbool("merge-tools", tool + "." + part, default)
+
+def _findtool(ui, tool):
+    return util.find_exe(_toolstr(ui, tool, "executable", tool))
+
+def _picktool(repo, ui, path, binary, symlink):
+    def check(tool, pat, symlink, binary):
+        tmsg = tool
+        if pat:
+            tmsg += " specified for " + pat
+        if pat and not _findtool(ui, tool): # skip search if not matching
+            ui.warn(_("couldn't find merge tool %s\n") % tmsg)
+        elif symlink and not _toolbool(ui, tool, "symlink"):
+            ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
+        elif binary and not _toolbool(ui, tool, "binary"):
+            ui.warn(_("tool %s can't handle binary\n") % tmsg)
+        else:
+            return True
+        return False
+
+    # HGMERGE takes precedence
+    if os.environ.get("HGMERGE"):
+        return os.environ.get("HGMERGE")
+
+    # then patterns
+    for pattern, tool in ui.configitems("merge-patterns"):
+        mf = util.matcher(repo.root, "", [pat], [], [])[1]
+        if mf(path) and check(tool, pat, symlink, False):
+                return tool
+
+    # then merge tools
+    tools = {}
+    for k,v in ui.configitems("merge-tools"):
+        t = k.split('.')[0]
+        if t not in tools:
+            tools[t] = int(_toolstr(ui, t, "priority", "0"))
+    tools = [(-p,t) for t,p in tools.items()]
+    tools.sort()
+    if ui.config("ui", "merge"):
+        tools.insert(0, (None, ui.config("ui", "merge"))) # highest priority
+    tools.append((None, "hgmerge")) # the old default, if found
+    tools.append((None, "internal:merge")) # internal merge as last resort
+    for p,t in tools:
+        if _findtool(ui, t) and check(t, None, symlink, binary):
+            return t
 
 def filemerge(repo, fw, fd, fo, wctx, mctx):
     """perform a 3-way merge in the working directory
@@ -27,38 +77,91 @@
         f.close()
         return name
 
-    fcm = wctx.filectx(fw)
-    fcmdata = wctx.filectx(fd).data()
+    def isbin(ctx):
+        try:
+            return util.binary(ctx.data())
+        except IOError:
+            return False
+
     fco = mctx.filectx(fo)
-
-    if not fco.cmp(fcmdata): # files identical?
+    if not fco.cmp(wctx.filectx(fd).data()): # files identical?
         return None
 
-    fca = fcm.ancestor(fco)
-    if not fca:
-        fca = repo.filectx(fw, fileid=nullrev)
+    ui = repo.ui
+    fcm = wctx.filectx(fw)
+    fca = fcm.ancestor(fco) or repo.filectx(fw, fileid=nullrev)
+    binary = isbin(fcm) or isbin(fco) or isbin(fca)
+    symlink = fcm.islink() or fco.islink()
+    tool = _picktool(repo, ui, fw, binary, symlink)
+    ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") %
+               (tool, fw, binary, symlink))
+
+    if not tool:
+        tool = "internal:local"
+        if ui.prompt(_(" no tool found to merge %s\n"
+                       "keep (l)ocal or take (o)ther?") % fw,
+                     _("[lo]"), _("l")) != _("l"):
+            tool = "internal:other"
+    if tool == "internal:local":
+        return 0
+    if tool == "internal:other":
+        repo.wwrite(fd, fco.data(), fco.fileflags())
+        return 0
+    if tool == "internal:fail":
+        return 1
+
+    # do the actual merge
     a = repo.wjoin(fd)
     b = temp("base", fca)
     c = temp("other", fco)
+    out = ""
+    back = a + ".orig"
+    util.copyfile(a, back)
 
     if fw != fo:
         repo.ui.status(_("merging %s and %s\n") % (fw, fo))
     else:
         repo.ui.status(_("merging %s\n") % fw)
-
     repo.ui.debug(_("my %s other %s ancestor %s\n") % (fcm, fco, fca))
 
-    cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
-           or "hgmerge")
-    r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
-                    environ={'HG_FILE': fd,
-                             'HG_MY_NODE': str(wctx.parents()[0]),
-                             'HG_OTHER_NODE': str(mctx),
-                             'HG_MY_ISLINK': fcm.islink(),
-                             'HG_OTHER_ISLINK': fco.islink(),
-                             'HG_BASE_ISLINK': fca.islink(),})
+    # do we attempt to simplemerge first?
+    if _toolbool(ui, tool, "premerge", not (binary or symlink)):
+        r = simplemerge.simplemerge(a, b, c, quiet=True)
+        if not r:
+            ui.debug(_(" premerge successful\n"))
+            os.unlink(back)
+            os.unlink(b)
+            os.unlink(c)
+            return 0
+        util.copyfile(back, a) # restore from backup and try again
+
+    env = dict(HG_FILE=fd,
+               HG_MY_NODE=str(wctx.parents()[0]),
+               HG_OTHER_NODE=str(mctx),
+               HG_MY_ISLINK=fcm.islink(),
+               HG_OTHER_ISLINK=fco.islink(),
+               HG_BASE_ISLINK=fca.islink())
+
+    if tool == "internal:merge":
+        r = simplemerge.simplemerge(a, b, c, label=['local', 'other'])
+    else:
+        toolpath = _findtool(ui, tool)
+        args = _toolstr(ui, tool, "args", '$local $base $other')
+        if "$output" in args:
+            out, a = a, back # read input from backup, write to original
+        replace = dict(local=a, base=b, other=c, output=out)
+        args = re.sub("\$(local|base|other|output)",
+                      lambda x: '"%s"' % replace[x.group()[1:]], args)
+        r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env)
+
+    if not r and _toolbool(ui, tool, "checkconflicts"):
+        if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcm.data()):
+            r = 1
+
     if r:
         repo.ui.warn(_("merging %s failed!\n") % fd)
+    else:
+        os.unlink(back)
 
     os.unlink(b)
     os.unlink(c)
--- a/tests/run-tests.py	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/run-tests.py	Sun Feb 03 19:29:05 2008 -0600
@@ -420,9 +420,7 @@
 HGRCPATH = None
 
 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
-os.environ["HGMERGE"]  = ('python "%s" -L my -L other'
-                          % os.path.join(TESTDIR, os.path.pardir,
-                                         'contrib', 'simplemerge'))
+os.environ["HGMERGE"] = "internal:merge"
 os.environ["HGUSER"]   = "test"
 os.environ["HGENCODING"] = "ascii"
 os.environ["HGENCODINGMODE"] = "strict"
--- a/tests/test-add.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-add.out	Sun Feb 03 19:29:05 2008 -0600
@@ -13,21 +13,26 @@
 % should fail
 a already tracked!
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+merging a
 warning: conflicts during merge.
-merging a
 merging a failed!
 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
 There are unresolved merges, you can redo the full merge using:
   hg update -C 2
   hg merge 1
 M a
+? a.orig
 % should fail
 a already tracked!
 M a
+? a.orig
 % issue683
 R a
+? a.orig
 M a
+? a.orig
 c does not exist!
 d does not exist!
 M a
 A c
+? a.orig
--- a/tests/test-conflict.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-conflict.out	Sun Feb 03 19:29:05 2008 -0600
@@ -1,15 +1,16 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+merging a
 warning: conflicts during merge.
-merging a
 merging a failed!
 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
 There are unresolved merges, you can redo the full merge using:
   hg update -C 2
   hg merge 1
 e7fe8eb3e180+0d24b7662d3e+ tip
-<<<<<<< my
+<<<<<<< local
 something else
 =======
 something
 >>>>>>> other
 M a
+? a.orig
--- a/tests/test-convert-svn-sink.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-convert-svn-sink.out	Sun Feb 03 19:29:05 2008 -0600
@@ -260,8 +260,8 @@
 adding right-1
 adding right-2
 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
+merging b
 warning: conflicts during merge.
-merging b
 merging b failed!
 2 files updated, 0 files merged, 0 files removed, 1 files unresolved
 There are unresolved merges, you can redo the full merge using:
--- a/tests/test-copy-move-merge.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-copy-move-merge.out	Sun Feb 03 19:29:05 2008 -0600
@@ -14,11 +14,15 @@
  a: remote moved to b -> m
 copying a to b
 copying a to c
+picked tool 'internal:merge' for a (binary False symlink False)
 merging a and b
 my a@fb3948d97f07+ other b@40da226db0f0 ancestor a@583c7b748052
+ premerge successful
 removing a
+picked tool 'internal:merge' for a (binary False symlink False)
 merging a and c
 my a@fb3948d97f07+ other c@40da226db0f0 ancestor a@583c7b748052
+ premerge successful
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 -- b --
--- a/tests/test-double-merge.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-double-merge.out	Sun Feb 03 19:29:05 2008 -0600
@@ -10,10 +10,14 @@
  foo: versions differ -> m
  foo: remote copied to bar -> m
 copying foo to bar
+picked tool 'internal:merge' for foo (binary False symlink False)
 merging foo and bar
 my foo@2092631ce82b+ other bar@7731dad1c2b9 ancestor foo@310fd17130da
+ premerge successful
+picked tool 'internal:merge' for foo (binary False symlink False)
 merging foo
 my foo@2092631ce82b+ other foo@7731dad1c2b9 ancestor foo@310fd17130da
+ premerge successful
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 -- foo --
--- a/tests/test-install.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-install.out	Sun Feb 03 19:29:05 2008 -0600
@@ -2,7 +2,6 @@
 Checking extensions...
 Checking templates...
 Checking patch...
-Checking merge helper...
 Checking commit editor...
 Checking username...
 No problems detected
--- a/tests/test-issue672.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-issue672.out	Sun Feb 03 19:29:05 2008 -0600
@@ -28,8 +28,10 @@
    1a -> 1 *
   checking for directory renames
  1a: local moved to 1 -> m
+picked tool 'internal:merge' for 1a (binary False symlink False)
 merging 1a and 1
 my 1a@ac7575e3c052+ other 1@746e9549ea96 ancestor 1@81f4b099af3d
+ premerge successful
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -44,8 +46,10 @@
   checking for directory renames
  1: remote moved to 1a -> m
 copying 1 to 1a
+picked tool 'internal:merge' for 1 (binary False symlink False)
 merging 1 and 1a
 my 1@746e9549ea96+ other 1a@ac7575e3c052 ancestor 1@81f4b099af3d
+ premerge successful
 removing 1
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
--- a/tests/test-merge-commit.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-merge-commit.out	Sun Feb 03 19:29:05 2008 -0600
@@ -25,8 +25,10 @@
  ancestor 0a3ab4856510 local 2d2f9a22c82b+ remote 7d3b554bfdf1
   searching for copies back to rev 1
  bar: versions differ -> m
+picked tool 'internal:merge' for bar (binary False symlink False)
 merging bar
 my bar@2d2f9a22c82b+ other bar@7d3b554bfdf1 ancestor bar@0a3ab4856510
+ premerge successful
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 % contents of bar should be line1 line2
@@ -71,8 +73,10 @@
  ancestor 0a3ab4856510 local 2d2f9a22c82b+ remote 96ab80c60897
   searching for copies back to rev 1
  bar: versions differ -> m
+picked tool 'internal:merge' for bar (binary False symlink False)
 merging bar
 my bar@2d2f9a22c82b+ other bar@96ab80c60897 ancestor bar@0a3ab4856510
+ premerge successful
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 % contents of bar should be line1 line2
--- a/tests/test-merge-local.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-merge-local.out	Sun Feb 03 19:29:05 2008 -0600
@@ -18,27 +18,28 @@
 M zzz2_merge_bad
 # local merge with bad merge tool
 merging zzz1_merge_ok
-merging zzz1_merge_ok failed!
 merging zzz2_merge_bad
 merging zzz2_merge_bad failed!
-3 files updated, 0 files merged, 2 files removed, 2 files unresolved
+3 files updated, 1 files merged, 2 files removed, 1 files unresolved
 There are unresolved merges with locally modified files.
-You can redo the full merge using:
+You can finish the partial merge using:
   hg update 0
   hg update 1
 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
 --- a/zzz1_merge_ok
 +++ b/zzz1_merge_ok
++new first line
 +new last line
 --- a/zzz2_merge_bad
 +++ b/zzz2_merge_bad
 +another last line
 M zzz1_merge_ok
 M zzz2_merge_bad
+? zzz2_merge_bad.orig
 # local merge with conflicts
-warning: conflicts during merge.
 merging zzz1_merge_ok
 merging zzz2_merge_bad
+warning: conflicts during merge.
 merging zzz2_merge_bad failed!
 3 files updated, 1 files merged, 2 files removed, 1 files unresolved
 There are unresolved merges with locally modified files.
@@ -57,6 +58,7 @@
 +new last line
 M zzz1_merge_ok
 M zzz2_merge_bad
+? zzz2_merge_bad.orig
 # local merge without conflicts
 merging zzz1_merge_ok
 4 files updated, 1 files merged, 2 files removed, 0 files unresolved
--- a/tests/test-merge-revert2.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-merge-revert2.out	Sun Feb 03 19:29:05 2008 -0600
@@ -9,8 +9,8 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 f248da0d4c3e tip
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+merging file1
 warning: conflicts during merge.
-merging file1
 merging file1 failed!
 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
 There are unresolved merges with locally modified files.
@@ -23,15 +23,19 @@
 @@ -1,3 +1,7 @@
  added file1
  another line of text
-+<<<<<<< my
++<<<<<<< local
 +changed file1 different
 +=======
  changed file1
 +>>>>>>> other
 M file1
+? file1.orig
 f248da0d4c3e+ tip
 reverting file1
+? file1.orig
 f248da0d4c3e tip
+? file1.orig
 f248da0d4c3e tip
 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+? file1.orig
 f248da0d4c3e tip
--- a/tests/test-merge7.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-merge7.out	Sun Feb 03 19:29:05 2008 -0600
@@ -6,8 +6,8 @@
 adding file changes
 added 1 changesets with 1 changes to 1 files (+1 heads)
 (run 'hg heads' to see heads, 'hg merge' to merge)
+merging test.txt
 warning: conflicts during merge.
-merging test.txt
 merging test.txt failed!
 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
 There are unresolved merges, you can redo the full merge using:
@@ -20,21 +20,22 @@
 adding file changes
 added 1 changesets with 1 changes to 1 files (+1 heads)
 (run 'hg heads' to see heads, 'hg merge' to merge)
-warning: conflicts during merge.
 resolving manifests
  overwrite None partial False
  ancestor faaea63e63a9 local 451c744aabcc+ remote a070d41e8360
   searching for copies back to rev 1
  test.txt: versions differ -> m
+picked tool 'internal:merge' for test.txt (binary False symlink False)
 merging test.txt
 my test.txt@451c744aabcc+ other test.txt@a070d41e8360 ancestor test.txt@faaea63e63a9
+warning: conflicts during merge.
 merging test.txt failed!
 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
 There are unresolved merges, you can redo the full merge using:
   hg update -C 3
   hg merge 4
 one
-<<<<<<< my
+<<<<<<< local
 two-point-five
 =======
 two-point-one
--- a/tests/test-merge9.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-merge9.out	Sun Feb 03 19:29:05 2008 -0600
@@ -5,8 +5,7 @@
 merging bar
 merging bar failed!
 merging foo and baz
-merging baz failed!
-1 files updated, 0 files merged, 0 files removed, 2 files unresolved
+1 files updated, 1 files merged, 0 files removed, 1 files unresolved
 There are unresolved merges, you can redo the full merge using:
   hg update -C 2
   hg merge 1
@@ -14,8 +13,7 @@
 merging bar
 merging bar failed!
 merging baz and foo
-merging baz failed!
-1 files updated, 0 files merged, 0 files removed, 2 files unresolved
+1 files updated, 1 files merged, 0 files removed, 1 files unresolved
 There are unresolved merges, you can redo the full merge using:
   hg update -C 1
   hg merge 2
--- a/tests/test-rename-merge1.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-rename-merge1.out	Sun Feb 03 19:29:05 2008 -0600
@@ -19,8 +19,10 @@
  a: remote moved to b -> m
  b2: remote created -> g
 copying a to b
+picked tool 'internal:merge' for a (binary False symlink False)
 merging a and b
 my a@f26ec4fc3fa3+ other b@8e765a822af2 ancestor a@af1939970a1c
+ premerge successful
 removing a
 warning: detected divergent renames of a2 to:
  c2
--- a/tests/test-rename-merge2.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-rename-merge2.out	Sun Feb 03 19:29:05 2008 -0600
@@ -13,8 +13,11 @@
  rev: versions differ -> m
  a: remote copied to b -> m
 copying a to b
+picked tool 'python ../merge' for a (binary False symlink False)
 merging a and b
 my a@e300d1c794ec+ other b@735846fee2d7 ancestor a@924404dff337
+ premerge successful
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@e300d1c794ec+ other rev@735846fee2d7 ancestor rev@924404dff337
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -41,8 +44,11 @@
  b: local copied to a -> m
  rev: versions differ -> m
 getting a
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b and a
 my b@ac809aeed39a+ other a@f4db7e329e71 ancestor a@924404dff337
+ premerge successful
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ac809aeed39a+ other rev@f4db7e329e71 ancestor rev@924404dff337
 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -68,9 +74,12 @@
  rev: versions differ -> m
  a: remote moved to b -> m
 copying a to b
+picked tool 'python ../merge' for a (binary False symlink False)
 merging a and b
 my a@e300d1c794ec+ other b@e03727d2d66b ancestor a@924404dff337
+ premerge successful
 removing a
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@e300d1c794ec+ other rev@e03727d2d66b ancestor rev@924404dff337
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -94,8 +103,11 @@
   checking for directory renames
  b: local moved to a -> m
  rev: versions differ -> m
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b and a
 my b@ecf3cb2a4219+ other a@f4db7e329e71 ancestor a@924404dff337
+ premerge successful
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ecf3cb2a4219+ other rev@f4db7e329e71 ancestor rev@924404dff337
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -120,6 +132,7 @@
  rev: versions differ -> m
  b: remote created -> g
 getting b
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@94b33a1b7f2d+ other rev@735846fee2d7 ancestor rev@924404dff337
 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
@@ -142,6 +155,7 @@
    b -> a 
   checking for directory renames
  rev: versions differ -> m
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ac809aeed39a+ other rev@97c705ade336 ancestor rev@924404dff337
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
@@ -168,6 +182,7 @@
  b: remote created -> g
 removing a
 getting b
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@94b33a1b7f2d+ other rev@e03727d2d66b ancestor rev@924404dff337
 1 files updated, 1 files merged, 1 files removed, 0 files unresolved
@@ -189,6 +204,7 @@
    b -> a 
   checking for directory renames
  rev: versions differ -> m
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ecf3cb2a4219+ other rev@97c705ade336 ancestor rev@924404dff337
 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
@@ -206,8 +222,10 @@
   searching for copies back to rev 1
  b: versions differ -> m
  rev: versions differ -> m
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b
 my b@ec03c2ca8642+ other b@79cc6877a3b7 ancestor a@924404dff337
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ec03c2ca8642+ other rev@79cc6877a3b7 ancestor rev@924404dff337
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -238,6 +256,7 @@
  b
  c
 getting c
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ecf3cb2a4219+ other rev@e6abcc1a30c2 ancestor rev@924404dff337
 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
@@ -256,8 +275,10 @@
   searching for copies back to rev 1
  b: versions differ -> m
  rev: versions differ -> m
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b
 my b@ac809aeed39a+ other b@af30c7647fc7 ancestor b@000000000000
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ac809aeed39a+ other rev@af30c7647fc7 ancestor rev@924404dff337
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -278,8 +299,10 @@
  b: versions differ -> m
  rev: versions differ -> m
 removing a
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b
 my b@59318016310c+ other b@e03727d2d66b ancestor b@000000000000
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@59318016310c+ other rev@e03727d2d66b ancestor rev@924404dff337
 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
@@ -299,8 +322,10 @@
  b: versions differ -> m
  rev: versions differ -> m
 getting a
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b
 my b@ac809aeed39a+ other b@8dbce441892a ancestor b@000000000000
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ac809aeed39a+ other rev@8dbce441892a ancestor rev@924404dff337
 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -321,8 +346,10 @@
  b: versions differ -> m
  rev: versions differ -> m
 removing a
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b
 my b@59318016310c+ other b@e03727d2d66b ancestor b@000000000000
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@59318016310c+ other rev@e03727d2d66b ancestor rev@924404dff337
 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
@@ -342,8 +369,10 @@
  b: versions differ -> m
  rev: versions differ -> m
 getting a
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b
 my b@ac809aeed39a+ other b@8dbce441892a ancestor b@000000000000
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ac809aeed39a+ other rev@8dbce441892a ancestor rev@924404dff337
 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -362,8 +391,10 @@
   searching for copies back to rev 1
  b: versions differ -> m
  rev: versions differ -> m
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b
 my b@0b76e65c8289+ other b@735846fee2d7 ancestor b@000000000000
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@0b76e65c8289+ other rev@735846fee2d7 ancestor rev@924404dff337
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -384,8 +415,10 @@
  rev: versions differ -> m
  a: prompt recreating -> g
 getting a
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b
 my b@ecf3cb2a4219+ other b@8dbce441892a ancestor b@000000000000
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ecf3cb2a4219+ other rev@8dbce441892a ancestor rev@924404dff337
 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -404,8 +437,10 @@
   searching for copies back to rev 1
  b: versions differ -> m
  rev: versions differ -> m
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b
 my b@0b76e65c8289+ other b@e03727d2d66b ancestor b@000000000000
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@0b76e65c8289+ other rev@e03727d2d66b ancestor rev@924404dff337
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -430,9 +465,11 @@
  rev: versions differ -> m
  a: remote moved to b -> m
 copying a to b
+picked tool 'python ../merge' for a (binary False symlink False)
 merging a and b
 my a@e300d1c794ec+ other b@79cc6877a3b7 ancestor a@924404dff337
 removing a
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@e300d1c794ec+ other rev@79cc6877a3b7 ancestor rev@924404dff337
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -456,8 +493,10 @@
   checking for directory renames
  b: local moved to a -> m
  rev: versions differ -> m
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b and a
 my b@ec03c2ca8642+ other a@f4db7e329e71 ancestor a@924404dff337
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ec03c2ca8642+ other rev@f4db7e329e71 ancestor rev@924404dff337
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
@@ -484,9 +523,12 @@
  b: local moved to a -> m
  rev: versions differ -> m
  c: remote created -> g
+picked tool 'python ../merge' for b (binary False symlink False)
 merging b and a
 my b@ecf3cb2a4219+ other a@2b958612230f ancestor a@924404dff337
+ premerge successful
 getting c
+picked tool 'python ../merge' for rev (binary False symlink False)
 merging rev
 my rev@ecf3cb2a4219+ other rev@2b958612230f ancestor rev@924404dff337
 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-up-local-change.out	Sun Feb 03 19:29:05 2008 -0600
+++ b/tests/test-up-local-change.out	Sun Feb 03 19:29:05 2008 -0600
@@ -22,6 +22,7 @@
    b
  a: versions differ -> m
  b: remote created -> g
+picked tool 'true' for a (binary False symlink False)
 merging a
 my a@33aaa84a386b+ other a@802f095af299 ancestor a@33aaa84a386b
 getting b
@@ -58,6 +59,7 @@
    b
  a: versions differ -> m
  b: remote created -> g
+picked tool 'true' for a (binary False symlink False)
 merging a
 my a@33aaa84a386b+ other a@802f095af299 ancestor a@33aaa84a386b
 getting b
@@ -109,8 +111,10 @@
   searching for copies back to rev 1
  a: versions differ -> m
  b: versions differ -> m
+picked tool 'true' for a (binary False symlink False)
 merging a
 my a@802f095af299+ other a@030602aee63d ancestor a@33aaa84a386b
+picked tool 'true' for b (binary False symlink False)
 merging b
 my b@802f095af299+ other b@030602aee63d ancestor b@000000000000
 0 files updated, 2 files merged, 0 files removed, 0 files unresolved