resolve: new command
authorMatt Mackall <mpm@selenic.com>
Fri, 11 Apr 2008 12:52:56 -0500
changeset 6518 92ccccb55ba3
parent 6517 fcfb6a0a0a84
child 6523 98dfc5751fdc
resolve: new command - add basic resolve command functionality - point failed update and merge at resolve
mercurial/commands.py
mercurial/hg.py
mercurial/merge.py
tests/test-add.out
tests/test-conflict.out
tests/test-convert-svn-sink.out
tests/test-debugcomplete.out
tests/test-globalopts.out
tests/test-help.out
tests/test-merge-local.out
tests/test-merge-revert2.out
tests/test-merge7.out
tests/test-merge9
tests/test-merge9.out
--- a/mercurial/commands.py	Fri Apr 11 12:04:26 2008 +0200
+++ b/mercurial/commands.py	Fri Apr 11 12:52:56 2008 -0500
@@ -13,6 +13,7 @@
 import difflib, patch, time, help, mdiff, tempfile
 import version, socket
 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
+import merge as merge_
 
 # Commands start here, listed alphabetically
 
@@ -2236,6 +2237,35 @@
     finally:
         del wlock
 
+def resolve(ui, repo, *pats, **opts):
+    """resolve file merges from a branch merge or update
+
+    This command will attempt to resolve unresolved merges from the
+    last update or merge command. This will use the local file
+    revision preserved at the last update or merge to cleanly retry
+    the file merge attempt. With no file or options specified, this
+    command will attempt to resolve all unresolved files.
+    """
+
+    if len([x for x in opts if opts[x]]) > 1:
+        raise util.Abort(_("too many options specified"))
+
+    ms = merge_.mergestate(repo)
+    mf = util.matcher(repo.root, "", pats, [], [])[1]
+
+    for f in ms:
+        if mf(f):
+            if opts.get("list"):
+                ui.write("%s %s\n" % (ms[f].upper(), f))
+            elif opts.get("mark"):
+                ms.mark(f, "r")
+            elif opts.get("unmark"):
+                ms.mark(f, "u")
+            else:
+                wctx = repo.workingctx()
+                mctx = wctx.parents()[-1]
+                ms.resolve(f, wctx, mctx)
+
 def revert(ui, repo, *pats, **opts):
     """restore individual files or dirs to an earlier state
 
@@ -3196,6 +3226,12 @@
            _('forcibly copy over an existing managed file')),
          ] + walkopts + dryrunopts,
          _('hg rename [OPTION]... SOURCE... DEST')),
+    "resolve":
+        (resolve,
+         [('l', 'list', None, _('list state of files needing merge')),
+          ('m', 'mark', None, _('mark files as resolved')),
+          ('u', 'unmark', None, _('unmark files as resolved'))],
+          ('hg resolve [OPTION] [FILES...]')),
     "revert":
         (revert,
          [('a', 'all', None, _('revert all changes when no arguments given')),
--- a/mercurial/hg.py	Fri Apr 11 12:04:26 2008 +0200
+++ b/mercurial/hg.py	Fri Apr 11 12:52:56 2008 -0500
@@ -271,15 +271,7 @@
     stats = _merge.update(repo, node, False, False, None)
     _showstats(repo, stats)
     if stats[3]:
-        repo.ui.status(_("There are unresolved merges with"
-                         " locally modified files.\n"))
-        if stats[1]:
-            repo.ui.status(_("You can finish the partial merge using:\n"))
-        else:
-            repo.ui.status(_("You can redo the full merge using:\n"))
-        # len(pl)==1, otherwise _merge.update() would have raised util.Abort:
-        repo.ui.status(_("  hg update %s\n  hg update %s\n")
-                       % (pl[0].rev(), repo.changectx(node).rev()))
+        repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
     return stats[3] > 0
 
 def clean(repo, node, show_stats=True):
@@ -294,11 +286,7 @@
     _showstats(repo, stats)
     if stats[3]:
         pl = repo.parents()
-        repo.ui.status(_("There are unresolved merges,"
-                         " you can redo the full merge using:\n"
-                         "  hg update -C %s\n"
-                         "  hg merge %s\n")
-                       % (pl[0].rev(), pl[1].rev()))
+        repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
     elif remind:
         repo.ui.status(_("(branch merge, don't forget to commit)\n"))
     return stats[3] > 0
--- a/mercurial/merge.py	Fri Apr 11 12:04:26 2008 +0200
+++ b/mercurial/merge.py	Fri Apr 11 12:52:56 2008 -0500
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-from node import nullid, nullrev, hex
+from node import nullid, nullrev, hex, bin
 from i18n import _
 import errno, util, os, filemerge, copies, shutil
 
@@ -13,27 +13,49 @@
     '''track 3-way merge state of individual files'''
     def __init__(self, repo):
         self._repo = repo
+        self._read()
+    def reset(self, node):
         self._state = {}
-        self._data = {}
-    def reset(self, node):
         self._local = node
         shutil.rmtree(self._repo.join("merge"), True)
+    def _read(self):
+        self._state = {}
+        try:
+            f = self._repo.opener("merge/state")
+            self._local = bin(f.readline()[:-1])
+            for l in f:
+                bits = l[:-1].split("\0")
+                self._state[bits[0]] = bits[1:]
+        except IOError, err:
+            if err.errno != errno.ENOENT:
+                raise
+    def _write(self):
+        f = self._repo.opener("merge/state", "w")
+        f.write(hex(self._local) + "\n")
+        for d, v in self._state.items():
+            f.write("\0".join([d] + v) + "\n")
     def add(self, fcl, fco, fca, fd, flags):
         hash = util.sha1(fcl.path()).hexdigest()
         self._repo.opener("merge/" + hash, "w").write(fcl.data())
-        self._state[fd] = 'u'
-        self._data[fd] = (hash, fcl.path(), fca.path(), hex(fca.filenode()),
-                          fco.path(), flags)
+        self._state[fd] = ['u', hash, fcl.path(), fca.path(),
+                           hex(fca.filenode()), fco.path(), flags]
+        self._write()
     def __contains__(self, dfile):
         return dfile in self._state
     def __getitem__(self, dfile):
-        return self._state[dfile]
+        return self._state[dfile][0]
+    def __iter__(self):
+        l = self._state.keys()
+        l.sort()
+        for f in l:
+            yield f
     def mark(self, dfile, state):
-        self._state[dfile] = state
+        self._state[dfile][0] = state
+        self._write()
     def resolve(self, dfile, wctx, octx):
         if self[dfile] == 'r':
             return 0
-        hash, lfile, afile, anode, ofile, flags = self._data[dfile]
+        state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
         f = self._repo.opener("merge/" + hash)
         self._repo.wwrite(dfile, f.read(), flags)
         fcd = wctx[dfile]
@@ -41,7 +63,6 @@
         fca = self._repo.filectx(afile, fileid=anode)
         r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
         if not r:
-            util.set_flags(self._repo.wjoin(dfile), flags)
             self.mark(dfile, 'r')
         return r
 
--- a/tests/test-add.out	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-add.out	Fri Apr 11 12:52:56 2008 -0500
@@ -18,9 +18,7 @@
 warning: conflicts during merge.
 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
+use 'hg resolve' to retry unresolved file merges
 M a
 ? a.orig
 % should fail
--- a/tests/test-conflict.out	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-conflict.out	Fri Apr 11 12:52:56 2008 -0500
@@ -4,9 +4,7 @@
 warning: conflicts during merge.
 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
+use 'hg resolve' to retry unresolved file merges
 e7fe8eb3e180+0d24b7662d3e+ tip
 <<<<<<< local
 something else
--- a/tests/test-convert-svn-sink.out	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-convert-svn-sink.out	Fri Apr 11 12:52:56 2008 -0500
@@ -265,9 +265,7 @@
 warning: conflicts during merge.
 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:
-  hg update -C 2
-  hg merge 4
+use 'hg resolve' to retry unresolved file merges
 assuming destination b-hg
 initializing svn repo 'b-hg'
 initializing svn wc 'b-hg-wc'
--- a/tests/test-debugcomplete.out	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-debugcomplete.out	Fri Apr 11 12:52:56 2008 -0500
@@ -33,6 +33,7 @@
 recover
 remove
 rename
+resolve
 revert
 rollback
 root
@@ -79,6 +80,7 @@
 recover
 remove
 rename
+resolve
 revert
 rollback
 root
--- a/tests/test-globalopts.out	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-globalopts.out	Fri Apr 11 12:52:56 2008 -0500
@@ -183,6 +183,7 @@
  recover      roll back an interrupted transaction
  remove       remove the specified files on the next commit
  rename       rename files; equivalent of copy + remove
+ resolve      resolve file merges from a branch merge or update
  revert       restore individual files or dirs to an earlier state
  rollback     roll back the last transaction
  root         print the root (top) of the current working dir
@@ -236,6 +237,7 @@
  recover      roll back an interrupted transaction
  remove       remove the specified files on the next commit
  rename       rename files; equivalent of copy + remove
+ resolve      resolve file merges from a branch merge or update
  revert       restore individual files or dirs to an earlier state
  rollback     roll back the last transaction
  root         print the root (top) of the current working dir
--- a/tests/test-help.out	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-help.out	Fri Apr 11 12:52:56 2008 -0500
@@ -74,6 +74,7 @@
  recover      roll back an interrupted transaction
  remove       remove the specified files on the next commit
  rename       rename files; equivalent of copy + remove
+ resolve      resolve file merges from a branch merge or update
  revert       restore individual files or dirs to an earlier state
  rollback     roll back the last transaction
  root         print the root (top) of the current working dir
@@ -123,6 +124,7 @@
  recover      roll back an interrupted transaction
  remove       remove the specified files on the next commit
  rename       rename files; equivalent of copy + remove
+ resolve      resolve file merges from a branch merge or update
  revert       restore individual files or dirs to an earlier state
  rollback     roll back the last transaction
  root         print the root (top) of the current working dir
--- a/tests/test-merge-local.out	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-merge-local.out	Fri Apr 11 12:52:56 2008 -0500
@@ -21,10 +21,7 @@
 merging zzz2_merge_bad
 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.
-You can finish the partial merge using:
-  hg update 0
-  hg update 1
+use 'hg resolve' to retry unresolved file merges
 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
 --- a/zzz1_merge_ok
 +++ b/zzz1_merge_ok
@@ -42,10 +39,7 @@
 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.
-You can finish the partial merge using:
-  hg update 0
-  hg update 1
+use 'hg resolve' to retry unresolved file merges
 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
 --- a/zzz1_merge_ok
 +++ b/zzz1_merge_ok
--- a/tests/test-merge-revert2.out	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-merge-revert2.out	Fri Apr 11 12:52:56 2008 -0500
@@ -13,10 +13,7 @@
 warning: conflicts during merge.
 merging file1 failed!
 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
-There are unresolved merges with locally modified files.
-You can redo the full merge using:
-  hg update 0
-  hg update 1
+use 'hg resolve' to retry unresolved file merges
 diff -r f248da0d4c3e file1
 --- a/file1
 +++ b/file1
--- a/tests/test-merge7.out	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-merge7.out	Fri Apr 11 12:52:56 2008 -0500
@@ -11,9 +11,7 @@
 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 1
-  hg merge 2
+use 'hg resolve' to retry unresolved file merges
 pulling from ../test-a
 searching for changes
 adding changesets
@@ -33,9 +31,7 @@
 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
+use 'hg resolve' to retry unresolved file merges
 one
 <<<<<<< local
 two-point-five
--- a/tests/test-merge9	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-merge9	Fri Apr 11 12:52:56 2008 -0500
@@ -23,9 +23,31 @@
 
 # test with the rename on the remote side
 HGMERGE=false hg merge
+hg resolve -l
 
 # test with the rename on the local side
 hg up -C 1
 HGMERGE=false hg merge
 
+echo % show unresolved
+hg resolve -l
+
+echo % unmark baz
+hg resolve -u baz
+
+echo % show
+hg resolve -l
+
+echo % re-resolve baz
+hg resolve baz
+
+echo % after
+hg resolve -l
+
+echo % resolve all
+hg resolve
+
+echo % after
+hg resolve -l
+
 true
--- a/tests/test-merge9.out	Fri Apr 11 12:04:26 2008 +0200
+++ b/tests/test-merge9.out	Fri Apr 11 12:52:56 2008 -0500
@@ -7,14 +7,31 @@
 merging bar failed!
 merging foo and baz to baz
 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
+use 'hg resolve' to retry unresolved file merges
+U bar
+R baz
 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
 merging bar
 merging bar failed!
 merging baz and foo to baz
 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
+use 'hg resolve' to retry unresolved file merges
+% show unresolved
+U bar
+R baz
+% unmark baz
+% show
+U bar
+U baz
+% re-resolve baz
+merging baz and foo to baz
+% after
+U bar
+R baz
+% resolve all
+merging bar
+warning: conflicts during merge.
+merging bar failed!
+% after
+U bar
+R baz