Patchwork [shelve-ext,v2] shelve: add logic to preserve active bookmarks

login
register
mail settings
Submitter Kostia Balytskyi
Date March 26, 2017, 11:52 p.m.
Message ID <89739c20e31944f987de.1490572376@devvm1416.lla2.facebook.com>
Download mbox | patch
Permalink /patch/19713/
State Accepted
Headers show

Comments

Kostia Balytskyi - March 26, 2017, 11:52 p.m.
# HG changeset patch
# User Kostia Balytskyi <ikostia@fb.com>
# Date 1490572279 25200
#      Sun Mar 26 16:51:19 2017 -0700
# Node ID 89739c20e31944f987de17620d76c82df6f0eda7
# Parent  c60091fa1426892552dd6c0dd4b9c49e3c3da045
shelve: add logic to preserve active bookmarks

This adds an explicit active-bookmark-handling logic
to shelve. Traditional shelve handles it by transaction aborts,
but it is a bit ugly and having an explicit functionality
seems better.
Augie Fackler - March 27, 2017, 4:34 p.m.
On Sun, Mar 26, 2017 at 04:52:56PM -0700, Kostia Balytskyi wrote:
> # HG changeset patch
> # User Kostia Balytskyi <ikostia@fb.com>
> # Date 1490572279 25200
> #      Sun Mar 26 16:51:19 2017 -0700
> # Node ID 89739c20e31944f987de17620d76c82df6f0eda7
> # Parent  c60091fa1426892552dd6c0dd4b9c49e3c3da045
> shelve: add logic to preserve active bookmarks

Queued this, very happy to have this explicit instead of implicit.

>
> This adds an explicit active-bookmark-handling logic
> to shelve. Traditional shelve handles it by transaction aborts,
> but it is a bit ugly and having an explicit functionality
> seems better.
>
> diff --git a/hgext/shelve.py b/hgext/shelve.py
> --- a/hgext/shelve.py
> +++ b/hgext/shelve.py
> @@ -28,6 +28,7 @@ import itertools
>
>  from mercurial.i18n import _
>  from mercurial import (
> +    bookmarks,
>      bundle2,
>      bundlerepo,
>      changegroup,
> @@ -170,6 +171,8 @@ class shelvedstate(object):
>      _filename = 'shelvedstate'
>      _keep = 'keep'
>      _nokeep = 'nokeep'
> +    # colon is essential to differentiate from a real bookmark name
> +    _noactivebook = ':no-active-bookmark'
>
>      @classmethod
>      def load(cls, repo):
> @@ -187,6 +190,7 @@ class shelvedstate(object):
>              nodestoprune = [nodemod.bin(h) for h in fp.readline().split()]
>              branchtorestore = fp.readline().strip()
>              keep = fp.readline().strip() == cls._keep
> +            activebook = fp.readline().strip()
>          except (ValueError, TypeError) as err:
>              raise error.CorruptedState(str(err))
>          finally:
> @@ -201,6 +205,9 @@ class shelvedstate(object):
>              obj.nodestoprune = nodestoprune
>              obj.branchtorestore = branchtorestore
>              obj.keep = keep
> +            obj.activebookmark = ''
> +            if activebook != cls._noactivebook:
> +                obj.activebookmark = activebook
>          except error.RepoLookupError as err:
>              raise error.CorruptedState(str(err))
>
> @@ -208,7 +215,7 @@ class shelvedstate(object):
>
>      @classmethod
>      def save(cls, repo, name, originalwctx, pendingctx, nodestoprune,
> -             branchtorestore, keep=False):
> +             branchtorestore, keep=False, activebook=''):
>          fp = repo.vfs(cls._filename, 'wb')
>          fp.write('%i\n' % cls._version)
>          fp.write('%s\n' % name)
> @@ -220,6 +227,7 @@ class shelvedstate(object):
>                   ' '.join([nodemod.hex(n) for n in nodestoprune]))
>          fp.write('%s\n' % branchtorestore)
>          fp.write('%s\n' % (cls._keep if keep else cls._nokeep))
> +        fp.write('%s\n' % (activebook or cls._noactivebook))
>          fp.close()
>
>      @classmethod
> @@ -244,6 +252,16 @@ def cleanupoldbackups(repo):
>          for ext in shelvefileextensions:
>              vfs.tryunlink(base + '.' + ext)
>
> +def _backupactivebookmark(repo):
> +    activebookmark = repo._activebookmark
> +    if activebookmark:
> +        bookmarks.deactivate(repo)
> +    return activebookmark
> +
> +def _restoreactivebookmark(repo, mark):
> +    if mark:
> +        bookmarks.activate(repo, mark)
> +
>  def _aborttransaction(repo):
>      '''Abort current transaction for shelve/unshelve, but keep dirstate
>      '''
> @@ -377,7 +395,7 @@ def _docreatecmd(ui, repo, pats, opts):
>      if not opts.get('message'):
>          opts['message'] = desc
>
> -    lock = tr = None
> +    lock = tr = activebookmark = None
>      try:
>          lock = repo.lock()
>
> @@ -390,6 +408,7 @@ def _docreatecmd(ui, repo, pats, opts):
>                            not opts.get('addremove', False))
>
>          name = getshelvename(repo, parent, opts)
> +        activebookmark = _backupactivebookmark(repo)
>          extra = {}
>          if includeunknown:
>              _includeunknownfiles(repo, pats, opts, extra)
> @@ -404,7 +423,8 @@ def _docreatecmd(ui, repo, pats, opts):
>              node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
>          else:
>              node = cmdutil.dorecord(ui, repo, commitfunc, None,
> -                                    False, cmdutil.recordfilter, *pats, **opts)
> +                                    False, cmdutil.recordfilter, *pats,
> +                                    **opts)
>          if not node:
>              _nothingtoshelvemessaging(ui, repo, pats, opts)
>              return 1
> @@ -420,6 +440,7 @@ def _docreatecmd(ui, repo, pats, opts):
>
>          _finishshelve(repo)
>      finally:
> +        _restoreactivebookmark(repo, activebookmark)
>          lockmod.release(tr, lock)
>
>  def _isbareshelve(pats, opts):
> @@ -639,6 +660,7 @@ def unshelvecontinue(ui, repo, state, op
>          restorebranch(ui, repo, state.branchtorestore)
>
>          repair.strip(ui, repo, state.nodestoprune, backup=False, topic='shelve')
> +        _restoreactivebookmark(repo, state.activebookmark)
>          shelvedstate.clear(repo)
>          unshelvecleanup(ui, repo, state.name, opts)
>          ui.status(_("unshelve of '%s' complete\n") % state.name)
> @@ -672,7 +694,8 @@ def _unshelverestorecommit(ui, repo, bas
>      return repo, shelvectx
>
>  def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx,
> -                          tmpwctx, shelvectx, branchtorestore):
> +                          tmpwctx, shelvectx, branchtorestore,
> +                          activebookmark):
>      """Rebase restored commit from its original location to a destination"""
>      # If the shelve is not immediately on top of the commit
>      # we'll be merging with, rebase it to be on top.
> @@ -693,7 +716,7 @@ def _rebaserestoredcommit(ui, repo, opts
>          nodestoprune = [repo.changelog.node(rev)
>                          for rev in xrange(oldtiprev, len(repo))]
>          shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoprune,
> -                          branchtorestore, opts.get('keep'))
> +                          branchtorestore, opts.get('keep'), activebookmark)
>
>          repo.vfs.rename('rebasestate', 'unshelverebasestate')
>          raise error.InterventionRequired(
> @@ -719,7 +742,8 @@ def _forgetunknownfiles(repo, shelvectx,
>      toforget = (addedafter & shelveunknown) - addedbefore
>      repo[None].forget(toforget)
>
> -def _finishunshelve(repo, oldtiprev, tr):
> +def _finishunshelve(repo, oldtiprev, tr, activebookmark):
> +    _restoreactivebookmark(repo, activebookmark)
>      # The transaction aborting will strip all the commits for us,
>      # but it doesn't update the inmemory structures, so addchangegroup
>      # hooks still fire and try to operate on the missing commits.
> @@ -865,6 +889,7 @@ def _dounshelve(ui, repo, *shelved, **op
>          # and shelvectx is the unshelved changes. Then we merge it all down
>          # to the original pctx.
>
> +        activebookmark = _backupactivebookmark(repo)
>          overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
>          with ui.configoverride(overrides, 'unshelve'):
>              tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts,
> @@ -879,13 +904,14 @@ def _dounshelve(ui, repo, *shelved, **op
>
>              shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev,
>                                                basename, pctx, tmpwctx,
> -                                              shelvectx, branchtorestore)
> +                                              shelvectx, branchtorestore,
> +                                              activebookmark)
>              mergefiles(ui, repo, pctx, shelvectx)
>              restorebranch(ui, repo, branchtorestore)
>              _forgetunknownfiles(repo, shelvectx, addedbefore)
>
>              shelvedstate.clear(repo)
> -            _finishunshelve(repo, oldtiprev, tr)
> +            _finishunshelve(repo, oldtiprev, tr, activebookmark)
>              unshelvecleanup(ui, repo, basename, opts)
>      finally:
>          ui.quiet = oldquiet
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

Patch

diff --git a/hgext/shelve.py b/hgext/shelve.py
--- a/hgext/shelve.py
+++ b/hgext/shelve.py
@@ -28,6 +28,7 @@  import itertools
 
 from mercurial.i18n import _
 from mercurial import (
+    bookmarks,
     bundle2,
     bundlerepo,
     changegroup,
@@ -170,6 +171,8 @@  class shelvedstate(object):
     _filename = 'shelvedstate'
     _keep = 'keep'
     _nokeep = 'nokeep'
+    # colon is essential to differentiate from a real bookmark name
+    _noactivebook = ':no-active-bookmark'
 
     @classmethod
     def load(cls, repo):
@@ -187,6 +190,7 @@  class shelvedstate(object):
             nodestoprune = [nodemod.bin(h) for h in fp.readline().split()]
             branchtorestore = fp.readline().strip()
             keep = fp.readline().strip() == cls._keep
+            activebook = fp.readline().strip()
         except (ValueError, TypeError) as err:
             raise error.CorruptedState(str(err))
         finally:
@@ -201,6 +205,9 @@  class shelvedstate(object):
             obj.nodestoprune = nodestoprune
             obj.branchtorestore = branchtorestore
             obj.keep = keep
+            obj.activebookmark = ''
+            if activebook != cls._noactivebook:
+                obj.activebookmark = activebook
         except error.RepoLookupError as err:
             raise error.CorruptedState(str(err))
 
@@ -208,7 +215,7 @@  class shelvedstate(object):
 
     @classmethod
     def save(cls, repo, name, originalwctx, pendingctx, nodestoprune,
-             branchtorestore, keep=False):
+             branchtorestore, keep=False, activebook=''):
         fp = repo.vfs(cls._filename, 'wb')
         fp.write('%i\n' % cls._version)
         fp.write('%s\n' % name)
@@ -220,6 +227,7 @@  class shelvedstate(object):
                  ' '.join([nodemod.hex(n) for n in nodestoprune]))
         fp.write('%s\n' % branchtorestore)
         fp.write('%s\n' % (cls._keep if keep else cls._nokeep))
+        fp.write('%s\n' % (activebook or cls._noactivebook))
         fp.close()
 
     @classmethod
@@ -244,6 +252,16 @@  def cleanupoldbackups(repo):
         for ext in shelvefileextensions:
             vfs.tryunlink(base + '.' + ext)
 
+def _backupactivebookmark(repo):
+    activebookmark = repo._activebookmark
+    if activebookmark:
+        bookmarks.deactivate(repo)
+    return activebookmark
+
+def _restoreactivebookmark(repo, mark):
+    if mark:
+        bookmarks.activate(repo, mark)
+
 def _aborttransaction(repo):
     '''Abort current transaction for shelve/unshelve, but keep dirstate
     '''
@@ -377,7 +395,7 @@  def _docreatecmd(ui, repo, pats, opts):
     if not opts.get('message'):
         opts['message'] = desc
 
-    lock = tr = None
+    lock = tr = activebookmark = None
     try:
         lock = repo.lock()
 
@@ -390,6 +408,7 @@  def _docreatecmd(ui, repo, pats, opts):
                           not opts.get('addremove', False))
 
         name = getshelvename(repo, parent, opts)
+        activebookmark = _backupactivebookmark(repo)
         extra = {}
         if includeunknown:
             _includeunknownfiles(repo, pats, opts, extra)
@@ -404,7 +423,8 @@  def _docreatecmd(ui, repo, pats, opts):
             node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
         else:
             node = cmdutil.dorecord(ui, repo, commitfunc, None,
-                                    False, cmdutil.recordfilter, *pats, **opts)
+                                    False, cmdutil.recordfilter, *pats,
+                                    **opts)
         if not node:
             _nothingtoshelvemessaging(ui, repo, pats, opts)
             return 1
@@ -420,6 +440,7 @@  def _docreatecmd(ui, repo, pats, opts):
 
         _finishshelve(repo)
     finally:
+        _restoreactivebookmark(repo, activebookmark)
         lockmod.release(tr, lock)
 
 def _isbareshelve(pats, opts):
@@ -639,6 +660,7 @@  def unshelvecontinue(ui, repo, state, op
         restorebranch(ui, repo, state.branchtorestore)
 
         repair.strip(ui, repo, state.nodestoprune, backup=False, topic='shelve')
+        _restoreactivebookmark(repo, state.activebookmark)
         shelvedstate.clear(repo)
         unshelvecleanup(ui, repo, state.name, opts)
         ui.status(_("unshelve of '%s' complete\n") % state.name)
@@ -672,7 +694,8 @@  def _unshelverestorecommit(ui, repo, bas
     return repo, shelvectx
 
 def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx,
-                          tmpwctx, shelvectx, branchtorestore):
+                          tmpwctx, shelvectx, branchtorestore,
+                          activebookmark):
     """Rebase restored commit from its original location to a destination"""
     # If the shelve is not immediately on top of the commit
     # we'll be merging with, rebase it to be on top.
@@ -693,7 +716,7 @@  def _rebaserestoredcommit(ui, repo, opts
         nodestoprune = [repo.changelog.node(rev)
                         for rev in xrange(oldtiprev, len(repo))]
         shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoprune,
-                          branchtorestore, opts.get('keep'))
+                          branchtorestore, opts.get('keep'), activebookmark)
 
         repo.vfs.rename('rebasestate', 'unshelverebasestate')
         raise error.InterventionRequired(
@@ -719,7 +742,8 @@  def _forgetunknownfiles(repo, shelvectx,
     toforget = (addedafter & shelveunknown) - addedbefore
     repo[None].forget(toforget)
 
-def _finishunshelve(repo, oldtiprev, tr):
+def _finishunshelve(repo, oldtiprev, tr, activebookmark):
+    _restoreactivebookmark(repo, activebookmark)
     # The transaction aborting will strip all the commits for us,
     # but it doesn't update the inmemory structures, so addchangegroup
     # hooks still fire and try to operate on the missing commits.
@@ -865,6 +889,7 @@  def _dounshelve(ui, repo, *shelved, **op
         # and shelvectx is the unshelved changes. Then we merge it all down
         # to the original pctx.
 
+        activebookmark = _backupactivebookmark(repo)
         overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
         with ui.configoverride(overrides, 'unshelve'):
             tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts,
@@ -879,13 +904,14 @@  def _dounshelve(ui, repo, *shelved, **op
 
             shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev,
                                               basename, pctx, tmpwctx,
-                                              shelvectx, branchtorestore)
+                                              shelvectx, branchtorestore,
+                                              activebookmark)
             mergefiles(ui, repo, pctx, shelvectx)
             restorebranch(ui, repo, branchtorestore)
             _forgetunknownfiles(repo, shelvectx, addedbefore)
 
             shelvedstate.clear(repo)
-            _finishunshelve(repo, oldtiprev, tr)
+            _finishunshelve(repo, oldtiprev, tr, activebookmark)
             unshelvecleanup(ui, repo, basename, opts)
     finally:
         ui.quiet = oldquiet