diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -50,6 +50,7 @@ pycompat, rcutil, registrar, + repair, revsetlang, rewriteutil, scmutil, @@ -2109,6 +2110,7 @@ [('r', 'rev', [], _('revisions to graft'), _('REV')), ('c', 'continue', False, _('resume interrupted graft')), ('', 'stop', False, _('stop interrupted graft')), + ('', 'abort', False, _('abort interrupted graft')), ('e', 'edit', False, _('invoke editor on commit messages')), ('', 'log', None, _('append graft info to log message')), ('f', 'force', False, _('force graft')), @@ -2206,11 +2208,24 @@ if opts.get('continue'): raise error.Abort(_("cannot use '--continue' and " "'--stop' together")) + if opts.get('abort'): + raise error.Abort(_("cannot use '--abort' and '--stop' together")) + if any((opts.get('edit'), opts.get('log'), opts.get('user'), opts.get('date'), opts.get('currentdate'), opts.get('currentuser'), opts.get('rev'))): raise error.Abort(_("cannot specify any other flag with '--stop'")) return _stopgraft(ui, repo, graftstate) + elif opts.get('abort'): + if opts.get('continue'): + raise error.Abort(_("cannot use '--continue' and " + "'--abort' together")) + if any((opts.get('edit'), opts.get('log'), opts.get('user'), + opts.get('date'), opts.get('currentdate'), + opts.get('currentuser'), opts.get('rev'))): + raise error.Abort(_("cannot specify any other flag with '--abort'")) + + return _abortgraft(ui, repo, graftstate) elif opts.get('continue'): cont = True if revs: @@ -2379,6 +2394,45 @@ return 0 +def _abortgraft(ui, repo, graftstate): + """abort the interrupted graft and rollbacks to the state before interrupted + graft""" + if not graftstate.exists(): + raise error.Abort(_("no interrupted graft to abort")) + statedata = _readgraftstate(repo, graftstate) + startnode = statedata.get('startnode') + if not startnode: + # and old graft state which does not have all the data required to abort + # the graft + raise error.Abort(_("cannot abort using an old graftstate")) + newnodes = statedata.get('newnodes') + hg.updaterepo(repo, startnode, True) + if newnodes: + newnodes = [repo[r].rev() for r in newnodes] + + # checking that none of the newnodes turned public or is public + immutable = [c for c in newnodes if not repo[c].mutable()] + if immutable: + repo.ui.warn(_("cannot clean up public changesets %s\n") + % ','.join(bytes(repo[r]) for r in immutable), + hint=_("see 'hg help phases' for details")) + + # checking that no new nodes are created on top of grafted revs + desc = set(repo.changelog.descendants(newnodes)) + if desc - set(newnodes): + repo.ui.warn(_("new changesets detected on destination " + "branch, can't strip\n")) + + with repo.wlock(), repo.lock(): + # stripping the new nodes created + strippoints = [c.node() for c in repo.set("roots(%ld)", newnodes)] + repair.strip(repo.ui, repo, strippoints, False) + + ui.write(_("graft aborted\nworking directory is now at %s\n") + % repo[startnode].hex()[:12]) + graftstate.delete() + return 0 + def _readgraftstate(repo, graftstate): """read the graft state file and return a dict of the data stored in it""" try: diff --git a/tests/test-completion.t b/tests/test-completion.t --- a/tests/test-completion.t +++ b/tests/test-completion.t @@ -312,7 +312,7 @@ debugwireargs: three, four, five, ssh, remotecmd, insecure debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure files: rev, print0, include, exclude, template, subrepos - graft: rev, continue, stop, edit, log, force, currentdate, currentuser, date, user, tool, dry-run + graft: rev, continue, stop, abort, edit, log, force, currentdate, currentuser, date, user, tool, dry-run grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, template, include, exclude heads: rev, topo, active, closed, style, template help: extension, command, keyword, system diff --git a/tests/test-graft.t b/tests/test-graft.t --- a/tests/test-graft.t +++ b/tests/test-graft.t @@ -1670,3 +1670,219 @@ | o 0:9092f1db7931 added a + $ cd .. + +Testing the --abort flag for `hg graft` which aborts and rollback to state +before the graft + + $ hg init abortgraft + $ cd abortgraft + $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done; + + $ hg up '.^^' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + + $ echo x > x + $ hg ci -Aqm "added x" + $ hg up '.^' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo foo > c + $ hg ci -Aqm "added foo to c" + + $ hg log -GT "{rev}:{node|short} {desc}" + @ 5:36b793615f78 added foo to c + | + | o 4:863a25e1a9ea added x + |/ + | o 3:9150fe93bec6 added d + | | + | o 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + + $ hg up 9150fe93bec6 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ hg graft --abort + abort: no interrupted graft to abort + [255] + +when stripping is required + $ hg graft -r 4 -r 5 + grafting 4:863a25e1a9ea "added x" + grafting 5:36b793615f78 "added foo to c" (tip) + merging c + warning: conflicts while merging c! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ hg graft --continue --abort + abort: cannot use '--continue' and '--abort' together + [255] + + $ hg graft --abort --stop + abort: cannot use '--abort' and '--stop' together + [255] + + $ hg graft --abort --currentuser + abort: cannot specify any other flag with '--abort' + [255] + + $ hg graft --abort --edit + abort: cannot specify any other flag with '--abort' + [255] + + $ hg graft --abort + graft aborted + working directory is now at 9150fe93bec6 + $ hg log -GT "{rev}:{node|short} {desc}" + o 5:36b793615f78 added foo to c + | + | o 4:863a25e1a9ea added x + |/ + | @ 3:9150fe93bec6 added d + | | + | o 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + +when stripping is not required + $ hg graft -r 5 + grafting 5:36b793615f78 "added foo to c" (tip) + merging c + warning: conflicts while merging c! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ hg graft --abort + graft aborted + working directory is now at 9150fe93bec6 + $ hg log -GT "{rev}:{node|short} {desc}" + o 5:36b793615f78 added foo to c + | + | o 4:863a25e1a9ea added x + |/ + | @ 3:9150fe93bec6 added d + | | + | o 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + +when some of the changesets became public + + $ hg graft -r 4 -r 5 + grafting 4:863a25e1a9ea "added x" + grafting 5:36b793615f78 "added foo to c" (tip) + merging c + warning: conflicts while merging c! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ hg log -GT "{rev}:{node|short} {desc}" + @ 6:6ec71c037d94 added x + | + | o 5:36b793615f78 added foo to c + | | + | | o 4:863a25e1a9ea added x + | |/ + o | 3:9150fe93bec6 added d + | | + o | 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + + $ hg phase -r 6 --public + + $ hg graft --abort + cannot clean up public changesets 6ec71c037d94 + graft aborted + working directory is now at 9150fe93bec6 + +when we created new changesets on top of existing one + + $ hg up '.^' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo y > y + $ hg ci -Aqm "added y" + $ echo z > z + $ hg ci -Aqm "added z" + + $ hg up 3 + 1 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ hg log -GT "{rev}:{node|short} {desc}" + o 7:637f9e9bbfd4 added z + | + o 6:123221671fd4 added y + | + | o 5:36b793615f78 added foo to c + | | + | | o 4:863a25e1a9ea added x + | |/ + +---@ 3:9150fe93bec6 added d + | | + o | 2:155349b645be added c + |/ + o 1:5f6d8a4bf34a added b + | + o 0:9092f1db7931 added a + + $ hg ci -Aqm "added w" + [1] + $ hg graft -r 6 -r 7 -r 5 + grafting 6:123221671fd4 "added y" + grafting 7:637f9e9bbfd4 "added z" (tip) + grafting 5:36b793615f78 "added foo to c" + merging c + warning: conflicts while merging c! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + + $ cd .. + $ hg init pullrepo + $ cd pullrepo + $ cat >> .hg/hgrc < [phases] + > publish=False + > EOF + $ hg pull ../abortgraft --config phases.publish=False + pulling from ../abortgraft + requesting all changes + adding changesets + adding manifests + adding file changes + added 10 changesets with 9 changes to 8 files (+3 heads) + new changesets 9092f1db7931:f69642afdae0 + (run 'hg heads' to see heads, 'hg merge' to merge) + $ hg up 9 + 7 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo w > w + $ hg ci -Aqm "added w" --config phases.publish=False + + $ cd ../abortgraft + $ hg pull ../pullrepo + pulling from ../pullrepo + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + new changesets 0d1829ee29bf + (run 'hg update' to get a working copy) + + $ hg graft --abort + new changesets detected on destination branch, can't strip + graft aborted + working directory is now at 9150fe93bec6 +