# HG changeset patch # User Matt Mackall # Date 1207936376 18000 # Node ID 92ccccb55ba33bdf203e70e3e701fc909707819a # Parent fcfb6a0a0a84960861b022f5bb3911f728734220 resolve: new command - add basic resolve command functionality - point failed update and merge at resolve diff -r fcfb6a0a0a84 -r 92ccccb55ba3 mercurial/commands.py --- 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')), diff -r fcfb6a0a0a84 -r 92ccccb55ba3 mercurial/hg.py --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 mercurial/merge.py --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-add.out --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-conflict.out --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-convert-svn-sink.out --- 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' diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-debugcomplete.out --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-globalopts.out --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-help.out --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-merge-local.out --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-merge-revert2.out --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-merge7.out --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-merge9 --- 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 diff -r fcfb6a0a0a84 -r 92ccccb55ba3 tests/test-merge9.out --- 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