# HG changeset patch # User Pierre-Yves David # Date 1435302325 25200 # Node ID 44a9dcb3fefcf8281ebe4e359e7dbb637512cf7f # Parent 773f3edb3cb27a7dd6613d86ae6ca5ef772b303a# Parent 49e43f20a579bb703b545cc351096b64d52bdb48 merge with 3.3 diff -r 49e43f20a579 -r 44a9dcb3fefc .hgtags --- a/.hgtags Fri Jun 26 00:04:38 2015 -0700 +++ b/.hgtags Fri Jun 26 00:05:25 2015 -0700 @@ -36,3 +36,5 @@ c52c15100fb2d85c3525d6d085b3663ec4f90fe6 5.1.3 891c3ce894fa879532299910735bcc2a968245b1 5.1.4 1377f6a7f9ecb25e9b8885fce8f6f42e0d6f3f12 5.1.5 +c79bdc8563509cd4e99946593d0b39af40271a30 5.2.0 +00026533ff9f52733a45df008e3d56a5d3a8e76a 5.2.0 diff -r 49e43f20a579 -r 44a9dcb3fefc hgext/directaccess.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/directaccess.py Fri Jun 26 00:05:25 2015 -0700 @@ -0,0 +1,175 @@ +""" 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 import hg +from mercurial.i18n import _ + +cmdtable = {} +command = cmdutil.command(cmdtable) + +# By default, all the commands have directaccess with warnings +# List of commands that have no directaccess and directaccess with no warning +directaccesslevel = [ + # Format: + # ('nowarning', 'evolve', 'prune'), + # means: no directaccess warning, for the command in evolve named prune + # + # ('error', None, 'serve'), + # means: no directaccess for the command in core named serve + # + # The list is ordered alphabetically by command names, starting with all + # the commands in core then all the commands in the extensions + # + # The general guideline is: + # - remove directaccess warnings for read only commands + # - no direct access for commands with consequences outside of the repo + # - leave directaccess warnings for all the other commands + # + ('nowarning', None, 'annotate'), + ('nowarning', None, 'archive'), + ('nowarning', None, 'bisect'), + ('nowarning', None, 'bookmarks'), + ('nowarning', None, 'bundle'), + ('nowarning', None, 'cat'), + ('nowarning', None, 'diff'), + ('nowarning', None, 'export'), + ('nowarning', None, 'identify'), + ('nowarning', None, 'incoming'), + ('nowarning', None, 'log'), + ('nowarning', None, 'manifest'), + ('error', None, 'outgoing'), # confusing if push errors and not outgoing + ('error', None, 'push'), # destructive + ('nowarning', None, 'revert'), + ('error', None, 'serve'), + ('nowarning', None, 'tags'), + ('nowarning', None, 'unbundle'), + ('nowarning', None, 'update'), +] + +def reposetup(ui, repo): + repo._explicitaccess = set() + +def _computehidden(repo): + hidden = repoview.filterrevs(repo, 'visible') + 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: + try: + cmdtable = extensions.find(ext).cmdtable if ext else commands.table + wrapper = wrapwitherror if warn == 'error' else wrapwithoutwarning + extensions.wrapcommand(cmdtable, cmd, wrapper) + except (error.UnknownCommand, KeyError): + pass + +def wrapwitherror(orig, ui, repo, *args, **kwargs): + if repo and repo.filtername == 'visible-directaccess-warn': + repo = repo.filtered('visible') + return orig(ui, repo, *args, **kwargs) + +def wrapwithoutwarning(orig, ui, repo, *args, **kwargs): + if repo and repo.filtername == 'visible-directaccess-warn': + repo = repo.filtered("visible-directaccess-nowarn") + return orig(ui, repo, *args, **kwargs) + +def uisetup(ui): + """ Change ordering of extensions to ensure that directaccess extsetup comes + after the one of the extensions in the loadsafter list """ + loadsafter = ui.configlist('directaccess','loadsafter') + order = list(extensions._order) + directaccesidx = order.index('directaccess') + + # The min idx for directaccess to load after all the extensions in loadafter + minidxdirectaccess = directaccesidx + + for ext in loadsafter: + try: + minidxdirectaccess = max(minidxdirectaccess, order.index(ext)) + except ValueError: + pass # extension not loaded + + if minidxdirectaccess > directaccesidx: + order.insert(minidxdirectaccess + 1, 'directaccess') + order.remove('directaccess') + extensions._order = order + +def _repository(orig, *args, **kwargs): + """Make visible-directaccess-warn the default filter for new repos""" + repo = orig(*args, **kwargs) + return repo.filtered("visible-directaccess-warn") + +def extsetup(ui): + extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook) + extensions.wrapfunction(hg, 'repository', _repository) + 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 49e43f20a579 -r 44a9dcb3fefc hgext/evolve.py --- a/hgext/evolve.py Fri Jun 26 00:04:38 2015 -0700 +++ b/hgext/evolve.py Fri Jun 26 00:05:25 2015 -0700 @@ -682,16 +682,18 @@ @eh.wrapcommand("pull") def wrapmayobsoletewc(origfn, ui, repo, *args, **opts): """Warn that the working directory parent is an obsolete changeset""" - - prior = (repo['.'].obsolete(), repo['.'].rev) - try: - res = origfn(ui, repo, *args, **opts) - finally: - after = (repo['.'].obsolete(), repo['.'].rev) - if prior != after and after[0]: + def warnobsolete(): + if repo['.'].obsolete(): ui.warn(_('working directory parent is obsolete!\n')) if (not ui.quiet) and obsolete.isenabled(repo, commandopt): ui.warn(_('(use "hg evolve" to update to its successor)\n')) + wlock = None + try: + wlock = repo.wlock() + repo._afterlock(warnobsolete) + res = origfn(ui, repo, *args, **opts) + finally: + lockmod.release(wlock) return res # XXX this could wrap transaction code diff -r 49e43f20a579 -r 44a9dcb3fefc hgext/inhibit.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/inhibit.py Fri Jun 26 00:05:25 2015 -0700 @@ -0,0 +1,227 @@ +"""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 +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 +concept and provide feedback + +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 commands +from mercurial import lock as lockmod +from mercurial import bookmarks +from mercurial.i18n import _ + +cmdtable = {} +command = cmdutil.command(cmdtable) + +def reposetup(ui, repo): + + class obsinhibitedrepo(repo.__class__): + + @localrepo.storecache('obsinhibit') + def _obsinhibit(self): + # XXX we should make sure it is invalidated by transaction failure + obsinhibit = set() + raw = self.sopener.tryread('obsinhibit') + for i in xrange(0, len(raw), 20): + obsinhibit.add(raw[i:i+20]) + return obsinhibit + + def commit(self, *args, **kwargs): + newnode = super(obsinhibitedrepo, self).commit(*args, **kwargs) + if newnode is not None: + _inhibitmarkers(repo, [newnode]) + return newnode + + repo.__class__ = obsinhibitedrepo + +def _update(orig, ui, repo, *args, **kwargs): + """ + When moving to a commit we want to inhibit any obsolete commit affecting + the changeset we are updating to. In other words we don't want any visible + commit to be obsolete. + """ + wlock = None + try: + # Evolve is running a hook on lock release to display a warning message + # if the workind dir's parent is obsolete. + # We take the lock here to make sure that we inhibit the parent before + # that hook get a chance to run. + wlock = repo.wlock() + res = orig(ui, repo, *args, **kwargs) + newhead = repo['.'].node() + _inhibitmarkers(repo, [newhead]) + return res + finally: + lockmod.release(wlock) + +def _bookmarkchanged(orig, bkmstoreinst, *args, **kwargs): + """ Add inhibition markers to every obsolete bookmarks """ + repo = bkmstoreinst._repo + bkmstorenodes = [repo[v].node() for v in bkmstoreinst.values()] + _inhibitmarkers(repo, bkmstorenodes) + return orig(bkmstoreinst, *args, **kwargs) + +def _bookmark(orig, ui, repo, *bookmarks, **opts): + """ Add a -D option to the bookmark command, map it to prune -B """ + haspruneopt = opts.get('prune', False) + if not haspruneopt: + return orig(ui, repo, *bookmarks, **opts) + + # Call prune -B + evolve = extensions.find('evolve') + optsdict = { + 'new': [], + 'succ': [], + 'rev': [], + 'bookmark': bookmarks[0], + 'keep': None, + 'biject': False, + } + evolve.cmdprune(ui, repo, **optsdict) + +# obsolescence inhibitor +######################## + +def _schedulewrite(tr, obsinhibit): + """Make sure on disk content will be updated on transaction commit""" + def writer(fp): + """Serialize the inhibited list to disk. + """ + raw = ''.join(obsinhibit) + fp.write(raw) + tr.addfilegenerator('obsinhibit', ('obsinhibit',), writer) + tr.hookargs['obs_inbihited'] = '1' + +def _filterpublic(repo, nodes): + """filter out inhibitor on public changeset + + Public changesets are already immune to obsolescence""" + getrev = repo.changelog.nodemap.get + getphase = repo._phasecache.phase + return (n for n in repo._obsinhibit + if getrev(n) is not None and getphase(repo, getrev(n))) + +def _inhibitmarkers(repo, nodes): + """add marker inhibitor for all obsolete revision under + + Content of and all mutable ancestors are considered. Marker for + obsolete revision only are created. + """ + newinhibit = repo.set('::%ln and obsolete()', nodes) + if newinhibit: + lock = tr = None + try: + lock = repo.lock() + tr = repo.transaction('obsinhibit') + repo._obsinhibit.update(c.node() for c in newinhibit) + _schedulewrite(tr, _filterpublic(repo, repo._obsinhibit)) + repo.invalidatevolatilesets() + tr.close() + finally: + lockmod.release(tr, lock) + +def _deinhibitmarkers(repo, nodes): + """lift obsolescence inhibition on a set of nodes + + This will be triggered when inhibited nodes received new obsolescence + markers. Otherwise the new obsolescence markers would also be inhibited. + """ + deinhibited = repo._obsinhibit & set(nodes) + if deinhibited: + tr = repo.transaction('obsinhibit') + try: + repo._obsinhibit -= deinhibited + _schedulewrite(tr, _filterpublic(repo, repo._obsinhibit)) + repo.invalidatevolatilesets() + tr.close() + finally: + tr.release() + +def _createmarkers(orig, repo, relations, flag=0, date=None, metadata=None): + """wrap markers create to make sure we de-inhibit target nodes""" + # wrapping transactio to unify the one in each function + tr = repo.transaction('add-obsolescence-marker') + try: + orig(repo, relations, flag, date, metadata) + precs = (r[0].node() for r in relations) + _deinhibitmarkers(repo, precs) + tr.close() + finally: + tr.release() + +def transactioncallback(orig, repo, *args, **kwargs): + """ Wrap localrepo.transaction to inhibit new obsolete changes """ + def inhibitposttransaction(transaction): + # At the end of the transaction we catch all the new visible and + # obsolete commit to inhibit them + visibleobsolete = repo.revs('obsolete() - hidden()') + ignoreset = set(getattr(repo, '_rebaseset', [])) + visibleobsolete = list(r for r in visibleobsolete if r not in ignoreset) + if visibleobsolete: + _inhibitmarkers(repo, [repo[r].node() for r in visibleobsolete]) + transaction = orig(repo, *args, **kwargs) + transaction.addpostclose('inhibitposttransaction', inhibitposttransaction) + return transaction + +def extsetup(ui): + # lets wrap the computation of the obsolete set + # We apply inhibition there + obsfunc = obsolete.cachefuncs['obsolete'] + def _computeobsoleteset(repo): + """remove any inhibited nodes from the obsolete set + + This will trickle down to other part of mercurial (hidden, log, etc)""" + obs = obsfunc(repo) + getrev = repo.changelog.nodemap.get + 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) + + # Wrapping this to inhibit obsolete revs resulting from a transaction + extensions.wrapfunction(localrepo.localrepository, + 'transaction', transactioncallback) + + obsolete.cachefuncs['obsolete'] = _computeobsoleteset + # wrap create marker to make it able to lift the inhibition + extensions.wrapfunction(obsolete, 'createmarkers', _createmarkers) + # 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): + """inhibit obsolescence markers effect on a set of revs""" + nodes = (repo[r].node() for r in scmutil.revrange(repo, revs)) + _inhibitmarkers(repo, nodes) diff -r 49e43f20a579 -r 44a9dcb3fefc setup.py --- a/setup.py Fri Jun 26 00:04:38 2015 -0700 +++ b/setup.py Fri Jun 26 00:05:25 2015 -0700 @@ -19,6 +19,10 @@ 'hgext.evolve', ] +if os.environ.get('INCLUDE_INHIBIT'): + py_modules.append('hgext.inhibit') + py_modules.append('hgext.directaccess') + setup( name='hg-evolve', version=get_version('hgext/evolve.py'), diff -r 49e43f20a579 -r 44a9dcb3fefc tests/test-amend.t --- a/tests/test-amend.t Fri Jun 26 00:04:38 2015 -0700 +++ b/tests/test-amend.t Fri Jun 26 00:05:25 2015 -0700 @@ -155,5 +155,6 @@ -u --user USER record the specified user as committer -D --current-date record the current date as commit date -U --current-user record the current user as committer + -i --interactive use interactive mode (some details hidden, use --verbose to show complete help) diff -r 49e43f20a579 -r 44a9dcb3fefc tests/test-inhibit.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-inhibit.t Fri Jun 26 00:05:25 2015 -0700 @@ -0,0 +1,762 @@ + $ cat >> $HGRCPATH < [ui] + > logtemplate = {rev}:{node|short} {desc}\n + > [experimental] + > prunestrip=True + > evolution=createmarkers + > [extensions] + > rebase= + > 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" + > hg add "$1" + > hg ci -m "add $1" + > } + + $ hg init inhibit + $ cd inhibit + $ mkcommit cA + $ mkcommit cB + $ mkcommit cC + $ mkcommit cD + $ hg up 'desc(cA)' + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ mkcommit cE + created new head + $ mkcommit cG + $ mkcommit cH + $ mkcommit cJ + $ hg log -G + @ 7:18214586bf78 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | o 3:2db36d8066ff add cD + | | + | o 2:7df62a38b9bf add cC + | | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + +plain prune + + $ hg strip 1:: + 3 changesets pruned + $ hg log -G + @ 7:18214586bf78 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg debugobsinhibit --hidden 1:: + $ hg log -G + @ 7:18214586bf78 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | o 3:2db36d8066ff add cD + | | + | o 2:7df62a38b9bf add cC + | | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + $ hg strip --hidden 1:: + 3 changesets pruned + $ hg log -G + @ 7:18214586bf78 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + +after amend + + $ echo babar > cJ + $ hg commit --amend + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg debugobsinhibit --hidden 18214586bf78 + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + +and no divergence + + $ hg summary + parent: 9:55c73a90e4b4 tip + add cJ + branch: default + commit: (clean) + update: 1 new changesets, 2 branch heads (merge) + +check public revision got cleared +(when adding the second inhibitor, the first one is removed because it is public) + + $ wc -m .hg/store/obsinhibit | sed -e 's/^[ \t]*//' + 20 .hg/store/obsinhibit + $ hg strip 7 + 1 changesets pruned + $ hg debugobsinhibit --hidden 18214586bf78 + $ wc -m .hg/store/obsinhibit | sed -e 's/^[ \t]*//' + 20 .hg/store/obsinhibit + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg phase --public 7 + $ hg strip 9 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + working directory now at cf5c4f4554ce + 1 changesets pruned + $ hg log -G + o 7:18214586bf78 add cJ + | + @ 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg debugobsinhibit --hidden 55c73a90e4b4 + $ wc -m .hg/store/obsinhibit | sed -e 's/^[ \t]*//' + 20 .hg/store/obsinhibit + $ hg log -G + o 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + @ 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + +Update should inhibit all related unstable commits + + $ hg update 2 --hidden + 2 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ hg log -G + o 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | @ 2:7df62a38b9bf add cC + | | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + + $ hg update 9 + 4 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | o 2:7df62a38b9bf add cC + | | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + $ hg strip --hidden 1:: + 3 changesets pruned + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + +Bookmark should inhibit all related unstable commits + $ hg bookmark -r 2 book1 --hidden + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | o 2:7df62a38b9bf add cC + | | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + +Removing a bookmark with bookmark -D should prune the changes underneath +that are not reachable from another bookmark or head + + $ hg bookmark -r 1 book2 + $ hg bookmark -D book1 --config experimental.evolution=createmarkers #--config to make sure prune is not registered as a command. + bookmark 'book1' deleted + 1 changesets pruned + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + $ hg bookmark -D book2 + bookmark 'book2' deleted + 1 changesets pruned + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + +Test that direct access make changesets visible + + $ hg export 2db36d8066ff 02bcbc3f6e56 + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 2db36d8066ff50e8be3d3e6c2da1ebc0a8381d82 + # Parent 7df62a38b9bf9daf968de235043ba88a8ef43393 + add cD + + diff -r 7df62a38b9bf -r 2db36d8066ff cD + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/cD Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +cD + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 02bcbc3f6e56fb2928efec2c6e24472720bf5511 + # Parent 54ccbc537fc2d6845a5d61337c1cfb80d1d2815e + add cB + + diff -r 54ccbc537fc2 -r 02bcbc3f6e56 cB + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/cB Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +cB + +But only with hash + + $ hg export 2db36d8066ff:: + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 2db36d8066ff50e8be3d3e6c2da1ebc0a8381d82 + # Parent 7df62a38b9bf9daf968de235043ba88a8ef43393 + add cD + + diff -r 7df62a38b9bf -r 2db36d8066ff cD + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/cD Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +cD + + $ hg export 1 3 + abort: filtered revision '1' (not in 'visible-directaccess-nowarn' subset)! + [255] + + +With severals hidden sha, rebase of one hidden stack onto another one: + $ hg update -C 0 + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + $ mkcommit cK + created new head + $ mkcommit cL + $ hg update -C 9 + 4 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg log -G + o 11:53a94305e133 add cL + | + o 10:ad78ff7d621f add cK + | + | @ 9:55c73a90e4b4 add cJ + | | + | | o 7:18214586bf78 add cJ + | |/ + | o 6:cf5c4f4554ce add cH + | | + | o 5:5419eb264a33 add cG + | | + | o 4:98065434e5c6 add cE + |/ + o 0:54ccbc537fc2 add cA + + $ hg strip --hidden 10: + 2 changesets pruned + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg rebase -s 10 -d 3 + abort: filtered revision '3' (not in 'visible-directaccess-warn' subset)! + [255] + $ hg rebase -r ad78ff7d621f -r 53a94305e133 -d 2db36d8066ff + Warning: accessing hidden changesets 2db36d8066ff for write operation + Warning: accessing hidden changesets ad78ff7d621f for write operation + Warning: accessing hidden changesets 53a94305e133 for write operation + rebasing 10:ad78ff7d621f "add cK" + rebasing 11:53a94305e133 "add cL" + $ hg log -G + o 13:2f7b7704d714 add cL + | + o 12:fe1634cbe235 add cK + | + | @ 9:55c73a90e4b4 add cJ + | | + | | o 7:18214586bf78 add cJ + | |/ + | o 6:cf5c4f4554ce add cH + | | + | o 5:5419eb264a33 add cG + | | + | o 4:98065434e5c6 add cE + | | + o | 3:2db36d8066ff add cD + | | + o | 2:7df62a38b9bf add cC + | | + o | 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + +Check that amending in the middle of a stack does not show obsolete revs +Since we are doing operation in the middle of the stack we cannot just +have createmarkers as we are creating instability + + $ cat >> $HGRCPATH < [experimental] + > evolution=all + > EOF + + $ hg strip --hidden 1:: + 5 changesets pruned + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg up 7 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ mkcommit cL + $ mkcommit cM + $ mkcommit cN + $ hg log -G + @ 16:a438c045eb37 add cN + | + o 15:2d66e189f5b5 add cM + | + o 14:d66ccb8c5871 add cL + | + | o 9:55c73a90e4b4 add cJ + | | + o | 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg up 15 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo "mmm" >> cM + $ hg amend + $ hg log -G + @ 18:210589181b14 add cM + | + | o 16:a438c045eb37 add cN + | | + | o 15:2d66e189f5b5 add cM + |/ + o 14:d66ccb8c5871 add cL + | + | o 9:55c73a90e4b4 add cJ + | | + o | 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + +Check that rebasing a commit twice makes the commit visible again + + $ hg rebase -d 18 -r 16 --keep + rebasing 16:a438c045eb37 "add cN" + $ hg log -r 14:: -G + o 19:104eed5354c7 add cN + | + @ 18:210589181b14 add cM + | + | o 16:a438c045eb37 add cN + | | + | o 15:2d66e189f5b5 add cM + |/ + o 14:d66ccb8c5871 add cL + | + $ hg strip -r 104eed5354c7 + 1 changesets pruned + $ hg rebase -d 18 -r 16 --keep + rebasing 16:a438c045eb37 "add cN" + $ hg log -r 14:: -G + o 19:104eed5354c7 add cN + | + @ 18:210589181b14 add cM + | + | o 16:a438c045eb37 add cN + | | + | o 15:2d66e189f5b5 add cM + |/ + o 14:d66ccb8c5871 add cL + | + +Test prunestrip + + $ hg book foo -r 104eed5354c7 + $ hg strip -r 210589181b14 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + working directory now at d66ccb8c5871 + 2 changesets pruned + $ hg log -r 14:: -G -T '{rev}:{node|short} {desc|firstline} {bookmarks}\n' + o 16:a438c045eb37 add cN + | + o 15:2d66e189f5b5 add cM + | + @ 14:d66ccb8c5871 add cL foo + | + +Check that --hidden used with inhibit does not hide every obsolete commit +We show the log before and after a log -G --hidden, they should be the same + $ hg log -G + o 16:a438c045eb37 add cN + | + o 15:2d66e189f5b5 add cM + | + @ 14:d66ccb8c5871 add cL + | + | o 9:55c73a90e4b4 add cJ + | | + o | 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg log -G --hidden + x 19:104eed5354c7 add cN + | + x 18:210589181b14 add cM + | + | x 17:b3c3274523f9 temporary amend commit for 2d66e189f5b5 + | | + | | o 16:a438c045eb37 add cN + | |/ + | o 15:2d66e189f5b5 add cM + |/ + @ 14:d66ccb8c5871 add cL + | + | x 13:2f7b7704d714 add cL + | | + | x 12:fe1634cbe235 add cK + | | + | | x 11:53a94305e133 add cL + | | | + | | x 10:ad78ff7d621f add cK + | | | + | | | o 9:55c73a90e4b4 add cJ + | | | | + +-------x 8:e84f73d9ad36 temporary amend commit for 18214586bf78 + | | | | + o-----+ 7:18214586bf78 add cJ + / / / + | | o 6:cf5c4f4554ce add cH + | | | + | | o 5:5419eb264a33 add cG + | | | + | | o 4:98065434e5c6 add cE + | |/ + x | 3:2db36d8066ff add cD + | | + x | 2:7df62a38b9bf add cC + | | + x | 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + + $ hg log -G + o 16:a438c045eb37 add cN + | + o 15:2d66e189f5b5 add cM + | + @ 14:d66ccb8c5871 add cL + | + | o 9:55c73a90e4b4 add cJ + | | + o | 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + +check that pruning and inhibited node does not confuse anything + + $ hg up --hidden 210589181b14 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg strip --bundle 210589181b14 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/inhibit/.hg/strip-backup/210589181b14-e09c7b88-backup.hg (glob) + $ hg unbundle .hg/strip-backup/210589181b14-e09c7b88-backup.hg # restore state + adding changesets + adding manifests + adding file changes + 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 +(also disable evolve commands to avoid hint about using evolve) + $ cat >> $HGRCPATH < [extensions] + > inhibit=! + > [experimental] + > evolution=createmarkers + > EOF + + $ hg up 15 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + working directory parent is obsolete! + $ cat >> $HGRCPATH < [experimental] + > evolution=all + > EOF + $ echo "CM" > cM + $ hg amend + $ hg log -G + @ 21:721c3c279519 add cM + | + | o 16:a438c045eb37 add cN + | | + | x 15:2d66e189f5b5 add cM + |/ + o 14:d66ccb8c5871 add cL + | + o 7:18214586bf78 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ cat >> $HGRCPATH < [extensions] + > EOF + $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH + +Empty commit + $ hg amend + nothing changed + [1] + +Directaccess should load after some extensions precised in the conf +With no extension specified: + + $ cat >$TESTTMP/test_extension.py << EOF + > from mercurial import extensions + > def uisetup(ui): + > print extensions._order + > EOF + $ cat >> $HGRCPATH << EOF + > [extensions] + > testextension=$TESTTMP/test_extension.py + > EOF + $ hg id + ['rebase', 'strip', 'evolve', 'directaccess', 'inhibit', 'testextension'] + 721c3c279519 tip + +With test_extension specified: + $ cat >> $HGRCPATH << EOF + > [directaccess] + > loadsafter=testextension + > EOF + $ hg id + ['rebase', 'strip', 'evolve', 'inhibit', 'testextension', 'directaccess'] + 721c3c279519 tip + +Inhibit should not work without directaccess + $ cat >> $HGRCPATH < [extensions] + > directaccess=! + > testextension=! + > EOF + $ hg up 15 + abort: Cannot use inhibit without the direct access extension + [255] + $ echo "directaccess=$(echo $(dirname $TESTDIR))/hgext/directaccess.py" >> $HGRCPATH + $ cd .. + + +hg push should not allow directaccess unless forced with --hidden +We copy the inhibhit repo to inhibit2 and make some changes to push to inhibit + + $ cp -r inhibit inhibit2 + $ pwd=$(pwd) + $ cd inhibit + $ mkcommit pk + $ hg id + 003a4735afde tip + $ echo "OO" > pk + $ hg amend + $ hg id + 71eb4f100663 tip + +Hidden commits cannot be pushed without --hidden + $ hg push -r 003a4735afde file://$pwd/inhibit2 + pushing to file://$TESTTMP/inhibit2 + abort: hidden revision '003a4735afde'! + (use --hidden to access hidden revisions) + [255] + +Visible commits can still be pushed + $ hg push -r 71eb4f100663 file://$pwd/inhibit2 + pushing to file://$TESTTMP/inhibit2 + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + pushing 33 obsolescence markers (*) (glob) + 2 obsolescence markers added diff -r 49e43f20a579 -r 44a9dcb3fefc tests/test-obsolete.t --- a/tests/test-obsolete.t Fri Jun 26 00:04:38 2015 -0700 +++ b/tests/test-obsolete.t Fri Jun 26 00:05:25 2015 -0700 @@ -130,6 +130,8 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: add obsol_c + working directory parent is obsolete! + (use "hg evolve" to update to its successor) $ mkcommit d # 5 (on 3) 1 new unstable changesets $ qlog -r 'obsolete()' diff -r 49e43f20a579 -r 44a9dcb3fefc tests/test-tutorial.t --- a/tests/test-tutorial.t Fri Jun 26 00:04:38 2015 -0700 +++ b/tests/test-tutorial.t Fri Jun 26 00:05:25 2015 -0700 @@ -745,16 +745,20 @@ $ cd ../remote $ hg pull local # we up again to trigger the warning. it was displayed during the push - pulling from $TESTTMP/local + pulling from $TESTTMP/local (glob) searching for changes no changes found pull obsolescence markers 0 obsolescence markers added + working directory parent is obsolete! + (use "hg evolve" to update to its successor) now let's see where we are, and update to the successor $ hg parents bf1b0d202029 (draft): animals + working directory parent is obsolete! + (use "hg evolve" to update to its successor) $ hg evolve update:[8] animals 1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r 49e43f20a579 -r 44a9dcb3fefc tests/test-wireproto.t --- a/tests/test-wireproto.t Fri Jun 26 00:04:38 2015 -0700 +++ b/tests/test-wireproto.t Fri Jun 26 00:05:25 2015 -0700 @@ -72,6 +72,7 @@ remote: adding manifests remote: adding file changes remote: added 1 changesets with 1 changes to 1 files (+1 heads) + remote: obsmarker-exchange: 139 bytes received remote: 2 new obsolescence markers $ hg push pushing to ssh://user@dummy/server @@ -89,6 +90,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to [12] files \(\+1 heads\) (re) + obsmarker-exchange: 139 bytes received 2 new obsolescence markers (run 'hg heads' to see heads, 'hg merge' to merge) $ hg -R ../other pull @@ -111,6 +113,7 @@ remote: adding manifests remote: adding file changes remote: added 1 changesets with 1 changes to 1 files + remote: obsmarker-exchange: 139 bytes received remote: 2 new obsolescence markers $ hg -R ../other pull pulling from ssh://user@dummy/server @@ -119,6 +122,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files + obsmarker-exchange: 139 bytes received 2 new obsolescence markers (run 'hg update' to get a working copy) @@ -132,6 +136,7 @@ remote: adding manifests remote: adding file changes remote: added 1 changesets with 0 changes to 1 files (+1 heads) + remote: obsmarker-exchange: 208 bytes received remote: 1 new obsolescence markers $ hg -R ../other pull pulling from ssh://user@dummy/server @@ -140,6 +145,7 @@ adding manifests adding file changes added 1 changesets with 0 changes to 3 files (+1 heads) + obsmarker-exchange: 208 bytes received 1 new obsolescence markers (run 'hg heads' to see heads, 'hg merge' to merge)