# HG changeset patch # User Pierre-Yves David # Date 1490967560 -7200 # Node ID e200dbfb45150bd50e9d9766f2905b173122ee6d # Parent 90ab79764ce466a5df036fa4c69acc94c6ccd6ca# Parent 94432e742a0211a4d8ffcf620ea2da17bf8e93bd merge with future 6.0.0 diff -r 90ab79764ce4 -r e200dbfb4515 README --- a/README Tue Mar 14 14:38:10 2017 -0700 +++ b/README Fri Mar 31 15:39:20 2017 +0200 @@ -57,7 +57,7 @@ $ hg clone https://www.mercurial-scm.org/repo/evolve/ $ cd evolve - $ make install-home + $ pip install --user . Then just enable it in you hgrc:: @@ -67,8 +67,8 @@ Documentation lives in ``doc/``. -Server Only Version -=================== +Server Only Setup +================= It is possible to enable a smaller subset of the extensions aimed at server serving repository. It skips the additions of the new commands and local UI @@ -87,8 +87,9 @@ .. _evolution: https://bz.mercurial-scm.org/buglist.cgi?component=evolution&query_format=advanced&resolution=--- -Please use the patchbomb extension to send email to mercurial devel. Please -make sure to use the evolve-ext flag when doing so. You can use a command like +Please use the patchbomb extension to send email to `mercurial devel +`_. Please make +sure to use the evolve-ext flag when doing so. You can use a command like this:: $ hg email --to mercurial-devel@mercurial-scm.org --flag evolve-ext --rev '' @@ -114,6 +115,7 @@ 6.0.0 -- In progress -------------------- +- push: improved detection of obsoleted remote branch (issue4354), - drop compatibility for Mercurial < 3.8, - removed old (unpackaged) pushexperiment extension, - move all extensions in the official 'hgext3rd' namespace package, @@ -125,6 +127,24 @@ to disable obsmarkers echange. The old '__temporary__.advertiseobsolete' option is no longer supported. +- a new prototype of obsmarker discovery is available. The prototype is still + at early stage and not recommended for production. + Examples of current limitations: + + - write access to the repo is highly recommanded for all operation, + - large memory footprint, + - initial caching is slow, + - unusable on large repo (because of various issue pointed earlier), + - likely to constains various bugs. + + It can be tested by setting `experimental.obshashrange=1` on both client and + server. It is recommanded to get in touch with the evolve maintainer if you + decide to test it. + +- the 'debugrecordpruneparents' have been moved into the 'evolve.legacy' + separate extension. enable that extentions if you need to convert/update + markers in an old repository. + 5.6.1 -- 2017-02-28 ------------------- diff -r 90ab79764ce4 -r e200dbfb4515 hgext3rd/evolve/__init__.py --- a/hgext3rd/evolve/__init__.py Tue Mar 14 14:38:10 2017 -0700 +++ b/hgext3rd/evolve/__init__.py Fri Mar 31 15:39:20 2017 +0200 @@ -111,6 +111,8 @@ from mercurial.node import nullid from . import ( + checkheads, + debugcmd, obsdiscovery, obsexchange, exthelper, @@ -143,8 +145,10 @@ # - Older format compat eh = exthelper.exthelper() +eh.merge(debugcmd.eh) eh.merge(obsdiscovery.eh) eh.merge(obsexchange.eh) +eh.merge(checkheads.eh) uisetup = eh.final_uisetup extsetup = eh.final_extsetup reposetup = eh.final_reposetup @@ -814,153 +818,6 @@ _deprecatealias('gup', 'next') _deprecatealias('gdown', 'previous') -@eh.command('debugrecordpruneparents', [], '') -def cmddebugrecordpruneparents(ui, repo): - """add parent data to prune markers when possible - - This command searches the repo for prune markers without parent information. - If the pruned node is locally known, it creates a new marker with parent - data. - """ - pgop = 'reading markers' - - # lock from the beginning to prevent race - wlock = lock = tr = None - try: - wlock = repo.wlock() - lock = repo.lock() - tr = repo.transaction('recordpruneparents') - unfi = repo.unfiltered() - nm = unfi.changelog.nodemap - store = repo.obsstore - pgtotal = len(store._all) - for idx, mark in enumerate(list(store._all)): - if not mark[1]: - rev = nm.get(mark[0]) - if rev is not None: - ctx = unfi[rev] - parents = tuple(p.node() for p in ctx.parents()) - before = len(store._all) - store.create(tr, mark[0], mark[1], mark[2], mark[3], - parents=parents) - if len(store._all) - before: - ui.write(_('created new markers for %i\n') % rev) - ui.progress(pgop, idx, total=pgtotal) - tr.close() - ui.progress(pgop, None) - finally: - lockmod.release(tr, lock, wlock) - -@eh.command('debugobsstorestat', [], '') -def cmddebugobsstorestat(ui, repo): - """print statistics about obsolescence markers in the repo""" - def _updateclustermap(nodes, mark, clustersmap): - c = (set(nodes), set([mark])) - toproceed = set(nodes) - while toproceed: - n = toproceed.pop() - other = clustersmap.get(n) - if (other is not None - and other is not c): - other[0].update(c[0]) - other[1].update(c[1]) - for on in c[0]: - if on in toproceed: - continue - clustersmap[on] = other - c = other - clustersmap[n] = c - - store = repo.obsstore - unfi = repo.unfiltered() - nm = unfi.changelog.nodemap - ui.write(_('markers total: %9i\n') % len(store._all)) - sucscount = [0, 0, 0, 0] - known = 0 - parentsdata = 0 - metakeys = {} - # node -> cluster mapping - # a cluster is a (set(nodes), set(markers)) tuple - clustersmap = {} - # same data using parent information - pclustersmap = {} - for mark in store: - if mark[0] in nm: - known += 1 - nbsucs = len(mark[1]) - sucscount[min(nbsucs, 3)] += 1 - meta = mark[3] - for key, value in meta: - metakeys.setdefault(key, 0) - metakeys[key] += 1 - meta = dict(meta) - parents = [meta.get('p1'), meta.get('p2')] - parents = [node.bin(p) for p in parents if p is not None] - if parents: - parentsdata += 1 - # cluster handling - nodes = set(mark[1]) - nodes.add(mark[0]) - _updateclustermap(nodes, mark, clustersmap) - # same with parent data - nodes.update(parents) - _updateclustermap(nodes, mark, pclustersmap) - - # freezing the result - for c in clustersmap.values(): - fc = (frozenset(c[0]), frozenset(c[1])) - for n in fc[0]: - clustersmap[n] = fc - # same with parent data - for c in pclustersmap.values(): - fc = (frozenset(c[0]), frozenset(c[1])) - for n in fc[0]: - pclustersmap[n] = fc - ui.write((' for known precursors: %9i\n' % known)) - ui.write((' with parents data: %9i\n' % parentsdata)) - # successors data - ui.write(('markers with no successors: %9i\n' % sucscount[0])) - ui.write((' 1 successors: %9i\n' % sucscount[1])) - ui.write((' 2 successors: %9i\n' % sucscount[2])) - ui.write((' more than 2 successors: %9i\n' % sucscount[3])) - # meta data info - ui.write((' available keys:\n')) - for key in sorted(metakeys): - ui.write((' %15s: %9i\n' % (key, metakeys[key]))) - - allclusters = list(set(clustersmap.values())) - allclusters.sort(key=lambda x: len(x[1])) - ui.write(('disconnected clusters: %9i\n' % len(allclusters))) - - ui.write(' any known node: %9i\n' - % len([c for c in allclusters - if [n for n in c[0] if nm.get(n) is not None]])) - if allclusters: - nbcluster = len(allclusters) - ui.write((' smallest length: %9i\n' % len(allclusters[0][1]))) - ui.write((' longer length: %9i\n' - % len(allclusters[-1][1]))) - median = len(allclusters[nbcluster // 2][1]) - ui.write((' median length: %9i\n' % median)) - mean = sum(len(x[1]) for x in allclusters) // nbcluster - ui.write((' mean length: %9i\n' % mean)) - allpclusters = list(set(pclustersmap.values())) - allpclusters.sort(key=lambda x: len(x[1])) - ui.write((' using parents data: %9i\n' % len(allpclusters))) - ui.write(' any known node: %9i\n' - % len([c for c in allclusters - if [n for n in c[0] if nm.get(n) is not None]])) - if allpclusters: - nbcluster = len(allpclusters) - ui.write((' smallest length: %9i\n' - % len(allpclusters[0][1]))) - ui.write((' longer length: %9i\n' - % len(allpclusters[-1][1]))) - median = len(allpclusters[nbcluster // 2][1]) - ui.write((' median length: %9i\n' % median)) - mean = sum(len(x[1]) for x in allpclusters) // nbcluster - ui.write((' mean length: %9i\n' % mean)) - def _solveone(ui, repo, ctx, dryrun, confirm, progresscb, category): """Resolve the troubles affecting one revision""" wlock = lock = tr = None diff -r 90ab79764ce4 -r e200dbfb4515 hgext3rd/evolve/checkheads.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext3rd/evolve/checkheads.py Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,281 @@ +# Code dedicated to the postprocessing new heads check with obsolescence +# +# Copyright 2017 Pierre-Yves David +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +import functools + +from mercurial import ( + discovery, + error, + extensions, + node as nodemod, + phases, + util, +) + +from mercurial.i18n import _ + +from . import exthelper + +nullid = nodemod.nullid +short = nodemod.short +_headssummary = discovery._headssummary +_oldheadssummary = discovery._oldheadssummary +_nowarnheads = discovery._nowarnheads + +eh = exthelper.exthelper() + +@eh.uisetup +def setupcheckheadswrapper(ui): + if util.safehasattr(discovery, '_postprocessobsolete'): + extensions.wrapfunction(discovery, '_postprocessobsolete', + checkheadslightoverlay) + else: + extensions.wrapfunction(discovery, 'checkheads', + checkheadsfulloverlay) + +# have dedicated wrapper to keep the rest as close as core as possible +def checkheadsfulloverlay(orig, pushop): + if pushop.repo.obsstore: + return corecheckheads(pushop) + else: + return orig(pushop) + +def checkheadslightoverlay(orig, *args, **kwargs): + return _postprocessobsolete(*args, **kwargs) + +# copied from mercurial.discovery.checkheads as in a5bad127128d (4.1) +# +# The only differences are: +# * the _postprocessobsolete section have been extracted, +# * minor test adjustment to please flake8 +def corecheckheads(pushop): + """Check that a push won't add any outgoing head + + raise Abort error and display ui message as needed. + """ + + repo = pushop.repo.unfiltered() + remote = pushop.remote + outgoing = pushop.outgoing + remoteheads = pushop.remoteheads + newbranch = pushop.newbranch + inc = bool(pushop.incoming) + + # Check for each named branch if we're creating new remote heads. + # To be a remote head after push, node must be either: + # - unknown locally + # - a local outgoing head descended from update + # - a remote head that's known locally and not + # ancestral to an outgoing head + if remoteheads == [nullid]: + # remote is empty, nothing to check. + return + + if remote.capable('branchmap'): + headssum = _headssummary(repo, remote, outgoing) + else: + headssum = _oldheadssummary(repo, remoteheads, outgoing, inc) + newbranches = [branch for branch, heads in headssum.iteritems() + if heads[0] is None] + # 1. Check for new branches on the remote. + if newbranches and not newbranch: # new branch requires --new-branch + branchnames = ', '.join(sorted(newbranches)) + raise error.Abort(_("push creates new remote branches: %s!") + % branchnames, + hint=_("use 'hg push --new-branch' to create" + " new remote branches")) + + # 2. Find heads that we need not warn about + nowarnheads = _nowarnheads(pushop) + + # 3. Check for new heads. + # If there are more heads after the push than before, a suitable + # error message, depending on unsynced status, is displayed. + errormsg = None + # If there is no obsstore, allfuturecommon won't be used, so no + # need to compute it. + if repo.obsstore: + allmissing = set(outgoing.missing) + cctx = repo.set('%ld', outgoing.common) + allfuturecommon = set(c.node() for c in cctx) + allfuturecommon.update(allmissing) + for branch, heads in sorted(headssum.iteritems()): + remoteheads, newheads, unsyncedheads = heads + candidate_newhs = set(newheads) + # add unsynced data + if remoteheads is None: + oldhs = set() + else: + oldhs = set(remoteheads) + oldhs.update(unsyncedheads) + candidate_newhs.update(unsyncedheads) + dhs = None # delta heads, the new heads on branch + if not repo.obsstore: + discardedheads = set() + newhs = candidate_newhs + else: + newhs, discardedheads = _postprocessobsolete(pushop, + allfuturecommon, + candidate_newhs) + unsynced = sorted(h for h in unsyncedheads if h not in discardedheads) + if unsynced: + if None in unsynced: + # old remote, no heads data + heads = None + elif len(unsynced) <= 4 or repo.ui.verbose: + heads = ' '.join(short(h) for h in unsynced) + else: + heads = (' '.join(short(h) for h in unsynced[:4]) + + ' ' + _("and %s others") % (len(unsynced) - 4)) + if heads is None: + repo.ui.status(_("remote has heads that are " + "not known locally\n")) + elif branch is None: + repo.ui.status(_("remote has heads that are " + "not known locally: %s\n") % heads) + else: + repo.ui.status(_("remote has heads on branch '%s' that are " + "not known locally: %s\n") % (branch, heads)) + if remoteheads is None: + if len(newhs) > 1: + dhs = list(newhs) + if errormsg is None: + errormsg = (_("push creates new branch '%s' " + "with multiple heads") % (branch)) + hint = _("merge or" + " see 'hg help push' for details about" + " pushing new heads") + elif len(newhs) > len(oldhs): + # remove bookmarked or existing remote heads from the new heads list + dhs = sorted(newhs - nowarnheads - oldhs) + if dhs: + if errormsg is None: + if branch not in ('default', None): + errormsg = _("push creates new remote head %s " + "on branch '%s'!") % (short(dhs[0]), branch) + elif repo[dhs[0]].bookmarks(): + errormsg = (_("push creates new remote head %s " + "with bookmark '%s'!") + % (short(dhs[0]), repo[dhs[0]].bookmarks()[0])) + else: + errormsg = _("push creates new remote head %s!" + ) % short(dhs[0]) + if unsyncedheads: + hint = _("pull and merge or" + " see 'hg help push' for details about" + " pushing new heads") + else: + hint = _("merge or" + " see 'hg help push' for details about" + " pushing new heads") + if branch is None: + repo.ui.note(_("new remote heads:\n")) + else: + repo.ui.note(_("new remote heads on branch '%s':\n") % branch) + for h in dhs: + repo.ui.note((" %s\n") % short(h)) + if errormsg: + raise error.Abort(errormsg, hint=hint) + +def _postprocessobsolete(pushop, futurecommon, candidate): + """post process the list of new heads with obsolescence information + + Exist as a subfunction to contains the complexity and allow extensions to + experiment with smarter logic. + Returns (newheads, discarded_heads) tuple + """ + # remove future heads which are actually obsoleted by another + # pushed element: + # + # known issue + # + # * We "silently" skip processing on all changeset unknown locally + # + # * if is public on the remote, it won't be affected by obsolete + # marker and a new is created + repo = pushop.repo + unfi = repo.unfiltered() + tonode = unfi.changelog.node + public = phases.public + getphase = unfi._phasecache.phase + ispublic = (lambda r: getphase(unfi, r) == public) + hasoutmarker = functools.partial(pushingmarkerfor, unfi.obsstore, futurecommon) + successorsmarkers = unfi.obsstore.successors + newhs = set() + discarded = set() + # I leave the print in the code because they are so handy at debugging + # and I keep getting back to this piece of code. + # + localcandidate = set() + unknownheads = set() + for h in candidate: + if h in unfi: + localcandidate.add(h) + else: + if successorsmarkers.get(h) is not None: + msg = ('checkheads: remote head unknown locally has' + ' local marker: %s\n') + repo.ui.debug(msg % nodemod.hex(h)) + unknownheads.add(h) + if len(localcandidate) == 1: + return unknownheads | set(candidate), set() + while localcandidate: + nh = localcandidate.pop() + # run this check early to skip the revset on the whole branch + if (nh in futurecommon + or unfi[nh].phase() <= public): + newhs.add(nh) + continue + # XXX there is a corner case if there is a merge in the branch. we + # might end up with -more- heads. However, these heads are not "added" + # by the push, but more by the "removal" on the remote so I think is a + # okay to ignore them, + branchrevs = unfi.revs('only(%n, (%ln+%ln))', + nh, localcandidate, newhs) + branchnodes = [tonode(r) for r in branchrevs] + + # The branch will still exist on the remote if + # * any part of it is public, + # * any part of it is considered part of the result by previous logic, + # * if we have no markers to push to obsolete it. + if (any(ispublic(r) for r in branchrevs) + or any(n in futurecommon for n in branchnodes) + or any(not hasoutmarker(n) for n in branchnodes)): + newhs.add(nh) + else: + discarded.add(nh) + newhs |= unknownheads + return newhs, discarded + +def pushingmarkerfor(obsstore, pushset, node): + """True if some markers are to be pushed for node + + We cannot just look in to the pushed obsmarkers from the pushop because + discover might have filtered relevant markers. In addition listing all + markers relevant to all changeset in the pushed set would be too expensive. + + The is probably some cache opportunity in this function. but it would + requires a two dimentions stack. + """ + successorsmarkers = obsstore.successors + stack = [node] + seen = set(stack) + while stack: + current = stack.pop() + if current in pushset: + return True + markers = successorsmarkers.get(current, ()) + # markers fields = ('prec', 'succs', 'flag', 'meta', 'date', 'parents') + for m in markers: + nexts = m[1] # successors + if not nexts: # this is a prune marker + nexts = m[5] # parents + for n in nexts: + if n not in seen: + seen.add(n) + stack.append(n) + return False diff -r 90ab79764ce4 -r e200dbfb4515 hgext3rd/evolve/debugcmd.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext3rd/evolve/debugcmd.py Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,129 @@ +# Code dedicated to debug commands around evolution +# +# Copyright 2017 Pierre-Yves David +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +# Status: Ready to Upstream +# +# * We could have the same code in core as `hg debugobsolete --stat`, +# * We probably want a way for the extension to hook in for extra data. + +from mercurial import node + +from mercurial.i18n import _ + +from . import exthelper + +eh = exthelper.exthelper() + +@eh.command('debugobsstorestat', [], '') +def cmddebugobsstorestat(ui, repo): + """print statistics about obsolescence markers in the repo""" + def _updateclustermap(nodes, mark, clustersmap): + c = (set(nodes), set([mark])) + toproceed = set(nodes) + while toproceed: + n = toproceed.pop() + other = clustersmap.get(n) + if (other is not None + and other is not c): + other[0].update(c[0]) + other[1].update(c[1]) + for on in c[0]: + if on in toproceed: + continue + clustersmap[on] = other + c = other + clustersmap[n] = c + + store = repo.obsstore + unfi = repo.unfiltered() + nm = unfi.changelog.nodemap + ui.write(_('markers total: %9i\n') % len(store._all)) + sucscount = [0, 0, 0, 0] + known = 0 + parentsdata = 0 + metakeys = {} + # node -> cluster mapping + # a cluster is a (set(nodes), set(markers)) tuple + clustersmap = {} + # same data using parent information + pclustersmap = {} + for mark in store: + if mark[0] in nm: + known += 1 + nbsucs = len(mark[1]) + sucscount[min(nbsucs, 3)] += 1 + meta = mark[3] + for key, value in meta: + metakeys.setdefault(key, 0) + metakeys[key] += 1 + meta = dict(meta) + parents = [meta.get('p1'), meta.get('p2')] + parents = [node.bin(p) for p in parents if p is not None] + if parents: + parentsdata += 1 + # cluster handling + nodes = set(mark[1]) + nodes.add(mark[0]) + _updateclustermap(nodes, mark, clustersmap) + # same with parent data + nodes.update(parents) + _updateclustermap(nodes, mark, pclustersmap) + + # freezing the result + for c in clustersmap.values(): + fc = (frozenset(c[0]), frozenset(c[1])) + for n in fc[0]: + clustersmap[n] = fc + # same with parent data + for c in pclustersmap.values(): + fc = (frozenset(c[0]), frozenset(c[1])) + for n in fc[0]: + pclustersmap[n] = fc + ui.write((' for known precursors: %9i\n' % known)) + ui.write((' with parents data: %9i\n' % parentsdata)) + # successors data + ui.write(('markers with no successors: %9i\n' % sucscount[0])) + ui.write((' 1 successors: %9i\n' % sucscount[1])) + ui.write((' 2 successors: %9i\n' % sucscount[2])) + ui.write((' more than 2 successors: %9i\n' % sucscount[3])) + # meta data info + ui.write((' available keys:\n')) + for key in sorted(metakeys): + ui.write((' %15s: %9i\n' % (key, metakeys[key]))) + + allclusters = list(set(clustersmap.values())) + allclusters.sort(key=lambda x: len(x[1])) + ui.write(('disconnected clusters: %9i\n' % len(allclusters))) + + ui.write(' any known node: %9i\n' + % len([c for c in allclusters + if [n for n in c[0] if nm.get(n) is not None]])) + if allclusters: + nbcluster = len(allclusters) + ui.write((' smallest length: %9i\n' % len(allclusters[0][1]))) + ui.write((' longer length: %9i\n' + % len(allclusters[-1][1]))) + median = len(allclusters[nbcluster // 2][1]) + ui.write((' median length: %9i\n' % median)) + mean = sum(len(x[1]) for x in allclusters) // nbcluster + ui.write((' mean length: %9i\n' % mean)) + allpclusters = list(set(pclustersmap.values())) + allpclusters.sort(key=lambda x: len(x[1])) + ui.write((' using parents data: %9i\n' % len(allpclusters))) + ui.write(' any known node: %9i\n' + % len([c for c in allclusters + if [n for n in c[0] if nm.get(n) is not None]])) + if allpclusters: + nbcluster = len(allpclusters) + ui.write((' smallest length: %9i\n' + % len(allpclusters[0][1]))) + ui.write((' longer length: %9i\n' + % len(allpclusters[-1][1]))) + median = len(allpclusters[nbcluster // 2][1]) + ui.write((' median length: %9i\n' % median)) + mean = sum(len(x[1]) for x in allpclusters) // nbcluster + ui.write((' mean length: %9i\n' % mean)) diff -r 90ab79764ce4 -r e200dbfb4515 hgext3rd/evolve/legacy.py --- a/hgext3rd/evolve/legacy.py Tue Mar 14 14:38:10 2017 -0700 +++ b/hgext3rd/evolve/legacy.py Fri Mar 31 15:39:20 2017 +0200 @@ -26,6 +26,7 @@ from mercurial import cmdutil from mercurial.i18n import _ +from mercurial import lock as lockmod from mercurial.node import bin, nullid from mercurial import util @@ -163,3 +164,40 @@ ui.status('%i obsolete marker converted\n' % cnt) if err: ui.write_err('%i conversion failed. check you graph!\n' % err) + +@command('debugrecordpruneparents', [], '') +def cmddebugrecordpruneparents(ui, repo): + """add parent data to prune markers when possible + + This command searches the repo for prune markers without parent information. + If the pruned node is locally known, it creates a new marker with parent + data. + """ + pgop = 'reading markers' + + # lock from the beginning to prevent race + wlock = lock = tr = None + try: + wlock = repo.wlock() + lock = repo.lock() + tr = repo.transaction('recordpruneparents') + unfi = repo.unfiltered() + nm = unfi.changelog.nodemap + store = repo.obsstore + pgtotal = len(store._all) + for idx, mark in enumerate(list(store._all)): + if not mark[1]: + rev = nm.get(mark[0]) + if rev is not None: + ctx = unfi[rev] + parents = tuple(p.node() for p in ctx.parents()) + before = len(store._all) + store.create(tr, mark[0], mark[1], mark[2], mark[3], + parents=parents) + if len(store._all) - before: + ui.write(_('created new markers for %i\n') % rev) + ui.progress(pgop, idx, total=pgtotal) + tr.close() + ui.progress(pgop, None) + finally: + lockmod.release(tr, lock, wlock) diff -r 90ab79764ce4 -r e200dbfb4515 hgext3rd/evolve/metadata.py --- a/hgext3rd/evolve/metadata.py Tue Mar 14 14:38:10 2017 -0700 +++ b/hgext3rd/evolve/metadata.py Fri Mar 31 15:39:20 2017 +0200 @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -__version__ = '5.6.0' +__version__ = '6.0.0.dev' testedwith = '3.8.4 3.9.2 4.0.2 4.1' minimumhgversion = '3.8' buglink = 'https://bz.mercurial-scm.org/' diff -r 90ab79764ce4 -r e200dbfb4515 hgext3rd/evolve/obsdiscovery.py --- a/hgext3rd/evolve/obsdiscovery.py Tue Mar 14 14:38:10 2017 -0700 +++ b/hgext3rd/evolve/obsdiscovery.py Fri Mar 31 15:39:20 2017 +0200 @@ -5,6 +5,14 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +# Status: Experiment in progress // open question +# +# The final discovery algorithm and protocol will go into core when we'll be +# happy with it. +# +# Some of the code in this module is for compatiblity with older version +# of evolve and will be eventually dropped. + from __future__ import absolute_import try: @@ -14,16 +22,13 @@ import io StringIO = io.StringIO -import collections import hashlib import heapq -import math +import sqlite3 import struct +import weakref from mercurial import ( - bundle2, - cmdutil, - commands, dagutil, error, exchange, @@ -42,12 +47,15 @@ from . import ( exthelper, utility, + stablerange, ) _pack = struct.pack _unpack = struct.unpack +_calcsize = struct.calcsize eh = exthelper.exthelper() +eh.merge(stablerange.eh) obsexcmsg = utility.obsexcmsg ########################################## @@ -145,32 +153,6 @@ ### Code performing discovery ### ################################## -def _canobshashrange(local, remote): - return (local.ui.configbool('experimental', 'obshashrange', False) - and remote.capable('_donotusemeever_evoext_obshashrange_1')) - - -def _obshashrange_capabilities(orig, repo, proto): - """wrapper to advertise new capability""" - caps = orig(repo, proto) - enabled = repo.ui.configbool('experimental', 'obshashrange', False) - if obsolete.isenabled(repo, obsolete.exchangeopt) and enabled: - caps = caps.split() - caps.append('_donotusemeever_evoext_obshashrange_1') - caps.sort() - caps = ' '.join(caps) - return caps - -@eh.extsetup -def obshashrange_extsetup(ui): - extensions.wrapfunction(wireproto, 'capabilities', _obshashrange_capabilities) - # wrap command content - oldcap, args = wireproto.commands['capabilities'] - - def newcap(repo, proto): - return _obshashrange_capabilities(oldcap, repo, proto) - wireproto.commands['capabilities'] = (newcap, args) - def findcommonobsmarkers(ui, local, remote, probeset, initialsamplesize=100, fullsamplesize=200): @@ -234,7 +216,10 @@ missing = set() heads = local.revs('heads(%ld)', probeset) + local.stablerange.warmup(local) + rangelength = local.stablerange.rangelength + subranges = local.stablerange.subranges # size of slice ? heappop = heapq.heappop heappush = heapq.heappush @@ -253,7 +238,7 @@ return True for h in heads: - entry = _range(local, h, 0) + entry = (h, 0) addentry(entry) querycount = 0 @@ -269,21 +254,23 @@ overflow = sample[samplesize:] sample = sample[:samplesize] elif len(sample) < samplesize: + ui.debug("query %i; add more sample (target %i, current %i)\n" + % (querycount, samplesize, len(sample))) # we need more sample ! needed = samplesize - len(sample) sliceme = [] heapify(sliceme) for entry in sample: - if 1 < len(entry): - heappush(sliceme, (-len(entry), entry)) + if 1 < rangelength(local, entry): + heappush(sliceme, (-rangelength(local, entry), entry)) while sliceme and 0 < needed: _key, target = heappop(sliceme) - for new in target.subranges(): + for new in subranges(local, target): # XXX we could record hierarchy to optimise drop - if addentry(entry): - if 1 < len(entry): - heappush(sliceme, (-len(entry), entry)) + if addentry(new): + if 1 < len(new): + heappush(sliceme, (-rangelength(local, new), new)) needed -= 1 if needed <= 0: break @@ -292,181 +279,37 @@ samplesize = fullsamplesize nbsample = len(sample) - maxsize = max([len(r) for r in sample]) + maxsize = max([rangelength(local, r) for r in sample]) ui.debug("query %i; sample size is %i, largest range %i\n" - % (querycount, maxsize, nbsample)) + % (querycount, nbsample, maxsize)) nbreplies = 0 replies = list(_queryrange(ui, local, remote, sample)) sample = [] + n = local.changelog.node for entry, remotehash in replies: nbreplies += 1 - if remotehash == entry.obshash: + if remotehash == _obshashrange(local, entry): continue - elif 1 == len(entry): - missing.add(entry.node) + elif 1 == rangelength(local, entry): + missing.add(n(entry[0])) else: - for new in entry.subranges(): + for new in subranges(local, entry): addentry(new) assert nbsample == nbreplies querycount += 1 ui.progress(_("comparing obsmarker with other"), querycount) ui.progress(_("comparing obsmarker with other"), None) + local.obsstore.rangeobshashcache.save(local) return sorted(missing) def _queryrange(ui, repo, remote, allentries): - mapping = {} - - def gen(): - for entry in allentries: - key = entry.node + _pack('>I', entry.index) - mapping[key] = entry - yield key - - bundler = bundle2.bundle20(ui, bundle2.bundle2caps(remote)) - capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo)) - bundler.newpart('replycaps', data=capsblob) - bundler.newpart('_donotusemeever_evoext_obshashrange_1', data=gen()) - - stream = util.chunkbuffer(bundler.getchunks()) - try: - reply = remote.unbundle( - stream, ['force'], remote.url()) - except error.BundleValueError as exc: - raise error.Abort(_('missing support for %s') % exc) - try: - op = bundle2.processbundle(repo, reply) - except error.BundleValueError as exc: - raise error.Abort(_('missing support for %s') % exc) - except bundle2.AbortFromPart as exc: - ui.status(_('remote: %s\n') % exc) - if exc.hint is not None: - ui.status(_('remote: %s\n') % ('(%s)' % exc.hint)) - raise error.Abort(_('push failed on remote')) - for rep in op.records['_donotusemeever_evoext_obshashrange_1']: - yield mapping[rep['key']], rep['value'] - - -@bundle2.parthandler('_donotusemeever_evoext_obshashrange_1', ()) -def _processqueryrange(op, inpart): - assert op.reply is not None - replies = [] - data = inpart.read(24) - while data: - n = data[:20] - index = _unpack('>I', data[20:])[0] - r = op.repo.changelog.rev(n) - rhash = _range(op.repo, r, index).obshash - replies.append(data + rhash) - data = inpart.read(24) - op.reply.newpart('reply:_donotusemeever_evoext_obshashrange_1', data=iter(replies)) - - -@bundle2.parthandler('reply:_donotusemeever_evoext_obshashrange_1', ()) -def _processqueryrangereply(op, inpart): - data = inpart.read(44) - while data: - key = data[:24] - rhash = data[24:] - op.records.add('_donotusemeever_evoext_obshashrange_1', {'key': key, 'value': rhash}) - data = inpart.read(44) - -################################## -### Stable topological sorting ### -################################## -@eh.command( - 'debugstablesort', - [ - ('', 'rev', [], 'heads to start from'), - ] + commands.formatteropts, - _('')) -def debugstablesort(ui, repo, **opts): - """display the ::REVS set topologically sorted in a stable way - """ - revs = scmutil.revrange(repo, opts['rev']) - displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True) - for r in _stablesort(repo, revs): - ctx = repo[r] - displayer.show(ctx) - displayer.flush(ctx) - displayer.close() - -def _stablesort(repo, revs): - """return '::revs' topologically sorted in "stable" order - - This is a depth first traversal starting from 'nullrev', using node as a - tie breaker. - """ - # Various notes: - # - # * Bitbucket is used dates as tie breaker, that might be a good idea. - # - # * It seemds we can traverse in the same order from (one) head to bottom, - # if we the following record data for each merge: - # - # - highest (stablesort-wise) common ancestors, - # - order of parents (tablesort-wise) - cl = repo.changelog - parents = cl.parentrevs - nullrev = node.nullrev - n = cl.node - # step 1: We need a parents -> children mapping for 2 reasons. - # - # * we build the order from nullrev to tip - # - # * we need to detect branching - children = collections.defaultdict(list) - for r in cl.ancestors(revs, inclusive=True): - p1, p2 = parents(r) - children[p1].append(r) - if p2 != nullrev: - children[p2].append(r) - # step two: walk back up - # * pick lowest node in case of branching - # * stack disregarded part of the branching - # * process merge when both parents are yielded - - # track what changeset has been - seen = [0] * (max(revs) + 2) - seen[-1] = True # nullrev is known - # starts from repository roots - # reuse the list form the mapping as we won't need it again anyway - stack = children[nullrev] - if not stack: - return [] - if 1 < len(stack): - stack.sort(key=n, reverse=True) - - # list of rev, maybe we should yield, but since we built a children mapping we are 'O(N)' already + # question are asked with node + n = repo.changelog.node + noderanges = [(n(entry[0]), entry[1]) for entry in allentries] + replies = remote.evoext_obshashrange_v0(noderanges) result = [] - - current = stack.pop() - while current is not None or stack: - if current is None: - # previous iteration reached a merge or an unready merge, - current = stack.pop() - if seen[current]: - current = None - continue - p1, p2 = parents(current) - if not (seen[p1] and seen[p2]): - # we can't iterate on this merge yet because other child is not - # yielded yet (and we are topo sorting) we can discard it for now - # because it will be reached from the other child. - current = None - continue - assert not seen[current] - seen[current] = True - result.append(current) # could be yield, cf earlier comment - cs = children[current] - if not cs: - current = None - elif 1 == len(cs): - current = cs[0] - else: - cs.sort(key=n, reverse=True) - current = cs.pop() # proceed on smallest - stack.extend(cs) # stack the rest for later - assert len(result) == len(set(result)) + for idx, entry in enumerate(allentries): + result.append((entry, replies[idx])) return result ############################## @@ -474,188 +317,330 @@ ############################## @eh.command( - 'debugstablerange', + 'debugobshashrange', [ - ('', 'rev', [], 'heads to start from'), + ('', 'rev', [], 'display obshash for all (rev, 0) range in REVS'), + ('', 'subranges', False, 'display all subranges'), ], _('')) -def debugstablerange(ui, repo, **opts): +def debugobshashrange(ui, repo, **opts): """display the ::REVS set topologically sorted in a stable way """ s = node.short revs = scmutil.revrange(repo, opts['rev']) # prewarm depth cache - for r in repo.revs("::%ld", revs): - utility.depth(repo, r) - toproceed = [_range(repo, r, 0, ) for r in revs] - ranges = set(toproceed) - while toproceed: - entry = toproceed.pop() - for r in entry.subranges(): - if r not in ranges: - ranges.add(r) - toproceed.append(r) - ranges = list(ranges) - ranges.sort(key=lambda r: (-len(r), r.node)) - ui.status('rev node index size depth obshash\n') + if revs: + repo.stablerange.warmup(repo, max(revs)) + cl = repo.changelog + rangelength = repo.stablerange.rangelength + depthrev = repo.stablerange.depthrev + if opts['subranges']: + ranges = stablerange.subrangesclosure(repo, revs) + else: + ranges = [(r, 0) for r in revs] + headers = ('rev', 'node', 'index', 'size', 'depth', 'obshash') + linetemplate = '%12d %12s %12d %12d %12d %12s\n' + headertemplate = linetemplate.replace('d', 's') + ui.status(headertemplate % headers) for r in ranges: - d = (r.head, s(r.node), r.index, len(r), r.depth, node.short(r.obshash)) - ui.status('%3d %s %5d %4d %5d %s\n' % d) + d = (r[0], + s(cl.node(r[0])), + r[1], + rangelength(repo, r), + depthrev(repo, r[0]), + node.short(_obshashrange(repo, r))) + ui.status(linetemplate % d) + repo.obsstore.rangeobshashcache.save(repo) -def _hlp2(i): - """return highest power of two lower than 'i'""" - return 2 ** int(math.log(i - 1, 2)) +def _obshashrange(repo, rangeid): + """return the obsolete hash associated to a range""" + cache = repo.obsstore.rangeobshashcache + cl = repo.changelog + obshash = cache.get(rangeid) + if obshash is not None: + return obshash + pieces = [] + nullid = node.nullid + if repo.stablerange.rangelength(repo, rangeid) == 1: + rangenode = cl.node(rangeid[0]) + tmarkers = repo.obsstore.relevantmarkers([rangenode]) + pieces = [] + for m in tmarkers: + mbin = obsolete._fm1encodeonemarker(m) + pieces.append(mbin) + pieces.sort() + else: + for subrange in repo.stablerange.subranges(repo, rangeid): + obshash = _obshashrange(repo, subrange) + if obshash != nullid: + pieces.append(obshash) -class _range(object): + sha = hashlib.sha1() + # note: if there is only one subrange with actual data, we'll just + # reuse the same hash. + if not pieces: + obshash = node.nullid + elif len(pieces) != 1 or obshash is None: + sha = hashlib.sha1() + for p in pieces: + sha.update(p) + obshash = sha.digest() + cache[rangeid] = obshash + return obshash + +### sqlite caching - def __init__(self, repo, head, index, revs=None): - self._repo = repo.unfiltered() - self.head = head - self.index = index - if revs is not None: - assert len(revs) == len(self) - self._revs = revs - assert index < self.depth, (head, index, self.depth, revs) +_sqliteschema = [ + """CREATE TABLE meta(schemaversion INTEGER NOT NULL, + nbobsmarker INTEGER NOT NULL, + obstipdata BLOB NOT NULL, + tiprev INTEGER NOT NULL, + tipnode BLOB NOT NULL + );""", + """CREATE TABLE obshashrange(rev INTEGER NOT NULL, + idx INTEGER NOT NULL, + obshash BLOB NOT NULL, + PRIMARY KEY(rev, idx));""", + "CREATE INDEX range_index ON obshashrange(rev, idx);", +] +_queryexist = "SELECT name FROM sqlite_master WHERE type='table' AND name='meta';" +_newmeta = """INSERT INTO meta (schemaversion, nbobsmarker, obstipdata, tiprev, tipnode) + VALUES (?,?,?,?,?);""" +_updateobshash = "INSERT INTO obshashrange(rev, idx, obshash) VALUES (?,?,?);" +_querymeta = "SELECT schemaversion, nbobsmarker, obstipdata, tiprev, tipnode FROM meta;" +_queryobshash = "SELECT obshash FROM obshashrange WHERE (rev = ? AND idx = ?);" - def __repr__(self): - return '%s %d %d %s' % (node.short(self.node), self.depth, self.index, node.short(self.obshash)) +class _obshashcache(dict): - def __hash__(self): - return self._id + _schemaversion = 0 - def __eq__(self, other): - if type(self) != type(other): - raise NotImplementedError() - return self.stablekey == other.stablekey + def __init__(self, repo): + super(_obshashcache, self).__init__() + self._path = repo.vfs.join('cache/evoext_obshashrange_v0.sqlite') + self._new = set() + self._valid = True + self._repo = weakref.ref(repo.unfiltered()) + # cache status + self._ondiskcachekey = None - @util.propertycache - def _id(self): - return hash(self.stablekey) + def clear(self): + self._valid = False + super(_obshashcache, self).clear() + self._new.clear() - @util.propertycache - def stablekey(self): - return (self.node, self.index) + def get(self, rangeid): + value = super(_obshashcache, self).get(rangeid) + if value is None and self._con is not None: + nrange = (rangeid[0], rangeid[1]) + obshash = self._con.execute(_queryobshash, nrange).fetchone() + if obshash is not None: + value = obshash[0] + return value - @util.propertycache - def node(self): - return self._repo.changelog.node(self.head) + def __setitem__(self, rangeid, obshash): + self._new.add(rangeid) + super(_obshashcache, self).__setitem__(rangeid, obshash) - def __len__(self): - return self.depth - self.index - - @util.propertycache - def depth(self): - return utility.depth(self._repo, self.head) + def _cachekey(self, repo): + # XXX for now the cache is very volatile, but this is still a win + nbobsmarker = len(repo.obsstore._all) + if nbobsmarker: + tipdata = obsolete._fm1encodeonemarker(repo.obsstore._all[-1]) + else: + tipdata = node.nullid + tiprev = len(repo.changelog) - 1 + tipnode = repo.changelog.node(tiprev) + return (self._schemaversion, nbobsmarker, tipdata, tiprev, tipnode) @util.propertycache - def _revs(self): - r = _stablesort(self._repo, [self.head])[self.index:] - assert len(r) == len(self), (self.head, self.index, len(r), len(self)) - return r - - def _slicesat(self, globalindex): - localindex = globalindex - self.index - - cl = self._repo.changelog - - result = [] - bottom = self._revs[:localindex] - top = _range(self._repo, self.head, globalindex, self._revs[localindex:]) - # - toprootdepth = utility.depth(self._repo, top._revs[0]) - if toprootdepth + len(top) == self.depth + 1: - bheads = [bottom[-1]] - else: - bheads = set(bottom) - parentrevs = cl.parentrevs - du = bheads.difference_update - for r in bottom: - du(parentrevs(r)) - # if len(bheads) == 1: - # assert 1 == len(self._repo.revs('roots(%ld)', top._revs)) - if len(bheads) == 1: - newhead = bottom[-1] - bottomdepth = utility.depth(self._repo, newhead) - newstart = bottomdepth - len(bottom) - result.append(_range(self._repo, newhead, newstart, bottom)) - else: - # assert 1 < len(bheads), (toprootdepth, len(top), len(self)) - cl = self._repo.changelog - for h in bheads: - subset = cl.ancestors([h], inclusive=True) - hrevs = [r for r in bottom if r in subset] - start = utility.depth(self._repo, h) - len(hrevs) - entry = _range(self._repo, h, start, [r for r in bottom if r in subset]) - result.append(entry) - result.append(top) - return result + def _con(self): + if not self._valid: + return None + repo = self._repo() + if repo is None: + return None + cachekey = self._cachekey(repo) + con = sqlite3.connect(self._path) + con.text_factory = str + cur = con.execute(_queryexist) + if cur.fetchone() is None: + self._valid = False + return None + meta = con.execute(_querymeta).fetchone() + if meta != cachekey: + self._valid = False + return None + self._ondiskcachekey = meta + return con - def subranges(self): - if not util.safehasattr(self._repo, '_subrangecache'): - self._repo._subrangecache = {} - cached = self._repo._subrangecache.get(self) - if cached is not None: - return cached - if len(self) == 1: - return [] - step = _hlp2(self.depth) - standard_start = 0 - while standard_start < self.index and 0 < step: - if standard_start + step < self.depth: - standard_start += step - step //= 2 - if self.index == standard_start: - slicesize = _hlp2(len(self)) - slicepoint = self.index + slicesize - else: - assert standard_start < self.depth - slicepoint = standard_start - result = self._slicesat(slicepoint) - self._repo._subrangecache[self] = result - return result + def save(self, repo): + repo = repo.unfiltered() + try: + if not self._new: + return + with repo.lock(): + self._save(repo) + except error.LockError: + # Exceptionnally we are noisy about it since performance impact + # is large We should address that before using this more + # widely. + msg = _('obshashrange cache: skipping save unable to lock repo\n') + repo.ui.warn(msg) + + def _save(self, repo): + if self._con is None: + util.unlinkpath(self._path, ignoremissing=True) + if '_con' in vars(self): + del self._con - @util.propertycache - def obshash(self): - cache = self._repo.obsstore.rangeobshashcache - obshash = cache.get(self) - if obshash is not None: - return obshash - pieces = [] - nullid = node.nullid - if len(self) == 1: - tmarkers = self._repo.obsstore.relevantmarkers([self.node]) - pieces = [] - for m in tmarkers: - mbin = obsolete._fm1encodeonemarker(m) - pieces.append(mbin) - pieces.sort() + con = sqlite3.connect(self._path) + con.text_factory = str + with con: + for req in _sqliteschema: + con.execute(req) + + con.execute(_newmeta, self._cachekey(repo)) else: - for subrange in self.subranges(): - obshash = subrange.obshash - if obshash != nullid: - pieces.append(obshash) - - sha = hashlib.sha1() - # note: if there is only one subrange with actual data, we'll just - # reuse the same hash. - if not pieces: - obshash = node.nullid - elif len(pieces) != 1 or obshash is None: - sha = hashlib.sha1() - for p in pieces: - sha.update(p) - obshash = cache[self] = sha.digest() - return obshash + con = self._con + if self._ondiskcachekey is not None: + meta = con.execute(_querymeta).fetchone() + if meta != self._ondiskcachekey: + # drifting is currently an issue because this means another + # process might have already added the cache line we are about + # to add. This will confuse sqlite + msg = _('obshashrange cache: skipping write, ' + 'database drifted under my feet\n') + data = (meta[2], meta[1], self._ondisktiprev, self._ondisktipnode) + repo.ui.warn(msg) + data = ((rangeid[0], rangeid[1], self[rangeid]) for rangeid in self._new) + con.executemany(_updateobshash, data) + cachekey = self._cachekey(repo) + con.execute(_newmeta, cachekey) + con.commit() + self._new.clear() + self._ondiskcachekey = cachekey @eh.wrapfunction(obsolete.obsstore, '_addmarkers') def _addmarkers(orig, obsstore, *args, **kwargs): obsstore.rangeobshashcache.clear() return orig(obsstore, *args, **kwargs) -@eh.addattr(obsolete.obsstore, 'rangeobshashcache') -@util.propertycache -def rangeobshashcache(obsstore): - return {} +try: + obsstorefilecache = localrepo.localrepository.obsstore +except AttributeError: + # XXX hg-3.8 compat + # + # mercurial 3.8 has issue with accessing file cache property from their + # cache. This is fix by 36fbd72c2f39fef8ad52d7c559906c2bc388760c in core + # and shipped in 3.9 + obsstorefilecache = localrepo.localrepository.__dict__['obsstore'] + + +# obsstore is a filecache so we have do to some spacial dancing +@eh.wrapfunction(obsstorefilecache, 'func') +def obsstorewithcache(orig, repo): + obsstore = orig(repo) + obsstore.rangeobshashcache = _obshashcache(repo.unfiltered()) + return obsstore + +@eh.reposetup +def setupcache(ui, repo): + + class obshashrepo(repo.__class__): + @localrepo.unfilteredmethod + def destroyed(self): + if 'stablerange' in vars(self): + del self.stablerange + + repo.__class__ = obshashrepo + +### wire protocol commands + +def _obshashrange_v0(repo, ranges): + """return a list of hash from a list of range + + The range have the id encoded as a node + + return 'wdirid' for unknown range""" + nm = repo.changelog.nodemap + ranges = [(nm.get(n), idx) for n, idx in ranges] + if ranges: + maxrev = max(r for r, i in ranges) + if maxrev is not None: + repo.stablerange.warmup(repo, upto=maxrev) + result = [] + for r in ranges: + if r[0] is None: + result.append(node.wdirid) + else: + result.append(_obshashrange(repo, r)) + repo.obsstore.rangeobshashcache.save(repo) + return result + +@eh.addattr(localrepo.localpeer, 'evoext_obshashrange_v0') +def local_obshashrange_v0(peer, ranges): + return _obshashrange_v0(peer._repo, ranges) + + +_indexformat = '>I' +_indexsize = _calcsize(_indexformat) +def _encrange(node_rangeid): + """encode a (node) range""" + headnode, index = node_rangeid + return headnode + _pack(_indexformat, index) + +def _decrange(data): + """encode a (node) range""" + assert _indexsize < len(data), len(data) + headnode = data[:-_indexsize] + index = _unpack(_indexformat, data[-_indexsize:])[0] + return (headnode, index) + +@eh.addattr(wireproto.wirepeer, 'evoext_obshashrange_v0') +def peer_obshashrange_v0(self, ranges): + binranges = [_encrange(r) for r in ranges] + encranges = wireproto.encodelist(binranges) + d = self._call("evoext_obshashrange_v0", ranges=encranges) + try: + return wireproto.decodelist(d) + except ValueError: + self._abort(error.ResponseError(_("unexpected response:"), d)) + +def srv_obshashrange_v0(repo, proto, ranges): + ranges = wireproto.decodelist(ranges) + ranges = [_decrange(r) for r in ranges] + hashes = _obshashrange_v0(repo, ranges) + return wireproto.encodelist(hashes) + + +def _canobshashrange(local, remote): + return (local.ui.configbool('experimental', 'obshashrange', False) + and remote.capable('_evoext_obshashrange_v0')) + +def _obshashrange_capabilities(orig, repo, proto): + """wrapper to advertise new capability""" + caps = orig(repo, proto) + enabled = repo.ui.configbool('experimental', 'obshashrange', False) + if obsolete.isenabled(repo, obsolete.exchangeopt) and enabled: + caps = caps.split() + caps.append('_evoext_obshashrange_v0') + caps.sort() + caps = ' '.join(caps) + return caps + +@eh.extsetup +def obshashrange_extsetup(ui): + hgweb_mod.perms['evoext_obshashrange_v0'] = 'pull' + + wireproto.commands['evoext_obshashrange_v0'] = (srv_obshashrange_v0, 'ranges') + ### + extensions.wrapfunction(wireproto, 'capabilities', _obshashrange_capabilities) + # wrap command content + oldcap, args = wireproto.commands['capabilities'] + + def newcap(repo, proto): + return _obshashrange_capabilities(oldcap, repo, proto) + wireproto.commands['capabilities'] = (newcap, args) ############################# ### Tree Hash computation ### diff -r 90ab79764ce4 -r e200dbfb4515 hgext3rd/evolve/obsexchange.py --- a/hgext3rd/evolve/obsexchange.py Tue Mar 14 14:38:10 2017 -0700 +++ b/hgext3rd/evolve/obsexchange.py Fri Mar 31 15:39:20 2017 +0200 @@ -34,7 +34,6 @@ from . import ( exthelper, - serveronly, utility, obsdiscovery, ) @@ -338,7 +337,7 @@ @eh.addattr(localrepo.localpeer, 'evoext_pushobsmarkers_0') def local_pushobsmarkers(peer, obsfile): data = obsfile.read() - serveronly._pushobsmarkers(peer._repo, data) + _pushobsmarkers(peer._repo, data) # compat-code: _pullobsolete # @@ -417,8 +416,8 @@ @eh.addattr(localrepo.localpeer, 'evoext_pullobsmarkers_0') def local_pullobsmarkers(self, heads=None, common=None): - return serveronly._getobsmarkersstream(self._repo, heads=heads, - common=common) + return _getobsmarkersstream(self._repo, heads=heads, + common=common) def _legacypush_capabilities(orig, repo, proto): """wrapper to advertise new capability""" diff -r 90ab79764ce4 -r e200dbfb4515 hgext3rd/evolve/stablerange.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext3rd/evolve/stablerange.py Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,907 @@ +# Code dedicated to the computation and properties of "stable ranges" +# +# These stable ranges are use for obsolescence markers discovery +# +# Copyright 2017 Pierre-Yves David +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +import collections +import heapq +import math +import sqlite3 +import weakref + +from mercurial import ( + commands, + cmdutil, + error, + localrepo, + node as nodemod, + scmutil, + util, +) + +from mercurial.i18n import _ + +from . import ( + exthelper, +) + +eh = exthelper.exthelper() + +################################## +### Stable topological sorting ### +################################## +@eh.command( + 'debugstablesort', + [ + ('', 'rev', [], 'heads to start from'), + ] + commands.formatteropts, + _('')) +def debugstablesort(ui, repo, **opts): + """display the ::REVS set topologically sorted in a stable way + """ + revs = scmutil.revrange(repo, opts['rev']) + displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True) + for r in stablesort(repo, revs): + ctx = repo[r] + displayer.show(ctx) + displayer.flush(ctx) + displayer.close() + +def stablesort(repo, revs, mergecallback=None): + """return '::revs' topologically sorted in "stable" order + + This is a depth first traversal starting from 'nullrev', using node as a + tie breaker. + """ + # Various notes: + # + # * Bitbucket is used dates as tie breaker, that might be a good idea. + # + # * It seemds we can traverse in the same order from (one) head to bottom, + # if we the following record data for each merge: + # + # - highest (stablesort-wise) common ancestors, + # - order of parents (tablesort-wise) + cl = repo.changelog + parents = cl.parentrevs + nullrev = nodemod.nullrev + n = cl.node + # step 1: We need a parents -> children mapping for 2 reasons. + # + # * we build the order from nullrev to tip + # + # * we need to detect branching + children = collections.defaultdict(list) + for r in cl.ancestors(revs, inclusive=True): + p1, p2 = parents(r) + children[p1].append(r) + if p2 != nullrev: + children[p2].append(r) + # step two: walk back up + # * pick lowest node in case of branching + # * stack disregarded part of the branching + # * process merge when both parents are yielded + + # track what changeset has been + seen = [0] * (max(revs) + 2) + seen[-1] = True # nullrev is known + # starts from repository roots + # reuse the list form the mapping as we won't need it again anyway + stack = children[nullrev] + if not stack: + return [] + if 1 < len(stack): + stack.sort(key=n, reverse=True) + + # list of rev, maybe we should yield, but since we built a children mapping we are 'O(N)' already + result = [] + + current = stack.pop() + while current is not None or stack: + if current is None: + # previous iteration reached a merge or an unready merge, + current = stack.pop() + if seen[current]: + current = None + continue + p1, p2 = parents(current) + if not (seen[p1] and seen[p2]): + # we can't iterate on this merge yet because other child is not + # yielded yet (and we are topo sorting) we can discard it for now + # because it will be reached from the other child. + current = None + continue + assert not seen[current] + seen[current] = True + result.append(current) # could be yield, cf earlier comment + if mergecallback is not None and p2 != nullrev: + mergecallback(result, current) + cs = children[current] + if not cs: + current = None + elif 1 == len(cs): + current = cs[0] + else: + cs.sort(key=n, reverse=True) + current = cs.pop() # proceed on smallest + stack.extend(cs) # stack the rest for later + assert len(result) == len(set(result)) + return result + +################################# +### Stable Range computation ### +################################# + +def _hlp2(i): + """return highest power of two lower than 'i'""" + return 2 ** int(math.log(i - 1, 2)) + +def subrangesclosure(repo, heads): + """set of all standard subrange under heads + + This is intended for debug purposes. Range are returned from largest to + smallest in terms of number of revision it contains.""" + subranges = repo.stablerange.subranges + toproceed = [(r, 0, ) for r in heads] + ranges = set(toproceed) + while toproceed: + entry = toproceed.pop() + for r in subranges(repo, entry): + if r not in ranges: + ranges.add(r) + toproceed.append(r) + ranges = list(ranges) + n = repo.changelog.node + rangelength = repo.stablerange.rangelength + ranges.sort(key=lambda r: (-rangelength(repo, r), n(r[0]))) + return ranges + +@eh.command( + 'debugstablerange', + [ + ('', 'rev', [], 'operate on (rev, 0) ranges for rev in REVS'), + ('', 'subranges', False, 'recursively display data for subranges too'), + ('', 'verify', False, 'checks subranges content (EXPENSIVE)'), + ], + _('')) +def debugstablerange(ui, repo, **opts): + """display standard stable subrange for a set of ranges + + Range as displayed as '- (, , )', use + --verbose to get the extra details in (). + """ + short = nodemod.short + revs = scmutil.revrange(repo, opts['rev']) + # prewarm depth cache + unfi = repo.unfiltered() + node = unfi.changelog.node + stablerange = unfi.stablerange + depth = stablerange.depthrev + length = stablerange.rangelength + subranges = stablerange.subranges + repo.stablerange.warmup(repo, max(revs)) + if opts['subranges']: + ranges = subrangesclosure(repo, revs) + else: + ranges = [(r, 0) for r in revs] + if ui.verbose: + template = '%s-%d (%d, %d, %d)' + + def _rangestring(repo, rangeid): + return template % ( + short(node(rangeid[0])), + rangeid[1], + rangeid[0], + depth(unfi, rangeid[0]), + length(unfi, rangeid) + ) + else: + template = '%s-%d' + + def _rangestring(repo, rangeid): + return template % ( + short(node(rangeid[0])), + rangeid[1], + ) + + for r in ranges: + subs = subranges(unfi, r) + subsstr = ', '.join(_rangestring(unfi, s) for s in subs) + rstr = _rangestring(unfi, r) + if opts['verify']: + status = 'leaf' + if 1 < length(unfi, r): + status = 'complete' + revs = set(stablerange.revsfromrange(unfi, r)) + subrevs = set() + for s in subs: + subrevs.update(stablerange.revsfromrange(unfi, s)) + if revs != subrevs: + status = 'missing' + ui.status('%s [%s] - %s\n' % (rstr, status, subsstr)) + else: + ui.status('%s - %s\n' % (rstr, subsstr)) + +class stablerange(object): + + def __init__(self): + # The point up to which we have data in cache + self._tiprev = None + self._tipnode = None + # cache the 'depth' of a changeset, the size of '::rev' + self._depthcache = {} + # cache the standard stable subranges or a range + self._subrangescache = {} + # To slices merge, we need to walk their descendant in reverse stable + # sort order. For now we perform a full stable sort their descendant + # and then use the relevant top most part. This order is going to be + # the same for all ranges headed at the same merge. So we cache these + # value to reuse them accross the same invocation. + self._stablesortcache = {} + # something useful to compute the above + # mergerev -> stablesort, length + self._stablesortprepared = {} + # caching parent call # as we do so many of them + self._parentscache = {} + # The first part of the stable sorted list of revision of a merge will + # shared with the one of others. This means we can reuse subranges + # computed from that point to compute some of the subranges from the + # merge. + self._inheritancecache = {} + + def warmup(self, repo, upto=None): + """warm the cache up""" + repo = repo.unfiltered() + cl = repo.changelog + # subrange should be warmed from head to range to be able to benefit + # from revsfromrange cache. otherwise each merge will trigger its own + # stablesort. + # + # we use the revnumber as an approximation for depth + ui = repo.ui + + if upto is None: + upto = len(cl) - 1 + if self._tiprev is None: + revs = cl.revs(stop=upto) + nbrevs = upto + 1 + else: + assert cl.node(self._tiprev) == self._tipnode + if upto <= self._tiprev: + return + revs = cl.revs(start=self._tiprev + 1, stop=upto) + nbrevs = upto - self._tiprev + rangeheap = [] + for idx, r in enumerate(revs): + if not idx % 1000: + ui.progress(_("filling depth cache"), idx, total=nbrevs) + # warm up depth + self.depthrev(repo, r) + rangeheap.append((-r, (r, 0))) + ui.progress(_("filling depth cache"), None, total=nbrevs) + + heappop = heapq.heappop + heappush = heapq.heappush + heapify = heapq.heapify + + original = set(rangeheap) + seen = 0 + heapify(rangeheap) + while rangeheap: + value = heappop(rangeheap) + if value in original: + if not seen % 1000: + ui.progress(_("filling stablerange cache"), seen, total=nbrevs) + seen += 1 + original.remove(value) # might have been added from other source + __, rangeid = value + if self._getsub(rangeid) is None: + for sub in self.subranges(repo, rangeid): + if self._getsub(sub) is None: + heappush(rangeheap, (-sub[0], sub)) + ui.progress(_("filling stablerange cache"), None, total=nbrevs) + + self._tiprev = upto + self._tipnode = cl.node(upto) + + def depthrev(self, repo, rev): + repo = repo.unfiltered() + cl = repo.changelog + depth = self._getdepth + nullrev = nodemod.nullrev + stack = [rev] + while stack: + revdepth = None + current = stack[-1] + revdepth = depth(current) + if revdepth is not None: + stack.pop() + continue + p1, p2 = self._parents(current, cl.parentrevs) + if p1 == nullrev: + # root case + revdepth = 1 + elif p2 == nullrev: + # linear commit case + parentdepth = depth(p1) + if parentdepth is None: + stack.append(p1) + else: + revdepth = parentdepth + 1 + else: + # merge case + revdepth = self._depthmerge(cl, current, p1, p2, stack) + if revdepth is not None: + self._setdepth(current, revdepth) + stack.pop() + # actual_depth = len(list(cl.ancestors([rev], inclusive=True))) + # assert revdepth == actual_depth, (rev, revdepth, actual_depth) + return revdepth + + def rangelength(self, repo, rangeid): + headrev, index = rangeid[0], rangeid[1] + return self.depthrev(repo, headrev) - index + + def subranges(self, repo, rangeid): + cached = self._getsub(rangeid) + if cached is not None: + return cached + value = self._subranges(repo, rangeid) + self._setsub(rangeid, value) + return value + + def revsfromrange(self, repo, rangeid): + headrev, index = rangeid + rangelength = self.rangelength(repo, rangeid) + if rangelength == 1: + revs = [headrev] + else: + # get all revs under heads in stable order + # + # note: In the general case we can just walk down and then request + # data about the merge. But I'm not sure this function will be even + # call for the general case. + allrevs = self._stablesortcache.get(headrev) + if allrevs is None: + allrevs = self._getrevsfrommerge(repo, headrev) + if allrevs is None: + allrevs = stablesort(repo, [headrev], + mergecallback=self._filestablesortcache) + self._stablesortcache[headrev] = allrevs + # takes from index + revs = allrevs[index:] + # sanity checks + assert len(revs) == rangelength + return revs + + def _parents(self, rev, func): + parents = self._parentscache.get(rev) + if parents is None: + parents = func(rev) + self._parentscache[rev] = parents + return parents + + def _getdepth(self, rev): + """utility function used to access the depth cache + + This mostly exist to help the on disk persistence.""" + return self._depthcache.get(rev) + + def _setdepth(self, rev, value): + """utility function used to set the depth cache + + This mostly exist to help the on disk persistence.""" + self._depthcache[rev] = value + + def _getsub(self, rev): + """utility function used to access the subranges cache + + This mostly exist to help the on disk persistence""" + return self._subrangescache.get(rev) + + def _setsub(self, rev, value): + """utility function used to set the subranges cache + + This mostly exist to help the on disk persistence.""" + self._subrangescache[rev] = value + + def _filestablesortcache(self, sortedrevs, merge): + if merge not in self._stablesortprepared: + self._stablesortprepared[merge] = (sortedrevs, len(sortedrevs)) + + def _getrevsfrommerge(self, repo, merge): + prepared = self._stablesortprepared.get(merge) + if prepared is None: + return None + + mergedepth = self.depthrev(repo, merge) + allrevs = prepared[0][:prepared[1]] + nbextrarevs = prepared[1] - mergedepth + if not nbextrarevs: + return allrevs + + anc = repo.changelog.ancestors([merge], inclusive=True) + top = [] + counter = nbextrarevs + for rev in reversed(allrevs): + if rev in anc: + top.append(rev) + else: + counter -= 1 + if counter <= 0: + break + + bottomidx = prepared[1] - (nbextrarevs + len(top)) + revs = allrevs[:bottomidx] + revs.extend(reversed(top)) + return revs + + def _inheritancepoint(self, repo, merge): + """Find the inheritance point of a Merge + + The first part of the stable sorted list of revision of a merge will shared with + the one of others. This means we can reuse subranges computed from that point to + compute some of the subranges from the merge. + + That point is latest point in the stable sorted list where the depth of the + revisions match its index (that means all revision earlier in the stable sorted + list are its ancestors, no dangling unrelated branches exists). + """ + value = self._inheritancecache.get(merge) + if value is None: + revs = self.revsfromrange(repo, (merge, 0)) + i = reversed(revs) + i.next() # pop the merge + expected = len(revs) - 1 + # Since we do warmup properly, we can expect the cache to be hot + # for everythin under the merge we investigate + cache = self._depthcache + # note: we cannot do a binary search because element under the + # inherited point might have mismatching depth because of inner + # branching. + for rev in i: + if cache[rev] == expected: + break + expected -= 1 + value = (expected - 1, rev) + self._inheritancecache[merge] = value + return value + + def _depthmerge(self, cl, rev, p1, p2, stack): + # sub method to simplify the main 'depthrev' one + revdepth = None + depth = self._getdepth + depth_p1 = depth(p1) + depth_p2 = depth(p2) + missingparent = False + if depth_p1 is None: + stack.append(p1) + missingparent = True + if depth_p2 is None: + stack.append(p2) + missingparent = True + if missingparent: + return None + # computin depth of a merge + # XXX the common ancestors heads could be cached + ancnodes = cl.commonancestorsheads(cl.node(p1), cl.node(p2)) + ancrevs = [cl.rev(a) for a in ancnodes] + anyunkown = False + ancdepth = [] + for r in ancrevs: + d = depth(r) + if d is None: + anyunkown = True + stack.append(r) + ancdepth.append((r, d)) + if anyunkown: + return None + if not ancrevs: + # unrelated branch, (no common root) + revdepth = depth_p1 + depth_p2 + 1 + elif len(ancrevs) == 1: + # one unique branch point: + # we can compute depth without any walk + depth_anc = ancdepth[0][1] + revdepth = depth_p1 + (depth_p2 - depth_anc) + 1 + else: + # multiple ancestors, we pick one that is + # * the deepest (less changeset outside of it), + # * lowest revs because more chance to have descendant of other "above" + anc, revdepth = max(ancdepth, key=lambda x: (x[1], -x[0])) + revdepth += len(cl.findmissingrevs(common=[anc], heads=[rev])) + return revdepth + + def _subranges(self, repo, rangeid): + if self.rangelength(repo, rangeid) == 1: + return [] + slicepoint = self._slicepoint(repo, rangeid) + + # make sure we have cache for all relevant parent first to prevent + # recursion (python is bad with recursion + stack = [] + current = rangeid + while current is not None: + current = self._cold_reusable(repo, current, slicepoint) + if current is not None: + stack.append(current) + while stack: + # these call will directly compute the subranges + self.subranges(repo, stack.pop()) + return self._slicesrangeat(repo, rangeid, slicepoint) + + def _cold_reusable(self, repo, rangeid, slicepoint): + """return parent range that it would be useful to prepare to slice + rangeid at slicepoint + + This function also have the important task to update the revscache of + the parent rev s if possible and needed""" + p1, p2 = self._parents(rangeid[0], repo.changelog.parentrevs) + if p2 == nodemod.nullrev: + # regular changesets, we pick the parent + reusablerev = p1 + else: + # merge, we try the inheritance point + # if it is too low, it will be ditched by the depth check anyway + index, reusablerev = self._inheritancepoint(repo, rangeid[0]) + + # if we reached the slicepoint, no need to go further + if self.depthrev(repo, reusablerev) <= slicepoint: + return None + + reurange = (reusablerev, rangeid[1]) + # if we have an entry for the current range, lets update the cache + # if we already have subrange for this range, no need to prepare it. + if self._getsub(reurange) is not None: + return None + + # look like we found a relevent parentrange with no cache yet + return reurange + + def _slicepoint(self, repo, rangeid): + rangedepth = self.depthrev(repo, rangeid[0]) + step = _hlp2(rangedepth) + standard_start = 0 + while standard_start < rangeid[1] and 0 < step: + if standard_start + step < rangedepth: + standard_start += step + step //= 2 + if rangeid[1] == standard_start: + slicesize = _hlp2(self.rangelength(repo, rangeid)) + slicepoint = rangeid[1] + slicesize + else: + assert standard_start < rangedepth + slicepoint = standard_start + return slicepoint + + def _slicesrangeat(self, repo, rangeid, globalindex): + p1, p2 = self._parents(rangeid[0], repo.changelog.parentrevs) + if p2 == nodemod.nullrev: + reuserev = p1 + else: + index, reuserev = self._inheritancepoint(repo, rangeid[0]) + if index < globalindex: + return self._slicesrangeatmerge(repo, rangeid, globalindex) + + assert reuserev != nodemod.nullrev + + reuserange = (reuserev, rangeid[1]) + top = (rangeid[0], globalindex) + + if rangeid[1] + self.rangelength(repo, reuserange) == globalindex: + return [reuserange, top] + # This will not initiate a recursion since we took appropriate + # precaution in the caller of this method to ensure it will be so. + # It the parent is a merge that will not be the case but computing + # subranges from a merge will not recurse. + reusesubranges = self.subranges(repo, reuserange) + slices = reusesubranges[:-1] # pop the top + slices.append(top) + return slices + + def _slicesrangeatmerge(self, repo, rangeid, globalindex): + localindex = globalindex - rangeid[1] + cl = repo.changelog + + result = [] + allrevs = self.revsfromrange(repo, rangeid) + bottomrevs = allrevs[:localindex] + + if globalindex == self.depthrev(repo, bottomrevs[-1]): + # simple case, top revision in the bottom set contains exactly the + # revision we needs + result.append((bottomrevs[-1], rangeid[1])) + else: + parentrevs = cl.parentrevs + parents = self._parents + bheads = set(bottomrevs) + du = bheads.difference_update + reachableroots = repo.changelog.reachableroots + minrev = min(bottomrevs) + for r in bottomrevs: + du(parents(r, parentrevs)) + for h in bheads: + # reachable roots is fast because is C + # + # It is worth noting that will use this kind of filtering from + # "h" multiple time in a warming run. So using "ancestors" and + # caching that should be faster. But python code filtering on + # the ancestors end up being slower. + hrevs = reachableroots(minrev, [h], bottomrevs, True) + start = self.depthrev(repo, h) - len(hrevs) + entry = (h, start) + result.append(entry) + + # Talking about python code being slow, the following code is an + # alternative implementation. + # + # It complexity is better since is does a single traversal on the + # bottomset. However since it is all python it end up being + # slower. + # I'm keeping it here as an inspiration for a future C version + # branches = [] + # for current in reversed(bottomrevs): + # ps = parents(current, parentrevs) + # found = False + # for brevs, bexpect in branches: + # if current in bexpect: + # found = True + # brevs.append(current) + # bexpect.discard(current) + # bexpect.update(ps) + # if not found: + # branches.append(([current], set(ps))) + # for revs, __ in reversed(branches): + # head = revs[0] + # index = self.depthrev(repo, head) - len(revs) + # result.append((head, index)) + + # top part is trivial + top = (rangeid[0], globalindex) + result.append(top) + return result + +############################# +### simple sqlite caching ### +############################# + +_sqliteschema = [ + """CREATE TABLE meta(schemaversion INTEGER NOT NULL, + tiprev INTEGER NOT NULL, + tipnode BLOB NOT NULL + );""", + "CREATE TABLE depth(rev INTEGER NOT NULL PRIMARY KEY, depth INTEGER NOT NULL);", + """CREATE TABLE range(rev INTEGER NOT NULL, + idx INTEGER NOT NULL, + PRIMARY KEY(rev, idx));""", + """CREATE TABLE subranges(listidx INTEGER NOT NULL, + suprev INTEGER NOT NULL, + supidx INTEGER NOT NULL, + subrev INTEGER NOT NULL, + subidx INTEGER NOT NULL, + PRIMARY KEY(listidx, suprev, supidx), + FOREIGN KEY (suprev, supidx) REFERENCES range(rev, idx), + FOREIGN KEY (subrev, subidx) REFERENCES range(rev, idx) + );""", + "CREATE INDEX subranges_index ON subranges (suprev, supidx);", + "CREATE INDEX range_index ON range (rev, idx);", + "CREATE INDEX depth_index ON depth (rev);" +] +_newmeta = "INSERT INTO meta (schemaversion, tiprev, tipnode) VALUES (?,?,?);" +_updatemeta = "UPDATE meta SET tiprev = ?, tipnode = ?;" +_updatedepth = "INSERT INTO depth(rev, depth) VALUES (?,?);" +_updaterange = "INSERT INTO range(rev, idx) VALUES (?,?);" +_updatesubranges = """INSERT + INTO subranges(listidx, suprev, supidx, subrev, subidx) + VALUES (?,?,?,?,?);""" +_queryexist = "SELECT name FROM sqlite_master WHERE type='table' AND name='meta';" +_querymeta = "SELECT schemaversion, tiprev, tipnode FROM meta;" +_querydepth = "SELECT depth FROM depth WHERE rev = ?;" +_batchdepth = "SELECT rev, depth FROM depth;" +_queryrange = "SELECT * FROM range WHERE (rev = ? AND idx = ?);" +_querysubranges = """SELECT subrev, subidx + FROM subranges + WHERE (suprev = ? AND supidx = ?) + ORDER BY listidx;""" + +class sqlstablerange(stablerange): + + _schemaversion = 0 + + def __init__(self, repo): + super(sqlstablerange, self).__init__() + self._path = repo.vfs.join('cache/evoext_stablerange_v0.sqlite') + self._cl = repo.unfiltered().changelog # (okay to keep an old one) + self._ondisktiprev = None + self._ondisktipnode = None + self._unsaveddepth = {} + self._unsavedsubranges = {} + self._fulldepth = False + + def warmup(self, repo, upto=None): + self._con # make sure the data base is loaded + try: + # samelessly lock the repo to ensure nobody will update the repo + # concurently. This should not be too much of an issue if we warm + # at the end of the transaction. + # + # XXX However, we lock even if we are up to date so we should check + # before locking + with repo.lock(): + super(sqlstablerange, self).warmup(repo, upto) + self._save(repo) + except error.LockError: + # Exceptionnally we are noisy about it since performance impact is + # large We should address that before using this more widely. + repo.ui.warn('stable-range cache: unable to lock repo while warming\n') + repo.ui.warn('(cache will not be saved)\n') + super(sqlstablerange, self).warmup(repo, upto) + + def _getdepth(self, rev): + cache = self._depthcache + if rev not in cache and rev <= self._ondisktiprev and self._con is not None: + value = None + result = self._con.execute(_querydepth, (rev,)).fetchone() + if result is not None: + value = result[0] + # in memory caching of the value + cache[rev] = value + return cache.get(rev) + + def _setdepth(self, rev, depth): + assert rev not in self._unsaveddepth + self._unsaveddepth[rev] = depth + super(sqlstablerange, self)._setdepth(rev, depth) + + def _getsub(self, rangeid): + cache = self._subrangescache + if rangeid not in cache and rangeid[0] <= self._ondisktiprev and self._con is not None: + value = None + result = self._con.execute(_queryrange, rangeid).fetchone() + if result is not None: # database know about this node (skip in the future?) + value = self._con.execute(_querysubranges, rangeid).fetchall() + # in memory caching of the value + cache[rangeid] = value + return cache.get(rangeid) + + def _setsub(self, rangeid, value): + assert rangeid not in self._unsavedsubranges + self._unsavedsubranges[rangeid] = value + super(sqlstablerange, self)._setsub(rangeid, value) + + def _inheritancepoint(self, *args, **kwargs): + self._loaddepth() + return super(sqlstablerange, self)._inheritancepoint(*args, **kwargs) + + @util.propertycache + def _con(self): + con = sqlite3.connect(self._path) + con.text_factory = str + cur = con.execute(_queryexist) + if cur.fetchone() is None: + return None + meta = con.execute(_querymeta).fetchone() + if meta is None: + return None + if meta[0] != self._schemaversion: + return None + if len(self._cl) <= meta[1]: + return None + if self._cl.node(meta[1]) != meta[2]: + return None + self._ondisktiprev = meta[1] + self._ondisktipnode = meta[2] + if self._tiprev < self._ondisktiprev: + self._tiprev = self._ondisktiprev + self._tipnode = self._ondisktipnode + return con + + def _save(self, repo): + repo = repo.unfiltered() + if not (self._unsavedsubranges or self._unsaveddepth): + return # no new data + + if self._con is None: + util.unlinkpath(self._path, ignoremissing=True) + if '_con' in vars(self): + del self._con + + con = sqlite3.connect(self._path) + con.text_factory = str + with con: + for req in _sqliteschema: + con.execute(req) + + meta = [self._schemaversion, + self._tiprev, + self._tipnode, + ] + con.execute(_newmeta, meta) + else: + con = self._con + meta = con.execute(_querymeta).fetchone() + if meta[2] != self._ondisktipnode or meta[1] != self._ondisktiprev: + # drifting is currently an issue because this means another + # process might have already added the cache line we are about + # to add. This will confuse sqlite + msg = _('stable-range cache: skipping write, ' + 'database drifted under my feet\n') + hint = _('(disk: %s-%s vs mem: %s%s)\n') + data = (meta[2], meta[1], self._ondisktiprev, self._ondisktipnode) + repo.ui.warn(msg) + repo.ui.warn(hint % data) + return + meta = [self._tiprev, + self._tipnode, + ] + con.execute(_updatemeta, meta) + + self._savedepth(con, repo) + self._saverange(con, repo) + con.commit() + self._ondisktiprev = self._tiprev + self._ondisktipnode = self._tipnode + self._unsaveddepth.clear() + self._unsavedsubranges.clear() + + def _savedepth(self, con, repo): + repo = repo.unfiltered() + data = self._unsaveddepth.items() + con.executemany(_updatedepth, data) + + def _loaddepth(self): + """batch load all data about depth""" + if not (self._fulldepth or self._con is None): + result = self._con.execute(_batchdepth) + self._depthcache.update(result.fetchall()) + self._fulldepth = True + + def _saverange(self, con, repo): + repo = repo.unfiltered() + data = [] + allranges = set() + for key, value in self._unsavedsubranges.items(): + allranges.add(key) + for idx, sub in enumerate(value): + data.append((idx, key[0], key[1], sub[0], sub[1])) + + con.executemany(_updaterange, allranges) + con.executemany(_updatesubranges, data) + + +@eh.reposetup +def setupcache(ui, repo): + + class stablerangerepo(repo.__class__): + + @localrepo.unfilteredpropertycache + def stablerange(self): + return sqlstablerange(repo) + + @localrepo.unfilteredmethod + def destroyed(self): + if 'stablerange' in vars(self): + del self.stablerange + + def transaction(self, *args, **kwargs): + tr = super(stablerangerepo, self).transaction(*args, **kwargs) + if not repo.ui.configbool('experimental', 'obshashrange', False): + return tr + reporef = weakref.ref(self) + + def _warmcache(tr): + repo = reporef() + if repo is None: + return + if 'node' in tr.hookargs: + # new nodes ! + repo.stablerange.warmup(repo) + + tr.addpostclose('warmcache-stablerange', _warmcache) + return tr + + repo.__class__ = stablerangerepo diff -r 90ab79764ce4 -r e200dbfb4515 hgext3rd/evolve/utility.py --- a/hgext3rd/evolve/utility.py Tue Mar 14 14:38:10 2017 -0700 +++ b/hgext3rd/evolve/utility.py Fri Mar 31 15:39:20 2017 +0200 @@ -4,7 +4,6 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from mercurial import node def obsexcmsg(ui, message, important=False): verbose = ui.configbool('experimental', 'verbose-obsolescence-exchange', @@ -19,36 +18,3 @@ if ui.configbool('experimental', 'verbose-obsolescence-exchange', False): topic = 'OBSEXC' ui.progress(topic, *args, **kwargs) - -_depthcache = {} -def depth(repo, rev): - cl = repo.changelog - n = cl.node(rev) - revdepth = _depthcache.get(n, None) - if revdepth is None: - p1, p2 = cl.parentrevs(rev) - if p1 == node.nullrev: - revdepth = 1 - elif p2 == node.nullrev: - revdepth = depth(repo, p1) + 1 - else: - ancs = cl.commonancestorsheads(cl.node(p1), cl.node(p2)) - depth_p1 = depth(repo, p1) - depth_p2 = depth(repo, p2) - if not ancs: - revdepth = depth_p1 + depth_p2 + 1 - elif len(ancs) == 1: - anc = cl.rev(ancs[0]) - revdepth = depth_anc = depth(repo, anc) - revdepth += depth_p1 - depth_anc - revdepth += depth_p2 - depth_anc - revdepth += 1 - else: - # multiple ancestors, we pick the highest and search all missing bits - anc = max(cl.rev(a) for a in ancs) - revdepth = depth(repo, anc) - revdepth += len(cl.findmissingrevs(common=[anc], heads=[rev])) - _depthcache[n] = revdepth - # actual_depth = len(list(cl.ancestors([rev], inclusive=True))) - # assert revdepth == actual_depth, (rev, revdepth, actual_depth) - return revdepth diff -r 90ab79764ce4 -r e200dbfb4515 tests/_exc-util.sh --- a/tests/_exc-util.sh Tue Mar 14 14:38:10 2017 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -#!/bin/sh - -cat >> $HGRCPATH <> $HGRCPATH - -mkcommit() { - echo "$1" > "$1" - hg add "$1" - hg ci -m "$1" -} -getid() { - hg log --hidden --template '{node}\n' --rev "$1" -} - -setuprepos() { - echo creating test repo for test case $1 - mkdir $1 - cd $1 - echo - pulldest - hg init pushdest - cd pushdest - mkcommit O - hg phase --public . - cd .. - echo - main - hg clone -q pushdest main - echo - pushdest - hg clone -q main pulldest - echo 'cd into `main` and proceed with env setup' -} - -dotest() { -# dotest TESTNAME [TARGETNODE] - - testcase=$1 - shift - target="$1" - if [ $# -gt 0 ]; then - shift - fi - targetnode="" - desccall="" - cd $testcase - echo "## Running testcase $testcase" - if [ -n "$target" ]; then - desccall="desc("\'"$target"\'")" - targetnode="`hg -R main id -qr \"$desccall\"`" - echo "# testing echange of \"$target\" ($targetnode)" - fi - echo "## initial state" - echo "# obstore: main" - hg -R main debugobsolete | sort - echo "# obstore: pushdest" - hg -R pushdest debugobsolete | sort - echo "# obstore: pulldest" - hg -R pulldest debugobsolete | sort - - if [ -n "$target" ]; then - echo "## pushing \"$target\"" from main to pushdest - hg -R main push -r "$desccall" $@ pushdest - else - echo "## pushing from main to pushdest" - hg -R main push pushdest $@ - fi - echo "## post push state" - echo "# obstore: main" - hg -R main debugobsolete | sort - echo "# obstore: pushdest" - hg -R pushdest debugobsolete | sort - echo "# obstore: pulldest" - hg -R pulldest debugobsolete | sort - if [ -n "$target" ]; then - echo "## pulling \"$targetnode\"" from main into pulldest - hg -R pulldest pull -r $targetnode $@ main - else - echo "## pulling from main into pulldest" - hg -R pulldest pull main $@ - fi - echo "## post pull state" - echo "# obstore: main" - hg -R main debugobsolete | sort - echo "# obstore: pushdest" - hg -R pushdest debugobsolete | sort - echo "# obstore: pulldest" - hg -R pulldest debugobsolete | sort - - cd .. - -} diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-check-flake8.t --- a/tests/test-check-flake8.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-check-flake8.t Fri Mar 31 15:39:20 2017 +0200 @@ -14,5 +14,5 @@ run flake8 if it exists; if it doesn't, then just skip - $ hg files -0 'set:(**.py or grep("^!#.*python")) - removed()' 2>/dev/null \ + $ hg files -0 'set:(**.py or grep("^#!.*python")) - removed()' 2>/dev/null \ > | xargs -0 flake8 diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-check-pyflakes.t --- a/tests/test-check-pyflakes.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-check-pyflakes.t Fri Mar 31 15:39:20 2017 +0200 @@ -7,5 +7,5 @@ run pyflakes on all tracked files ending in .py or without a file ending (skipping binary file random-seed) - $ hg locate 'set:(**.py or grep("^!#.*python")) - removed()' 2>/dev/null \ + $ hg locate 'set:(**.py or grep("^#!.*python")) - removed()' 2>/dev/null \ > | xargs pyflakes 2>/dev/null diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-check-setup-manifest.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-check-setup-manifest.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,19 @@ +#require test-repo + + $ checkcm() { + > if ! (which check-manifest > /dev/null); then + > echo skipped: missing tool: check-manifest; + > exit 80; + > fi; + > }; + $ checkcm + $ cat << EOF >> $HGRCPATH + > [experimental] + > evolution=all + > EOF + +Run check manifest: + + $ cd $TESTDIR/.. + $ check-manifest + lists of files in version control and sdist match diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-partial-C1.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-partial-C1.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,79 @@ +==================================== +Testing head checking code: Case C-1 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category C: checking case were the branch is only partially obsoleted. +TestCase 1: 2 changeset branch, only the head is rewritten + +.. old-state: +.. +.. * 2 changeset branch +.. +.. new-state: +.. +.. * 1 new changesets branches superceeding only the head of the old one +.. * base of the old branch is still alive +.. +.. expected-result: +.. +.. * push denied +.. +.. graph-summary: +.. +.. B ø⇠◔ B' +.. | | +.. A ○ | +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B1 + created new head + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + $ hg log -G --hidden + @ 25c56d33e4c4 (draft): B1 + | + | x d73caddc5533 (draft): B0 + | | + | o 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head 25c56d33e4c4! + (merge or see 'hg help push' for details about pushing new heads) + [255] diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-partial-C2.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-partial-C2.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,79 @@ +==================================== +Testing head checking code: Case C-2 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category C: checking case were the branch is only partially obsoleted. +TestCase 2: 2 changeset branch, only the base is rewritten + +.. old-state: +.. +.. * 2 changeset branch +.. +.. new-state: +.. +.. * 1 new changesets branches superceeding only the base of the old one +.. * The old branch is still alive (base is obsolete, head is alive) +.. +.. expected-result: +.. +.. * push denied +.. +.. graph-summary: +.. +.. B ○ +.. | +.. A ø⇠◔ A' +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg log -G --hidden + @ f6082bc4ffef (draft): A1 + | + | o d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push --rev 'desc(A1)' + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head f6082bc4ffef! + (merge or see 'hg help push' for details about pushing new heads) + [255] diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-partial-C3.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-partial-C3.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,78 @@ +==================================== +Testing head checking code: Case C-3 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category C: checking case were the branch is only partially obsoleted. +TestCase 3: 2 changeset branch, only the head is pruned + +.. old-state: +.. +.. * 2 changeset branch +.. +.. new-state: +.. +.. * old head is pruned +.. * 1 new unrelated branch +.. +.. expected-result: +.. +.. * push denied +.. +.. graph-summary: +.. +.. B ⊗ +.. | +.. A ◔ ◔ C +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit C0 + created new head + $ hg debugobsolete --record-parents `getid "desc(B0)"` + $ hg log -G --hidden + @ 0f88766e02d6 (draft): C0 + | + | x d73caddc5533 (draft): B0 + | | + | o 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head 0f88766e02d6! + (merge or see 'hg help push' for details about pushing new heads) + [255] diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-partial-C4.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-partial-C4.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,78 @@ +==================================== +Testing head checking code: Case C-4 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category C: checking case were the branch is only partially obsoleted. +TestCase 4: 2 changeset branch, only the base is pruned + +.. old-state: +.. +.. * 2 changeset branch +.. +.. new-state: +.. +.. * old base is pruned +.. * 1 new unrelated branch +.. +.. expected-result: +.. +.. * push denied +.. +.. graph-summary: +.. +.. B ◔ +.. | +.. A ⊗ ◔ C +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit C0 + created new head + $ hg debugobsolete --record-parents `getid "desc(A0)"` + $ hg log -G --hidden + @ 0f88766e02d6 (draft): C0 + | + | o d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push --rev 'desc(C0)' + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head 0f88766e02d6! + (merge or see 'hg help push' for details about pushing new heads) + [255] diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-pruned-B1.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-pruned-B1.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,71 @@ +==================================== +Testing head checking code: Case B-1 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category B: checking simple case involving pruned changesets +TestCase 1: single pruned changeset + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * old branch is pruned +.. * 1 new unrelated branch +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. ◔ B +.. | +.. A ⊗ | +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B0 + created new head + $ hg debugobsolete --record-parents `getid "desc(A0)"` + $ hg log -G --hidden + @ 74ff5441d343 (draft): B0 + | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 1 new obsolescence markers + + + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-pruned-B2.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-pruned-B2.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,82 @@ +==================================== +Testing head checking code: Case B-2 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category B: checking simple case involving pruned changesets +TestCase 2: multi-changeset branch, head is pruned, rest is superceeded + +.. old-state: +.. +.. * 2 changeset branch +.. +.. new-state: +.. +.. * old head is pruned +.. * 1 new branch succeeding to the other changeset in the old branch +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. B ⊗ +.. | +.. A ø⇠◔ A' +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete --record-parents `getid "desc(B0)"` + $ hg log -G --hidden + @ f6082bc4ffef (draft): A1 + | + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 2 new obsolescence markers + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-pruned-B3.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-pruned-B3.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,82 @@ +==================================== +Testing head checking code: Case B-3 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category B: checking simple case involving pruned changesets +TestCase 3: multi-changeset branch, other is pruned, rest is superceeded + +.. old-state: +.. +.. * 2 changeset branch +.. +.. new-state: +.. +.. * old head is superceeded +.. * old other is pruned +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. B ø⇠◔ B' +.. | | +.. A ⊗ | +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B1 + created new head + $ hg debugobsolete --record-parents `getid "desc(A0)"` + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + $ hg log -G --hidden + @ 25c56d33e4c4 (draft): B1 + | + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 2 new obsolescence markers + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-pruned-B4.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-pruned-B4.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,83 @@ +==================================== +Testing head checking code: Case B-4 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category B: checking simple case involving pruned changesets +TestCase 4: multi-changeset branch, all are pruned + +.. old-state: +.. +.. * 2 changeset branch +.. +.. new-state: +.. +.. * old branch is pruned +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. B ⊗ +.. | +.. A ⊗ +.. | +.. | ◔ C +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit C0 + created new head + $ hg debugobsolete --record-parents `getid "desc(A0)"` + $ hg debugobsolete --record-parents `getid "desc(B0)"` + $ hg log -G --hidden + @ 0f88766e02d6 (draft): C0 + | + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 2 new obsolescence markers + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-pruned-B5.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-pruned-B5.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,89 @@ +==================================== +Testing head checking code: Case B-5 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category B: checking simple case involving pruned changesets +TestCase 5: multi-changeset branch, mix of pruned and superceeded + +.. old-state: +.. +.. * 3 changeset branch +.. +.. new-state: +.. +.. * old head is pruned +.. * old mid is superceeded +.. * old root is pruned +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. B ⊗ +.. | +.. A ø⇠◔ A' +.. | | +.. B ⊗ | +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ mkcommit C0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B1 + created new head + $ hg debugobsolete --record-parents `getid "desc(A0)"` + $ hg debugobsolete `getid "desc(B0)"` `getid "desc(B1)"` + $ hg debugobsolete --record-parents `getid "desc(C0)"` + $ hg log -G --hidden + @ 25c56d33e4c4 (draft): B1 + | + | x 821fb21d0dd2 (draft): C0 + | | + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 3 new obsolescence markers + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-pruned-B6.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-pruned-B6.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,74 @@ +==================================== +Testing head checking code: Case B-6 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category B: checking simple case involving pruned changesets +TestCase 6: single changesets, pruned then superseeded (on a new changeset) + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * old branch is rewritten onto another one, +.. * the new version is then pruned. +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. A ø⇠⊗ A' +.. | | +.. | ◔ B +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B0 + created new head + $ mkcommit A1 + $ hg up 'desc(B0)' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg debugobsolete `getid "desc(A0)"` `getid "desc(A1)"` + $ hg debugobsolete --record-parents `getid "desc(A1)"` + $ hg log -G --hidden + x ba93660aff8d (draft): A1 + | + @ 74ff5441d343 (draft): B0 + | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 2 new obsolescence markers diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-pruned-B7.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-pruned-B7.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,73 @@ +==================================== +Testing head checking code: Case B-7 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category B: checking simple case involving pruned changesets +TestCase 7: single changesets, pruned then superseeded (on an existing changeset) + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * old branch is rewritten onto the common set, +.. * the new version is then pruned. +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. A ø⇠⊗ A' +.. B ◔ | | +.. \|/ +.. ● + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B0 + created new head + $ mkcommit A1 + $ hg up 'desc(B0)' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg debugobsolete `getid "desc(A0)"` `getid "desc(A1)"` + $ hg debugobsolete --record-parents `getid "desc(A1)"` + $ hg log -G --hidden + x ba93660aff8d (draft): A1 + | + @ 74ff5441d343 (draft): B0 + | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 2 new obsolescence markers diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-pruned-B8.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-pruned-B8.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,95 @@ +==================================== +Testing head checking code: Case B-2 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category B: checking simple case involving pruned changesets +TestCase 2: multi-changeset branch, head is pruned, rest is superceeded, through other + +.. old-state: +.. +.. * 2 changeset branch +.. +.. new-state: +.. +.. * old head is rewritten then pruned +.. * 1 new branch succeeding to the other changeset in the old branch (through another obsolete branch) +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. B ø⇠⊗ B' +.. | | A' +.. A ø⇠ø⇠◔ A'' +.. |/ / +.. | / +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ mkcommit B1 + $ hg up 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit A2 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + $ hg debugobsolete --record-parents `getid "desc(B1)"` + $ hg debugobsolete `getid "desc(A1)" ` `getid "desc(A2)"` + $ hg log -G --hidden + @ c1f8d089020f (draft): A2 + | + | x 262c8c798096 (draft): B1 + | | + | x f6082bc4ffef (draft): A1 + |/ + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 4 new obsolescence markers + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-superceed-A1.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-superceed-A1.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,68 @@ +==================================== +Testing head checking code: Case A-1 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category A: checking simple case invoving a branch being superceeded by another. +TestCase 1: single-changeset branch + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * 1 changeset branch succeeding to A +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. A ø⇠◔ A' +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg log -G --hidden + @ f6082bc4ffef (draft): A1 + | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 1 new obsolescence markers + + + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-superceed-A2.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-superceed-A2.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,84 @@ +==================================== +Testing head checking code: Case A-2 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category A: checking simple case invoving a branch being superceeded by another. +TestCase 2: multi-changeset branch + +.. old-state: +.. +.. * 2 changeset branch +.. +.. new-state: +.. +.. * 2 changeset branch succeeding the old one +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. B ø⇠◔ B' +.. | | +.. A ø⇠◔ A' +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ mkcommit B1 + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + $ hg log -G --hidden + @ 262c8c798096 (draft): B1 + | + o f6082bc4ffef (draft): A1 + | + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + 2 new obsolescence markers diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-superceed-A3.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-superceed-A3.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,87 @@ +==================================== +Testing head checking code: Case A-3 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category A: checking simple case invoving a branch being superceeded by another. +TestCase 3: multi-changeset branch with reordering + +Push should be allowed +.. old-state: +.. +.. * 2 changeset branch +.. +.. new-state: +.. +.. * 2 changeset branch succeeding the old one with reordering +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. B ø⇠⇠ +.. | ⇡ +.. A ø⇠⇠⇠○ A' +.. | ⇡/ +.. | ○ B' +.. |/ +.. ● O + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B1 + created new head + $ mkcommit A1 + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + $ hg log -G --hidden + @ c1c7524e9488 (draft): A1 + | + o 25c56d33e4c4 (draft): B1 + | + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + 2 new obsolescence markers diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-superceed-A4.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-superceed-A4.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,74 @@ +==================================== +Testing head checking code: Case A-4 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category A: checking simple case invoving a branch being superceeded by another. +TestCase 4: New changeset as children of the successor + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * 2 changeset branch, first is a successor, but head is new +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. ◔ B +.. | +.. A ø⇠◔ A' +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ mkcommit B0 + $ hg log -G --hidden + @ f40ded968333 (draft): B0 + | + o f6082bc4ffef (draft): A1 + | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + 1 new obsolescence markers + + + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-superceed-A5.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-superceed-A5.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,74 @@ +==================================== +Testing head checking code: Case A-5 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category A: checking simple case invoving a branch being superceeded by another. +TestCase 5: New changeset as parent of the successor + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * 2 changeset branch, head is a successor, but other is new +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. A ø⇠◔ A' +.. | | +.. | ◔ B +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B0 + created new head + $ mkcommit A1 + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg log -G --hidden + @ ba93660aff8d (draft): A1 + | + o 74ff5441d343 (draft): B0 + | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + 1 new obsolescence markers + + + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-superceed-A6.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-superceed-A6.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,95 @@ +==================================== +Testing head checking code: Case A-6 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category A: checking simple case invoving a branch being superceeded by another. +TestCase 6: multi-changeset branch, split on multiple other, (base on its own branch) + +.. old-state: +.. +.. * 2 branch (1 changeset, and 2 changesets) +.. +.. new-state: +.. +.. * 1 new branch superceeding the base of the old-2-changesets-branch, +.. * 1 new changesets on the old-1-changeset-branch superceeding the head of the other +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. B'◔⇢ø B +.. | | +.. A | ø⇠◔ A' +.. | |/ +.. C ● | +.. \| +.. ● + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ hg up 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit C0 + created new head + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + (run 'hg heads' to see heads, 'hg merge' to merge) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ hg up 'desc(C0)' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B1 + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + $ hg log -G --hidden + @ d70a1f75a020 (draft): B1 + | + | o f6082bc4ffef (draft): A1 + | | + o | 0f88766e02d6 (draft): C0 + |/ + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + 2 new obsolescence markers diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-superceed-A7.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-superceed-A7.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,95 @@ +==================================== +Testing head checking code: Case A-7 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category A: checking simple case invoving a branch being superceeded by another. +TestCase 7: multi-changeset branch, split on multiple other, (head on its own branch) + +.. old-state: +.. +.. * 2 branch (1 changeset, and 2 changesets) +.. +.. new-state: +.. +.. * 1 new branch superceeding the head of the old-2-changesets-branch, +.. * 1 new changesets on the old-1-changeset-branch superceeding the base of the other +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. B ø⇠◔ B' +.. | | +.. A'◔⇢ø | +.. | |/ +.. C ● | +.. \| +.. ● + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ hg up 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit C0 + created new head + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + (run 'hg heads' to see heads, 'hg merge' to merge) + $ hg up 'desc(C0)' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + $ hg up 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit B1 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + $ hg log -G --hidden + @ 25c56d33e4c4 (draft): B1 + | + | o a0802eb7fc1b (draft): A1 + | | + | o 0f88766e02d6 (draft): C0 + |/ + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + 2 new obsolescence markers diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-superceed-A8.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-superceed-A8.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,78 @@ +==================================== +Testing head checking code: Case A-8 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category A: checking simple case invoving a branch being superceeded by another. +TestCase 8: single-changeset branch indirect rewrite + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * 1 changeset branch succeeding to A, through another unpushed changesets +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. A' +.. A ø⇠ø⇠◔ A'' +.. |/ / +.. | / +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A2 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete `getid "desc(A1)" ` `getid "desc(A2)"` + $ hg log -G --hidden + @ c1f8d089020f (draft): A2 + | + | x f6082bc4ffef (draft): A1 + |/ + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 2 new obsolescence markers + + + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-unpushed-D1.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-unpushed-D1.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,75 @@ +==================================== +Testing head checking code: Case D-1 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category D: remote head is "obs-affected" locally, but result is not part of the push. +TestCase 1: remote head is rewritten, but successors is not part of the push + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * 1 changeset branch succeeding the old branch +.. * 1 new unrelated branch +.. +.. expected-result: +.. +.. * pushing only the unrelated branch: denied +.. +.. graph-summary: +.. +.. A ø⇠○ A' +.. |/ +.. | ◔ B +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B0 + created new head + $ hg log -G --hidden + @ 74ff5441d343 (draft): B0 + | + | o f6082bc4ffef (draft): A1 + |/ + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push -r 'desc(B0)' + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head 74ff5441d343! + (merge or see 'hg help push' for details about pushing new heads) + [255] + + + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-unpushed-D2.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-unpushed-D2.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,90 @@ +==================================== +Testing head checking code: Case D-2 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category D: remote head is "obs-affected" locally, but result is not part of the push. +TestCase 1: remote branch has 2 changes, head is pruned, second is rewritten but result is not pushed + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * old head is pruned +.. * 1 new branch succeeding to the other changeset in the old branch +.. * 1 new unrelated branch +.. +.. expected-result: +.. +.. * push allowed +.. * pushing only the unrelated branch: denied +.. +.. graph-summary: +.. +.. B ⊗ +.. | +.. A ø⇠○ A' +.. |/ +.. | ◔ C +.. |/ +.. ○ + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete --record-parents `getid "desc(B0)"` + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit C0 + created new head + $ hg log -G --hidden + @ 0f88766e02d6 (draft): C0 + | + | o f6082bc4ffef (draft): A1 + |/ + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push --rev 'desc(C0)' + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head 0f88766e02d6! + (merge or see 'hg help push' for details about pushing new heads) + [255] + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-unpushed-D3.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-unpushed-D3.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,107 @@ +==================================== +Testing head checking code: Case D-3 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category D: remote head is "obs-affected" locally, but result is not part of the push. +TestCase 3: multi-changeset branch, split on multiple new others, only one of them is pushed + +.. old-state: +.. +.. * 2 changesets branch +.. +.. new-state: +.. +.. * 2 new branches, each superseding one changeset in the old one. +.. +.. expected-result: +.. +.. * pushing only one of the resulting branch (either of them) +.. * push denied +.. +.. graph-summary: +.. +.. B'◔⇢ø B +.. | | +.. A | ø⇠◔ A' +.. | |/ +.. \| +.. ● + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ hg up 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ hg up '0' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B1 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + $ hg log -G --hidden + @ 25c56d33e4c4 (draft): B1 + | + | o f6082bc4ffef (draft): A1 + |/ + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push --rev 'desc(A1)' + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head f6082bc4ffef! + (merge or see 'hg help push' for details about pushing new heads) + [255] + $ hg push --rev 'desc(B1)' + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head 25c56d33e4c4! + (merge or see 'hg help push' for details about pushing new heads) + [255] + +Extra testing +------------- + +In this case, even a bare push is creating more heads + + $ hg push + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head 25c56d33e4c4! + (merge or see 'hg help push' for details about pushing new heads) + [255] diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-unpushed-D4.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-unpushed-D4.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,102 @@ +==================================== +Testing head checking code: Case D-4 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category D: remote head is "obs-affected" locally, but result is not part of the push. +TestCase 4: multi-changeset branch, split on multiple other, (base on its own new branch) + +.. old-state: +.. +.. * 2 branch (1 changeset, and 2 changesets) +.. +.. new-state: +.. +.. * 1 new branch superceeding the base of the old-2-changesets-branch, +.. * 1 new changesets on the old-1-changeset-branch superceeding the head of the other +.. +.. expected-result: +.. +.. * push the new branch only -> push denied +.. * push the existing branch only -> push allowed +.. +.. graph-summary: +.. +.. B'◔⇢ø B +.. | | +.. A | ø⇠◔ A' +.. | |/ +.. C ● | +.. \| +.. ● + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ hg up 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit C0 + created new head + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + (run 'hg heads' to see heads, 'hg merge' to merge) + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + created new head + $ hg up 'desc(C0)' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B1 + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + $ hg log -G --hidden + @ d70a1f75a020 (draft): B1 + | + | o f6082bc4ffef (draft): A1 + | | + o | 0f88766e02d6 (draft): C0 + |/ + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push --rev 'desc(A1)' + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head f6082bc4ffef! + (merge or see 'hg help push' for details about pushing new heads) + [255] + $ hg push --rev 'desc(B1)' + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + 1 new obsolescence markers diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-unpushed-D5.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-unpushed-D5.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,104 @@ +==================================== +Testing head checking code: Case D-5 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category D: remote head is "obs-affected" locally, but result is not part of the push. +TestCase 5: multi-changeset branch, split on multiple other, (head on its own new branch) + +.. old-state: +.. +.. * 2 branch (1 changeset, and 2 changesets) +.. +.. new-state: +.. +.. * 1 new branch superceeding the head of the old-2-changesets-branch, +.. * 1 new changesets on the old-1-changeset-branch superceeding the base of the other +.. +.. expected-result: +.. +.. * push the new branch only -> push denied +.. * push the existing branch only -> push allowed +.. /!\ This push create unstability/orphaning on the other hand and we should +.. probably detect/warn agains that. +.. +.. graph-summary: +.. +.. B ø⇠◔ B' +.. | | +.. A'◔⇢ø | +.. | |/ +.. C ● | +.. \| +.. ● + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd server + $ mkcommit B0 + $ hg up 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit C0 + created new head + $ cd ../client + $ hg pull + pulling from $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + (run 'hg heads' to see heads, 'hg merge' to merge) + $ hg up 'desc(C0)' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit A1 + $ hg up 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit B1 + created new head + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + $ hg log -G --hidden + @ 25c56d33e4c4 (draft): B1 + | + | o a0802eb7fc1b (draft): A1 + | | + | o 0f88766e02d6 (draft): C0 + |/ + | x d73caddc5533 (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + + +Actual testing +-------------- + + $ hg push --rev 'desc(B1)' + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head 25c56d33e4c4! + (merge or see 'hg help push' for details about pushing new heads) + [255] + $ hg push --rev 'desc(A1)' + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + 1 new obsolescence markers diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-unpushed-D6.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-unpushed-D6.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,78 @@ +==================================== +Testing head checking code: Case D-6 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category D: remote head is "obs-affected" locally, but result is not part of the push. +TestCase 6: single changesets, superseeded then pruned (on a new changeset unpushed) changeset + +This is a partial push variation of B6 + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * old branch is rewritten onto another one, +.. * the new version is then pruned. +.. +.. expected-result: +.. +.. * push denied +.. +.. graph-summary: +.. +.. A ø⇠⊗ A' +.. | | +.. C ◔ | ◔ B +.. \|/ +.. ● + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B0 + created new head + $ mkcommit A1 + $ hg up '0' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit C0 + created new head + $ hg debugobsolete `getid "desc(A0)"` `getid "desc(A1)"` + $ hg debugobsolete --record-parents `getid "desc(A1)"` + $ hg log -G --hidden + @ 0f88766e02d6 (draft): C0 + | + | x ba93660aff8d (draft): A1 + | | + | o 74ff5441d343 (draft): B0 + |/ + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push --rev 'desc(C0)' + pushing to $TESTTMP/server + searching for changes + abort: push creates new remote head 0f88766e02d6! + (merge or see 'hg help push' for details about pushing new heads) + [255] diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-checkheads-unpushed-D7.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-checkheads-unpushed-D7.t Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,92 @@ +==================================== +Testing head checking code: Case D-7 +==================================== + +Mercurial checks for the introduction of multiple heads on push. Evolution +comes into play to detect if existing heads on the server are being replaced by +some of the new heads we push. + +This test file is part of a series of tests checking this behavior. + +Category D: remote head is "obs-affected" locally, but result is not part of the push. +TestCase 7: single changesets, superseeded multiple time then pruned (on a new changeset unpushed) changeset + +This is a partial push variation of B6 + +.. old-state: +.. +.. * 1 changeset branch +.. +.. new-state: +.. +.. * old branch is rewritten onto another one, +.. * The rewriting it again rewritten on the root +.. * the new version is then pruned. +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. A' +.. A ø⇠ø⇠⊗ A'' +.. | | | +.. C ◔ | ◔ | B +.. \|/ / +.. | / +.. |/ +.. | +.. ● + + $ . $TESTDIR/testlib/checkheads-util.sh + +Test setup +---------- + + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit B0 + created new head + $ mkcommit A1 + $ hg up '0' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit A2 + created new head + $ hg up '0' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit C0 + created new head + $ hg debugobsolete `getid "desc(A0)"` `getid "desc(A1)"` + $ hg debugobsolete `getid "desc(A1)"` `getid "desc(A2)"` + $ hg debugobsolete --record-parents `getid "desc(A2)"` + $ hg log -G --hidden + @ 0f88766e02d6 (draft): C0 + | + | x c1f8d089020f (draft): A2 + |/ + | x ba93660aff8d (draft): A1 + | | + | o 74ff5441d343 (draft): B0 + |/ + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push --rev 'desc(C0)' + pushing to $TESTTMP/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 3 new obsolescence markers diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-discovery-obshashrange.t --- a/tests/test-discovery-obshashrange.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-discovery-obshashrange.t Fri Mar 31 15:39:20 2017 +0200 @@ -57,23 +57,23 @@ dddddddddddddddddddddddddddddddddddddddd c8d03c1b5e94af74b772900c58259d2e08917735 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'} eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 4de32a90b66cd083ebf3c00b41277aa7abca51dd 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'} - $ hg debugstablerange --rev tip - rev node index size depth obshash - 7 4de32a90b66c 0 8 8 38d1e7ad86ea - 3 2dc09a01254d 0 4 4 000000000000 - 7 4de32a90b66c 4 4 8 38d1e7ad86ea - 3 2dc09a01254d 2 2 4 000000000000 - 7 4de32a90b66c 6 2 8 033544c939f0 - 1 66f7d451a68b 0 2 2 17ff8dd63509 - 5 c8d03c1b5e94 4 2 6 57f6cf3757a2 - 2 01241442b3c2 2 1 3 1ed3c61fb39a - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 7 4de32a90b66c 7 1 8 033544c939f0 - 1 66f7d451a68b 1 1 2 17ff8dd63509 - 4 bebd167eb94d 4 1 5 bbe4d7fe27a8 - 5 c8d03c1b5e94 5 1 6 446c2dc3bce5 - 6 f69452c5b1af 6 1 7 000000000000 + $ hg debugobshashrange --subranges --rev tip + rev node index size depth obshash + 7 4de32a90b66c 0 8 8 38d1e7ad86ea + 3 2dc09a01254d 0 4 4 000000000000 + 7 4de32a90b66c 4 4 8 38d1e7ad86ea + 3 2dc09a01254d 2 2 4 000000000000 + 7 4de32a90b66c 6 2 8 033544c939f0 + 1 66f7d451a68b 0 2 2 17ff8dd63509 + 5 c8d03c1b5e94 4 2 6 57f6cf3757a2 + 2 01241442b3c2 2 1 3 1ed3c61fb39a + 0 1ea73414a91b 0 1 1 000000000000 + 3 2dc09a01254d 3 1 4 000000000000 + 7 4de32a90b66c 7 1 8 033544c939f0 + 1 66f7d451a68b 1 1 2 17ff8dd63509 + 4 bebd167eb94d 4 1 5 bbe4d7fe27a8 + 5 c8d03c1b5e94 5 1 6 446c2dc3bce5 + 6 f69452c5b1af 6 1 7 000000000000 $ cd .. testing simple pull diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-evolve.t --- a/tests/test-evolve.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-evolve.t Fri Mar 31 15:39:20 2017 +0200 @@ -171,11 +171,11 @@ Smoketest stablerange.obshash: - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 1 7c3bad9141dc 0 2 2 * (glob) - 0 1f0dee641bb7 0 1 1 000000000000 - 1 7c3bad9141dc 1 1 2 * (glob) + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 1 7c3bad9141dc 0 2 2 * (glob) + 0 1f0dee641bb7 0 1 1 000000000000 + 1 7c3bad9141dc 1 1 2 * (glob) $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-A1.t --- a/tests/test-exchange-A1.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-A1.t Fri Mar 31 15:39:20 2017 +0200 @@ -1,7 +1,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh ==== A.1.1 pushing a single head ==== .. @@ -48,11 +48,11 @@ $ hg debugobsrelsethashtree a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 0000000000000000000000000000000000000000 f5bc6836db60e308a17ba08bf050154ba9c4fad7 50656e04a95ecdfed94659dd61f663b2caa55e98 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 1 f5bc6836db60 0 2 2 50656e04a95e - 0 a9bdc8b26820 0 1 1 000000000000 - 1 f5bc6836db60 1 1 2 50656e04a95e + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 1 f5bc6836db60 0 2 2 50656e04a95e + 0 a9bdc8b26820 0 1 1 000000000000 + 1 f5bc6836db60 1 1 2 50656e04a95e $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-A2.t --- a/tests/test-exchange-A2.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-A2.t Fri Mar 31 15:39:20 2017 +0200 @@ -2,7 +2,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === A.2 Two heads === @@ -61,13 +61,13 @@ a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 0000000000000000000000000000000000000000 f5bc6836db60e308a17ba08bf050154ba9c4fad7 50656e04a95ecdfed94659dd61f663b2caa55e98 35b1839966785d5703a01607229eea932db42f87 b9c8f20eef8938ebab939fe6a592587feacf3245 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 2 35b183996678 0 2 2 b9c8f20eef89 - 1 f5bc6836db60 0 2 2 50656e04a95e - 2 35b183996678 1 1 2 b9c8f20eef89 - 0 a9bdc8b26820 0 1 1 000000000000 - 1 f5bc6836db60 1 1 2 50656e04a95e + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 2 35b183996678 0 2 2 b9c8f20eef89 + 1 f5bc6836db60 0 2 2 50656e04a95e + 2 35b183996678 1 1 2 b9c8f20eef89 + 0 a9bdc8b26820 0 1 1 000000000000 + 1 f5bc6836db60 1 1 2 50656e04a95e $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-A3.t --- a/tests/test-exchange-A3.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-A3.t Fri Mar 31 15:39:20 2017 +0200 @@ -1,7 +1,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === A.3 new branch created === @@ -76,13 +76,13 @@ 6e72f0a95b5e01a7504743aa941f69cb1fbef8b0 0000000000000000000000000000000000000000 e5ea8f9c73143125d36658e90ef70c6d2027a5b7 3bc2ee626e11a7cf8fee7a66d069271e17d5a597 f6298a8ac3a4b78bbeae5f1d3dc5bc3c3812f0f3 91716bfd671b5a5854a47ac5d392edfdd25e431a - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 3 e5ea8f9c7314 0 2 2 3bc2ee626e11 - 4 f6298a8ac3a4 0 2 2 91716bfd671b - 0 a9bdc8b26820 0 1 1 000000000000 - 3 e5ea8f9c7314 1 1 2 3bc2ee626e11 - 4 f6298a8ac3a4 1 1 2 91716bfd671b + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 3 e5ea8f9c7314 0 2 2 3bc2ee626e11 + 4 f6298a8ac3a4 0 2 2 91716bfd671b + 0 a9bdc8b26820 0 1 1 000000000000 + 3 e5ea8f9c7314 1 1 2 3bc2ee626e11 + 4 f6298a8ac3a4 1 1 2 91716bfd671b $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-A4.t --- a/tests/test-exchange-A4.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-A4.t Fri Mar 31 15:39:20 2017 +0200 @@ -1,7 +1,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === A.4 Push in the middle of the obsolescence chain === @@ -68,15 +68,15 @@ 28b51eb45704506b5c603decd6bf7ac5e0f6a52f 5d69322fad9eb1ba8f8f2c2312346ed347fdde76 06055a7959d4128e6e3bccfd01482e83a2db8a3a fd3e5712c9c2d216547d7a1b87ac815ee1fb7542 e5ea8f9c73143125d36658e90ef70c6d2027a5b7 cf518031fa753e9b049d727e6b0e19f645bab38f - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 2 06055a7959d4 0 3 3 000000000000 - 1 28b51eb45704 0 2 2 5d69322fad9e - 3 e5ea8f9c7314 0 2 2 cf518031fa75 - 2 06055a7959d4 2 1 3 000000000000 - 1 28b51eb45704 1 1 2 5d69322fad9e - 0 a9bdc8b26820 0 1 1 000000000000 - 3 e5ea8f9c7314 1 1 2 cf518031fa75 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 2 06055a7959d4 0 3 3 000000000000 + 1 28b51eb45704 0 2 2 5d69322fad9e + 3 e5ea8f9c7314 0 2 2 cf518031fa75 + 2 06055a7959d4 2 1 3 000000000000 + 1 28b51eb45704 1 1 2 5d69322fad9e + 0 a9bdc8b26820 0 1 1 000000000000 + 3 e5ea8f9c7314 1 1 2 cf518031fa75 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-A5.t --- a/tests/test-exchange-A5.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-A5.t Fri Mar 31 15:39:20 2017 +0200 @@ -2,7 +2,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === A.5 partial reordering === @@ -75,13 +75,13 @@ 6e72f0a95b5e01a7504743aa941f69cb1fbef8b0 fd3e5712c9c2d216547d7a1b87ac815ee1fb7542 f6298a8ac3a4b78bbeae5f1d3dc5bc3c3812f0f3 91716bfd671b5a5854a47ac5d392edfdd25e431a 8c0a98c8372212c6efde4bfdcef006f27ff759d3 6e8c8c71c47a2bfc27c7cf2b1f4174977ede9f21 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 4 8c0a98c83722 0 3 3 70185b996296 - 3 f6298a8ac3a4 0 2 2 91716bfd671b - 4 8c0a98c83722 2 1 3 4d835a45c1e9 - 0 a9bdc8b26820 0 1 1 000000000000 - 3 f6298a8ac3a4 1 1 2 91716bfd671b + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 4 8c0a98c83722 0 3 3 70185b996296 + 3 f6298a8ac3a4 0 2 2 91716bfd671b + 4 8c0a98c83722 2 1 3 4d835a45c1e9 + 0 a9bdc8b26820 0 1 1 000000000000 + 3 f6298a8ac3a4 1 1 2 91716bfd671b $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-A6.t --- a/tests/test-exchange-A6.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-A6.t Fri Mar 31 15:39:20 2017 +0200 @@ -3,7 +3,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === A.6 between existing changeset === @@ -63,11 +63,11 @@ a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 0000000000000000000000000000000000000000 28b51eb45704506b5c603decd6bf7ac5e0f6a52f 0000000000000000000000000000000000000000 e5ea8f9c73143125d36658e90ef70c6d2027a5b7 3bc2ee626e11a7cf8fee7a66d069271e17d5a597 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 2 e5ea8f9c7314 0 2 2 3bc2ee626e11 - 0 a9bdc8b26820 0 1 1 000000000000 - 2 e5ea8f9c7314 1 1 2 3bc2ee626e11 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 2 e5ea8f9c7314 0 2 2 3bc2ee626e11 + 0 a9bdc8b26820 0 1 1 000000000000 + 2 e5ea8f9c7314 1 1 2 3bc2ee626e11 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-A7.t --- a/tests/test-exchange-A7.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-A7.t Fri Mar 31 15:39:20 2017 +0200 @@ -1,7 +1,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === A.7 Non targeted common changeset === @@ -45,11 +45,11 @@ $ hg debugobsrelsethashtree a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 0000000000000000000000000000000000000000 f5bc6836db60e308a17ba08bf050154ba9c4fad7 50656e04a95ecdfed94659dd61f663b2caa55e98 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 1 f5bc6836db60 0 2 2 50656e04a95e - 0 a9bdc8b26820 0 1 1 000000000000 - 1 f5bc6836db60 1 1 2 50656e04a95e + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 1 f5bc6836db60 0 2 2 50656e04a95e + 0 a9bdc8b26820 0 1 1 000000000000 + 1 f5bc6836db60 1 1 2 50656e04a95e $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-B1.t --- a/tests/test-exchange-B1.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-B1.t Fri Mar 31 15:39:20 2017 +0200 @@ -1,7 +1,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === B.1 Prune on non-targeted common changeset === @@ -50,11 +50,11 @@ a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 0000000000000000000000000000000000000000 f5bc6836db60e308a17ba08bf050154ba9c4fad7 926d9d84b97b3483891ae983990ad87c1f7827e9 f6fbb35d8ac958bbe70035e4c789c18471cdc0af e041f7ff1c7bd5501c7ab602baa35f0873128021 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 1 f5bc6836db60 0 2 2 926d9d84b97b - 0 a9bdc8b26820 0 1 1 000000000000 - 1 f5bc6836db60 1 1 2 926d9d84b97b + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 1 f5bc6836db60 0 2 2 926d9d84b97b + 0 a9bdc8b26820 0 1 1 000000000000 + 1 f5bc6836db60 1 1 2 926d9d84b97b $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-B2.t --- a/tests/test-exchange-B2.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-B2.t Fri Mar 31 15:39:20 2017 +0200 @@ -1,7 +1,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === B.2 Pruned changeset on head: nothing pushed === @@ -44,9 +44,9 @@ $ hg debugobsrelsethashtree a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 52a5380bc04783a9ad43bb2ab2f47a02ef02adcc f5bc6836db60e308a17ba08bf050154ba9c4fad7 c5a567339e205e8cc4c494e4fb82944daaec449c - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 0 a9bdc8b26820 0 1 1 52a5380bc047 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 0 a9bdc8b26820 0 1 1 52a5380bc047 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-B3.t --- a/tests/test-exchange-B3.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-B3.t Fri Mar 31 15:39:20 2017 +0200 @@ -2,7 +2,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === B.3 Pruned changeset on non-pushed part of the history === @@ -62,13 +62,13 @@ f5bc6836db60e308a17ba08bf050154ba9c4fad7 0000000000000000000000000000000000000000 35b1839966785d5703a01607229eea932db42f87 631ab4cd02ffa1d144dc8f32a18be574076031e3 e56289ab6378dc752fd7965f8bf66b58bda740bd 47c9d2d8db5d4b1eddd0266329ad260ccc84772c - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 2 35b183996678 0 2 2 631ab4cd02ff - 1 f5bc6836db60 0 2 2 000000000000 - 2 35b183996678 1 1 2 631ab4cd02ff - 0 a9bdc8b26820 0 1 1 000000000000 - 1 f5bc6836db60 1 1 2 000000000000 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 2 35b183996678 0 2 2 631ab4cd02ff + 1 f5bc6836db60 0 2 2 000000000000 + 2 35b183996678 1 1 2 631ab4cd02ff + 0 a9bdc8b26820 0 1 1 000000000000 + 1 f5bc6836db60 1 1 2 000000000000 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-B4.t --- a/tests/test-exchange-B4.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-B4.t Fri Mar 31 15:39:20 2017 +0200 @@ -1,7 +1,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === B.4 Pruned changeset on common part of history === @@ -72,13 +72,13 @@ f5bc6836db60e308a17ba08bf050154ba9c4fad7 c27e764c783f451ef3aa40daf2a3795e6674cd06 f6fbb35d8ac958bbe70035e4c789c18471cdc0af 907beff79fdff2b82b5d3bed7989107a6d744508 7f7f229b13a629a5b20581c6cb723f4e2ca54bed c27e764c783f451ef3aa40daf2a3795e6674cd06 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 2 f6fbb35d8ac9 0 3 3 000000000000 - 1 f5bc6836db60 0 2 2 000000000000 - 0 a9bdc8b26820 0 1 1 1900882e85db - 1 f5bc6836db60 1 1 2 000000000000 - 2 f6fbb35d8ac9 2 1 3 000000000000 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 2 f6fbb35d8ac9 0 3 3 000000000000 + 1 f5bc6836db60 0 2 2 000000000000 + 0 a9bdc8b26820 0 1 1 1900882e85db + 1 f5bc6836db60 1 1 2 000000000000 + 2 f6fbb35d8ac9 2 1 3 000000000000 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-B5.t --- a/tests/test-exchange-B5.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-B5.t Fri Mar 31 15:39:20 2017 +0200 @@ -3,7 +3,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === B.5 Push of a children of changeset which successors is pruned === @@ -71,13 +71,13 @@ 28b51eb45704506b5c603decd6bf7ac5e0f6a52f 5c81c58ce0a8ad61dd9cf4c6949846b5990af30d 06055a7959d4128e6e3bccfd01482e83a2db8a3a 201e20697f2a6b0752335af7cd813f140e9e653e e5ea8f9c73143125d36658e90ef70c6d2027a5b7 ae1ac676a5e6d6f4216595c53da763d588929970 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 2 06055a7959d4 0 3 3 000000000000 - 1 28b51eb45704 0 2 2 000000000000 - 2 06055a7959d4 2 1 3 000000000000 - 1 28b51eb45704 1 1 2 000000000000 - 0 a9bdc8b26820 0 1 1 554c0b12f7d9 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 2 06055a7959d4 0 3 3 000000000000 + 1 28b51eb45704 0 2 2 000000000000 + 2 06055a7959d4 2 1 3 000000000000 + 1 28b51eb45704 1 1 2 000000000000 + 0 a9bdc8b26820 0 1 1 554c0b12f7d9 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-B6.t --- a/tests/test-exchange-B6.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-B6.t Fri Mar 31 15:39:20 2017 +0200 @@ -4,7 +4,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh == B.6 Pruned changeset with ancestors not in pushed set === @@ -61,11 +61,11 @@ f5bc6836db60e308a17ba08bf050154ba9c4fad7 f2e05412d3f1d5bc1ae647cf9efc43e0399c26ca 962ecf6b1afc94e15c7e48fdfb76ef8abd11372b 974507d1c466d0aa86d288836194339ed3b98736 f6298a8ac3a4b78bbeae5f1d3dc5bc3c3812f0f3 04e03a8959d8a39984e6a8f4a16fba975b364747 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 1 f5bc6836db60 0 2 2 000000000000 - 0 a9bdc8b26820 0 1 1 86e41541149f - 1 f5bc6836db60 1 1 2 000000000000 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 1 f5bc6836db60 0 2 2 000000000000 + 0 a9bdc8b26820 0 1 1 86e41541149f + 1 f5bc6836db60 1 1 2 000000000000 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-B7.t --- a/tests/test-exchange-B7.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-B7.t Fri Mar 31 15:39:20 2017 +0200 @@ -1,7 +1,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === B.7 Prune on non-targeted common changeset === @@ -53,11 +53,11 @@ a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 0000000000000000000000000000000000000000 f5bc6836db60e308a17ba08bf050154ba9c4fad7 926d9d84b97b3483891ae983990ad87c1f7827e9 f6fbb35d8ac958bbe70035e4c789c18471cdc0af e041f7ff1c7bd5501c7ab602baa35f0873128021 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 1 f5bc6836db60 0 2 2 926d9d84b97b - 0 a9bdc8b26820 0 1 1 000000000000 - 1 f5bc6836db60 1 1 2 926d9d84b97b + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 1 f5bc6836db60 0 2 2 926d9d84b97b + 0 a9bdc8b26820 0 1 1 000000000000 + 1 f5bc6836db60 1 1 2 926d9d84b97b $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-C1.t --- a/tests/test-exchange-C1.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-C1.t Fri Mar 31 15:39:20 2017 +0200 @@ -1,7 +1,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === C.1 Multiple pruned changeset atop each other === .. @@ -52,9 +52,9 @@ a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 1ce18e5a71f78d443a80c819f2f7197c4706af70 f5bc6836db60e308a17ba08bf050154ba9c4fad7 92af733686ce7e0469d8b2b87b4612a4c2d33468 f6fbb35d8ac958bbe70035e4c789c18471cdc0af 3800aeba3728457abb9c508c94f6abc59e698c55 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 0 a9bdc8b26820 0 1 1 1ce18e5a71f7 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 0 a9bdc8b26820 0 1 1 1ce18e5a71f7 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-C2.t --- a/tests/test-exchange-C2.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-C2.t Fri Mar 31 15:39:20 2017 +0200 @@ -2,7 +2,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === C.2 Pruned changeset on precursors === @@ -60,11 +60,11 @@ 28b51eb45704506b5c603decd6bf7ac5e0f6a52f 72f95b7b9fa12243aeb90433d211f2c38263da31 06055a7959d4128e6e3bccfd01482e83a2db8a3a 58ecf9a107b10986d88da605eb0d03b7f24ae486 e5ea8f9c73143125d36658e90ef70c6d2027a5b7 289cb0d058c81c763eca8bb438657dba9a7ba646 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 3 e5ea8f9c7314 0 2 2 289cb0d058c8 - 0 a9bdc8b26820 0 1 1 000000000000 - 3 e5ea8f9c7314 1 1 2 289cb0d058c8 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 3 e5ea8f9c7314 0 2 2 289cb0d058c8 + 0 a9bdc8b26820 0 1 1 000000000000 + 3 e5ea8f9c7314 1 1 2 289cb0d058c8 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-C3.t --- a/tests/test-exchange-C3.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-C3.t Fri Mar 31 15:39:20 2017 +0200 @@ -2,7 +2,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === C.3 Pruned changeset on precursors of another pruned one === @@ -65,9 +65,9 @@ 28b51eb45704506b5c603decd6bf7ac5e0f6a52f beac7228bbe708bc7c9181c3c27f8a17f21dbd9f 06055a7959d4128e6e3bccfd01482e83a2db8a3a 8b648bd67281e9e525919285ac7b3bb2836c2f02 e5ea8f9c73143125d36658e90ef70c6d2027a5b7 dcd2b566ad0983333be704afdc205066e1a6b742 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 0 a9bdc8b26820 0 1 1 40be80b35671 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 0 a9bdc8b26820 0 1 1 40be80b35671 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-C4.t --- a/tests/test-exchange-C4.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-C4.t Fri Mar 31 15:39:20 2017 +0200 @@ -2,7 +2,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === C.4 multiple successors, one is pruned === @@ -75,11 +75,11 @@ f5bc6836db60e308a17ba08bf050154ba9c4fad7 619b4d13bd9878f04d7208dcfcf1e89da826f6be 35b1839966785d5703a01607229eea932db42f87 ddeb7b7a87378f59cecb36d5146df0092b6b3327 7f7f229b13a629a5b20581c6cb723f4e2ca54bed 58ef2e726c5bd89bceffb6243294b38eadbf3d60 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 2 35b183996678 0 2 2 2a098b4a877f - 2 35b183996678 1 1 2 916e804c50de - 0 a9bdc8b26820 0 1 1 a9c02d134f5b + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 2 35b183996678 0 2 2 2a098b4a877f + 2 35b183996678 1 1 2 916e804c50de + 0 a9bdc8b26820 0 1 1 a9c02d134f5b $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-D1.t --- a/tests/test-exchange-D1.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-D1.t Fri Mar 31 15:39:20 2017 +0200 @@ -1,7 +1,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === D.1 Pruned changeset based on missing precursor of something not present === @@ -55,11 +55,11 @@ $ hg debugobsrelsethashtree a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 0000000000000000000000000000000000000000 e5ea8f9c73143125d36658e90ef70c6d2027a5b7 289cb0d058c81c763eca8bb438657dba9a7ba646 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 1 e5ea8f9c7314 0 2 2 289cb0d058c8 - 0 a9bdc8b26820 0 1 1 000000000000 - 1 e5ea8f9c7314 1 1 2 289cb0d058c8 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 1 e5ea8f9c7314 0 2 2 289cb0d058c8 + 0 a9bdc8b26820 0 1 1 000000000000 + 1 e5ea8f9c7314 1 1 2 289cb0d058c8 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-D2.t --- a/tests/test-exchange-D2.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-D2.t Fri Mar 31 15:39:20 2017 +0200 @@ -2,7 +2,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === D.2 missing prune target (prune in "pushed set") === @@ -52,9 +52,9 @@ $ hg debugobsrelsethashtree a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 554c0b12f7d9fff20cb904c26e12eee337e3309c 28b51eb45704506b5c603decd6bf7ac5e0f6a52f 5c81c58ce0a8ad61dd9cf4c6949846b5990af30d - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 0 a9bdc8b26820 0 1 1 554c0b12f7d9 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 0 a9bdc8b26820 0 1 1 554c0b12f7d9 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-D3.t --- a/tests/test-exchange-D3.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-D3.t Fri Mar 31 15:39:20 2017 +0200 @@ -3,7 +3,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === D.2 missing prune target (prune in "pushed set") === @@ -57,11 +57,11 @@ a9bdc8b26820b1b87d585b82eb0ceb4a2ecdbc04 0000000000000000000000000000000000000000 28b51eb45704506b5c603decd6bf7ac5e0f6a52f 0000000000000000000000000000000000000000 35b1839966785d5703a01607229eea932db42f87 65a9f21dff0702355e973a8f31d3b3b7e59376fb - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 2 35b183996678 0 2 2 65a9f21dff07 - 2 35b183996678 1 1 2 65a9f21dff07 - 0 a9bdc8b26820 0 1 1 000000000000 + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 2 35b183996678 0 2 2 65a9f21dff07 + 2 35b183996678 1 1 2 65a9f21dff07 + 0 a9bdc8b26820 0 1 1 000000000000 $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-exchange-D4.t --- a/tests/test-exchange-D4.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-exchange-D4.t Fri Mar 31 15:39:20 2017 +0200 @@ -2,7 +2,7 @@ Initial setup - $ . $TESTDIR/_exc-util.sh + $ . $TESTDIR/testlib/exchange-util.sh === D.4 Unknown changeset in between known one === @@ -71,13 +71,13 @@ 6e72f0a95b5e01a7504743aa941f69cb1fbef8b0 0000000000000000000000000000000000000000 e5ea8f9c73143125d36658e90ef70c6d2027a5b7 0aacc2f86e8fca29f2d5fd8d0790644620acd58a 069b05c3876d56f62895e853a501ea58ea85f68d 40b98bc2b5b1152416ea8e9665ae1c6a3ce32ba0 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 4 069b05c3876d 0 3 3 a2b2331da650 - 3 e5ea8f9c7314 0 2 2 0aacc2f86e8f - 4 069b05c3876d 2 1 3 901f118d4333 - 0 a9bdc8b26820 0 1 1 000000000000 - 3 e5ea8f9c7314 1 1 2 0aacc2f86e8f + $ hg debugobshashrange --subranges --rev 'head()' + rev node index size depth obshash + 4 069b05c3876d 0 3 3 a2b2331da650 + 3 e5ea8f9c7314 0 2 2 0aacc2f86e8f + 4 069b05c3876d 2 1 3 901f118d4333 + 0 a9bdc8b26820 0 1 1 000000000000 + 3 e5ea8f9c7314 1 1 2 0aacc2f86e8f $ cd .. $ cd .. diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-inhibit.t --- a/tests/test-inhibit.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-inhibit.t Fri Mar 31 15:39:20 2017 +0200 @@ -807,7 +807,7 @@ [255] Visible commits can still be pushed - $ hg push -r 71eb4f100663 $pwd/inhibit2 + $ hg push -fr 71eb4f100663 $pwd/inhibit2 pushing to $TESTTMP/inhibit2 searching for changes adding changesets @@ -911,7 +911,7 @@ $ cd not-inhibit $ hg book -d foo $ hg pull - pulling from $TESTTMP/inhibit + pulling from $TESTTMP/inhibit (glob) searching for changes no changes found adding remote bookmark foo diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-stablerange.t --- a/tests/test-stablerange.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-stablerange.t Fri Mar 31 15:39:20 2017 +0200 @@ -16,114 +16,107 @@ $ hg init repo_linear $ cd repo_linear $ hg debugbuilddag '.+6' - $ hg debugstablerange --rev 1 - rev node index size depth obshash - 1 66f7d451a68b 0 2 2 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - $ hg debugstablerange --rev 1 > 1.range + $ hg debugstablerange --verify --verbose --subranges --rev 1 + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 1 > 1.range bigger subset reuse most of the previous one - $ hg debugstablerange --rev 4 - rev node index size depth obshash - 4 bebd167eb94d 0 5 5 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - 4 bebd167eb94d 4 1 5 000000000000 - $ hg debugstablerange --rev 4 > 4.range + $ hg debugstablerange --verify --verbose --subranges --rev 4 + bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + bebd167eb94d-4 (4, 5, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 4 > 4.range $ diff -u 1.range 4.range --- 1.range * (glob) +++ 4.range * (glob) - @@ -1,4 +1,10 @@ - rev node index size depth obshash - + 4 bebd167eb94d 0 5 5 000000000000 - + 3 2dc09a01254d 0 4 4 000000000000 - + 3 2dc09a01254d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - + 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - + 3 2dc09a01254d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - + 4 bebd167eb94d 4 1 5 000000000000 + @@ -1,3 +1,9 @@ + +bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1) + +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + +01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + +2dc09a01254d-3 (3, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + +bebd167eb94d-4 (4, 5, 1) [leaf] - [1] Using a range not ending on 2**N boundary we fall back on 2**N as much as possible - $ hg debugstablerange --rev 5 - rev node index size depth obshash - 5 c8d03c1b5e94 0 6 6 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 5 c8d03c1b5e94 4 2 6 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - 4 bebd167eb94d 4 1 5 000000000000 - 5 c8d03c1b5e94 5 1 6 000000000000 - $ hg debugstablerange --rev 5 > 5.range + $ hg debugstablerange --verify --verbose --subranges --rev 5 + c8d03c1b5e94-0 (5, 6, 6) [complete] - 2dc09a01254d-0 (3, 4, 4), c8d03c1b5e94-4 (5, 6, 2) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + c8d03c1b5e94-4 (5, 6, 2) [complete] - bebd167eb94d-4 (4, 5, 1), c8d03c1b5e94-5 (5, 6, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + bebd167eb94d-4 (4, 5, 1) [leaf] - + c8d03c1b5e94-5 (5, 6, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 5 > 5.range $ diff -u 4.range 5.range --- 4.range * (glob) +++ 5.range * (glob) - @@ -1,10 +1,12 @@ - rev node index size depth obshash - - 4 bebd167eb94d 0 5 5 000000000000 - + 5 c8d03c1b5e94 0 6 6 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - + 5 c8d03c1b5e94 4 2 6 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - 4 bebd167eb94d 4 1 5 000000000000 - + 5 c8d03c1b5e94 5 1 6 000000000000 + @@ -1,9 +1,11 @@ + -bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1) + +c8d03c1b5e94-0 (5, 6, 6) [complete] - 2dc09a01254d-0 (3, 4, 4), c8d03c1b5e94-4 (5, 6, 2) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + +c8d03c1b5e94-4 (5, 6, 2) [complete] - bebd167eb94d-4 (4, 5, 1), c8d03c1b5e94-5 (5, 6, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + bebd167eb94d-4 (4, 5, 1) [leaf] - + +c8d03c1b5e94-5 (5, 6, 1) [leaf] - [1] Even two unperfect range overlap a lot - $ hg debugstablerange --rev tip - rev node index size depth obshash - 6 f69452c5b1af 0 7 7 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - 6 f69452c5b1af 4 3 7 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 5 c8d03c1b5e94 4 2 6 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - 4 bebd167eb94d 4 1 5 000000000000 - 5 c8d03c1b5e94 5 1 6 000000000000 - 6 f69452c5b1af 6 1 7 000000000000 - $ hg debugstablerange --rev tip > tip.range + $ hg debugstablerange --verify --verbose --subranges --rev tip + f69452c5b1af-0 (6, 7, 7) [complete] - 2dc09a01254d-0 (3, 4, 4), f69452c5b1af-4 (6, 7, 3) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + f69452c5b1af-4 (6, 7, 3) [complete] - c8d03c1b5e94-4 (5, 6, 2), f69452c5b1af-6 (6, 7, 1) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + c8d03c1b5e94-4 (5, 6, 2) [complete] - bebd167eb94d-4 (4, 5, 1), c8d03c1b5e94-5 (5, 6, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + bebd167eb94d-4 (4, 5, 1) [leaf] - + c8d03c1b5e94-5 (5, 6, 1) [leaf] - + f69452c5b1af-6 (6, 7, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev tip > tip.range $ diff -u 5.range tip.range --- 5.range * (glob) +++ tip.range * (glob) - @@ -1,6 +1,7 @@ - rev node index size depth obshash - - 5 c8d03c1b5e94 0 6 6 000000000000 - + 6 f69452c5b1af 0 7 7 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - + 6 f69452c5b1af 4 3 7 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 5 c8d03c1b5e94 4 2 6 000000000000 - @@ -10,3 +11,4 @@ - 1 66f7d451a68b 1 1 2 000000000000 - 4 bebd167eb94d 4 1 5 000000000000 - 5 c8d03c1b5e94 5 1 6 000000000000 - + 6 f69452c5b1af 6 1 7 000000000000 + @@ -1,5 +1,6 @@ + -c8d03c1b5e94-0 (5, 6, 6) [complete] - 2dc09a01254d-0 (3, 4, 4), c8d03c1b5e94-4 (5, 6, 2) + +f69452c5b1af-0 (6, 7, 7) [complete] - 2dc09a01254d-0 (3, 4, 4), f69452c5b1af-4 (6, 7, 3) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + +f69452c5b1af-4 (6, 7, 3) [complete] - c8d03c1b5e94-4 (5, 6, 2), f69452c5b1af-6 (6, 7, 1) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + c8d03c1b5e94-4 (5, 6, 2) [complete] - bebd167eb94d-4 (4, 5, 1), c8d03c1b5e94-5 (5, 6, 1) + @@ -9,3 +10,4 @@ + 66f7d451a68b-1 (1, 2, 1) [leaf] - + bebd167eb94d-4 (4, 5, 1) [leaf] - + c8d03c1b5e94-5 (5, 6, 1) [leaf] - + +f69452c5b1af-6 (6, 7, 1) [leaf] - [1] $ cd .. @@ -168,132 +161,123 @@ (left branch) - $ hg debugstablerange --rev 'left~2' - rev node index size depth obshash - 1 66f7d451a68b 0 2 2 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - $ hg debugstablerange --rev 'left~2' > left-2.range - $ hg debugstablerange --rev left - rev node index size depth obshash - 3 2dc09a01254d 0 4 4 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - $ hg debugstablerange --rev 'left' > left.range + $ hg debugstablerange --verify --verbose --subranges --rev 'left~2' + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'left~2' > left-2.range + $ hg debugstablerange --verify --verbose --subranges --rev left + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'left' > left.range $ diff -u left-2.range left.range --- left-2.range * (glob) +++ left.range * (glob) - @@ -1,4 +1,8 @@ - rev node index size depth obshash - + 3 2dc09a01254d 0 4 4 000000000000 - + 3 2dc09a01254d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - + 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - + 3 2dc09a01254d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 + @@ -1,3 +1,7 @@ + +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + +01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + +2dc09a01254d-3 (3, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - [1] (right branch) - $ hg debugstablerange --rev right~2 - rev node index size depth obshash - 4 e7bd5218ca15 0 2 2 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 4 e7bd5218ca15 1 1 2 000000000000 - $ hg debugstablerange --rev 'right~2' > right-2.range - $ hg debugstablerange --rev right - rev node index size depth obshash - 6 a2f58e9c1e56 0 4 4 000000000000 - 6 a2f58e9c1e56 2 2 4 000000000000 - 4 e7bd5218ca15 0 2 2 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 5 3a367db1fabc 2 1 3 000000000000 - 6 a2f58e9c1e56 3 1 4 000000000000 - 4 e7bd5218ca15 1 1 2 000000000000 - $ hg debugstablerange --rev 'right' > right.range + $ hg debugstablerange --verify --verbose --subranges --rev right~2 + e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1) + 1ea73414a91b-0 (0, 1, 1) [leaf] - + e7bd5218ca15-1 (4, 2, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'right~2' > right-2.range + $ hg debugstablerange --verify --verbose --subranges --rev right + a2f58e9c1e56-0 (6, 4, 4) [complete] - e7bd5218ca15-0 (4, 2, 2), a2f58e9c1e56-2 (6, 4, 2) + a2f58e9c1e56-2 (6, 4, 2) [complete] - 3a367db1fabc-2 (5, 3, 1), a2f58e9c1e56-3 (6, 4, 1) + e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1) + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 3a367db1fabc-2 (5, 3, 1) [leaf] - + a2f58e9c1e56-3 (6, 4, 1) [leaf] - + e7bd5218ca15-1 (4, 2, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'right' > right.range $ diff -u right-2.range right.range --- right-2.range * (glob) +++ right.range * (glob) - @@ -1,4 +1,8 @@ - rev node index size depth obshash - + 6 a2f58e9c1e56 0 4 4 000000000000 - + 6 a2f58e9c1e56 2 2 4 000000000000 - 4 e7bd5218ca15 0 2 2 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - + 5 3a367db1fabc 2 1 3 000000000000 - + 6 a2f58e9c1e56 3 1 4 000000000000 - 4 e7bd5218ca15 1 1 2 000000000000 + @@ -1,3 +1,7 @@ + +a2f58e9c1e56-0 (6, 4, 4) [complete] - e7bd5218ca15-0 (4, 2, 2), a2f58e9c1e56-2 (6, 4, 2) + +a2f58e9c1e56-2 (6, 4, 2) [complete] - 3a367db1fabc-2 (5, 3, 1), a2f58e9c1e56-3 (6, 4, 1) + e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1) + 1ea73414a91b-0 (0, 1, 1) [leaf] - + +3a367db1fabc-2 (5, 3, 1) [leaf] - + +a2f58e9c1e56-3 (6, 4, 1) [leaf] - + e7bd5218ca15-1 (4, 2, 1) [leaf] - [1] The merge reuse as much of the slicing created for one of the branch - $ hg debugstablerange --rev merge - rev node index size depth obshash - 7 5f18015f9110 0 8 8 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - 7 5f18015f9110 4 4 8 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 5 3a367db1fabc 1 2 3 000000000000 - 7 5f18015f9110 6 2 8 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 5 3a367db1fabc 2 1 3 000000000000 - 7 5f18015f9110 7 1 8 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - 6 a2f58e9c1e56 3 1 4 000000000000 - 4 e7bd5218ca15 1 1 2 000000000000 - $ hg debugstablerange --rev 'merge' > merge.range + $ hg debugstablerange --verify --verbose --subranges --rev merge + 5f18015f9110-0 (7, 8, 8) [complete] - 2dc09a01254d-0 (3, 4, 4), 5f18015f9110-4 (7, 8, 4) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + 5f18015f9110-4 (7, 8, 4) [complete] - 3a367db1fabc-1 (5, 3, 2), 5f18015f9110-6 (7, 8, 2) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 3a367db1fabc-1 (5, 3, 2) [complete] - e7bd5218ca15-1 (4, 2, 1), 3a367db1fabc-2 (5, 3, 1) + 5f18015f9110-6 (7, 8, 2) [complete] - a2f58e9c1e56-3 (6, 4, 1), 5f18015f9110-7 (7, 8, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + 3a367db1fabc-2 (5, 3, 1) [leaf] - + 5f18015f9110-7 (7, 8, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + a2f58e9c1e56-3 (6, 4, 1) [leaf] - + e7bd5218ca15-1 (4, 2, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'merge' > merge.range $ diff -u left.range merge.range --- left.range * (glob) +++ merge.range * (glob) - @@ -1,8 +1,16 @@ - rev node index size depth obshash - + 7 5f18015f9110 0 8 8 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - + 7 5f18015f9110 4 4 8 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - + 5 3a367db1fabc 1 2 3 000000000000 - + 7 5f18015f9110 6 2 8 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - + 5 3a367db1fabc 2 1 3 000000000000 - + 7 5f18015f9110 7 1 8 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - + 6 a2f58e9c1e56 3 1 4 000000000000 - + 4 e7bd5218ca15 1 1 2 000000000000 + @@ -1,7 +1,15 @@ + +5f18015f9110-0 (7, 8, 8) [complete] - 2dc09a01254d-0 (3, 4, 4), 5f18015f9110-4 (7, 8, 4) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + +5f18015f9110-4 (7, 8, 4) [complete] - 3a367db1fabc-1 (5, 3, 2), 5f18015f9110-6 (7, 8, 2) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + +3a367db1fabc-1 (5, 3, 2) [complete] - e7bd5218ca15-1 (4, 2, 1), 3a367db1fabc-2 (5, 3, 1) + +5f18015f9110-6 (7, 8, 2) [complete] - a2f58e9c1e56-3 (6, 4, 1), 5f18015f9110-7 (7, 8, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + +3a367db1fabc-2 (5, 3, 1) [leaf] - + +5f18015f9110-7 (7, 8, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + +a2f58e9c1e56-3 (6, 4, 1) [leaf] - + +e7bd5218ca15-1 (4, 2, 1) [leaf] - [1] $ diff -u right.range merge.range --- right.range * (glob) +++ merge.range * (glob) - @@ -1,8 +1,16 @@ - rev node index size depth obshash - - 6 a2f58e9c1e56 0 4 4 000000000000 - - 6 a2f58e9c1e56 2 2 4 000000000000 - - 4 e7bd5218ca15 0 2 2 000000000000 - + 7 5f18015f9110 0 8 8 000000000000 - + 3 2dc09a01254d 0 4 4 000000000000 - + 7 5f18015f9110 4 4 8 000000000000 - + 3 2dc09a01254d 2 2 4 000000000000 - + 5 3a367db1fabc 1 2 3 000000000000 - + 7 5f18015f9110 6 2 8 000000000000 - + 1 66f7d451a68b 0 2 2 000000000000 - + 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - + 3 2dc09a01254d 3 1 4 000000000000 - 5 3a367db1fabc 2 1 3 000000000000 - + 7 5f18015f9110 7 1 8 000000000000 - + 1 66f7d451a68b 1 1 2 000000000000 - 6 a2f58e9c1e56 3 1 4 000000000000 - 4 e7bd5218ca15 1 1 2 000000000000 + @@ -1,7 +1,15 @@ + -a2f58e9c1e56-0 (6, 4, 4) [complete] - e7bd5218ca15-0 (4, 2, 2), a2f58e9c1e56-2 (6, 4, 2) + -a2f58e9c1e56-2 (6, 4, 2) [complete] - 3a367db1fabc-2 (5, 3, 1), a2f58e9c1e56-3 (6, 4, 1) + -e7bd5218ca15-0 (4, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), e7bd5218ca15-1 (4, 2, 1) + +5f18015f9110-0 (7, 8, 8) [complete] - 2dc09a01254d-0 (3, 4, 4), 5f18015f9110-4 (7, 8, 4) + +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + +5f18015f9110-4 (7, 8, 4) [complete] - 3a367db1fabc-1 (5, 3, 2), 5f18015f9110-6 (7, 8, 2) + +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + +3a367db1fabc-1 (5, 3, 2) [complete] - e7bd5218ca15-1 (4, 2, 1), 3a367db1fabc-2 (5, 3, 1) + +5f18015f9110-6 (7, 8, 2) [complete] - a2f58e9c1e56-3 (6, 4, 1), 5f18015f9110-7 (7, 8, 1) + +66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + +01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + +2dc09a01254d-3 (3, 4, 1) [leaf] - + 3a367db1fabc-2 (5, 3, 1) [leaf] - + +5f18015f9110-7 (7, 8, 1) [leaf] - + +66f7d451a68b-1 (1, 2, 1) [leaf] - + a2f58e9c1e56-3 (6, 4, 1) [leaf] - + e7bd5218ca15-1 (4, 2, 1) [leaf] - [1] $ cd .. @@ -348,85 +332,79 @@ (left branch) - $ hg debugstablerange --rev 'left~2' - rev node index size depth obshash - 2 01241442b3c2 0 3 3 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - $ hg debugstablerange --rev 'left~2' > left-2.range - $ hg debugstablerange --rev left - rev node index size depth obshash - 4 bebd167eb94d 0 5 5 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - 4 bebd167eb94d 4 1 5 000000000000 - $ hg debugstablerange --rev 'left' > left.range + $ hg debugstablerange --verify --verbose --subranges --rev 'left~2' + 01241442b3c2-0 (2, 3, 3) [complete] - 66f7d451a68b-0 (1, 2, 2), 01241442b3c2-2 (2, 3, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'left~2' > left-2.range + $ hg debugstablerange --verify --verbose --subranges --rev left + bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + bebd167eb94d-4 (4, 5, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'left' > left.range $ diff -u left-2.range left.range --- left-2.range * (glob) +++ left.range * (glob) - @@ -1,6 +1,10 @@ - rev node index size depth obshash - - 2 01241442b3c2 0 3 3 000000000000 - + 4 bebd167eb94d 0 5 5 000000000000 - + 3 2dc09a01254d 0 4 4 000000000000 - + 3 2dc09a01254d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - + 3 2dc09a01254d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - + 4 bebd167eb94d 4 1 5 000000000000 + @@ -1,5 +1,9 @@ + -01241442b3c2-0 (2, 3, 3) [complete] - 66f7d451a68b-0 (1, 2, 2), 01241442b3c2-2 (2, 3, 1) + +bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1) + +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + +2dc09a01254d-3 (3, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + +bebd167eb94d-4 (4, 5, 1) [leaf] - [1] (right branch) - $ hg debugstablerange --rev right~2 - rev node index size depth obshash - 7 42b07e8da27d 0 4 4 000000000000 - 7 42b07e8da27d 2 2 4 000000000000 - 5 de561312eff4 0 2 2 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 7 42b07e8da27d 3 1 4 000000000000 - 6 b9bc20507e0b 2 1 3 000000000000 - 5 de561312eff4 1 1 2 000000000000 - $ hg debugstablerange --rev 'right~2' > right-2.range - $ hg debugstablerange --rev right - rev node index size depth obshash - 9 f4b7da68b467 0 6 6 000000000000 - 7 42b07e8da27d 0 4 4 000000000000 - 7 42b07e8da27d 2 2 4 000000000000 - 5 de561312eff4 0 2 2 000000000000 - 9 f4b7da68b467 4 2 6 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 7 42b07e8da27d 3 1 4 000000000000 - 8 857477a9aebb 4 1 5 000000000000 - 6 b9bc20507e0b 2 1 3 000000000000 - 5 de561312eff4 1 1 2 000000000000 - 9 f4b7da68b467 5 1 6 000000000000 - $ hg debugstablerange --rev 'right' > right.range + $ hg debugstablerange --verify --verbose --subranges --rev right~2 + 42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2) + 42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1) + de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1) + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 42b07e8da27d-3 (7, 4, 1) [leaf] - + b9bc20507e0b-2 (6, 3, 1) [leaf] - + de561312eff4-1 (5, 2, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'right~2' > right-2.range + $ hg debugstablerange --verify --verbose --subranges --rev right + f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2) + 42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2) + 42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1) + de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1) + f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1) + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 42b07e8da27d-3 (7, 4, 1) [leaf] - + 857477a9aebb-4 (8, 5, 1) [leaf] - + b9bc20507e0b-2 (6, 3, 1) [leaf] - + de561312eff4-1 (5, 2, 1) [leaf] - + f4b7da68b467-5 (9, 6, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'right' > right.range $ diff -u right-2.range right.range --- right-2.range * (glob) +++ right.range * (glob) - @@ -1,8 +1,12 @@ - rev node index size depth obshash - + 9 f4b7da68b467 0 6 6 000000000000 - 7 42b07e8da27d 0 4 4 000000000000 - 7 42b07e8da27d 2 2 4 000000000000 - 5 de561312eff4 0 2 2 000000000000 - + 9 f4b7da68b467 4 2 6 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 7 42b07e8da27d 3 1 4 000000000000 - + 8 857477a9aebb 4 1 5 000000000000 - 6 b9bc20507e0b 2 1 3 000000000000 - 5 de561312eff4 1 1 2 000000000000 - + 9 f4b7da68b467 5 1 6 000000000000 + @@ -1,7 +1,11 @@ + +f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2) + 42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2) + 42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1) + de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1) + +f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1) + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 42b07e8da27d-3 (7, 4, 1) [leaf] - + +857477a9aebb-4 (8, 5, 1) [leaf] - + b9bc20507e0b-2 (6, 3, 1) [leaf] - + de561312eff4-1 (5, 2, 1) [leaf] - + +f4b7da68b467-5 (9, 6, 1) [leaf] - [1] In this case, the bottom of the split will have multiple heads, @@ -435,148 +413,143 @@ We are still able to reuse one of the branch however - $ hg debugstablerange --rev merge - rev node index size depth obshash - 10 8aca7f8c9bd2 0 11 11 000000000000 - 4 bebd167eb94d 0 5 5 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - 7 42b07e8da27d 0 4 4 000000000000 - 10 8aca7f8c9bd2 8 3 11 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 7 42b07e8da27d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 5 de561312eff4 0 2 2 000000000000 - 9 f4b7da68b467 4 2 6 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 7 42b07e8da27d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - 8 857477a9aebb 4 1 5 000000000000 - 10 8aca7f8c9bd2 10 1 11 000000000000 - 6 b9bc20507e0b 2 1 3 000000000000 - 4 bebd167eb94d 4 1 5 000000000000 - 5 de561312eff4 1 1 2 000000000000 - 9 f4b7da68b467 5 1 6 000000000000 - $ hg debugstablerange --rev 'merge' > merge.range + $ hg debugstablerange --verify --verbose --subranges --rev merge + 8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), 8aca7f8c9bd2-8 (10, 11, 3) + bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + 42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2) + 8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1) + f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + 42b07e8da27d-3 (7, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + 857477a9aebb-4 (8, 5, 1) [leaf] - + 8aca7f8c9bd2-10 (10, 11, 1) [leaf] - + b9bc20507e0b-2 (6, 3, 1) [leaf] - + bebd167eb94d-4 (4, 5, 1) [leaf] - + de561312eff4-1 (5, 2, 1) [leaf] - + f4b7da68b467-5 (9, 6, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'merge' > merge.range $ diff -u left.range merge.range --- left.range * (glob) +++ merge.range * (glob) - @@ -1,10 +1,22 @@ - rev node index size depth obshash - + 10 8aca7f8c9bd2 0 11 11 000000000000 - 4 bebd167eb94d 0 5 5 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - + 7 42b07e8da27d 0 4 4 000000000000 - + 10 8aca7f8c9bd2 8 3 11 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - + 7 42b07e8da27d 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - + 5 de561312eff4 0 2 2 000000000000 - + 9 f4b7da68b467 4 2 6 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - + 7 42b07e8da27d 3 1 4 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - + 8 857477a9aebb 4 1 5 000000000000 - + 10 8aca7f8c9bd2 10 1 11 000000000000 - + 6 b9bc20507e0b 2 1 3 000000000000 - 4 bebd167eb94d 4 1 5 000000000000 - + 5 de561312eff4 1 1 2 000000000000 - + 9 f4b7da68b467 5 1 6 000000000000 + @@ -1,9 +1,21 @@ + +8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), 8aca7f8c9bd2-8 (10, 11, 3) + bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + +42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2) + +8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + +42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + +de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1) + +f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + +42b07e8da27d-3 (7, 4, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + +857477a9aebb-4 (8, 5, 1) [leaf] - + +8aca7f8c9bd2-10 (10, 11, 1) [leaf] - + +b9bc20507e0b-2 (6, 3, 1) [leaf] - + bebd167eb94d-4 (4, 5, 1) [leaf] - + +de561312eff4-1 (5, 2, 1) [leaf] - + +f4b7da68b467-5 (9, 6, 1) [leaf] - [1] $ diff -u right.range merge.range --- right.range * (glob) +++ merge.range * (glob) - @@ -1,12 +1,22 @@ - rev node index size depth obshash - - 9 f4b7da68b467 0 6 6 000000000000 - + 10 8aca7f8c9bd2 0 11 11 000000000000 - + 4 bebd167eb94d 0 5 5 000000000000 - + 3 2dc09a01254d 0 4 4 000000000000 - 7 42b07e8da27d 0 4 4 000000000000 - + 10 8aca7f8c9bd2 8 3 11 000000000000 - + 3 2dc09a01254d 2 2 4 000000000000 - 7 42b07e8da27d 2 2 4 000000000000 - + 1 66f7d451a68b 0 2 2 000000000000 - 5 de561312eff4 0 2 2 000000000000 - 9 f4b7da68b467 4 2 6 000000000000 - + 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - + 3 2dc09a01254d 3 1 4 000000000000 - 7 42b07e8da27d 3 1 4 000000000000 - + 1 66f7d451a68b 1 1 2 000000000000 - 8 857477a9aebb 4 1 5 000000000000 - + 10 8aca7f8c9bd2 10 1 11 000000000000 - 6 b9bc20507e0b 2 1 3 000000000000 - + 4 bebd167eb94d 4 1 5 000000000000 - 5 de561312eff4 1 1 2 000000000000 - 9 f4b7da68b467 5 1 6 000000000000 + @@ -1,11 +1,21 @@ + -f4b7da68b467-0 (9, 6, 6) [complete] - 42b07e8da27d-0 (7, 4, 4), f4b7da68b467-4 (9, 6, 2) + +8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), 8aca7f8c9bd2-8 (10, 11, 3) + +bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1) + +2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + 42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2) + +8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1) + +2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1) + +66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1) + f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1) + +01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + +2dc09a01254d-3 (3, 4, 1) [leaf] - + 42b07e8da27d-3 (7, 4, 1) [leaf] - + +66f7d451a68b-1 (1, 2, 1) [leaf] - + 857477a9aebb-4 (8, 5, 1) [leaf] - + +8aca7f8c9bd2-10 (10, 11, 1) [leaf] - + b9bc20507e0b-2 (6, 3, 1) [leaf] - + +bebd167eb94d-4 (4, 5, 1) [leaf] - + de561312eff4-1 (5, 2, 1) [leaf] - + f4b7da68b467-5 (9, 6, 1) [leaf] - [1] Range above the merge, reuse subrange from the merge - $ hg debugstablerange --rev tip - rev node index size depth obshash - 12 e6b8d5b46647 0 13 13 000000000000 - 4 bebd167eb94d 0 5 5 000000000000 - 12 e6b8d5b46647 8 5 13 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - 7 42b07e8da27d 0 4 4 000000000000 - 11 485383494a89 8 4 12 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 7 42b07e8da27d 2 2 4 000000000000 - 11 485383494a89 10 2 12 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 5 de561312eff4 0 2 2 000000000000 - 9 f4b7da68b467 4 2 6 000000000000 - 2 01241442b3c2 2 1 3 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 7 42b07e8da27d 3 1 4 000000000000 - 11 485383494a89 11 1 12 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - 8 857477a9aebb 4 1 5 000000000000 - 10 8aca7f8c9bd2 10 1 11 000000000000 - 6 b9bc20507e0b 2 1 3 000000000000 - 4 bebd167eb94d 4 1 5 000000000000 - 5 de561312eff4 1 1 2 000000000000 - 12 e6b8d5b46647 12 1 13 000000000000 - 9 f4b7da68b467 5 1 6 000000000000 - $ hg debugstablerange --rev 'tip' > tip.range + $ hg debugstablerange --verify --verbose --subranges --rev tip + e6b8d5b46647-0 (12, 13, 13) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), e6b8d5b46647-8 (12, 13, 5) + bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1) + e6b8d5b46647-8 (12, 13, 5) [complete] - 485383494a89-8 (11, 12, 4), e6b8d5b46647-12 (12, 13, 1) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + 42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2) + 485383494a89-8 (11, 12, 4) [complete] - f4b7da68b467-4 (9, 6, 2), 485383494a89-10 (11, 12, 2) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1) + 485383494a89-10 (11, 12, 2) [complete] - 8aca7f8c9bd2-10 (10, 11, 1), 485383494a89-11 (11, 12, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1) + f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1) + 01241442b3c2-2 (2, 3, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + 42b07e8da27d-3 (7, 4, 1) [leaf] - + 485383494a89-11 (11, 12, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + 857477a9aebb-4 (8, 5, 1) [leaf] - + 8aca7f8c9bd2-10 (10, 11, 1) [leaf] - + b9bc20507e0b-2 (6, 3, 1) [leaf] - + bebd167eb94d-4 (4, 5, 1) [leaf] - + de561312eff4-1 (5, 2, 1) [leaf] - + e6b8d5b46647-12 (12, 13, 1) [leaf] - + f4b7da68b467-5 (9, 6, 1) [leaf] - + $ hg debugstablerange --verify --verbose --subranges --rev 'tip' > tip.range $ diff -u merge.range tip.range --- merge.range * (glob) +++ tip.range * (glob) - @@ -1,11 +1,13 @@ - rev node index size depth obshash - - 10 8aca7f8c9bd2 0 11 11 000000000000 - + 12 e6b8d5b46647 0 13 13 000000000000 - 4 bebd167eb94d 0 5 5 000000000000 - + 12 e6b8d5b46647 8 5 13 000000000000 - 3 2dc09a01254d 0 4 4 000000000000 - 7 42b07e8da27d 0 4 4 000000000000 - - 10 8aca7f8c9bd2 8 3 11 000000000000 - + 11 485383494a89 8 4 12 000000000000 - 3 2dc09a01254d 2 2 4 000000000000 - 7 42b07e8da27d 2 2 4 000000000000 - + 11 485383494a89 10 2 12 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 5 de561312eff4 0 2 2 000000000000 - 9 f4b7da68b467 4 2 6 000000000000 - @@ -13,10 +15,12 @@ - 0 1ea73414a91b 0 1 1 000000000000 - 3 2dc09a01254d 3 1 4 000000000000 - 7 42b07e8da27d 3 1 4 000000000000 - + 11 485383494a89 11 1 12 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - 8 857477a9aebb 4 1 5 000000000000 - 10 8aca7f8c9bd2 10 1 11 000000000000 - 6 b9bc20507e0b 2 1 3 000000000000 - 4 bebd167eb94d 4 1 5 000000000000 - 5 de561312eff4 1 1 2 000000000000 - + 12 e6b8d5b46647 12 1 13 000000000000 - 9 f4b7da68b467 5 1 6 000000000000 + @@ -1,10 +1,12 @@ + -8aca7f8c9bd2-0 (10, 11, 11) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), 8aca7f8c9bd2-8 (10, 11, 3) + +e6b8d5b46647-0 (12, 13, 13) [complete] - bebd167eb94d-0 (4, 5, 5), 42b07e8da27d-0 (7, 4, 4), e6b8d5b46647-8 (12, 13, 5) + bebd167eb94d-0 (4, 5, 5) [complete] - 2dc09a01254d-0 (3, 4, 4), bebd167eb94d-4 (4, 5, 1) + +e6b8d5b46647-8 (12, 13, 5) [complete] - 485383494a89-8 (11, 12, 4), e6b8d5b46647-12 (12, 13, 1) + 2dc09a01254d-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2dc09a01254d-2 (3, 4, 2) + 42b07e8da27d-0 (7, 4, 4) [complete] - de561312eff4-0 (5, 2, 2), 42b07e8da27d-2 (7, 4, 2) + -8aca7f8c9bd2-8 (10, 11, 3) [complete] - f4b7da68b467-4 (9, 6, 2), 8aca7f8c9bd2-10 (10, 11, 1) + +485383494a89-8 (11, 12, 4) [complete] - f4b7da68b467-4 (9, 6, 2), 485383494a89-10 (11, 12, 2) + 2dc09a01254d-2 (3, 4, 2) [complete] - 01241442b3c2-2 (2, 3, 1), 2dc09a01254d-3 (3, 4, 1) + 42b07e8da27d-2 (7, 4, 2) [complete] - b9bc20507e0b-2 (6, 3, 1), 42b07e8da27d-3 (7, 4, 1) + +485383494a89-10 (11, 12, 2) [complete] - 8aca7f8c9bd2-10 (10, 11, 1), 485383494a89-11 (11, 12, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + de561312eff4-0 (5, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), de561312eff4-1 (5, 2, 1) + f4b7da68b467-4 (9, 6, 2) [complete] - 857477a9aebb-4 (8, 5, 1), f4b7da68b467-5 (9, 6, 1) + @@ -12,10 +14,12 @@ + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2dc09a01254d-3 (3, 4, 1) [leaf] - + 42b07e8da27d-3 (7, 4, 1) [leaf] - + +485383494a89-11 (11, 12, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + 857477a9aebb-4 (8, 5, 1) [leaf] - + 8aca7f8c9bd2-10 (10, 11, 1) [leaf] - + b9bc20507e0b-2 (6, 3, 1) [leaf] - + bebd167eb94d-4 (4, 5, 1) [leaf] - + de561312eff4-1 (5, 2, 1) [leaf] - + +e6b8d5b46647-12 (12, 13, 1) [leaf] - + f4b7da68b467-5 (9, 6, 1) [leaf] - [1] $ cd .. @@ -632,41 +605,73 @@ |/ o 0 1ea73414a91b r0 - $ hg debugstablerange --rev 'head()' - rev node index size depth obshash - 15 1d8d22637c2d 0 8 8 000000000000 - 9 dcbb326fdec2 0 7 7 000000000000 - 10 ff43616e5d0f 0 7 7 000000000000 - 13 b4594d867745 0 6 6 000000000000 - 12 e46a4836065c 0 6 6 000000000000 - 6 2702dd0c91e7 0 5 5 000000000000 - 15 1d8d22637c2d 4 4 8 000000000000 - 3 2b6d669947cd 0 4 4 000000000000 - 5 f0f3ef9a6cd5 0 4 4 000000000000 - 9 dcbb326fdec2 4 3 7 000000000000 - 10 ff43616e5d0f 4 3 7 000000000000 - 15 1d8d22637c2d 6 2 8 000000000000 - 3 2b6d669947cd 2 2 4 000000000000 - 1 66f7d451a68b 0 2 2 000000000000 - 13 b4594d867745 4 2 6 000000000000 - 8 d62d843c9a01 4 2 6 000000000000 - 12 e46a4836065c 4 2 6 000000000000 - 5 f0f3ef9a6cd5 2 2 4 000000000000 - 2 fa942426a6fd 0 2 2 000000000000 - 15 1d8d22637c2d 7 1 8 000000000000 - 0 1ea73414a91b 0 1 1 000000000000 - 6 2702dd0c91e7 4 1 5 000000000000 - 3 2b6d669947cd 3 1 4 000000000000 - 14 43227190fef8 4 1 5 000000000000 - 4 4c748ffd1a46 2 1 3 000000000000 - 1 66f7d451a68b 1 1 2 000000000000 - 13 b4594d867745 5 1 6 000000000000 - 11 bab5d5bf48bd 4 1 5 000000000000 - 8 d62d843c9a01 5 1 6 000000000000 - 9 dcbb326fdec2 6 1 7 000000000000 - 12 e46a4836065c 5 1 6 000000000000 - 7 e7d9710d9fc6 4 1 5 000000000000 - 5 f0f3ef9a6cd5 3 1 4 000000000000 - 2 fa942426a6fd 1 1 2 000000000000 - 10 ff43616e5d0f 6 1 7 000000000000 + $ hg debugstablerange --verify --verbose --subranges --rev 'head()' + 1d8d22637c2d-0 (15, 8, 8) [complete] - 2b6d669947cd-0 (3, 4, 4), 1d8d22637c2d-4 (15, 8, 4) + dcbb326fdec2-0 (9, 7, 7) [complete] - 2b6d669947cd-0 (3, 4, 4), dcbb326fdec2-4 (9, 7, 3) + ff43616e5d0f-0 (10, 7, 7) [complete] - 2b6d669947cd-0 (3, 4, 4), ff43616e5d0f-4 (10, 7, 3) + b4594d867745-0 (13, 6, 6) [complete] - 2b6d669947cd-0 (3, 4, 4), b4594d867745-4 (13, 6, 2) + e46a4836065c-0 (12, 6, 6) [complete] - 2b6d669947cd-0 (3, 4, 4), e46a4836065c-4 (12, 6, 2) + 2702dd0c91e7-0 (6, 5, 5) [complete] - f0f3ef9a6cd5-0 (5, 4, 4), 2702dd0c91e7-4 (6, 5, 1) + 1d8d22637c2d-4 (15, 8, 4) [complete] - 4c748ffd1a46-2 (4, 3, 1), 43227190fef8-4 (14, 5, 1), 1d8d22637c2d-6 (15, 8, 2) + 2b6d669947cd-0 (3, 4, 4) [complete] - 66f7d451a68b-0 (1, 2, 2), 2b6d669947cd-2 (3, 4, 2) + f0f3ef9a6cd5-0 (5, 4, 4) [complete] - fa942426a6fd-0 (2, 2, 2), f0f3ef9a6cd5-2 (5, 4, 2) + dcbb326fdec2-4 (9, 7, 3) [complete] - d62d843c9a01-4 (8, 6, 2), dcbb326fdec2-6 (9, 7, 1) + ff43616e5d0f-4 (10, 7, 3) [complete] - d62d843c9a01-4 (8, 6, 2), ff43616e5d0f-6 (10, 7, 1) + 1d8d22637c2d-6 (15, 8, 2) [complete] - f0f3ef9a6cd5-3 (5, 4, 1), 1d8d22637c2d-7 (15, 8, 1) + 2b6d669947cd-2 (3, 4, 2) [complete] - fa942426a6fd-1 (2, 2, 1), 2b6d669947cd-3 (3, 4, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + b4594d867745-4 (13, 6, 2) [complete] - bab5d5bf48bd-4 (11, 5, 1), b4594d867745-5 (13, 6, 1) + d62d843c9a01-4 (8, 6, 2) [complete] - e7d9710d9fc6-4 (7, 5, 1), d62d843c9a01-5 (8, 6, 1) + e46a4836065c-4 (12, 6, 2) [complete] - bab5d5bf48bd-4 (11, 5, 1), e46a4836065c-5 (12, 6, 1) + f0f3ef9a6cd5-2 (5, 4, 2) [complete] - 4c748ffd1a46-2 (4, 3, 1), f0f3ef9a6cd5-3 (5, 4, 1) + fa942426a6fd-0 (2, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), fa942426a6fd-1 (2, 2, 1) + 1d8d22637c2d-7 (15, 8, 1) [leaf] - + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 2702dd0c91e7-4 (6, 5, 1) [leaf] - + 2b6d669947cd-3 (3, 4, 1) [leaf] - + 43227190fef8-4 (14, 5, 1) [leaf] - + 4c748ffd1a46-2 (4, 3, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + b4594d867745-5 (13, 6, 1) [leaf] - + bab5d5bf48bd-4 (11, 5, 1) [leaf] - + d62d843c9a01-5 (8, 6, 1) [leaf] - + dcbb326fdec2-6 (9, 7, 1) [leaf] - + e46a4836065c-5 (12, 6, 1) [leaf] - + e7d9710d9fc6-4 (7, 5, 1) [leaf] - + f0f3ef9a6cd5-3 (5, 4, 1) [leaf] - + fa942426a6fd-1 (2, 2, 1) [leaf] - + ff43616e5d0f-6 (10, 7, 1) [leaf] - $ cd .. + +Tests range where a toprange is rooted on a merge +================================================= + + $ hg init slice_on_merge + $ cd slice_on_merge + $ hg debugbuilddag ' + > ..:a # 2 nodes, tagged "a" + > <2..:b # another branch with two node based on 0, tagged b + > *a/b:m # merge -1 and -2 (1, 2), tagged "m" + > ' + $ hg log -G + o 4 f37e476fba9a r4 m tip + |\ + | o 3 36315563e2fa r3 b + | | + | o 2 fa942426a6fd r2 + | | + o | 1 66f7d451a68b r1 a + |/ + o 0 1ea73414a91b r0 + + $ hg debugstablerange --verify --verbose --subranges --rev 'head()' + f37e476fba9a-0 (4, 5, 5) [complete] - 66f7d451a68b-0 (1, 2, 2), 36315563e2fa-0 (3, 3, 3), f37e476fba9a-4 (4, 5, 1) + 36315563e2fa-0 (3, 3, 3) [complete] - fa942426a6fd-0 (2, 2, 2), 36315563e2fa-2 (3, 3, 1) + 66f7d451a68b-0 (1, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), 66f7d451a68b-1 (1, 2, 1) + fa942426a6fd-0 (2, 2, 2) [complete] - 1ea73414a91b-0 (0, 1, 1), fa942426a6fd-1 (2, 2, 1) + 1ea73414a91b-0 (0, 1, 1) [leaf] - + 36315563e2fa-2 (3, 3, 1) [leaf] - + 66f7d451a68b-1 (1, 2, 1) [leaf] - + f37e476fba9a-4 (4, 5, 1) [leaf] - + fa942426a6fd-1 (2, 2, 1) [leaf] - + diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-stablesort.t --- a/tests/test-stablesort.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-stablesort.t Fri Mar 31 15:39:20 2017 +0200 @@ -138,7 +138,7 @@ updating to branch default 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R repo_B pull --rev 13 - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -146,7 +146,7 @@ added 4 changesets with 0 changes to 0 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) $ hg -R repo_B pull --rev 14 - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -154,7 +154,7 @@ added 1 changesets with 0 changes to 0 files (+1 heads) (run 'hg heads .' to see heads, 'hg merge' to merge) $ hg -R repo_B pull - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -204,7 +204,7 @@ updating to branch default 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R repo_C pull --rev 12 - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -212,7 +212,7 @@ added 2 changesets with 0 changes to 0 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) $ hg -R repo_C pull --rev 15 - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -220,7 +220,7 @@ added 4 changesets with 0 changes to 0 files (+1 heads) (run 'hg heads .' to see heads, 'hg merge' to merge) $ hg -R repo_C pull - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -270,7 +270,7 @@ updating to branch default 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R repo_D pull --rev 10 - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -278,7 +278,7 @@ added 5 changesets with 0 changes to 0 files (run 'hg update' to get a working copy) $ hg -R repo_D pull --rev 15 - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -286,7 +286,7 @@ added 4 changesets with 0 changes to 0 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) $ hg -R repo_D pull - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -404,7 +404,7 @@ updating to branch default 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R repo_E pull --rev e7d9710d9fc6 - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -420,7 +420,7 @@ updating to branch default 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R repo_F pull --rev d62d843c9a01 - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -436,7 +436,7 @@ updating to branch default 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R repo_G pull --rev 43227190fef8 - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests @@ -444,7 +444,7 @@ added 1 changesets with 0 changes to 0 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) $ hg -R repo_G pull --rev 2702dd0c91e7 - pulling from $TESTTMP/repo_A + pulling from $TESTTMP/repo_A (glob) searching for changes adding changesets adding manifests diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-topic-dest.t --- a/tests/test-topic-dest.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-topic-dest.t Fri Mar 31 15:39:20 2017 +0200 @@ -276,7 +276,7 @@ $ hg add other $ hg ci -m 'c_other' $ hg pull -r default --rebase - pulling from $TESTTMP/jungle + pulling from $TESTTMP/jungle (glob) searching for changes adding changesets adding manifests diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-topic-push.t --- a/tests/test-topic-push.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-topic-push.t Fri Mar 31 15:39:20 2017 +0200 @@ -32,12 +32,12 @@ $ hg add aaa $ hg commit -m 'CA' $ hg outgoing -G - comparing with $TESTTMP/main + comparing with $TESTTMP/main (glob) searching for changes @ 0 default draft CA $ hg push - pushing to $TESTTMP/main + pushing to $TESTTMP/main (glob) searching for changes adding changesets adding manifests @@ -56,25 +56,25 @@ $ hg commit -m 'CC' created new head $ hg outgoing -G - comparing with $TESTTMP/main + comparing with $TESTTMP/main (glob) searching for changes @ 2 default draft CC o 1 default draft CB $ hg push - pushing to $TESTTMP/main + pushing to $TESTTMP/main (glob) searching for changes abort: push creates new remote head 9fe81b7f425d! (merge or see 'hg help push' for details about pushing new heads) [255] $ hg outgoing -r 'desc(CB)' -G - comparing with $TESTTMP/main + comparing with $TESTTMP/main (glob) searching for changes o 1 default draft CB $ hg push -r 'desc(CB)' - pushing to $TESTTMP/main + pushing to $TESTTMP/main (glob) searching for changes adding changesets adding manifests @@ -88,18 +88,18 @@ (branches are permanent and global, did you want a bookmark?) $ hg commit --amend $ hg outgoing -G - comparing with $TESTTMP/main + comparing with $TESTTMP/main (glob) searching for changes @ 4 mountain draft CC $ hg push - pushing to $TESTTMP/main + pushing to $TESTTMP/main (glob) searching for changes abort: push creates new remote branches: mountain! (use 'hg push --new-branch' to create new remote branches) [255] $ hg push --new-branch - pushing to $TESTTMP/main + pushing to $TESTTMP/main (glob) searching for changes adding changesets adding manifests @@ -110,7 +110,7 @@ Including on non-publishing $ hg push --new-branch draft - pushing to $TESTTMP/draft + pushing to $TESTTMP/draft (glob) searching for changes adding changesets adding manifests @@ -143,7 +143,7 @@ Pushing a new topic to a non publishing server should not be seen as a new head $ hg push draft - pushing to $TESTTMP/draft + pushing to $TESTTMP/draft (glob) searching for changes adding changesets adding manifests @@ -162,7 +162,7 @@ Pushing a new topic to a publishing server should be seen as a new head $ hg push - pushing to $TESTTMP/main + pushing to $TESTTMP/main (glob) searching for changes abort: push creates new remote head 67f579af159d! (merge or see 'hg help push' for details about pushing new heads) @@ -289,7 +289,7 @@ $ hg push draft - pushing to $TESTTMP/draft + pushing to $TESTTMP/draft (glob) searching for changes abort: push creates new remote head f0bc62a661be on branch 'default:babar'! (merge or see 'hg help push' for details about pushing new heads) @@ -333,7 +333,7 @@ Reject when pushing to draft $ hg push draft -r . - pushing to $TESTTMP/draft + pushing to $TESTTMP/draft (glob) searching for changes abort: push creates new remote head 4937c4cad39e! (merge or see 'hg help push' for details about pushing new heads) @@ -343,7 +343,7 @@ Reject when pushing to publishing $ hg push -r . - pushing to $TESTTMP/main + pushing to $TESTTMP/main (glob) searching for changes adding changesets adding manifests diff -r 90ab79764ce4 -r e200dbfb4515 tests/test-topic-tutorial.t --- a/tests/test-topic-tutorial.t Tue Mar 14 14:38:10 2017 -0700 +++ b/tests/test-topic-tutorial.t Fri Mar 31 15:39:20 2017 +0200 @@ -193,7 +193,7 @@ Topic will also affect rebase and merge destination. Let's pull the latest update from the main server:: $ hg pull - pulling from $TESTTMP/server + pulling from $TESTTMP/server (glob) searching for changes adding changesets adding manifests @@ -276,7 +276,7 @@ $ hg topic food $ hg push - pushing to $TESTTMP/server + pushing to $TESTTMP/server (glob) searching for changes adding changesets adding manifests @@ -392,7 +392,7 @@ Lets see what other people did in the mean time:: $ hg pull - pulling from $TESTTMP/server + pulling from $TESTTMP/server (glob) searching for changes adding changesets adding manifests @@ -421,7 +421,7 @@ Pushing that topic would create a new heads will be prevented:: $ hg push --rev drinks - pushing to $TESTTMP/server + pushing to $TESTTMP/server (glob) searching for changes abort: push creates new remote head 70dfa201ed73! (merge or see 'hg help push' for details about pushing new heads) @@ -437,7 +437,7 @@ merging shopping switching to topic tools $ hg push - pushing to $TESTTMP/server + pushing to $TESTTMP/server (glob) searching for changes abort: push creates new remote head 4cd7c1591a67! (merge or see 'hg help push' for details about pushing new heads) @@ -446,7 +446,7 @@ Publishing only one of them is allowed (as long as it does not create a new branch head has we just saw in the previous case):: $ hg push -r drinks - pushing to $TESTTMP/server + pushing to $TESTTMP/server (glob) searching for changes adding changesets adding manifests diff -r 90ab79764ce4 -r e200dbfb4515 tests/testlib/checkheads-util.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/testlib/checkheads-util.sh Fri Mar 31 15:39:20 2017 +0200 @@ -0,0 +1,26 @@ +# common setup for head checking code + +. $TESTDIR/testlib/common.sh + +cat >> $HGRCPATH < "$1" + hg add "$1" + hg ci -m "$1" +} + +getid() { + hg log --hidden --template '{node}\n' --rev "$1" +} + +cat >> $HGRCPATH <> $HGRCPATH <> $HGRCPATH + +mkcommit() { + echo "$1" > "$1" + hg add "$1" + hg ci -m "$1" +} +getid() { + hg log --hidden --template '{node}\n' --rev "$1" +} + +setuprepos() { + echo creating test repo for test case $1 + mkdir $1 + cd $1 + echo - pulldest + hg init pushdest + cd pushdest + mkcommit O + hg phase --public . + cd .. + echo - main + hg clone -q pushdest main + echo - pushdest + hg clone -q main pulldest + echo 'cd into `main` and proceed with env setup' +} + +dotest() { +# dotest TESTNAME [TARGETNODE] + + testcase=$1 + shift + target="$1" + if [ $# -gt 0 ]; then + shift + fi + targetnode="" + desccall="" + cd $testcase + echo "## Running testcase $testcase" + if [ -n "$target" ]; then + desccall="desc("\'"$target"\'")" + targetnode="`hg -R main id -qr \"$desccall\"`" + echo "# testing echange of \"$target\" ($targetnode)" + fi + echo "## initial state" + echo "# obstore: main" + hg -R main debugobsolete | sort + echo "# obstore: pushdest" + hg -R pushdest debugobsolete | sort + echo "# obstore: pulldest" + hg -R pulldest debugobsolete | sort + + if [ -n "$target" ]; then + echo "## pushing \"$target\"" from main to pushdest + hg -R main push -r "$desccall" $@ pushdest + else + echo "## pushing from main to pushdest" + hg -R main push pushdest $@ + fi + echo "## post push state" + echo "# obstore: main" + hg -R main debugobsolete | sort + echo "# obstore: pushdest" + hg -R pushdest debugobsolete | sort + echo "# obstore: pulldest" + hg -R pulldest debugobsolete | sort + if [ -n "$target" ]; then + echo "## pulling \"$targetnode\"" from main into pulldest + hg -R pulldest pull -r $targetnode $@ main + else + echo "## pulling from main into pulldest" + hg -R pulldest pull main $@ + fi + echo "## post pull state" + echo "# obstore: main" + hg -R main debugobsolete | sort + echo "# obstore: pushdest" + hg -R pushdest debugobsolete | sort + echo "# obstore: pulldest" + hg -R pulldest debugobsolete | sort + + cd .. + +}