--- a/.hgtags Fri Sep 14 10:51:44 2018 +0200
+++ b/.hgtags Fri Oct 12 15:20:54 2018 +0200
@@ -71,3 +71,4 @@
e7abf863e1130e14cd4d65e53467a199d267b4fd 8.1.1
f1cde4c97806fc6d6cc4c1e09ea2f4081a3ebaec 8.1.2
8d8f08245f9715adf48d6f0f59772b04fd7de1f7 8.2.0
+c6362c4abd695fb96e2fd63c150c051852303c7e 8.2.1
--- a/CHANGELOG Fri Sep 14 10:51:44 2018 +0200
+++ b/CHANGELOG Fri Oct 12 15:20:54 2018 +0200
@@ -1,7 +1,17 @@
Changelog
=========
-8.2.1 - in progress
+8.3.0 - in progress
+-------------------
+
+ * evolve: avoid redundant output when handling linear orphans
+ * evolve: use stack alias s# in `hg evolve` messages
+ * next, prev: use stack alias s# when relevant
+ * rewind: add an undo alias
+ * caches: skip warming the stablerange cache on strip in "auto" mode
+ * topic: properly register the '{topicidx}' for mercurial <= 4.5
+
+8.2.1 -- 2018-09-14
-------------------
* obshashrange: issue the "long stable cache" update message only once
--- a/debian/changelog Fri Sep 14 10:51:44 2018 +0200
+++ b/debian/changelog Fri Oct 12 15:20:54 2018 +0200
@@ -1,8 +1,8 @@
-mercurial-evolve (8.2.0-1) UNRELEASED; urgency=medium
+mercurial-evolve (8.2.1-1) unstable; urgency=medium
- * New upstrean release
+ * new upstream release
- -- Pierre-Yves David <pierre-yves.david@ens-lyon.org> Mon, 03 Sep 2018 23:16:46 +0200
+ -- Pierre-Yves David <pierre-yves.david@ens-lyon.org> Fri, 14 Sep 2018 12:16:07 +0200
mercurial-evolve (8.1.2-1) unstable; urgency=medium
--- a/hgext3rd/evolve/__init__.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/evolve/__init__.py Fri Oct 12 15:20:54 2018 +0200
@@ -1076,9 +1076,14 @@
exc.hint = _('do you want --merge?')
raise
- displayer = compat.changesetdisplayer(ui, repo,
- {'template': shorttemplate})
topic = not opts.get("no_topic", False)
+ hastopic = bool(_getcurrenttopic(repo))
+
+ template = shorttemplate
+ if topic and hastopic:
+ template = utility.stacktemplate
+
+ displayer = compat.changesetdisplayer(ui, repo, {'template': template})
target, bookmark = _findprevtarget(repo, displayer,
opts.get('move_bookmark'), topic)
@@ -1143,11 +1148,12 @@
children = [ctx for ctx in wparents[0].children() if not ctx.obsolete()]
topic = _getcurrenttopic(repo)
filtered = set()
+ template = shorttemplate
if topic and not opts.get("no_topic", False):
filtered = set(ctx for ctx in children if ctx.topic() != topic)
children = [ctx for ctx in children if ctx not in filtered]
- displayer = compat.changesetdisplayer(ui, repo,
- {'template': shorttemplate})
+ template = utility.stacktemplate
+ displayer = compat.changesetdisplayer(ui, repo, {'template': template})
if len(children) == 1:
c = children[0]
return _updatetonext(ui, repo, c, displayer, opts)
--- a/hgext3rd/evolve/cmdrewrite.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/evolve/cmdrewrite.py Fri Oct 12 15:20:54 2018 +0200
@@ -182,11 +182,11 @@
bookmarkupdater(newnode)
tr.close()
finally:
- tr.release()
+ if tr is not None:
+ tr.release()
lockmod.release(lock, wlock)
def _editandapply(ui, repo, pats, old, p1, fp, diffopts):
- RETRYCHOICE = _('try to fix the patch (yn)?$$ &Yes $$ &No')
newnode = None
while newnode is None:
fp.seek(0)
@@ -220,7 +220,8 @@
defaultchoice = 0 # yes
if not ui.interactive:
defaultchoice = 1 # no
- if ui.promptchoice(RETRYCHOICE, default=defaultchoice):
+ retrychoice = _('try to fix the patch (yn)?$$ &Yes $$ &No')
+ if ui.promptchoice(retrychoice, default=defaultchoice):
raise error.Abort(_("Could not apply amended path"))
else:
# consider a third choice where we restore the original patch
--- a/hgext3rd/evolve/evolvecmd.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/evolve/evolvecmd.py Fri Oct 12 15:20:54 2018 +0200
@@ -44,6 +44,7 @@
TROUBLES = compat.TROUBLES
shorttemplate = utility.shorttemplate
+stacktemplate = utility.stacktemplate
_bookmarksupdater = rewriteutil.bookmarksupdater
sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
@@ -53,7 +54,7 @@
abortmessage = _("see `hg help evolve.interrupted`\n")
def _solveone(ui, repo, ctx, evolvestate, dryrun, confirm,
- progresscb, category):
+ progresscb, category, lastsolved=None, stacktmplt=False):
"""Resolve the troubles affecting one revision
returns a tuple (bool, newnode) where,
@@ -62,19 +63,28 @@
formed. newnode can be node, when resolution led to no new
commit. If bool is False, this is ''.
"""
+ displayer = None
+ if stacktmplt:
+ displayer = compat.changesetdisplayer(ui, repo,
+ {'template': stacktemplate})
+ else:
+ displayer = compat.changesetdisplayer(ui, repo,
+ {'template': shorttemplate})
wlock = lock = tr = None
try:
wlock = repo.wlock()
lock = repo.lock()
tr = repo.transaction("evolve")
if 'orphan' == category:
- result = _solveunstable(ui, repo, ctx, evolvestate,
- dryrun, confirm, progresscb)
+ result = _solveunstable(ui, repo, ctx, evolvestate, displayer,
+ dryrun, confirm, progresscb,
+ lastsolved=lastsolved)
elif 'phasedivergent' == category:
result = _solvephasedivergence(ui, repo, ctx, evolvestate,
- dryrun, confirm, progresscb)
+ displayer, dryrun, confirm,
+ progresscb)
elif 'contentdivergent' == category:
- result = _solvedivergent(ui, repo, ctx, evolvestate,
+ result = _solvedivergent(ui, repo, ctx, evolvestate, displayer,
dryrun, confirm, progresscb)
else:
assert False, "unknown trouble category: %s" % (category)
@@ -83,8 +93,8 @@
finally:
lockmod.release(tr, lock, wlock)
-def _solveunstable(ui, repo, orig, evolvestate, dryrun=False, confirm=False,
- progresscb=None):
+def _solveunstable(ui, repo, orig, evolvestate, displayer, dryrun=False,
+ confirm=False, progresscb=None, lastsolved=None):
""" Tries to stabilize the changeset orig which is orphan.
returns a tuple (bool, newnode) where,
@@ -153,13 +163,13 @@
target = repo[heads.first()]
else:
target = targets[0]
- displayer = compat.changesetdisplayer(ui, repo, {'template': shorttemplate})
target = repo[target]
if not ui.quiet or confirm:
repo.ui.write(_('move:'), label='evolve.operation')
displayer.show(orig)
- repo.ui.write(_('atop:'))
- displayer.show(target)
+ if lastsolved is None or target != repo[lastsolved]:
+ repo.ui.write(_('atop:'))
+ displayer.show(target)
if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y':
raise error.Abort(_('evolve aborted by user'))
if progresscb:
@@ -183,8 +193,8 @@
raise error.InterventionRequired(_("fix conflicts and see `hg help "
"evolve.interrupted`"))
-def _solvephasedivergence(ui, repo, bumped, evolvestate, dryrun=False,
- confirm=False, progresscb=None):
+def _solvephasedivergence(ui, repo, bumped, evolvestate, displayer,
+ dryrun=False, confirm=False, progresscb=None):
"""Stabilize a phase divergent changeset
returns a tuple (bool, newnode) where,
@@ -208,7 +218,6 @@
ui.write_err(msg)
return (False, '')
- displayer = compat.changesetdisplayer(ui, repo, {'template': shorttemplate})
if not ui.quiet or confirm:
repo.ui.write(_('recreate:'), label='evolve.operation')
displayer.show(bumped)
@@ -326,7 +335,7 @@
repo.dirstate.setparents(newid, nodemod.nullid)
return (True, replacementnode)
-def _solvedivergent(ui, repo, divergent, evolvestate, dryrun=False,
+def _solvedivergent(ui, repo, divergent, evolvestate, displayer, dryrun=False,
confirm=False, progresscb=None):
"""tries to solve content-divergence of a changeset
@@ -443,7 +452,6 @@
ui.write_err(hint)
return (False, '')
- displayer = compat.changesetdisplayer(ui, repo, {'template': shorttemplate})
if not ui.quiet or confirm:
ui.write(_('merge:'), label='evolve.operation')
displayer.show(divergent)
@@ -1566,14 +1574,30 @@
'command': 'evolve', 'orphanmerge': False,
'bookmarkchanges': [], 'temprevs': [], 'obsmarkers': []}
evolvestate.addopts(stateopts)
+ # lastsolved: keep track of successor of last troubled cset we evolved
+ # to confirm that if atop msg should be suppressed to remove redundancy
+ lastsolved = None
+
+ # check if revs to be evolved are in active topic to make sure that we
+ # can use stack aliases s# in evolve msgs.
+ activetopic = getattr(repo, 'currenttopic', '')
+ rev = revs[0]
+ revtopic = getattr(repo[rev], 'topic', '')
+ if revtopic:
+ revtopic = revtopic()
+ stacktmplt = False
+ if activetopic and revtopic and (activetopic == revtopic):
+ stacktmplt = True
for rev in revs:
curctx = repo[rev]
progresscb()
- ret = _solveone(ui, repo, curctx, evolvestate, dryrunopt, confirmopt,
- progresscb, targetcat)
+ ret = _solveone(ui, repo, curctx, evolvestate, dryrunopt,
+ confirmopt, progresscb, targetcat,
+ lastsolved=lastsolved, stacktmplt=stacktmplt)
seen += 1
if ret[0]:
evolvestate['replacements'][curctx.node()] = ret[1]
+ lastsolved = ret[1]
else:
evolvestate['skippedrevs'].append(curctx.node())
@@ -1581,7 +1605,8 @@
# we were processing an orphan merge with both parents obsolete,
# stabilized for second parent, re-stabilize for the first parent
ret = _solveone(ui, repo, repo[ret[1]], evolvestate, dryrunopt,
- confirmopt, progresscb, targetcat)
+ confirmopt, progresscb, targetcat,
+ stacktmplt=stacktmplt)
if ret[0]:
evolvestate['replacements'][curctx.node()] = ret[1]
else:
@@ -1718,15 +1743,21 @@
category = evolvestate['category']
confirm = evolvestate['confirm']
unfi = repo.unfiltered()
+ # lastsolved: keep track of successor of last troubled cset we
+ # evolved to confirm that if atop msg should be suppressed to remove
+ # redundancy
+ lastsolved = None
for rev in evolvestate['revs']:
# XXX: prevent this lookup by storing nodes instead of revnums
curctx = unfi[rev]
if (curctx.node() not in evolvestate['replacements'] and
curctx.node() not in evolvestate['skippedrevs']):
newnode = _solveone(ui, repo, curctx, evolvestate, False,
- confirm, progresscb, category)
+ confirm, progresscb, category,
+ lastsolved=lastsolved)
if newnode[0]:
evolvestate['replacements'][curctx.node()] = newnode[1]
+ lastsolved = newnode[1]
else:
evolvestate['skippedrevs'].append(curctx.node())
return
--- a/hgext3rd/evolve/metadata.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/evolve/metadata.py Fri Oct 12 15:20:54 2018 +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__ = '8.2.1.dev'
+__version__ = '8.3.0.dev'
testedwith = '4.3.2 4.4.2 4.5.2 4.6.2 4.7'
minimumhgversion = '4.3'
buglink = 'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/obsdiscovery.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/evolve/obsdiscovery.py Fri Oct 12 15:20:54 2018 +0200
@@ -24,6 +24,7 @@
import hashlib
import heapq
+import inspect
import sqlite3
import struct
import weakref
@@ -110,7 +111,13 @@
if len(undecided) < fullsamplesize:
sample = set(undecided)
else:
- sample = _takefullsample(dag, undecided, size=fullsamplesize)
+ # Mercurial 4.8 changed calling convention.
+ if len(inspect.getargspec(_takefullsample)[0]) == 4:
+ sample = _takefullsample(local, None, undecided,
+ size=fullsamplesize)
+ else:
+ # hg <= 4.7 version
+ sample = _takefullsample(dag, undecided, size=fullsamplesize)
roundtrips += 1
ui.progress(_("comparing with other"), totalnb - len(undecided),
@@ -689,7 +696,14 @@
def destroyed(self):
if 'obsstore' in vars(self):
self.obsstore.rangeobshashcache.clear()
- super(obshashrepo, self).destroyed()
+ toplevel = not util.safehasattr(self, '_destroying')
+ if toplevel:
+ self._destroying = True
+ try:
+ super(obshashrepo, self).destroyed()
+ finally:
+ if toplevel:
+ del self._destroying
@localrepo.unfilteredmethod
def updatecaches(self, tr=None, **kwargs):
--- a/hgext3rd/evolve/rewind.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/evolve/rewind.py Fri Oct 12 15:20:54 2018 +0200
@@ -26,7 +26,7 @@
identicalflag = 4
@eh.command(
- '^rewind',
+ '^rewind|undo',
[('', 'to', [], _("rewind to these revisions")),
('', 'as-divergence', None, _("preserve current latest successors")),
('', 'exact', None, _("only rewind explicitly selected revisions")),
--- a/hgext3rd/evolve/stablerange.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/evolve/stablerange.py Fri Oct 12 15:20:54 2018 +0200
@@ -195,7 +195,6 @@
assert standard_start < rangedepth
slicepoint = standard_start
return slicepoint
-
class stablerangebasic(abstractstablerange):
"""a very dummy implementation of stablerange
@@ -391,6 +390,12 @@
# find were we need to slice
slicepoint = self._slicepoint(repo, rangeid)
+ ret = self._slicesrangeat(repo, rangeid, slicepoint)
+
+ return ret
+
+ def _slicesrangeat(self, repo, rangeid, slicepoint):
+ headrev, initial_index = rangeid
self._warmcachefor(repo, rangeid, slicepoint)
stable_parent_data = self._parentrange(repo, rangeid)
@@ -412,7 +417,20 @@
# The parent is above the slice point,
# it's lower subrange will be the same so we just get them,
# (and the top range is always the same)
- subranges = self.subranges(repo, stable_parent_range)[:-1]
+ subranges = self.subranges(repo, stable_parent_range)[:]
+ parenttop = subranges.pop()
+ lenparenttop = self.rangelength(repo, parenttop)
+ skimfromparent = stable_parent_depth - slicepoint
+ if lenparenttop < skimfromparent:
+ # dropping the first subrange of the stable parent range is not
+ # enough to skip what we need to skip, change in approach is needed
+ subranges = self._slicesrangeat(repo, stable_parent_range, slicepoint)
+ subranges.pop()
+ elif lenparenttop > skimfromparent:
+ # The first subrange of the parent is longer that what we want
+ # to drop, we need to keep some of it.
+ midranges = self._slicesrangeat(repo, parenttop, slicepoint)
+ subranges.extend(midranges[:-1])
subranges.append(top_range)
elif initial_index < stable_parent_depth < slicepoint:
# the parent is below the range we are considering, we need to
@@ -429,6 +447,13 @@
slicepoint))
subranges.append(top_range)
+ ### slow code block to validated the slicing works as expected
+ # toprevs = self.revsfromrange(repo, rangeid)
+ # subrevs = []
+ # for s in subranges:
+ # subrevs.extend(self.revsfromrange(repo, s))
+ # assert toprevs == subrevs, (rangeid, slicepoint, stable_parent_range, stable_parent_depth, toprevs, subrevs)
+
return subranges
def _unique_subranges(self, repo, headrev, initial_index, slicepoint):
--- a/hgext3rd/evolve/stablerangecache.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/evolve/stablerangecache.py Fri Oct 12 15:20:54 2018 +0200
@@ -388,7 +388,7 @@
class mergepointsql(stablerangesql, stablerange.stablerange_mergepoint):
- _schemaversion = 2
+ _schemaversion = 3
_cachefile = 'cache/evoext_stablerange_v2.sqlite'
_cachename = 'evo-ext-stablerange-mergepoint'
--- a/hgext3rd/evolve/utility.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/evolve/utility.py Fri Oct 12 15:20:54 2018 +0200
@@ -14,6 +14,7 @@
from mercurial.node import nullrev
shorttemplate = "[{label('evolve.rev', rev)}] {desc|firstline}\n"
+stacktemplate = "[s{label('evolve.rev', topicidx)}] {desc|firstline}\n"
def obsexcmsg(ui, message, important=False):
verbose = ui.configbool('experimental', 'verbose-obsolescence-exchange',
@@ -49,7 +50,7 @@
desc = getattr(tr, 'desc', '')
autocase = False
- if tr is None:
+ if tr is None and not getattr(repo, '_destroying', False):
autocase = True
elif desc.startswith('serve'):
autocase = True
@@ -63,7 +64,6 @@
else:
# note: we should not get to the default case
warm = configbool('experimental', 'obshashrange.warm-cache', True)
-
if not configbool('experimental', 'obshashrange', True):
return False
if not warm:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/pullbundle.py Fri Oct 12 15:20:54 2018 +0200
@@ -0,0 +1,625 @@
+# Extension to provide automatic caching of bundle server for pull
+#
+# Copyright 2018 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+"""pullbundle: automatic server side bundle caching
+
+General principle
+=================
+
+This extension provides a means for server to use pre-computed bundle for
+serving arbitrary pulls. If missing, the necessary pre-computed bundle will be
+generated on demand.
+
+To maximize usage of existing cached bundle, each pull will be served through
+multiple bundles. The bundle will be created using "standard range" from the
+"stablerange" principle. The "stablerange" concept if already used for
+obsmarkers discovery in the evolve extensions.
+
+Using pull Bundle
+=================
+
+All configuration is only required server side.
+
+The "stablerange" code currently still live in the evolve extensions, so for
+now enabling that extensions is required:
+
+You need at minimum the following configuration:
+
+ [extensions]
+ evolve=yes
+ pullbundle=yes
+ [experimental]
+ obshashrange.warm-cache = yes
+
+If you do not want to use evolution server side, you should disable obsmarkers exchange:
+
+ [experimental]
+ evolution.exchange=no
+
+Extra Configuration
+===================
+
+ [pullbundle]
+ # By default bundles are stored `.hg/cache/pullbundles/.
+ # This can be changed with the following config:
+ cache-directory=/absolute/path
+
+Implementation status
+=====================
+
+Both for stablerange and pullbundle use "simple" initial implementations.
+Theses implemenations focus on testing the algorithms and proving the features
+works. Yet they are already useful and used in production.
+
+Performances are expected to greatly improved in the final implementation,
+especially if some of it end up being compiled code.
+
+This first implementation lacks the ability to server the cached bundle from a
+CDN. We'll want this limitation to be lifted quickly.
+
+The way mercurial core report progress is designed for the receival of a single
+changegroup. So currently using pullbundle means flooding the user with output.
+This will have to be fixed.
+
+Why is does this live in the same repository as evolve
+======================================================
+
+There is no fundamental reasons for live in the same repository. However, the
+stablerange data-structure lives in evolve, so it was simpler to put this new
+extensions next to it. As soon as stable range have been upstreamed, we won't
+need the dependency to the evolve extension anymore.
+"""
+
+import collections
+import errno
+import random
+import os
+
+from mercurial import (
+ changegroup,
+ discovery,
+ error,
+ exchange,
+ narrowspec,
+ node as nodemod,
+ registrar,
+ scmutil,
+ util,
+)
+
+from mercurial.i18n import _
+
+__version__ = '0.1.0.dev'
+testedwith = '4.7.1'
+# minimumhgversion = ''
+buglink = 'https://bz.mercurial-scm.org/'
+
+cmdtable = {}
+command = registrar.command(cmdtable)
+
+configtable = {}
+configitem = registrar.configitem(configtable)
+
+configitem('pullbundle', 'cache-directory',
+ default=None,
+)
+
+# generic wrapping
+
+def uisetup(ui):
+ exchange.getbundle2partsmapping['changegroup'] = _getbundlechangegrouppart
+
+def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None,
+ b2caps=None, heads=None, common=None, **kwargs):
+ """add a changegroup part to the requested bundle"""
+ if not kwargs.get(r'cg', True):
+ return
+
+ version = '01'
+ cgversions = b2caps.get('changegroup')
+ if cgversions: # 3.1 and 3.2 ship with an empty value
+ cgversions = [v for v in cgversions
+ if v in changegroup.supportedoutgoingversions(repo)]
+ if not cgversions:
+ raise ValueError(_('no common changegroup version'))
+ version = max(cgversions)
+
+ outgoing = exchange._computeoutgoing(repo, heads, common)
+ if not outgoing.missing:
+ return
+
+ if kwargs.get(r'narrow', False):
+ include = sorted(filter(bool, kwargs.get(r'includepats', [])))
+ exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
+ filematcher = narrowspec.match(repo.root, include=include,
+ exclude=exclude)
+ else:
+ filematcher = None
+
+ # START OF ALTERED PART
+ makeallcgpart(bundler.newpart, repo, outgoing, version, source, bundlecaps,
+ filematcher, cgversions)
+ # END OF ALTERED PART
+
+ if kwargs.get(r'narrow', False) and (include or exclude):
+ narrowspecpart = bundler.newpart('narrow:spec')
+ if include:
+ narrowspecpart.addparam(
+ 'include', '\n'.join(include), mandatory=True)
+ if exclude:
+ narrowspecpart.addparam(
+ 'exclude', '\n'.join(exclude), mandatory=True)
+
+def makeallcgpart(newpart, repo, outgoing, version, source,
+ bundlecaps, filematcher, cgversions):
+
+ pullbundle = not filematcher
+ if pullbundle and not util.safehasattr(repo, 'stablerange'):
+ repo.ui.warn('pullbundle: required extension "evolve" are missing, skipping pullbundle\n')
+ pullbundle = False
+ if filematcher:
+ makeonecgpart(newpart, repo, None, outgoing, version, source, bundlecaps,
+ filematcher, cgversions)
+ else:
+ start = util.timer()
+ slices = sliceoutgoing(repo, outgoing)
+ end = util.timer()
+ msg = _('pullbundle-cache: "missing" set sliced into %d subranges '
+ 'in %s seconds\n')
+ repo.ui.write(msg % (len(slices), end - start))
+ for sliceid, sliceout in slices:
+ makeonecgpart(newpart, repo, sliceid, sliceout, version, source, bundlecaps,
+ filematcher, cgversions)
+
+# stable range slicing
+
+DEBUG = False
+
+def sliceoutgoing(repo, outgoing):
+ cl = repo.changelog
+ rev = cl.nodemap.get
+ node = cl.node
+ revsort = repo.stablesort
+
+ missingrevs = set(rev(n) for n in outgoing.missing)
+ if DEBUG:
+ ms = missingrevs.copy()
+ ss = []
+ allslices = []
+ missingheads = [rev(n) for n in sorted(outgoing.missingheads, reverse=True)]
+ for head in missingheads:
+ localslices = []
+ localmissing = set(repo.revs('%ld and ::%d', missingrevs, head))
+ thisrunmissing = localmissing.copy()
+ while localmissing:
+ slicerevs = []
+ for r in revsort.walkfrom(repo, head):
+ if r not in thisrunmissing:
+ break
+ slicerevs.append(r)
+ slicenodes = [node(r) for r in slicerevs]
+ localslices.append(canonicalslices(repo, slicenodes))
+ if DEBUG:
+ ss.append(slicerevs)
+ missingrevs.difference_update(slicerevs)
+ localmissing.difference_update(slicerevs)
+ if localmissing:
+ heads = list(repo.revs('heads(%ld)', localmissing))
+ heads.sort(key=node)
+ head = heads.pop()
+ if heads:
+ thisrunmissing = repo.revs('%ld and only(%d, %ld)',
+ localmissing,
+ head,
+ heads)
+ else:
+ thisrunmissing = localmissing.copy()
+ if DEBUG:
+ for s in reversed(ss):
+ ms -= set(s)
+ missingbase = repo.revs('parents(%ld) and %ld', s, ms)
+ if missingbase:
+ repo.ui.write_err('!!! rev bundled while parents missing\n')
+ repo.ui.write_err(' parent: %s\n' % list(missingbase))
+ pb = repo.revs('%ld and children(%ld)', s, missingbase)
+ repo.ui.write_err(' children: %s\n' % list(pb))
+ h = repo.revs('heads(%ld)', s)
+ repo.ui.write_err(' heads: %s\n' % list(h))
+ raise error.ProgrammingError('issuing a range before its parents')
+
+ for s in reversed(localslices):
+ allslices.extend(s)
+ # unknown subrange might had to be computed
+ repo.stablerange.save(repo)
+ return [(rangeid, outgoingfromnodes(repo, nodes))
+ for rangeid, nodes in allslices]
+
+def canonicalslices(repo, nodes):
+ depth = repo.depthcache.get
+ stablerange = repo.stablerange
+ rangelength = lambda x: stablerange.rangelength(repo, x)
+ headrev = repo.changelog.rev(nodes[0])
+ nbrevs = len(nodes)
+ headdepth = depth(headrev)
+ skipped = headdepth - nbrevs
+ rangeid = (headrev, skipped)
+
+ subranges = canonicalsubranges(repo, stablerange, rangeid)
+ idx = 0
+ slices = []
+ nodes.reverse()
+ for rangeid in subranges:
+ size = rangelength(rangeid)
+ slices.append((rangeid, nodes[idx:idx + size]))
+ idx += size
+ ### slow code block to validate ranges content
+ # rev = repo.changelog.nodemap.get
+ # for ri, ns in slices:
+ # a = set(rev(n) for n in ns)
+ # b = set(repo.stablerange.revsfromrange(repo, ri))
+ # l = repo.stablerange.rangelength(repo, ri)
+ # repo.ui.write('range-length: %d-%d %s %s\n' % (ri[0], ri[1], l, len(a)))
+ # if a != b:
+ # d = (ri[0], ri[1], b - a, a - b)
+ # repo.ui.write("mismatching content: %d-%d -%s +%s\n" % d)
+ return slices
+
+def canonicalsubranges(repo, stablerange, rangeid):
+ """slice a size of nodes into most reusable subranges
+
+ We try to slice a range into a set of "largest" and "canonical" stable
+ range.
+
+ It might make sense to move this function as a 'stablerange' method.
+ """
+ headrev, skip = rangeid
+ rangedepth = stablerange.depthrev(repo, rangeid[0])
+ canonicals = []
+
+ # 0. find the first power of 2 higher than this range depth
+ cursor = 1
+ while cursor <= rangedepth:
+ cursor *= 2
+
+ # 1. find first cupt
+ precut = cut = 0
+ while True:
+ if skip <= cut:
+ break
+ if cut + cursor < rangedepth:
+ precut = cut
+ cut += cursor
+ if cursor == 1:
+ break
+ cursor //= 2
+
+ # 2. optimise, bottom part
+ if skip != cut:
+ currentsize = tailsize = cut - skip
+ assert 0 < tailsize, tailsize
+
+ # we need to take several "standard cut" in the bottom part
+ #
+ # This is similar to what we will do for the top part, we reusing the
+ # existing structure is a bit more complex.
+ allcuts = list(reversed(standardcut(tailsize)))
+ prerange = (headrev, precut)
+ ### slow code block to check we operate on the right data
+ # rev = repo.changelog.nodemap.get
+ # allrevs = [rev(n) for n in nodes]
+ # allrevs.reverse()
+ # prerevs = repo.stablerange.revsfromrange(repo, prerange)
+ # assert allrevs == prerevs[(len(prerevs) - len(allrevs)):]
+ # end of check
+ sub = list(stablerange.subranges(repo, prerange)[:-1])
+
+ bottomranges = []
+ # XXX we might be able to reuse core stable-range logic instead of
+ # redoing this manually
+ currentrange = sub.pop()
+ currentsize = stablerange.rangelength(repo, currentrange)
+ currentcut = None
+ while allcuts or currentcut is not None:
+ # get the next cut if needed
+ if currentcut is None:
+ currentcut = allcuts.pop()
+ # deal attemp a cut
+ if currentsize == currentcut:
+ bottomranges.append(currentrange)
+ currentcut = None
+ elif currentsize < currentcut:
+ bottomranges.append(currentrange)
+ currentcut -= currentsize
+ else: # currentsize > currentcut
+ newskip = currentrange[1] + (currentsize - currentcut)
+ currentsub = stablerange._slicesrangeat(repo, currentrange, newskip)
+ bottomranges.append(currentsub.pop())
+ sub.extend(currentsub)
+ currentcut = None
+ currentrange = sub.pop()
+ currentsize = stablerange.rangelength(repo, currentrange)
+ bottomranges.reverse()
+ canonicals.extend(bottomranges)
+
+ # 3. take recursive subrange until we get to a power of two size?
+ current = (headrev, cut)
+ while not poweroftwo(stablerange.rangelength(repo, current)):
+ sub = stablerange.subranges(repo, current)
+ canonicals.extend(sub[:-1])
+ current = sub[-1]
+ canonicals.append(current)
+
+ return canonicals
+
+def standardcut(size):
+ assert 0 < size
+ # 0. find the first power of 2 higher than this range depth
+ cut = 1
+ while cut <= size:
+ cut *= 2
+
+ allcuts = []
+ # 1. find all standard expected cut
+ while 1 < cut and size:
+ cut //= 2
+ if cut <= size:
+ allcuts.append(cut)
+ size -= cut
+ return allcuts
+
+def poweroftwo(num):
+ return num and not num & (num - 1)
+
+def outgoingfromnodes(repo, nodes):
+ return discovery.outgoing(repo,
+ missingroots=nodes,
+ missingheads=nodes)
+
+# changegroup part construction
+
+def _changegroupinfo(repo, nodes, source):
+ if repo.ui.verbose or source == 'bundle':
+ repo.ui.status(_("%d changesets found\n") % len(nodes))
+
+def _makenewstream(newpart, repo, outgoing, version, source,
+ bundlecaps, filematcher, cgversions):
+ old = changegroup._changegroupinfo
+ try:
+ changegroup._changegroupinfo = _changegroupinfo
+ if filematcher is not None:
+ cgstream = changegroup.makestream(repo, outgoing, version, source,
+ bundlecaps=bundlecaps,
+ filematcher=filematcher)
+ else:
+ cgstream = changegroup.makestream(repo, outgoing, version, source,
+ bundlecaps=bundlecaps)
+ finally:
+ changegroup._changegroupinfo = old
+
+ nbchanges = len(outgoing.missing)
+ pversion = None
+ if cgversions:
+ pversion = version
+ return (cgstream, nbchanges, pversion)
+
+def _makepartfromstream(newpart, repo, cgstream, nbchanges, version):
+ # same as upstream code
+
+ part = newpart('changegroup', data=cgstream)
+ if version:
+ part.addparam('version', version)
+
+ part.addparam('nbchanges', '%d' % nbchanges,
+ mandatory=False)
+
+ if 'treemanifest' in repo.requirements:
+ part.addparam('treemanifest', '1')
+
+# cache management
+
+def cachedir(repo):
+ cachedir = repo.ui.config('pullbundle', 'cache-directory')
+ if cachedir is not None:
+ return cachedir
+ return repo.cachevfs.join('pullbundles')
+
+def getcache(repo, bundlename):
+ cdir = cachedir(repo)
+ bundlepath = os.path.join(cdir, bundlename)
+ if not os.path.exists(bundlepath):
+ return None
+ # delay file opening as much as possible this introduce a small race
+ # condition if someone remove the file before we actually use it. However
+ # opening too many file will not work.
+
+ def data():
+ with open(bundlepath, 'rb') as fd:
+ for chunk in util.filechunkiter(fd):
+ yield chunk
+ return data()
+
+def cachewriter(repo, bundlename, stream):
+ cdir = cachedir(repo)
+ bundlepath = os.path.join(cdir, bundlename)
+ try:
+ os.makedirs(cdir)
+ except OSError as exc:
+ if exc.errno == errno.EEXIST:
+ pass
+ with util.atomictempfile(bundlepath) as cachefile:
+ for chunk in stream:
+ cachefile.write(chunk)
+ yield chunk
+
+BUNDLEMASK = "%s-%s-%010iskip-%010isize.hg"
+
+def makeonecgpart(newpart, repo, rangeid, outgoing, version, source,
+ bundlecaps, filematcher, cgversions):
+ bundlename = cachedata = None
+ if rangeid is not None:
+ nbchanges = repo.stablerange.rangelength(repo, rangeid)
+ headnode = nodemod.hex(repo.changelog.node(rangeid[0]))
+ # XXX do we need to use cgversion in there?
+ bundlename = BUNDLEMASK % (version, headnode, rangeid[1], nbchanges)
+ cachedata = getcache(repo, bundlename)
+ if cachedata is None:
+ partdata = _makenewstream(newpart, repo, outgoing, version, source,
+ bundlecaps, filematcher, cgversions)
+ if bundlename is not None:
+ cgstream = cachewriter(repo, bundlename, partdata[0])
+ partdata = (cgstream,) + partdata[1:]
+ else:
+ if repo.ui.verbose or source == 'bundle':
+ repo.ui.status(_("%d changesets found in caches\n") % nbchanges)
+ pversion = None
+ if cgversions:
+ pversion = version
+ partdata = (cachedata, nbchanges, pversion)
+ return _makepartfromstream(newpart, repo, *partdata)
+
+@command('^debugpullbundlecacheoverlap',
+ [('', 'count', 100, _('of "client" pulling')),
+ ('', 'min-cache', 1, _('minimum size of cached bundle')),
+ ],
+ _('hg debugpullbundlecacheoverlap [--client 100] REVSET'))
+def debugpullbundlecacheoverlap(ui, repo, *revs, **opts):
+ '''Display statistic on bundle cache hit
+
+ This command "simulate pulls from multiple clients. Each using a random
+ subset of revisions defined by REVSET. And display statistic about the
+ overlap in bundle necessary to serve them.
+ '''
+ actionrevs = scmutil.revrange(repo, revs)
+ if not revs:
+ raise error.Abort('No revision selected')
+ count = opts['count']
+ min_cache = opts['min_cache']
+
+ bundlehits = collections.defaultdict(lambda: 0)
+ pullstats = []
+
+ rlen = lambda rangeid: repo.stablerange.rangelength(repo, rangeid)
+
+ repo.ui.write("gathering %d sample pulls within %d revisions\n"
+ % (count, len(actionrevs)))
+ if 1 < min_cache:
+ repo.ui.write(" not caching ranges smaller than %d changesets\n" % min_cache)
+ for i in xrange(count):
+ repo.ui.progress('gathering data', i, total=count)
+ outgoing = takeonesample(repo, actionrevs)
+ ranges = sliceoutgoing(repo, outgoing)
+ hitranges = 0
+ hitchanges = 0
+ totalchanges = 0
+ largeranges = []
+ for rangeid, __ in ranges:
+ length = rlen(rangeid)
+ totalchanges += length
+ if bundlehits[rangeid]:
+ hitranges += 1
+ hitchanges += rlen(rangeid)
+ if min_cache <= length:
+ bundlehits[rangeid] += 1
+ largeranges.append(rangeid)
+
+ stats = (len(outgoing.missing),
+ totalchanges,
+ hitchanges,
+ len(largeranges),
+ hitranges,
+ )
+ pullstats.append(stats)
+ repo.ui.progress('gathering data', None)
+
+ sizes = []
+ changesmissing = []
+ totalchanges = 0
+ totalcached = 0
+ changesratio = []
+ rangesratio = []
+ bundlecount = []
+ for entry in pullstats:
+ sizes.append(entry[0])
+ changesmissing.append(entry[1] - entry[2])
+ changesratio.append(entry[2] / float(entry[1]))
+ if entry[3]:
+ rangesratio.append(entry[4] / float(entry[3]))
+ else:
+ rangesratio.append(1)
+ bundlecount.append(entry[3])
+ totalchanges += entry[1]
+ totalcached += entry[2]
+
+ cachedsizes = []
+ cachedhits = []
+ for rangeid, hits in bundlehits.items():
+ if hits <= 0:
+ continue
+ length = rlen(rangeid)
+ cachedsizes.append(length)
+ cachedhits.append(hits)
+
+ sizesdist = distribution(sizes)
+ repo.ui.write(fmtdist('pull size', sizesdist))
+
+ changesmissingdist = distribution(changesmissing)
+ repo.ui.write(fmtdist('non-cached changesets', changesmissingdist))
+
+ changesratiodist = distribution(changesratio)
+ repo.ui.write(fmtdist('ratio of cached changesets', changesratiodist))
+
+ bundlecountdist = distribution(bundlecount)
+ repo.ui.write(fmtdist('bundle count', bundlecountdist))
+
+ rangesratiodist = distribution(rangesratio)
+ repo.ui.write(fmtdist('ratio of cached bundles', rangesratiodist))
+
+ repo.ui.write('changesets served:\n')
+ repo.ui.write(' total: %7d\n' % totalchanges)
+ repo.ui.write(' from cache: %7d (%2d%%)\n'
+ % (totalcached, (totalcached * 100 // totalchanges)))
+ repo.ui.write(' bundle: %7d\n' % sum(bundlecount))
+
+ cachedsizesdist = distribution(cachedsizes)
+ repo.ui.write(fmtdist('size of cached bundles', cachedsizesdist))
+
+ cachedhitsdist = distribution(cachedhits)
+ repo.ui.write(fmtdist('hit on cached bundles', cachedhitsdist))
+
+def takeonesample(repo, revs):
+ node = repo.changelog.node
+ pulled = random.sample(revs, max(4, len(revs) // 1000))
+ pulled = repo.revs('%ld::%ld', pulled, pulled)
+ nodes = [node(r) for r in pulled]
+ return outgoingfromnodes(repo, nodes)
+
+def distribution(data):
+ data.sort()
+ length = len(data)
+ return {
+ 'min': data[0],
+ '10%': data[length // 10],
+ '25%': data[length // 4],
+ '50%': data[length // 2],
+ '75%': data[(length // 4) * 3],
+ '90%': data[(length // 10) * 9],
+ '95%': data[(length // 20) * 19],
+ 'max': data[-1],
+ }
+
+STATSFORMAT = """{name}:
+ min: {min}
+ 10%: {10%}
+ 25%: {25%}
+ 50%: {50%}
+ 75%: {75%}
+ 90%: {90%}
+ 95%: {95%}
+ max: {max}
+"""
+
+def fmtdist(name, data):
+ return STATSFORMAT.format(name=name, **data)
--- a/hgext3rd/topic/__init__.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/topic/__init__.py Fri Oct 12 15:20:54 2018 +0200
@@ -178,7 +178,7 @@
'topic.active': 'green',
}
-__version__ = '0.11.1.dev'
+__version__ = '0.12.0.dev'
testedwith = '4.3.3 4.4.2 4.5.2 4.6.2 4.7'
minimumhgversion = '4.3'
@@ -355,6 +355,7 @@
if not post45template:
templatekw.keywords['topic'] = topickw
+ templatekw.keywords['topicidx'] = topicidxkw
# Wrap workingctx extra to return the topic name
extensions.wrapfunction(context.workingctx, '__init__', wrapinit)
# Wrap changelog.add to drop empty topic
@@ -454,25 +455,38 @@
reporef = weakref.ref(self)
if self.ui.configbool('experimental', 'enforce-single-head'):
- origvalidator = tr.validator
+ if util.safehasattr(tr, 'validator'): # hg <= 4.7
+ origvalidator = tr.validator
+ else:
+ origvalidator = tr._validator
def validator(tr2):
repo = reporef()
flow.enforcesinglehead(repo, tr2)
origvalidator(tr2)
- tr.validator = validator
+
+ if util.safehasattr(tr, 'validator'): # hg <= 4.7
+ tr.validator = validator
+ else:
+ tr._validator = validator
topicmodeserver = self.ui.config('experimental',
'topic-mode.server', 'ignore')
ispush = (desc.startswith('push') or desc.startswith('serve'))
if (topicmodeserver != 'ignore' and ispush):
- origvalidator = tr.validator
+ if util.safehasattr(tr, 'validator'): # hg <= 4.7
+ origvalidator = tr.validator
+ else:
+ origvalidator = tr._validator
def validator(tr2):
repo = reporef()
flow.rejectuntopicedchangeset(repo, tr2)
return origvalidator(tr2)
- tr.validator = validator
+ if util.safehasattr(tr, 'validator'): # hg <= 4.7
+ tr.validator = validator
+ else:
+ tr._validator = validator
elif (self.ui.configbool('experimental', 'topic.publish-bare-branch')
and (desc.startswith('push')
@@ -503,9 +517,10 @@
empty = csetcount == 0
if empty and not ctwasempty:
ui.status('active topic %r is now empty\n' % ct)
- if ('phase' in getattr(tr, 'names', ())
+ trnames = getattr(tr, 'names', getattr(tr, '_names', ()))
+ if ('phase' in trnames
or any(n.startswith('push-response')
- for n in getattr(tr, 'names', ()))):
+ for n in trnames)):
ui.status(_("(use 'hg topic --clear' to clear it if needed)\n"))
hint = _("(see 'hg help topics' for more information)\n")
if ctwasempty and not empty:
@@ -532,18 +547,29 @@
""":topic: String. The topic of the changeset"""
ctx = context.resource(mapping, 'ctx')
return ctx.topic()
+
+ @templatekeyword('topicidx', requires={'ctx'})
+ def topicidxkw(context, mapping):
+ """:topicidx: Integer. Index of the changeset as a stack alias"""
+ ctx = context.resource(mapping, 'ctx')
+ return ctx.topicidx()
else:
def topickw(**args):
""":topic: String. The topic of the changeset"""
return args['ctx'].topic()
+ def topicidxkw(**args):
+ """:topicidx: Integer. Index of the changeset as a stack alias"""
+ return args['ctx'].topicidx()
+
def wrapinit(orig, self, repo, *args, **kwargs):
orig(self, repo, *args, **kwargs)
- if getattr(repo, 'currenttopic', ''):
- self._extra[constants.extrakey] = repo.currenttopic
- else:
- # Empty key will be dropped from extra by another hack at the changegroup level
- self._extra[constants.extrakey] = ''
+ if constants.extrakey not in self._extra:
+ if getattr(repo, 'currenttopic', ''):
+ self._extra[constants.extrakey] = repo.currenttopic
+ else:
+ # Empty key will be dropped from extra by another hack at the changegroup level
+ self._extra[constants.extrakey] = ''
def wrapadd(orig, cl, manifest, files, desc, transaction, p1, p2, user,
date=None, extra=None):
--- a/hgext3rd/topic/discovery.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/topic/discovery.py Fri Oct 12 15:20:54 2018 +0200
@@ -153,7 +153,10 @@
return
tr._prepushheads = _nbheads(op.repo)
reporef = weakref.ref(op.repo)
- oldvalidator = tr.validator
+ if util.safehasattr(tr, 'validator'): # hg <= 4.7
+ oldvalidator = tr.validator
+ else:
+ oldvalidator = tr._validator
def validator(tr):
repo = reporef()
@@ -171,7 +174,10 @@
% branch)
raise error.Abort(msg)
return oldvalidator(tr)
- tr.validator = validator
+ if util.safehasattr(tr, 'validator'): # hg <= 4.7
+ tr.validator = validator
+ else:
+ tr._validator = validator
handlecheckheads.params = frozenset()
def _pushb2phases(orig, pushop, bundler):
--- a/hgext3rd/topic/revset.py Fri Sep 14 10:51:44 2018 +0200
+++ b/hgext3rd/topic/revset.py Fri Oct 12 15:20:54 2018 +0200
@@ -24,7 +24,7 @@
revsetpredicate = registrar.revsetpredicate()
def getstringstrict(x, err):
- if x and (x[0] == 'string'):
+ if x and x[0] == 'string':
return x[1]
raise error.ParseError(err)
@@ -51,25 +51,19 @@
else:
kind, pattern, matcher = mkmatcher(topic)
+ if topic.startswith('literal:') and pattern not in repo.topics:
+ raise error.RepoLookupError("topic '%s' does not exist" % pattern)
+
def matches(r):
topic = repo[r].topic()
if not topic:
return False
return matcher(topic)
- if kind == 'literal':
- # note: falls through to the revset case if no topic with this name
- # exists and pattern kind is not specified explicitly
-
- if pattern not in repo.topics and topic.startswith('literal:'):
- raise error.RepoLookupError("topic '%s' does not exist"
- % pattern)
- return (subset & mutable).filter(matches)
- else:
- return (subset & mutable).filter(matches)
+ return (subset & mutable).filter(matches)
s = revset.getset(repo, revset.fullreposet(repo), x)
- topics = set(repo[r].topic() for r in s)
+ topics = {repo[r].topic() for r in s}
topics.discard('')
def matches(r):
--- a/tests/test-discovery-obshashrange.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-discovery-obshashrange.t Fri Oct 12 15:20:54 2018 +0200
@@ -1014,16 +1014,6 @@
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-obscache cache reset (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (5r, 11o) (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (3r, 0o) (glob)
- * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-firstmerge cache reset (glob)
- * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-firstmerge in *.???? seconds (8r) (glob)
- * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-depthcache cache reset (glob)
- * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
- * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-stablesort cache reset (glob)
- * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-stablesort in *.???? seconds (8r) (glob)
- * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
- * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-stablerange-mergepoint in *.???? seconds (8r) (glob)
- * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-obshashrange cache reset (glob)
- 1970/01/01 00:00:00 * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obshashrange in *.???? seconds (8r, 11o) (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated base branch cache in *.???? seconds (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> wrote base branch cache with 1 labels and 1 nodes (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> 3 incoming changes - new heads: 4de32a90b66c (glob)
@@ -1032,6 +1022,16 @@
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> log -G exited 0 after *.?? seconds (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> pull (glob)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-stablerange-mergepoint cache reset (glob)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-depthcache cache reset (glob)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-stablerange-mergepoint in *.???? seconds (8r) (glob)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-obshashrange cache reset (glob)
+ 1970/01/01 00:00:00 * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obshashrange in *.???? seconds (8r, 11o) (glob)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-stablesort cache reset (glob)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-stablesort in *.???? seconds (8r) (glob)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-firstmerge cache reset (glob)
+ * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-firstmerge in *.???? seconds (8r) (glob)
* @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> obsdiscovery, 1/8 mismatch - 1 obshashrange queries in *.???? seconds (glob)
1970/01/01 00:00:00 * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated base branch cache in *.???? seconds (glob)
1970/01/01 00:00:00 * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> wrote base branch cache with 1 labels and 2 nodes (glob)
@@ -1092,3 +1092,11 @@
.hg/cache/evoext-stablesortcache-00: size=100
.hg/cache/evoext_obshashrange_v2.sqlite: size=??* (glob)
.hg/cache/evoext_stablerange_v2.sqlite: size=??* (glob)
+
+ $ rm -f .hg/cache/evoext*
+ $ ls -1 .hg/cache/ | grep evoext
+ [1]
+ $ hg strip -r 5 --config extensions.strip=
+ saved backup bundle to $TESTTMP/client/.hg/strip-backup/c8d03c1b5e94-b257442b-backup.hg (glob)
+ $ f -s .hg/cache/evoext*
+ .hg/cache/evoext-obscache-00: size=70
--- a/tests/test-evolve-abort-orphan.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-evolve-abort-orphan.t Fri Oct 12 15:20:54 2018 +0200
@@ -157,7 +157,6 @@
move:[2] added b
atop:[9] added a
move:[6] added c
- atop:[10] added b
merging c
warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
fix conflicts and see `hg help evolve.interrupted`
@@ -469,7 +468,6 @@
move:[2] added b
atop:[5] added a
move:[3] added c
- atop:[6] added b
merging c
warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
fix conflicts and see `hg help evolve.interrupted`
--- a/tests/test-evolve-content-divergence.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-evolve-content-divergence.t Fri Oct 12 15:20:54 2018 +0200
@@ -374,7 +374,6 @@
move:[3] added c
atop:[9] added b
move:[4] added d
- atop:[10] added c
working directory is now at 4ae4427ee9f8
$ hg glog
@ 11:4ae4427ee9f8 added d
@@ -808,9 +807,7 @@
move:[2] added b
atop:[8] watbar to a
move:[3] added c
- atop:[9] added b
move:[4] added d
- atop:[10] added c
working directory is now at 15c781f93cac
$ hg glog
@ 11:15c781f93cac added d
@@ -834,9 +831,7 @@
move:[2] added b
atop:[6] watbar to a
move:[3] added c
- atop:[7] added b
move:[4] added d
- atop:[8] added c
working directory is now at c72d2885eb51
$ hg glog
@ 9:c72d2885eb51 added d
--- a/tests/test-evolve-continue.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-evolve-continue.t Fri Oct 12 15:20:54 2018 +0200
@@ -166,7 +166,6 @@
move:[6] added c
atop:[13] added b
move:[10] added d
- atop:[14] added c
working directory is now at 6642d2c9176e
$ hg glog
@@ -237,7 +236,6 @@
move:[15] added d
atop:[20] added c
move:[16] added f
- atop:[21] added d
merging f
warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
fix conflicts and see `hg help evolve.interrupted`
@@ -252,7 +250,6 @@
move:[17] added g
atop:[22] added f
move:[18] added h
- atop:[23] added g
merging h
warning: conflicts while merging h! (edit, then use 'hg resolve --mark')
fix conflicts and see `hg help evolve.interrupted`
--- a/tests/test-evolve-issue5832.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-evolve-issue5832.t Fri Oct 12 15:20:54 2018 +0200
@@ -117,7 +117,6 @@
move:[2] added b
atop:[6] added a
move:[4] merge commit
- atop:[9] added b
ancestor '7235ef625ea3' split over multiple topological branches.
choose an evolve destination:
0: [62fb70414f99] added c
@@ -258,7 +257,6 @@
move:[2] added b
atop:[6] added a
move:[4] merge commit
- atop:[9] added b
ancestor 'cdf2ea1b9312' split over multiple topological branches.
choose an evolve destination:
0: [62fb70414f99] added c
--- a/tests/test-evolve-noupdate.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-evolve-noupdate.t Fri Oct 12 15:20:54 2018 +0200
@@ -64,7 +64,6 @@
move:[3] added c
atop:[6] added b
move:[4] added d
- atop:[7] added c
$ hg glog
o 8:b6b20b8eefdc added d
@@ -108,9 +107,7 @@
move:[6] added b
atop:[10] added a
move:[7] added c
- atop:[11] added b
move:[8] added d
- atop:[12] added c
working directory is now at 12c720cb3782
$ hg glog
--- a/tests/test-evolve-order.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-evolve-order.t Fri Oct 12 15:20:54 2018 +0200
@@ -62,7 +62,6 @@
move:[2] add _b
atop:[5] add _a
move:[3] add _c
- atop:[6] add _b
working directory is now at 52b8f9b04f83
evolve --rev reorders the rev to solve instability. Harder case, obsolescence
@@ -106,9 +105,7 @@
move:[11] bprime
atop:[12] asecond
move:[7] add _c
- atop:[13] bprime
move:[8] add _d
- atop:[14] add _c
working directory is now at 739f18ac1d03
$ hg log -G
@ 15:739f18ac1d03@default(draft) add _d
@@ -214,7 +211,6 @@
move:[18] add c3_
atop:[29] add c2prime
move:[19] add c4_
- atop:[31] add c3_
working directory is now at 35e7b797ace5
$ hg log -G -r "desc(_d)::"
@ 32:35e7b797ace5@default(draft) add c4_
--- a/tests/test-evolve-orphan-merge.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-evolve-orphan-merge.t Fri Oct 12 15:20:54 2018 +0200
@@ -350,7 +350,6 @@
move:[20] added m
atop:[25] added l
move:[23] merge commit
- atop:[26] added m
working directory is now at a446ad3e6700
$ hg glog
--- a/tests/test-evolve-stop-orphan.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-evolve-stop-orphan.t Fri Oct 12 15:20:54 2018 +0200
@@ -242,9 +242,7 @@
move:[1] added a
atop:[9] added hgignore
move:[2] added b
- atop:[10] added a
move:[6] added c
- atop:[11] added b
merging c
warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
fix conflicts and see `hg help evolve.interrupted`
@@ -357,7 +355,6 @@
move:[11] added b
atop:[15] added a
move:[12] added c
- atop:[16] added b
merging c
warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
fix conflicts and see `hg help evolve.interrupted`
--- a/tests/test-evolve-topic.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-evolve-topic.t Fri Oct 12 15:20:54 2018 +0200
@@ -123,11 +123,19 @@
Run evolve --all
+ $ hg stack
+ ### topic: foo
+ ### target: default (branch)
+ s4$ add fff (current unstable)
+ s3$ add eee (unstable)
+ s2: add ddd
+ s1: add ccc
+ s0^ add bbb (base)
+
$ hg evolve --all
- move:[4] add eee
- atop:[11] add ddd
- move:[13] add fff
- atop:[14] add eee
+ move:[s3] add eee
+ atop:[s2] add ddd
+ move:[s4] add fff
working directory is now at 070c5573d8f9
$ hg log -G
@ 15 - {foo} 070c5573d8f9 add fff (draft)
@@ -164,11 +172,8 @@
move:[6] add ggg
atop:[15] add fff
move:[7] add hhh
- atop:[16] add ggg
move:[8] add iii
- atop:[17] add hhh
move:[9] add jjj
- atop:[18] add iii
working directory is now at 9bf430c106b7
$ hg log -G
@ 19 - {bar} 9bf430c106b7 add jjj (draft)
@@ -202,10 +207,10 @@
0 files updated, 0 files merged, 4 files removed, 0 files unresolved
$ hg prev
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- [14] add eee
+ [s3] add eee
$ hg next
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- [15] add fff
+ [s4] add fff
$ hg next
no children on topic "foo"
do you want --no-topic
@@ -217,7 +222,7 @@
$ hg prev
preserving the current topic 'bar'
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- [15] add fff
+ [s4] add fff
$ hg prev
no parent in topic "bar"
(do you want --no-topic)
@@ -268,7 +273,7 @@
$ hg prev
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- [20] add fff
+ [s1] add fff
Testing issue 5708 when we are on obsolete changeset and there is active topic
------------------------------------------------------------------------------
@@ -332,7 +337,7 @@
$ hg prev
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- [16] add ggg
+ [s2] add ggg
When the current topic and successors topic are same, but obsolete cset has
different topic
@@ -378,4 +383,4 @@
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg prev
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- [14] add eee
+ [s3] add eee
--- a/tests/test-evolve.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-evolve.t Fri Oct 12 15:20:54 2018 +0200
@@ -474,7 +474,6 @@
atop:[13] dansk!
merging main-file-1
move:[11] dansk 3!
- atop:[14] dansk 2!
merging main-file-1
working directory is now at 68557e4f0048
$ hg log -G
@@ -1345,7 +1344,6 @@
move:[22] add j2
atop:[26] add j1
move:[23] add j3
- atop:[27] add j2
working directory is now at c9a20e2d74aa
$ glog -r "edc3c9de504e::"
@ 28:c9a20e2d74aa@default(draft) add j3
@@ -1496,7 +1494,6 @@
move:[38] will be evolved safely
atop:[41] amended
move:[39] will cause conflict at evolve
- atop:[42] will be evolved safely
merging newfile
warning: conflicts while merging newfile! (edit, then use 'hg resolve --mark')
fix conflicts and see `hg help evolve.interrupted`
--- a/tests/test-split.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-split.t Fri Oct 12 15:20:54 2018 +0200
@@ -153,7 +153,6 @@
move:[6] split1
atop:[9] _cprim
move:[7] split2
- atop:[10] split1
working directory is now at * (glob)
$ hg log -r "desc(_cprim)" -v -p
changeset: 9:b434287e665c
@@ -222,7 +221,6 @@
move:[10] split1
atop:[13] split4
move:[11] split2
- atop:[14] split1
working directory is now at d74c6715e706
$ hg log -G
@ changeset: 15:d74c6715e706
--- a/tests/test-topic-stack-complex.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-topic-stack-complex.t Fri Oct 12 15:20:54 2018 +0200
@@ -48,7 +48,7 @@
s0^ Added foo (base)
$ hg prev
0 files updated, 0 files merged, 2 files removed, 0 files unresolved
- [2] Added c and d
+ [s2] Added c and d
$ echo 0 > num
$ cat > editor.sh << '__EOF__'
@@ -102,7 +102,7 @@
$ hg prev
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- [4] split1
+ [s2] split1
$ echo foo > c
$ hg diff
diff -r f26c1b9addde c
--- a/tests/test-topic-tutorial.t Fri Sep 14 10:51:44 2018 +0200
+++ b/tests/test-topic-tutorial.t Fri Oct 12 15:20:54 2018 +0200
@@ -1148,7 +1148,7 @@
$ hg previous
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- [14] Adding saw
+ [s2] Adding saw
$ hg stack
### topic: tools
@@ -1162,7 +1162,7 @@
$ hg next
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- [15] Adding drill
+ [s3] Adding drill
$ hg stack
### topic: tools