Mercurial > evolve
changeset 1313:31e96036acd3
merge with stable
bunch of cleanup on stable
author | Pierre-Yves David <pierre-yves.david@fb.com> |
---|---|
date | Mon, 04 May 2015 10:56:06 -0700 |
parents | 1218ad94a351 (current diff) 508f9911b042 (diff) |
children | d13d97363521 |
files | hgext/evolve.py |
diffstat | 17 files changed, 1209 insertions(+), 147 deletions(-) [+] |
line wrap: on
line diff
--- a/hgext/evolve.py Mon May 04 10:58:14 2015 -0700 +++ b/hgext/evolve.py Mon May 04 10:56:06 2015 -0700 @@ -45,6 +45,8 @@ except (ImportError, AttributeError): gboptslist = gboptsmap = None +# Flags for enabling optional parts of evolve +commandopt = 'allnewcommands' from mercurial import bookmarks from mercurial import cmdutil @@ -144,8 +146,11 @@ """ for cont, funcname, func in self._duckpunchers: setattr(cont, funcname, func) - for command, wrapper in self._commandwrappers: - extensions.wrapcommand(commands.table, command, wrapper) + for command, wrapper, opts in self._commandwrappers: + entry = extensions.wrapcommand(commands.table, command, wrapper) + if opts: + for short, long, val, msg in opts: + entry[1].append((short, long, val, msg)) for cont, funcname, wrapper in self._functionwrappers: extensions.wrapfunction(cont, funcname, wrapper) for c in self._uicallables: @@ -166,13 +171,20 @@ revset.symbols[name] = symbol for name, kw in self._templatekws: templatekw.keywords[name] = kw - for ext, command, wrapper in self._extcommandwrappers: + for ext, command, wrapper, opts in self._extcommandwrappers: if ext not in knownexts: - e = extensions.find(ext) - if e is None: - raise util.Abort('extension %s not found' % ext) + try: + e = extensions.find(ext) + except KeyError: + # Extension isn't enabled, so don't bother trying to wrap + # it. + continue knownexts[ext] = e.cmdtable - extensions.wrapcommand(knownexts[ext], commands, wrapper) + entry = extensions.wrapcommand(knownexts[ext], command, wrapper) + if opts: + for short, long, val, msg in opts: + entry[1].append((short, long, val, msg)) + for c in self._extcallables: c(ui) @@ -260,7 +272,7 @@ return keyword return dec - def wrapcommand(self, command, extension=None): + def wrapcommand(self, command, extension=None, opts=[]): """Decorated function is a command wrapper The name of the command must be given as the decorator argument. @@ -279,12 +291,16 @@ ui.note('Barry!') return orig(ui, repo, *args, **kwargs) + The `opts` argument allows specifying additional arguments for the + command. + """ def dec(wrapper): if extension is None: - self._commandwrappers.append((command, wrapper)) + self._commandwrappers.append((command, wrapper, opts)) else: - self._extcommandwrappers.append((extension, command, wrapper)) + self._extcommandwrappers.append((extension, command, wrapper, + opts)) return wrapper return dec @@ -330,6 +346,31 @@ reposetup = eh.final_reposetup ##################################################################### +### Option configuration ### +##################################################################### + +@eh.reposetup # must be the first of its kin. +def _configureoptions(ui, repo): + # If no capabilities are specified, enable everything. + # This is so existing evolve users don't need to change their config. + evolveopts = ui.configlist('experimental', 'evolution') + if not evolveopts: + evolveopts = ['all'] + ui.setconfig('experimental', 'evolution', evolveopts) + +@eh.uisetup +def _configurecmdoptions(ui): + # Unregister evolve commands if the command capability is not specified. + # + # This must be in the same function as the option configuration above to + # guarantee it happens after the above configuration, but before the + # extsetup functions. + evolveopts = ui.configlist('experimental', 'evolution') + if evolveopts and (commandopt not in evolveopts and + 'all' not in evolveopts): + cmdtable.clear() + +##################################################################### ### experimental behavior ### ##################################################################### @@ -573,9 +614,16 @@ @eh.wrapcommand("pull") def wrapmayobsoletewc(origfn, ui, repo, *args, **opts): """Warn that the working directory parent is an obsolete changeset""" - res = origfn(ui, repo, *args, **opts) - if repo['.'].obsolete(): - ui.warn(_('working directory parent is obsolete!\n')) + def warnobsolete(): + if repo['.'].obsolete(): + ui.warn(_('working directory parent is obsolete!\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 @@ -932,7 +980,11 @@ This function is loosely based on the extensions.wrapcommand function. ''' - aliases, entry = cmdutil.findcmd(newalias, cmdtable) + try: + aliases, entry = cmdutil.findcmd(newalias, cmdtable) + except error.UnknownCommand: + # Commands may be disabled + return for alias, e in cmdtable.iteritems(): if e is entry: break @@ -1420,6 +1472,9 @@ files = set() copied = copies.pathcopies(prec, bumped) precmanifest = prec.manifest() + # 3.3.2 needs a list. + # future 3.4 don't detect the size change during iteration + # this is fishy for key, val in list(bumped.manifest().iteritems()): precvalue = precmanifest.get(key, None) if precvalue is not None: @@ -1708,6 +1763,7 @@ [('n', 'new', [], _("successor changeset (DEPRECATED)")), ('s', 'succ', [], _("successor changeset")), ('r', 'rev', [], _("revisions to prune")), + ('k', 'keep', None, _("does not modify working copy during prune")), ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")), ('B', 'bookmark', '', _("remove revs only reachable from given" " bookmark"))] + metadataopts, @@ -1747,10 +1803,11 @@ if not revs: raise util.Abort(_('nothing to prune')) - wlock = lock = None + wlock = lock = tr = None try: wlock = repo.wlock() lock = repo.lock() + tr = repo.transaction('prune') # defines pruned changesets precs = [] revs.sort() @@ -1781,12 +1838,6 @@ if biject: relations = [(p, (s,)) for p, s in zip(precs, sucs)] - # create markers - obsolete.createmarkers(repo, relations, metadata=metadata) - - # informs that changeset have been pruned - ui.status(_('%i changesets pruned\n') % len(precs)) - wdp = repo['.'] if len(sucs) == 1 and len(precs) == 1 and wdp in precs: @@ -1796,15 +1847,43 @@ # update to an unkilled parent newnode = wdp - while newnode.obsolete(): + while newnode in precs or newnode.obsolete(): newnode = newnode.parents()[0] + if newnode.node() != wdp.node(): - commands.update(ui, repo, newnode.rev()) - ui.status(_('working directory now at %s\n') % newnode) + if opts.get('keep', False): + # This is largely the same as the implementation in + # strip.stripcmd(). We might want to refactor this somewhere + # common at some point. + + # only reset the dirstate for files that would actually change + # between the working context and uctx + descendantrevs = repo.revs("%d::." % newnode.rev()) + changedfiles = [] + for rev in descendantrevs: + # blindly reset the files, regardless of what actually changed + changedfiles.extend(repo[rev].files()) + + # reset files that only changed in the dirstate too + dirstate = repo.dirstate + dirchanges = [f for f in dirstate if dirstate[f] != 'n'] + changedfiles.extend(dirchanges) + repo.dirstate.rebuild(newnode.node(), newnode.manifest(), changedfiles) + repo.dirstate.write() + else: + commands.update(ui, repo, newnode.rev()) + ui.status(_('working directory now at %s\n') % newnode) # update bookmarks if bookmark: _deletebookmark(ui, marks, bookmark) + + # create markers + obsolete.createmarkers(repo, relations, metadata=metadata) + + # informs that changeset have been pruned + ui.status(_('%i changesets pruned\n') % len(precs)) + for ctx in repo.unfiltered().set('bookmark() and %ld', precs): # used to be: # @@ -1819,8 +1898,10 @@ updatebookmarks = _bookmarksupdater(repo, ctx.node()) updatebookmarks(dest.node()) break + + tr.close() finally: - lockmod.release(lock, wlock) + lockmod.release(tr, lock, wlock) @command('amend|refresh', [('A', 'addremove', None, @@ -2063,6 +2144,31 @@ finally: lockmod.release(lock, wlock) +@eh.wrapcommand('strip', extension='strip', opts=[ + ('', 'bundle', None, _("delete the commit entirely and move it to a " + "backup bundle")), + ]) +def stripwrapper(orig, ui, repo, *revs, **kwargs): + if (not ui.configbool('experimental', 'prunestrip') or + kwargs.get('bundle', False)): + return orig(ui, repo, *revs, **kwargs) + + if kwargs.get('force'): + ui.warn(_("warning: --force has no effect during strip with evolve " + "enabled\n")) + if kwargs.get('no_backup', False): + ui.warn(_("warning: --no-backup has no effect during strips with " + "evolve enabled\n")) + + revs = list(revs) + kwargs.pop('rev', []) + revs = set(scmutil.revrange(repo, revs)) + revs = repo.revs("(%ld)::", revs) + kwargs['rev'] = [] + kwargs['new'] = [] + kwargs['succ'] = [] + kwargs['biject'] = False + return cmdprune(ui, repo, *revs, **kwargs) + @command('^touch', [('r', 'rev', [], 'revision to update'), ('D', 'duplicate', False, @@ -2255,8 +2361,12 @@ @eh.extsetup def oldevolveextsetup(ui): for cmd in ['kill', 'uncommit', 'touch', 'fold']: - entry = extensions.wrapcommand(cmdtable, cmd, - warnobserrors) + try: + entry = extensions.wrapcommand(cmdtable, cmd, + warnobserrors) + except error.UnknownCommand: + # Commands may be disabled + continue entry = cmdutil.findcmd('commit', commands.table)[1] entry[1].append(('o', 'obsolete', [], @@ -2285,38 +2395,19 @@ topic = 'OBSEXC' ui.progress(topic, *args, **kwargs) -if getattr(exchange, '_pushdiscoveryobsmarkers', None) is not None: - @eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers') - def _pushdiscoveryobsmarkers(orig, pushop): - if (obsolete._enabled - and pushop.repo.obsstore - and 'obsolete' in pushop.remote.listkeys('namespaces')): - repo = pushop.repo - obsexcmsg(repo.ui, "computing relevant nodes\n") - revs = list(repo.revs('::%ln', pushop.futureheads)) - unfi = repo.unfiltered() - cl = unfi.changelog - if not pushop.remote.capable('_evoext_obshash_0'): - # do not trust core yet - # return orig(pushop) - nodes = [cl.node(r) for r in revs] - if nodes: - obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" - % len(nodes)) - pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) - else: - obsexcmsg(repo.ui, "markers already in sync\n") - pushop.outobsmarkers = [] - pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) - return - - common = [] - obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" - % len(revs)) - commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads)) - common = findcommonobsmarkers(pushop.ui, unfi, pushop.remote, commonrevs) - - revs = list(unfi.revs('%ld - (::%ln)', revs, common)) +@eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers') +def _pushdiscoveryobsmarkers(orig, pushop): + if (obsolete.isenabled(pushop.repo, obsolete.exchangeopt) + and pushop.repo.obsstore + and 'obsolete' in pushop.remote.listkeys('namespaces')): + repo = pushop.repo + obsexcmsg(repo.ui, "computing relevant nodes\n") + revs = list(repo.revs('::%ln', pushop.futureheads)) + unfi = repo.unfiltered() + cl = unfi.changelog + if not pushop.remote.capable('_evoext_obshash_0'): + # do not trust core yet + # return orig(pushop) nodes = [cl.node(r) for r in revs] if nodes: obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" @@ -2325,12 +2416,30 @@ else: obsexcmsg(repo.ui, "markers already in sync\n") pushop.outobsmarkers = [] + pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) + return + + common = [] + obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" + % len(revs)) + commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads)) + common = findcommonobsmarkers(pushop.ui, unfi, pushop.remote, commonrevs) + + revs = list(unfi.revs('%ld - (::%ln)', revs, common)) + nodes = [cl.node(r) for r in revs] + if nodes: + obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" + % len(nodes)) + pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) + else: + obsexcmsg(repo.ui, "markers already in sync\n") + pushop.outobsmarkers = [] @eh.wrapfunction(wireproto, 'capabilities') def discocapabilities(orig, repo, proto): """wrapper to advertise new capability""" caps = orig(repo, proto) - if obsolete._enabled: + if obsolete.isenabled(repo, obsolete.exchangeopt): caps += ' _evoext_obshash_0' return caps @@ -2490,7 +2599,7 @@ pushop.ui.debug('try to push obsolete markers to remote\n') repo = pushop.repo remote = pushop.remote - if (obsolete._enabled and repo.obsstore and + if (obsolete.isenabled(repo, obsolete.exchangeopt) and repo.obsstore and 'obsolete' in remote.listkeys('namespaces')): markers = pushop.outobsmarkers if not markers: @@ -2630,34 +2739,32 @@ kwargs['evo_obscommon'] = common return ret -if getattr(exchange, '_getbundleobsmarkerpart', None) is not None: - @eh.wrapfunction(exchange, '_getbundleobsmarkerpart') - def _getbundleobsmarkerpart(orig, bundler, repo, source, **kwargs): - if 'evo_obscommon' not in kwargs: - return orig(bundler, repo, source, **kwargs) - - heads = kwargs.get('heads') - if kwargs.get('obsmarkers', False): - if heads is None: - heads = repo.heads() - obscommon = kwargs.get('evo_obscommon', ()) - assert obscommon - obsset = repo.unfiltered().set('::%ln - ::%ln', heads, obscommon) - subset = [c.node() for c in obsset] - markers = repo.obsstore.relevantmarkers(subset) - exchange.buildobsmarkerspart(bundler, markers) - - @eh.uisetup - def installgetbundlepartgen(ui): - origfunc = exchange.getbundle2partsmapping['obsmarkers'] - def newfunc(*args, **kwargs): - return _getbundleobsmarkerpart(origfunc, *args, **kwargs) - exchange.getbundle2partsmapping['obsmarkers'] = newfunc - +@eh.wrapfunction(exchange, '_getbundleobsmarkerpart') +def _getbundleobsmarkerpart(orig, bundler, repo, source, **kwargs): + if 'evo_obscommon' not in kwargs: + return orig(bundler, repo, source, **kwargs) + + heads = kwargs.get('heads') + if kwargs.get('obsmarkers', False): + if heads is None: + heads = repo.heads() + obscommon = kwargs.get('evo_obscommon', ()) + assert obscommon + obsset = repo.unfiltered().set('::%ln - ::%ln', heads, obscommon) + subset = [c.node() for c in obsset] + markers = repo.obsstore.relevantmarkers(subset) + exchange.buildobsmarkerspart(bundler, markers) + +@eh.uisetup +def installgetbundlepartgen(ui): + origfunc = exchange.getbundle2partsmapping['obsmarkers'] + def newfunc(*args, **kwargs): + return _getbundleobsmarkerpart(origfunc, *args, **kwargs) + exchange.getbundle2partsmapping['obsmarkers'] = newfunc @eh.wrapfunction(exchange, '_pullobsolete') def _pullobsolete(orig, pullop): - if not obsolete._enabled: + if not obsolete.isenabled(pullop.repo, obsolete.exchangeopt): return None if 'obsmarkers' not in getattr(pullop, 'todosteps', ['obsmarkers']): return None @@ -2827,21 +2934,20 @@ _bestformat = max(obsolete.formats.keys()) -if getattr(obsolete, '_checkinvalidmarkers', None) is not None: - @eh.wrapfunction(obsolete, '_checkinvalidmarkers') - def _checkinvalidmarkers(orig, markers): - """search for marker with invalid data and raise error if needed - - Exist as a separated function to allow the evolve extension for a more - subtle handling. - """ - if 'debugobsconvert' in sys.argv: - return - for mark in markers: - if node.nullid in mark[1]: - raise util.Abort(_('bad obsolescence marker detected: ' - 'invalid successors nullid'), - hint=_('You should run `hg debugobsconvert`')) +@eh.wrapfunction(obsolete, '_checkinvalidmarkers') +def _checkinvalidmarkers(orig, markers): + """search for marker with invalid data and raise error if needed + + Exist as a separated function to allow the evolve extension for a more + subtle handling. + """ + if 'debugobsconvert' in sys.argv: + return + for mark in markers: + if node.nullid in mark[1]: + raise util.Abort(_('bad obsolescence marker detected: ' + 'invalid successors nullid'), + hint=_('You should run `hg debugobsconvert`')) @command( 'debugobsconvert', @@ -2876,7 +2982,7 @@ def capabilities(orig, repo, proto): """wrapper to advertise new capability""" caps = orig(repo, proto) - if obsolete._enabled: + if obsolete.isenabled(repo, obsolete.exchangeopt): caps += ' _evoext_pushobsmarkers_0' caps += ' _evoext_pullobsmarkers_0' caps += ' _evoext_obshash_0'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/inhibit.py Mon May 04 10:56:06 2015 -0700 @@ -0,0 +1,280 @@ +"""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 + +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. +""" +from mercurial import localrepo +from mercurial import obsolete +from mercurial import extensions +from mercurial import cmdutil +from mercurial import scmutil +from mercurial import repoview +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) + +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) + _inhibitmarkers(repo, [newnode]) + return newnode + + # Wrapping this to inhibit obsolete revs resulting from a transaction + extensions.wrapfunction(localrepo.localrepository, + 'transaction', transactioncallback) + + repo.__class__ = obsinhibitedrepo + repo._explicitaccess = set() + + +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 getphase(repo, getrev(n))) + +def _inhibitmarkers(repo, nodes): + """add marker inhibitor for all obsolete revision under <nodes> + + Content of <nodes> 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('(not hidden()) and obsolete()') + 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 + obsolete.cachefuncs['obsolete'] = _computeobsoleteset + # 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 create marker to make it able to lift the inhibition + extensions.wrapfunction(obsolete, 'createmarkers', _createmarkers) + extensions.wrapfunction(repoview, '_getdynamicblockers', _accessvisible) + extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook) + # 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) + if repo is not None and repo.filtername == 'visible': + prelength = len(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): + repo.invalidatevolatilesets() + +@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) + +# ensure revision accessed by hash are visible +############################################### + +def _accessvisible(orig, repo): + """ensure accessed revs stay visible""" + blockers = orig(repo) + blockers.update(getattr(repo, '_explicitaccess', ())) + return blockers
--- a/hgext/obsolete.py Mon May 04 10:58:14 2015 -0700 +++ b/hgext/obsolete.py Mon May 04 10:56:06 2015 -0700 @@ -14,8 +14,6 @@ try: from mercurial import obsolete - if not obsolete._enabled: - obsolete._enabled = True except ImportError: raise util.Abort('Obsolete extension requires Mercurial 2.3 (or later)') @@ -40,6 +38,10 @@ """ if not repo.local(): return + evolveopts = ui.configlist('experimental', 'evolution') + if not evolveopts: + evolveopts = 'all' + ui.setconfig('experimental', 'evolution', evolveopts) for arg in sys.argv: if 'debugc' in arg: break
--- a/hgext/pushexperiment.py Mon May 04 10:58:14 2015 -0700 +++ b/hgext/pushexperiment.py Mon May 04 10:56:06 2015 -0700 @@ -49,7 +49,8 @@ def syncpush(orig, repo, remote): """wraper for obsolete.syncpush to use the fast way if possible""" - if not (obsolete._enabled and repo.obsstore): + if not (obsolete.isenabled(repo, obsolete.exchangeopt) and + repo.obsstore): return if remote.capable('_push_experiment_pushobsmarkers_0'): return # already pushed before changeset @@ -75,7 +76,7 @@ """push wrapped that call the wire protocol command""" if not remote.canpush(): raise util.Abort(_("destination does not support push")) - if (obsolete._enabled and repo.obsstore + if (obsolete.isenabled(repo, obsolete.exchangeopt) and repo.obsstore and remote.capable('_push_experiment_pushobsmarkers_0')): # push marker early to limit damage of pushing too early. try: @@ -94,7 +95,7 @@ def capabilities(orig, repo, proto): """wrapper to advertise new capability""" caps = orig(repo, proto) - if obsolete._enabled: + if obsolete.isenabled(repo, obsolete.exchangeopt): caps += ' _push_experiment_pushobsmarkers_0' caps += ' _push_experiment_notifypushend_0' return caps
--- a/hgext/simple4server.py Mon May 04 10:58:14 2015 -0700 +++ b/hgext/simple4server.py Mon May 04 10:56:06 2015 -0700 @@ -12,7 +12,6 @@ buglink = 'https://bitbucket.org/marmoute/mutable-history/issues' import mercurial.obsolete -mercurial.obsolete._enabled = True import struct from mercurial import util @@ -31,8 +30,6 @@ gboptslist = gboptsmap = None try: from mercurial import obsolete - if not obsolete._enabled: - obsolete._enabled = True from mercurial import wireproto gboptslist = getattr(wireproto, 'gboptslist', None) gboptsmap = getattr(wireproto, 'gboptsmap', None) @@ -247,7 +244,7 @@ """wrapper to advertise new capability""" caps = orig(repo, proto) advertise = repo.ui.configbool('__temporary__', 'advertiseobsolete', True) - if obsolete._enabled and advertise: + if obsolete.isenabled(repo, obsolete.exchangeopt) and advertise: caps += ' _evoext_pushobsmarkers_0' caps += ' _evoext_pullobsmarkers_0' caps += ' _evoext_obshash_0' @@ -303,3 +300,8 @@ extensions.wrapfunction(pushkey, '_nslist', _nslist) pushkey._namespaces['namespaces'] = (lambda *x: False, pushkey._nslist) +def reposetup(ui, repo): + evolveopts = ui.configlist('experimental', 'evolution') + if not evolveopts: + evolveopts = 'all' + ui.setconfig('experimental', 'evolution', evolveopts)
--- a/setup.py Mon May 04 10:58:14 2015 -0700 +++ b/setup.py Mon May 04 10:56:06 2015 -0700 @@ -1,6 +1,7 @@ # Copied from histedit setup.py # Credit to Augie Fackler <durin42@gmail.com> +import os from distutils.core import setup from os.path import dirname, join @@ -14,6 +15,13 @@ if "'" in line: return line.split("'")[1] +py_modules = [ + 'hgext.evolve', +] + +if os.environ.get('INCLUDE_INHIBIT'): + py_modules.append('hgext.inhibit') + setup( name='hg-evolve', version=get_version('hgext/evolve.py'), @@ -25,5 +33,5 @@ long_description=open('README').read(), keywords='hg mercurial', license='GPLv2+', - py_modules=['hgext.evolve'], + py_modules=py_modules )
--- a/tests/killdaemons.py Mon May 04 10:58:14 2015 -0700 +++ b/tests/killdaemons.py Mon May 04 10:56:06 2015 -0700 @@ -1,25 +1,91 @@ #!/usr/bin/env python -import os, time, errno, signal +import os, sys, time, errno, signal + +if os.name =='nt': + import ctypes + + def _check(ret, expectederr=None): + if ret == 0: + winerrno = ctypes.GetLastError() + if winerrno == expectederr: + return True + raise ctypes.WinError(winerrno) -# Kill off any leftover daemon processes -try: - fp = open(os.environ['DAEMON_PIDS']) - for line in fp: + def kill(pid, logfn, tryhard=True): + logfn('# Killing daemon process %d' % pid) + PROCESS_TERMINATE = 1 + PROCESS_QUERY_INFORMATION = 0x400 + SYNCHRONIZE = 0x00100000 + WAIT_OBJECT_0 = 0 + WAIT_TIMEOUT = 258 + handle = ctypes.windll.kernel32.OpenProcess( + PROCESS_TERMINATE|SYNCHRONIZE|PROCESS_QUERY_INFORMATION, + False, pid) + if handle == 0: + _check(0, 87) # err 87 when process not found + return # process not found, already finished try: - pid = int(line) - except ValueError: - continue + r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100) + if r == WAIT_OBJECT_0: + pass # terminated, but process handle still available + elif r == WAIT_TIMEOUT: + _check(ctypes.windll.kernel32.TerminateProcess(handle, -1)) + else: + _check(r) + + # TODO?: forcefully kill when timeout + # and ?shorter waiting time? when tryhard==True + r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100) + # timeout = 100 ms + if r == WAIT_OBJECT_0: + pass # process is terminated + elif r == WAIT_TIMEOUT: + logfn('# Daemon process %d is stuck') + else: + _check(r) # any error + except: #re-raises + ctypes.windll.kernel32.CloseHandle(handle) # no _check, keep error + raise + _check(ctypes.windll.kernel32.CloseHandle(handle)) + +else: + def kill(pid, logfn, tryhard=True): try: os.kill(pid, 0) + logfn('# Killing daemon process %d' % pid) os.kill(pid, signal.SIGTERM) - for i in range(10): - time.sleep(0.05) + if tryhard: + for i in range(10): + time.sleep(0.05) + os.kill(pid, 0) + else: + time.sleep(0.1) os.kill(pid, 0) + logfn('# Daemon process %d is stuck - really killing it' % pid) os.kill(pid, signal.SIGKILL) except OSError, err: if err.errno != errno.ESRCH: raise - fp.close() -except IOError: - pass + +def killdaemons(pidfile, tryhard=True, remove=False, logfn=None): + if not logfn: + logfn = lambda s: s + # Kill off any leftover daemon processes + try: + fp = open(pidfile) + for line in fp: + try: + pid = int(line) + except ValueError: + continue + kill(pid, logfn, tryhard) + fp.close() + if remove: + os.unlink(pidfile) + except IOError: + pass + +if __name__ == '__main__': + path, = sys.argv[1:] + killdaemons(path)
--- a/tests/test-evolve.t Mon May 04 10:58:14 2015 -0700 +++ b/tests/test-evolve.t Mon May 04 10:56:06 2015 -0700 @@ -69,9 +69,9 @@ $ hg id -n 5 $ hg kill . - 1 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at fbb94e3a0ecf + 1 changesets pruned $ hg qlog 4 - fbb94e3a0ecf add e (draft) 3 - 47d2a3944de8 add d (draft) @@ -82,9 +82,9 @@ test multiple kill $ hg kill 4 -r 3 - 2 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at 7c3bad9141dc + 2 changesets pruned $ hg qlog 2 - 4538525df7e2 add c (draft) 1 - 7c3bad9141dc add b (public) @@ -97,9 +97,9 @@ $ echo 4 > g $ hg add g $ hg kill . - 1 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at 7c3bad9141dc + 1 changesets pruned $ hg st A g
--- a/tests/test-exchange-D2.t Mon May 04 10:58:14 2015 -0700 +++ b/tests/test-exchange-D2.t Mon May 04 10:56:06 2015 -0700 @@ -37,9 +37,9 @@ created new head $ hg debugobsolete `getid 'desc(A0)'` `getid 'desc(A1)'` $ hg prune --date '0 0' . - 1 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at a9bdc8b26820 + 1 changesets pruned $ hg strip --hidden -q 'desc(A1)' $ hg log -G --hidden x 28b51eb45704 (draft): A0
--- a/tests/test-exchange-D3.t Mon May 04 10:58:14 2015 -0700 +++ b/tests/test-exchange-D3.t Mon May 04 10:56:06 2015 -0700 @@ -39,9 +39,9 @@ $ mkcommit A1 $ hg debugobsolete `getid 'desc(A0)'` `getid 'desc(A1)'` $ hg prune -d '0 0' . - 1 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at 35b183996678 + 1 changesets pruned $ hg strip --hidden -q 'desc(A1)' $ hg log -G --hidden @ 35b183996678 (draft): B
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-inhibit.t Mon May 04 10:56:06 2015 -0700 @@ -0,0 +1,541 @@ + $ cat >> $HGRCPATH <<EOF + > [ui] + > logtemplate = {rev}:{node|short} {desc}\n + > [extensions] + > rebase= + > EOF + $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.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 prune 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 prune --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 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 prune 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 prune 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 prune --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: hidden revision '1'! + (use --hidden to access hidden revisions) + [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 prune 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: hidden revision '3'! + (use --hidden to access hidden revisions) + [255] + $ hg rebase -r ad78ff7d621f -r 53a94305e133 -d 2db36d8066ff + rebasing 10:ad78ff7d621f "add cK" + rebasing 11:53a94305e133 "add cL" + $ hg log -G + o 13:2f7b7704d714 add cL + | + o 12:fe1634cbe235 add cK + | + | 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 | 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 + + $ hg prune 1:: + 5 changesets pruned + $ hg prune 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 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 prune -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 --config experimental.prunestrip=True --config extensions.strip= + 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 + |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-options.t Mon May 04 10:56:06 2015 -0700 @@ -0,0 +1,30 @@ + $ cat >> $HGRCPATH <<EOF + > [ui] + > logtemplate={rev}:{node|short}[{bookmarks}] ({obsolete}/{phase}) {desc|firstline}\n + > [extensions] + > EOF + $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH + + $ mkcommit() { + > echo "$1" > "$1" + > hg add "$1" + > hg ci -m "add $1" + > } + + $ hg init repo + $ cd repo + $ mkcommit a + $ mkcommit b + +test disabling commands + + $ cat >> .hg/hgrc <<EOF + > [experimental] + > evolution=createmarkers + > allowunstable + > exchange + > EOF + $ hg prune | head -n 2 + hg: unknown command 'prune' + Mercurial Distributed SCM +
--- a/tests/test-prune.t Mon May 04 10:58:14 2015 -0700 +++ b/tests/test-prune.t Mon May 04 10:56:06 2015 -0700 @@ -38,10 +38,10 @@ prune current and tip changeset $ hg prune --user blah --date '1979-12-15' . - 1 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (leaving bookmark BABAR) working directory now at 47d2a3944de8 + 1 changesets pruned $ hg debugobsolete 9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'} @@ -57,9 +57,9 @@ pruning multiple changeset at once $ hg prune 2: - 2 changesets pruned 0 files updated, 0 files merged, 3 files removed, 0 files unresolved working directory now at 1f0dee641bb7 + 2 changesets pruned $ hg debugobsolete 9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'} 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {1f0dee641bb7258c56bd60e93edfa2405381c41e} (*) {'user': 'test'} (glob) @@ -118,9 +118,9 @@ $ hg up 'desc("add ee")' 4 files updated, 0 files merged, 4 files removed, 0 files unresolved $ hg prune 'desc("add ee")' -s 'desc("add nE")' - 1 changesets pruned 4 files updated, 0 files merged, 4 files removed, 0 files unresolved working directory now at 6e8148413dd5 + 1 changesets pruned $ hg debugobsolete 9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'} 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {1f0dee641bb7258c56bd60e93edfa2405381c41e} (*) {'user': 'test'} (glob) @@ -208,9 +208,9 @@ $ mkcommit n2 $ hg prune 'desc("add n1")::desc("add n2")' -s 'desc("add nD")::desc("add nE")' --biject - 2 changesets pruned 0 files updated, 0 files merged, 2 files removed, 0 files unresolved working directory now at 1f0dee641bb7 + 2 changesets pruned $ hg debugobsolete 9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'} 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {1f0dee641bb7258c56bd60e93edfa2405381c41e} (*) {'user': 'test'} (glob) @@ -223,6 +223,35 @@ cb7f8f706a6532967b98cf8583a81baab79a0fa7 8ee176ff1d4b2034ce51e3efc579c2de346b631d 0 (*) {'user': 'test'} (glob) 21b6f2f1cece8c10326e575dd38239189d467190 6e8148413dd541855b72a920a90c06fca127c7e7 0 (*) {'user': 'test'} (glob) +test hg strip replacement + + $ hg up 10 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ mkcommit n1 + created new head + $ mkcommit n2 + $ hg --config extensions.strip= --config experimental.prunestrip=True strip -r . + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + working directory now at c7e58696a948 + 1 changesets pruned + $ hg --config extensions.strip= --config experimental.prunestrip=True strip -r . --bundle + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/c7e58696a948-69ca36d3-backup.hg (glob) + +test hg prune --keep + $ mkcommit n1 + created new head + $ hg diff -r .^ + diff -r aa96dc3f04c2 n1 + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/n1 * +0000 (glob) + @@ -0,0 +1,1 @@ + +n1 + $ hg prune -r . --keep + 1 changesets pruned + $ hg status + ? n1 + test hg prune -B bookmark yoinked from test-mq-strip.t @@ -243,11 +272,11 @@ [255] $ hg tag --remove --local a $ hg prune -B todelete - 1 changesets pruned 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (leaving bookmark todelete) working directory now at d62d843c9a01 bookmark 'todelete' deleted + 1 changesets pruned $ hg id -ir dcbb326fdec2 abort: hidden revision 'dcbb326fdec2'! (use --hidden to access hidden revisions) @@ -258,8 +287,8 @@ B 10:ff43616e5d0f delete 6:2702dd0c91e7 $ hg prune -B delete + bookmark 'delete' deleted 3 changesets pruned - bookmark 'delete' deleted $ hg tag --remove --local c $ hg id -ir 6:2702dd0c91e7 abort: hidden revision '6'!
--- a/tests/test-simple4server-bundle2.t Mon May 04 10:58:14 2015 -0700 +++ b/tests/test-simple4server-bundle2.t Mon May 04 10:56:06 2015 -0700 @@ -140,7 +140,7 @@ $ echo '[__temporary__]' >> server/.hg/hgrc $ echo 'advertiseobsolete=False' >> server/.hg/hgrc - $ $TESTDIR/killdaemons.py + $ $TESTDIR/killdaemons.py $DAEMON_PIDS $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS @@ -148,13 +148,9 @@ bookmarks namespaces phases - $ wget -q -O - http://localhost:$HGPORT/?cmd=hello - capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 - $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities - lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 (no-eol) $ echo 'advertiseobsolete=True' >> server/.hg/hgrc - $ $TESTDIR/killdaemons.py + $ $TESTDIR/killdaemons.py $DAEMON_PIDS $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS @@ -163,6 +159,7 @@ namespaces obsolete phases + $ wget -q -O - http://localhost:$HGPORT/?cmd=hello capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
--- a/tests/test-simple4server.t Mon May 04 10:58:14 2015 -0700 +++ b/tests/test-simple4server.t Mon May 04 10:56:06 2015 -0700 @@ -141,7 +141,7 @@ $ echo '[__temporary__]' >> server/.hg/hgrc $ echo 'advertiseobsolete=False' >> server/.hg/hgrc - $ $TESTDIR/killdaemons.py + $ $TESTDIR/killdaemons.py $DAEMON_PIDS $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS @@ -155,7 +155,7 @@ lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 (no-eol) $ echo 'advertiseobsolete=True' >> server/.hg/hgrc - $ $TESTDIR/killdaemons.py + $ $TESTDIR/killdaemons.py $DAEMON_PIDS $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS
--- a/tests/test-tutorial.t Mon May 04 10:58:14 2015 -0700 +++ b/tests/test-tutorial.t Mon May 04 10:56:06 2015 -0700 @@ -289,9 +289,9 @@ not fit well in my standard shopping list) $ hg prune . # "." is for working directory parent - 1 changesets pruned 1 files updated, 0 files merged, 0 files removed, 0 files unresolved working directory now at 41aff6a42b75 + 1 changesets pruned The silly changeset is gone. @@ -804,9 +804,9 @@ In the mean time I noticed you can't buy animals in a super market and I prune the animal changeset: $ hg prune ee942144f952 - 1 changesets pruned 1 files updated, 0 files merged, 0 files removed, 0 files unresolved working directory now at a44c85f957d3 + 1 changesets pruned 1 new unstable changesets
--- a/tests/test-userguide.t Mon May 04 10:58:14 2015 -0700 +++ b/tests/test-userguide.t Mon May 04 10:56:06 2015 -0700 @@ -66,9 +66,9 @@ $ echo 'debug hack' >> file1.c $ hg commit -m 'debug hack' $ hg prune . - 1 changesets pruned 1 files updated, 0 files merged, 0 files removed, 0 files unresolved working directory now at 934359450037 + 1 changesets pruned $ hg parents --template '{rev}:{node|short} {desc|firstline}\n' 3:934359450037 implement feature Y $ hg --hidden shortlog -G -r 3: