Patchwork D3754: graft: introduce --abort flag to abort interrupted graft

login
register
mail settings
Submitter phabricator
Date June 25, 2018, 3:06 p.m.
Message ID <1bf588e2f6955bfc6dfa4cadcec99bd3@localhost.localdomain>
Download mbox | patch
Permalink /patch/32415/
State Not Applicable
Headers show

Comments

phabricator - June 25, 2018, 3:06 p.m.
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG5cdfc20bfd5f: graft: introduce --abort flag to abort interrupted graft (authored by pulkit, committed by ).

CHANGED PRIOR TO COMMIT
  https://phab.mercurial-scm.org/D3754?vs=9275&id=9282#toc

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D3754?vs=9275&id=9282

REVISION DETAIL
  https://phab.mercurial-scm.org/D3754

AFFECTED FILES
  mercurial/commands.py
  tests/test-completion.t
  tests/test-graft.t

CHANGE DETAILS




To: pulkit, #hg-reviewers
Cc: yuja, mercurial-devel

Patch

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,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
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, 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
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -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: