@@ 0,0 1,21 @@
+========================================================
+Automatic dirty working directory shelving for Mercurial
+========================================================
+
+AutoShelve is a Mercurial extension that shelves modified files
+before applying changes to the repository then unshelve them.
+
+Why would I use it ?
+
+I not sure that there is a valid reason for using it. But it's a
+pragmatic solution for durty use cases. Autoshelve will avoid you
+wasting time until you meet and find a good solution.
+
+Note: Shelve operations may be very long for certain repositories. In
+ this cases you may want to disable or enable autoshelve in the
+ configuration file of the repository.
+
+
+
+
+
@@ 0,0 1,78 @@
+# Copyright 2019 Alain Leufroy
+# Pythonian <contact@pythonian.fr>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import random
+from functools import partial
+
+from mercurial import cmdutil, commands, extensions
+from mercurial.i18n import _
+
+from . import metadata
+
+__version__ = metadata.__version__
+__doc__ = metadata.__doc__
+testedwith = metadata.testedwith
+minimumhgversion = metadata.minimumhgversion
+buglink = metadata.buglink
+
+
+def extsetup(ui):
+ for cmd in metadata.COMMANDS:
+ _wrap_cmd(commands.table, cmd)
+ for extension, cmds in metadata.EXTENSIONS.items():
+ _uisetup_extension(extension, cmds)
+
+
+def _uisetup_extension(name, cmds):
+ extensions.afterloaded(
+ name,
+ partial(_wrap_extension_cmd, name=name, cmds=cmds)
+ )
+
+
+def _wrap_extension_cmd(name, cmds, loaded=None):
+ try:
+ extension = extensions.find(name)
+ except KeyError:
+ return
+ for cmd in cmds:
+ _wrap_cmd(extension.cmdtable, cmd)
+
+
+def _wrap_cmd(table, cmd):
+ entry = extensions.wrapcommand(
+ table, cmd, _autoshelve_cmd,
+ synopsis=metadata.SYNOPSIS, docstring=metadata.DOCSTRING
+ )
+ entry[1].append(
+ (b'', 'noshelve', None, _(b'Disable autoshelve')))
+
+
+def _autoshelve_cmd(orig, ui, repo, *values, **opts):
+ shelve_name = _generate_shelve_name(15)
+ shelved = False
+ if not opts.get('abort') and not opts.pop('noshelve', False):
+ shelved = _shelve(ui, repo, shelve_name) is None
+ output = orig(ui, repo, *values, **opts)
+ if shelved:
+ _unshelve(ui, repo, shelve_name)
+ return output
+
+
+def _generate_shelve_name(length):
+ urandom = random.SystemRandom()
+ str_format = 'autoshelve-%%0%dx' % length
+ return str_format % urandom.getrandbits(length * 4)
+
+
+def _shelve(ui, repo, shelve_name):
+ _alias, shelvecmd = cmdutil.findcmd(b'shelve', commands.table)
+ return shelvecmd[0](ui, repo, name=shelve_name, keep=False)
+
+
+def _unshelve(ui, repo, shelve_name):
+ _alias, unshelvecmd = cmdutil.findcmd(b'unshelve', commands.table)
+ unshelvecmd[0](ui, repo, name=shelve_name, keep=False)
@@ 0,0 1,65 @@
+# Copyright 2019 Alain Leufroy
+# Pythonian <contact@pythonian.fr>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+__all__ = ['__version__', 'testedwith', 'minimumhgversion', 'buglink']
+
+__version__ = b'0.1'
+testedwith = b'5.1'
+minimumhgversion = b'5.1'
+buglink = b''
+
+SYNOPSIS = b'[--noshelve]'
+
+DOCSTRING = b'''
+ The autoshelve extension is activated. Dirty working directory
+ will be shelved before all then unshelved after all. You
+ can use --noshelve to inhibite this behaviour.'''
+
+COMMANDS = [
+ b'pull',
+ b'backout',
+ b'graft', b'merge',
+ b'bisect', b'update', b'import', b'unbundle'
+]
+
+EXTENSIONS = {
+ b'histedit': [b'histedit'],
+ b'rebase': [b'rebase'],
+ b'evolve': [
+ b'prune', b'rewind', b'touch', b'uncommit', b'split', b'pick',
+ b'metaedit', b'fold'
+ ]
+}
+__doc__ = b"""Automatic dirty working directory shelving.
+
+Automatically call `shelve` before a command then `unshelve`
+once it's done.
+
+Wrapped commands are %s.
+
+A `--noshelve` options is added to them to disable the feature.
+
+The unshelve operation is not performed when the main
+command fails. For example, rebasing a commit that results
+in conflicts will stop the unshelve operation. You will need
+to perform the unshelve manually afterward (`hg unshelve`)
+
+The unshelve operation may results in merge conflict. For example,
+updating to a commit onto which the shelved content cannot apply will
+results in merge conflicts. You have to:
+
+* fix the conflicts (`hg resolve --mark`) then continue the unshelve
+ operation (`hg unshelve --continue`)
+
+* or abort it (`hg unshelve --abort`).
+
+Shelves are named as `autoshelve-RANDOMHEX`, with an `autoshelve-`
+prefix flollwed by a random hex. They are removed immediatly, unless
+the command fails as explained previously. You can delete them using
+`hg shelve --delete --name autoshelve-RADOMEHEX`.
+""" % ', '.join(
+ "'%s'" % cmd for cmd in sorted(
+ COMMANDS + [cmd for cmds in EXTENSIONS.values() for cmd in cmds]))