# HG changeset patch # User Laurent Charignon # Date 1431627820 25200 # Node ID 0e2eb196923ab8f2735dfebae018ef37e6ef1808 # Parent 77cbf9121e8a0d9b06df90321d8d0da72738e069 inhibit: create direct access extension Since we want to use direct access without necessarily using inhibit, this patch separates both extensions. Inhibit depends on direct access and we add a test to check that it complains if that is not the case. diff -r 77cbf9121e8a -r 0e2eb196923a hgext/directaccess.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/directaccess.py Thu May 14 11:23:40 2015 -0700 @@ -0,0 +1,117 @@ +""" This extension provides direct access +It is the ability to refer and access hidden sha in commands provided that you +know their value. +For example hg log -r xxx where xxx is a commit has should work whether xxx is +hidden or not as we assume that the user knows what he is doing when referring +to xxx. +""" +from mercurial import extensions +from mercurial import cmdutil +from mercurial import repoview +from mercurial import branchmap +from mercurial import revset +from mercurial import error +from mercurial import commands +from mercurial.i18n import _ + +cmdtable = {} +command = cmdutil.command(cmdtable) + +# List of commands where no warning is shown for direct access +directaccesslevel = [ + # warning or not, extension (None if core), command name + (False, None, 'update'), + (False, None, 'export'), + (True, 'rebase', 'rebase'), + (False, 'evolve', 'prune'), +] + +def reposetup(ui, repo): + repo._explicitaccess = set() + +def _computehidden(repo): + hidden = repoview.computehidden(repo) + cl = repo.changelog + dynamic = hidden & repo._explicitaccess + if dynamic: + blocked = cl.ancestors(dynamic, inclusive=True) + hidden = frozenset(r for r in hidden if r not in blocked) + return hidden + +def setupdirectaccess(): + """ Add two new filtername that behave like visible to provide direct access + and direct access with warning. Wraps the commands to setup direct access """ + repoview.filtertable.update({'visible-directaccess-nowarn': _computehidden}) + repoview.filtertable.update({'visible-directaccess-warn': _computehidden}) + branchmap.subsettable['visible-directaccess-nowarn'] = 'visible' + branchmap.subsettable['visible-directaccess-warn'] = 'visible' + + for warn, ext, cmd in directaccesslevel: + cmdtable = extensions.find(ext).cmdtable if ext else commands.table + wrapper = wrapwithwarning if warn else wrapwithoutwarning + try: + extensions.wrapcommand(cmdtable, cmd, wrapper) + except error.UnknownCommand: + pass + +def wrapwithoutwarning(orig, ui, repo, *args, **kwargs): + if repo and repo.filtername == 'visible': + repo = repo.filtered("visible-directaccess-nowarn") + return orig(ui, repo, *args, **kwargs) + +def wrapwithwarning(orig, ui, repo, *args, **kwargs): + if repo and repo.filtername == 'visible': + repo = repo.filtered("visible-directaccess-warn") + return orig(ui, repo, *args, **kwargs) + +def extsetup(ui): + extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook) + setupdirectaccess() + +def gethashsymbols(tree): + # Returns the list of symbols of the tree that look like hashes + # for example for the revset 3::abe3ff it will return ('abe3ff') + if not tree: + return [] + + if len(tree) == 2 and tree[0] == "symbol": + try: + int(tree[1]) + return [] + except ValueError as e: + return [tree[1]] + elif len(tree) == 3: + return gethashsymbols(tree[1]) + gethashsymbols(tree[2]) + else: + return [] + +def _posttreebuilthook(orig, tree, repo): + # This is use to enabled direct hash access + # We extract the symbols that look like hashes and add them to the + # explicitaccess set + orig(tree, repo) + filternm = "" + if repo is not None: + filternm = repo.filtername + if filternm is not None and filternm.startswith('visible-directaccess'): + prelength = len(repo._explicitaccess) + accessbefore = set(repo._explicitaccess) + repo.symbols = gethashsymbols(tree) + cl = repo.unfiltered().changelog + for node in repo.symbols: + try: + node = cl._partialmatch(node) + except error.LookupError: + node = None + if node is not None: + rev = cl.rev(node) + if rev not in repo.changelog: + repo._explicitaccess.add(rev) + if prelength != len(repo._explicitaccess): + if repo.filtername != 'visible-directaccess-nowarn': + unhiddencommits = repo._explicitaccess - accessbefore + repo.ui.warn( _("Warning: accessing hidden changesets %s " + "for write operation\n") % + (",".join([str(repo.unfiltered()[l]) + for l in unhiddencommits]))) + repo.invalidatevolatilesets() diff -r 77cbf9121e8a -r 0e2eb196923a hgext/inhibit.py --- a/hgext/inhibit.py Thu May 14 15:59:06 2015 -0700 +++ b/hgext/inhibit.py Thu May 14 11:23:40 2015 -0700 @@ -1,52 +1,33 @@ -"""Reduce the changesets evolution feature scope for early and noob friendly UI +"""reduce the changesets evolution feature scope for early and noob friendly ui -The full scale changeset evolution have some massive bleeding edge and it is +the full scale changeset evolution have some massive bleeding edge and it is very easy for people not very intimate with the concept to end up in intricate -situation. In order to get some of the benefit sooner, this extension is -disabling some of the less polished aspect of evolution. It should gradually -get thinner and thinner as changeset evolution will get more polished. This -extension is only recommended for large scale organisations. Individual user -should probably stick on using Evolution in its current state, understand its +situation. in order to get some of the benefit sooner, this extension is +disabling some of the less polished aspect of evolution. it should gradually +get thinner and thinner as changeset evolution will get more polished. this +extension is only recommended for large scale organisations. individual user +should probably stick on using evolution in its current state, understand its concept and provide feedback -The first feature provided by this extension is the ability to "inhibit" -obsolescence markers. Obsolete revision can be cheaply brought back to life -that way. However as the inhibitor are not fitting in an append only model, -this is incompatible with sharing mutable history. - -The second feature is called direct access. It is the ability to refer and -access hidden sha in commands provided that you know their value. -For example hg log -r XXX where XXX is a commit has should work whether XXX is -hidden or not as we assume that the user knows what he is doing when referring -to XXX. +This extension provides the ability to "inhibit" obsolescence markers. obsolete +revision can be cheaply brought back to life that way. +However as the inhibitor are not fitting in an append only model, this is +incompatible with sharing mutable history. """ from mercurial import localrepo from mercurial import obsolete from mercurial import extensions from mercurial import cmdutil +from mercurial import error from mercurial import scmutil -from mercurial import repoview -from mercurial import branchmap -from mercurial import revset -from mercurial import error from mercurial import commands from mercurial import lock as lockmod from mercurial import bookmarks -from mercurial import lock as lockmod from mercurial.i18n import _ cmdtable = {} command = cmdutil.command(cmdtable) -# List of commands where no warning is shown for direct access -directaccesslevel = [ - # warning or not, extension (None if core), command name - (False, None, 'update'), - (False, None, 'export'), - (True, 'rebase', 'rebase'), - (False, 'evolve', 'prune'), -] - def reposetup(ui, repo): class obsinhibitedrepo(repo.__class__): @@ -66,36 +47,10 @@ return newnode repo.__class__ = obsinhibitedrepo - repo._explicitaccess = set() - if not ui.configbool('inhibit', 'onlydirectaccess', False): - # Wrapping this to inhibit obsolete revs resulting from a transaction - extensions.wrapfunction(localrepo.localrepository, - 'transaction', transactioncallback) - -def _computehidden(repo): - hidden = repoview.computehidden(repo) - cl = repo.changelog - dynamic = hidden & repo._explicitaccess - if dynamic: - blocked = cl.ancestors(dynamic, inclusive=True) - hidden = frozenset(r for r in hidden if r not in blocked) - return hidden + # Wrapping this to inhibit obsolete revs resulting from a transaction + extensions.wrapfunction(localrepo.localrepository, + 'transaction', transactioncallback) -def setupdirectaccess(): - """ Add two new filtername that behave like visible to provide direct access - and direct access with warning. Wraps the commands to setup direct access """ - repoview.filtertable.update({'visible-directaccess-nowarn': _computehidden}) - repoview.filtertable.update({'visible-directaccess-warn': _computehidden}) - branchmap.subsettable['visible-directaccess-nowarn'] = 'visible' - branchmap.subsettable['visible-directaccess-warn'] = 'visible' - - for warn, ext, cmd in directaccesslevel: - cmdtable = extensions.find(ext).cmdtable if ext else commands.table - wrapper = wrapwithwarning if warn else wrapwithoutwarning - try: - extensions.wrapcommand(cmdtable, cmd, wrapper) - except error.UnknownCommand: - pass def _update(orig, ui, repo, *args, **kwargs): """ When moving to a commit we want to inhibit any obsolete commit affecting @@ -211,7 +166,6 @@ finally: tr.release() - def transactioncallback(orig, repo, *args, **kwargs): """ Wrap localrepo.transaction to inhibit new obsolete changes """ def inhibitposttransaction(transaction): @@ -226,16 +180,6 @@ transaction.addpostclose('inhibitposttransaction', inhibitposttransaction) return transaction -def wrapwithoutwarning(orig, ui, repo, *args, **kwargs): - if repo and repo.filtername == 'visible': - repo = repo.filtered("visible-directaccess-nowarn") - return orig(ui, repo, *args, **kwargs) - -def wrapwithwarning(orig, ui, repo, *args, **kwargs): - if repo and repo.filtername == 'visible': - repo = repo.filtered("visible-directaccess-warn") - return orig(ui, repo, *args, **kwargs) - def extsetup(ui): # lets wrap the computation of the obsolete set # We apply inhibition there @@ -249,77 +193,29 @@ for n in repo._obsinhibit: obs.discard(getrev(n)) return obs + try: + extensions.find('directaccess') + except KeyError: + errormsg = _('Cannot use inhibit without the direct access extension') + raise error.Abort(errormsg) obsolete.cachefuncs['obsolete'] = _computeobsoleteset # wrap create marker to make it able to lift the inhibition extensions.wrapfunction(obsolete, 'createmarkers', _createmarkers) - extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook) - setupdirectaccess() - if not ui.configbool('inhibit', 'onlydirectaccess', False): - # drop divergence computation since it is incompatible with "light revive" - obsolete.cachefuncs['divergent'] = lambda repo: set() - # drop bumped computation since it is incompatible with "light revive" - obsolete.cachefuncs['bumped'] = lambda repo: set() - # wrap update to make sure that no obsolete commit is visible after an - # update - extensions.wrapcommand(commands.table, 'update', _update) - # There are two ways to save bookmark changes during a transation, we - # wrap both to add inhibition markers. - extensions.wrapfunction(bookmarks.bmstore, 'recordchange', _bookmarkchanged) - extensions.wrapfunction(bookmarks.bmstore, 'write', _bookmarkchanged) - # Add bookmark -D option - entry = extensions.wrapcommand(commands.table, 'bookmark', _bookmark) - entry[1].append(('D','prune',None, - _('delete the bookmark and prune the commits underneath'))) - - - -def gethashsymbols(tree): - # Returns the list of symbols of the tree that look like hashes - # for example for the revset 3::abe3ff it will return ('abe3ff') - if not tree: - return [] - - if len(tree) == 2 and tree[0] == "symbol": - try: - int(tree[1]) - return [] - except ValueError as e: - return [tree[1]] - elif len(tree) == 3: - return gethashsymbols(tree[1]) + gethashsymbols(tree[2]) - else: - return [] - -def _posttreebuilthook(orig, tree, repo): - # This is use to enabled direct hash access - # We extract the symbols that look like hashes and add them to the - # explicitaccess set - orig(tree, repo) - filternm = "" - if repo is not None: - filternm = repo.filtername - if filternm is not None and filternm.startswith('visible-directaccess'): - prelength = len(repo._explicitaccess) - accessbefore = set(repo._explicitaccess) - repo.symbols = gethashsymbols(tree) - cl = repo.unfiltered().changelog - for node in repo.symbols: - try: - node = cl._partialmatch(node) - except error.LookupError: - node = None - if node is not None: - rev = cl.rev(node) - if rev not in repo.changelog: - repo._explicitaccess.add(rev) - if prelength != len(repo._explicitaccess): - if repo.filtername != 'visible-directaccess-nowarn': - unhiddencommits = repo._explicitaccess - accessbefore - repo.ui.warn( _("Warning: accessing hidden changesets %s " - "for write operation\n") % - (",".join([str(repo.unfiltered()[l]) - for l in unhiddencommits]))) - repo.invalidatevolatilesets() + # drop divergence computation since it is incompatible with "light revive" + obsolete.cachefuncs['divergent'] = lambda repo: set() + # drop bumped computation since it is incompatible with "light revive" + obsolete.cachefuncs['bumped'] = lambda repo: set() + # wrap update to make sure that no obsolete commit is visible after an + # update + extensions.wrapcommand(commands.table, 'update', _update) + # There are two ways to save bookmark changes during a transation, we + # wrap both to add inhibition markers. + extensions.wrapfunction(bookmarks.bmstore, 'recordchange', _bookmarkchanged) + extensions.wrapfunction(bookmarks.bmstore, 'write', _bookmarkchanged) + # Add bookmark -D option + entry = extensions.wrapcommand(commands.table, 'bookmark', _bookmark) + entry[1].append(('D','prune',None, + _('delete the bookmark and prune the commits underneath'))) @command('debugobsinhibit', [], '') def cmddebugobsinhibit(ui, repo, *revs): diff -r 77cbf9121e8a -r 0e2eb196923a tests/test-inhibit.t --- a/tests/test-inhibit.t Thu May 14 15:59:06 2015 -0700 +++ b/tests/test-inhibit.t Thu May 14 11:23:40 2015 -0700 @@ -9,6 +9,7 @@ > strip= > EOF $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH + $ echo "directaccess=$(echo $(dirname $TESTDIR))/hgext/directaccess.py" >> $HGRCPATH $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH $ mkcommit() { > echo "$1" > "$1" @@ -562,17 +563,17 @@ added 2 changesets with 1 changes to 2 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) -Only allow direct access and check that evolve works like before + Only allow direct access and check that evolve works like before $ cat >> $HGRCPATH < [inhibit] - > onlydirectaccess = True + > [extensions] + > inhibit=! > EOF $ hg up 15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + working directory parent is obsolete! $ echo "CM" > cM $ hg amend - 1 new unstable changesets $ hg log -G @ 21:721c3c279519 add cM | @@ -582,10 +583,8 @@ |/ o 14:d66ccb8c5871 add cL | - | o 9:55c73a90e4b4 add cJ - | | - o | 7:18214586bf78 add cJ - |/ + o 7:18214586bf78 add cJ + | o 6:cf5c4f4554ce add cH | o 5:5419eb264a33 add cG @@ -594,3 +593,15 @@ | o 0:54ccbc537fc2 add cA + +Inhibit should not work without directaccess + $ cat >> $HGRCPATH < [extensions] + > directaccess=! + > EOF + $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH + + $ hg up 15 + abort: Cannot use inhibit without the direct access extension + [255] +