graft: introduce --abort flag to abort interrupted graft
authorPulkit Goyal <7895pulkit@gmail.com>
Fri, 25 May 2018 18:16:38 +0530
changeset 38453 5cdfc20bfd5f
parent 38452 afb7e15392b9
child 38454 d24ad71ff869
graft: introduce --abort flag to abort interrupted graft This patch introduces a new --abort flag to `hg graft` command which aborts an interrupted graft and rollbacks to the state before graft. The behavior when some of grafted changeset get's published while interrupted graft or we have new descendants on grafted changesets is same as that of rebase which is warn the user, don't strip and abort the abort the graft. Tests are added for the new flag. .. feature:: `hg graft` now has a `--abort` flag which aborts the interrupted graft and rollbacks to state before the graft. Differential Revision: https://phab.mercurial-scm.org/D3754
mercurial/commands.py
tests/test-completion.t
tests/test-graft.t
--- a/mercurial/commands.py	Fri Jun 15 02:46:34 2018 +0530
+++ b/mercurial/commands.py	Fri May 25 18:16:38 2018 +0530
@@ -49,6 +49,7 @@
     pycompat,
     rcutil,
     registrar,
+    repair,
     revsetlang,
     rewriteutil,
     scmutil,
@@ -2107,6 +2108,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')),
@@ -2204,11 +2206,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:
@@ -2375,6 +2390,62 @@
 
     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)
+    newnodes = statedata.get('newnodes')
+    if newnodes is None:
+        # 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"))
+
+    # changeset from which graft operation was started
+    startctx = None
+    if len(newnodes) > 0:
+        startctx = repo[newnodes[0]].p1()
+    else:
+        startctx = repo['.']
+    # whether to strip or not
+    cleanup = False
+    if newnodes:
+        newnodes = [repo[r].rev() for r in newnodes]
+        cleanup = True
+        # 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"))
+            cleanup = False
+
+        # 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"))
+            cleanup = False
+
+        if cleanup:
+            with repo.wlock(), repo.lock():
+                hg.updaterepo(repo, startctx.node(), True)
+                # stripping the new nodes created
+                strippoints = [c.node() for c in repo.set("roots(%ld)",
+                                                          newnodes)]
+                repair.strip(repo.ui, repo, strippoints, backup=False)
+
+    if not cleanup:
+        # we don't update to the startnode if we can't strip
+        startctx = repo['.']
+        hg.updaterepo(repo, startctx.node(), True)
+
+    ui.status(_("graft aborted\n"))
+    ui.status(_("working directory is now at %s\n") % startctx.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:
--- a/tests/test-completion.t	Fri Jun 15 02:46:34 2018 +0530
+++ b/tests/test-completion.t	Fri May 25 18:16:38 2018 +0530
@@ -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, diff, text, follow, ignore-case, files-with-matches, line-number, rev, allfiles, user, date, template, include, exclude
   heads: rev, topo, active, closed, style, template
   help: extension, command, keyword, system
--- a/tests/test-graft.t	Fri Jun 15 02:46:34 2018 +0530
+++ b/tests/test-graft.t	Fri May 25 18:16:38 2018 +0530
@@ -1670,3 +1670,218 @@
   |
   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 6ec71c037d94
+
+when we created new changesets on top of existing one
+
+  $ hg up '.^^'
+  0 files updated, 0 files merged, 2 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  8:637f9e9bbfd4 added z
+  |
+  o  7:123221671fd4 added y
+  |
+  | o  6:6ec71c037d94 added x
+  | |
+  | | 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 graft -r 8 -r 7 -r 5
+  grafting 8:637f9e9bbfd4 "added z" (tip)
+  grafting 7:123221671fd4 "added y"
+  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 <<EOF
+  > [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 11 changesets with 9 changes to 8 files (+4 heads)
+  new changesets 9092f1db7931:6b98ff0062dd
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg up 9
+  5 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 (+1 heads)
+  new changesets 311dfc6cf3bf
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
+
+  $ hg graft --abort
+  new changesets detected on destination branch, can't strip
+  graft aborted
+  working directory is now at 6b98ff0062dd