--- a/README Mon Mar 03 19:27:42 2014 -0800
+++ b/README Mon Mar 03 19:28:43 2014 -0800
@@ -42,6 +42,18 @@
Changelog
=========
+3.3.0 --
+
+- drop `latercomer` and `conflicting` compatibility. Those old alias are
+ deprecated for a long time now.
+- added Augie Facklers `fastop` extension (usage not recommended yet)
+- add verbose hint about how to handle corner case by hand.
+ This should help people until evolve is able to to it itself.
+- removed the qsync extension. The only user I knew about (logilab) is not
+ using it anymore. It not compatible with coming Mercurial version 2.9.
+- add progress indicator for long evolve command
+- report troubles creation from `hg import`
+
3.2.0 -- 2013-11-15
- conform to the Mercurial custom of lowercase messages
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/drophack.py Mon Mar 03 19:28:43 2014 -0800
@@ -0,0 +1,162 @@
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+'''This extension add a hacky command to drop changeset during review
+
+This extension is intended as a temporary hack to allow Matt Mackall to use
+evolve in the Mercurial review it self. You should probably not use it if your
+name is not Matt Mackall.
+'''
+
+import os
+import time
+import contextlib
+
+from mercurial.i18n import _
+from mercurial import cmdutil
+from mercurial import repair
+from mercurial import scmutil
+from mercurial import lock as lockmod
+from mercurial import util
+from mercurial import commands
+
+cmdtable = {}
+command = cmdutil.command(cmdtable)
+
+
+@contextlib.contextmanager
+def timed(ui, caption):
+ ostart = os.times()
+ cstart = time.time()
+ yield
+ cstop = time.time()
+ ostop = os.times()
+ wall = cstop - cstart
+ user = ostop[0] - ostart[0]
+ sys = ostop[1] - ostart[1]
+ comb = user + sys
+ ui.write("%s: wall %f comb %f user %f sys %f\n"
+ % (caption, wall, comb, user, sys))
+
+def obsmarkerchainfrom(obsstore, nodes):
+ """return all marker chain starting from node
+
+ Starting from mean "use as successors"."""
+ # XXX need something smarter for descendant of bumped changeset
+ seennodes = set(nodes)
+ seenmarkers = set()
+ pendingnodes = set(nodes)
+ precursorsmarkers = obsstore.precursors
+ while pendingnodes:
+ current = pendingnodes.pop()
+ new = set()
+ for precmark in precursorsmarkers.get(current, ()):
+ if precmark in seenmarkers:
+ continue
+ seenmarkers.add(precmark)
+ new.add(precmark[0])
+ yield precmark
+ new -= seennodes
+ pendingnodes |= new
+
+def stripmarker(ui, repo, markers):
+ """remove <markers> from the repo obsstore
+
+ The old obsstore content is saved in a `obsstore.prestrip` file
+ """
+ repo = repo.unfiltered()
+ repo.destroying()
+ oldmarkers = list(repo.obsstore._all)
+ util.rename(repo.sjoin('obsstore'),
+ repo.join('obsstore.prestrip'))
+ del repo.obsstore # drop the cache
+ newstore = repo.obsstore
+ assert not newstore # should be empty after rename
+ newmarkers = [m for m in oldmarkers if m not in markers]
+ tr = repo.transaction('drophack')
+ try:
+ newstore.add(tr, newmarkers)
+ tr.close()
+ finally:
+ tr.release()
+ repo.destroyed()
+
+
+@command('drop', [('r', 'rev', [], 'revision to update')], _('[-r] revs'))
+def cmddrop(ui, repo, *revs, **opts):
+ """I'm hacky do not use me!
+
+ This command strip a changeset, its precursors and all obsolescence marker
+ associated to its chain.
+
+ There is no way to limit the extend of the purge yet. You may have to
+ repull from other source to get some changeset and obsolescence marker
+ back.
+
+ This intended for Matt Mackall usage only. do not use me.
+ """
+ revs = list(revs)
+ revs.extend(opts['rev'])
+ if not revs:
+ revs = ['.']
+ # get the changeset
+ revs = scmutil.revrange(repo, revs)
+ if not revs:
+ ui.write_err('no revision to drop\n')
+ return 1
+ # lock from the beginning to prevent race
+ wlock = lock = None
+ try:
+ lock = repo.wlock()
+ lock = repo.lock()
+ # check they have no children
+ if repo.revs('%ld and public()', revs):
+ ui.write_err('cannot drop public revision')
+ return 1
+ if repo.revs('children(%ld) - %ld', revs, revs):
+ ui.write_err('cannot drop revision with children')
+ return 1
+ if repo.revs('. and %ld', revs):
+ newrevs = repo.revs('max(::. - %ld)', revs)
+ if newrevs:
+ assert len(newrevs) == 1
+ newrev = newrevs[0]
+ else:
+ newrev = -1
+ commands.update(ui, repo, newrev)
+ ui.status(_('working directory now at %s\n') % repo[newrev])
+ # get all markers and successors up to root
+ nodes = [repo[r].node() for r in revs]
+ with timed(ui, 'search obsmarker'):
+ markers = set(obsmarkerchainfrom(repo.obsstore, nodes))
+ ui.write('%i obsmarkers found\n' % len(markers))
+ cl = repo.unfiltered().changelog
+ with timed(ui, 'search nodes'):
+ allnodes = set(nodes)
+ allnodes.update(m[0] for m in markers if cl.hasnode(m[0]))
+ ui.write('%i nodes found\n' % len(allnodes))
+ cl = repo.changelog
+ visiblenodes = set(n for n in allnodes if cl.hasnode(n))
+ # check constraint again
+ if repo.revs('%ln and public()', visiblenodes):
+ ui.write_err('cannot drop public revision')
+ return 1
+ if repo.revs('children(%ln) - %ln', visiblenodes, visiblenodes):
+ ui.write_err('cannot drop revision with children')
+ return 1
+
+ if markers:
+ # strip them
+ with timed(ui, 'strip obsmarker'):
+ stripmarker(ui, repo, markers)
+ # strip the changeset
+ with timed(ui, 'strip nodes'):
+ repair.strip(ui, repo, allnodes, backup="all", topic='drophack')
+
+ finally:
+ lockmod.release(lock, wlock)
+
+ # rewrite the whole file.
+ # print data.
+ # - time to compute the chain
+ # - time to strip the changeset
+ # - time to strip the obs marker.
--- a/hgext/evolve.py Mon Mar 03 19:27:42 2014 -0800
+++ b/hgext/evolve.py Mon Mar 03 19:28:43 2014 -0800
@@ -46,6 +46,7 @@
from mercurial import context
from mercurial import copies
from mercurial import error
+from mercurial import exchange
from mercurial import extensions
from mercurial import hg
from mercurial import lock as lockmod
@@ -301,6 +302,56 @@
reposetup = eh.final_reposetup
#####################################################################
+### experimental behavior ###
+#####################################################################
+
+@eh.wrapfunction(mercurial.obsolete, 'createmarkers')
+def _createmarkers(orig, repo, relations, *args, **kwargs):
+ """register parent information at prune time"""
+ # every time this test is run, a kitten is slain.
+ # Change it as soon as possible
+ if '[,{metadata}]' in orig.__doc__:
+ for idx, rel in enumerate(relations):
+ prec = rel[0]
+ sucs = rel[1]
+ if not sucs:
+ meta = {}
+ if 2 < len(rel):
+ meta.update(rel[2])
+ for i, p in enumerate(prec.parents(), 1):
+ meta['p%i' % i] = p.hex()
+ relations[idx] = (prec, sucs, meta)
+ return orig(repo, relations, *args, **kwargs)
+
+def createmarkers(*args, **kwargs):
+ return obsolete.createmarkers(*args, **kwargs)
+
+class pruneobsstore(obsolete.obsstore):
+
+ def __init__(self, *args, **kwargs):
+ self.prunedchildren = {}
+ return super(pruneobsstore, self).__init__(*args, **kwargs)
+
+ def _load(self, markers):
+ markers = self._prunedetectingmarkers(markers)
+ return super(pruneobsstore, self)._load(markers)
+
+
+ def _prunedetectingmarkers(self, markers):
+ for m in markers:
+ if not m[1]: # no successors
+ meta = obsolete.decodemeta(m[3])
+ if 'p1' in meta:
+ p1 = node.bin(meta['p1'])
+ self.prunedchildren.setdefault(p1, set()).add(m)
+ if 'p2' in meta:
+ p2 = node.bin(meta['p2'])
+ self.prunedchildren.setdefault(p2, set()).add(m)
+ yield m
+
+obsolete.obsstore = pruneobsstore
+
+#####################################################################
### Critical fix ###
#####################################################################
@@ -326,34 +377,6 @@
getrevs = obsolete.getrevs
#####################################################################
-### Complete troubles computation logic ###
-#####################################################################
-
-
-### Cache computation
-latediff = 1 # flag to prevent taking late comer fix into account
-
-### changectx method
-
-@eh.addattr(context.changectx, 'latecomer')
-def latecomer(ctx):
- """is the changeset bumped (Try to succeed to public change)"""
- return ctx.bumped()
-
-@eh.addattr(context.changectx, 'conflicting')
-def conflicting(ctx):
- """is the changeset divergent (Try to succeed to public change)"""
- return ctx.divergent()
-
-### revset symbol
-
-eh.revset('latecomer')(revset.symbols['bumped'])
-eh.revset('conflicting')(revset.symbols['divergent'])
-
-
-
-
-#####################################################################
### Additional Utilities ###
#####################################################################
@@ -365,8 +388,6 @@
# - function to travel throught the obsolescence graph
# - function to find useful changeset to stabilize
-createmarkers = obsolete.createmarkers
-
### Useful alias
@@ -569,6 +590,7 @@
# XXX this could wrap transaction code
# XXX (but this is a bit a layer violation)
@eh.wrapcommand("commit")
+@eh.wrapcommand("import")
@eh.wrapcommand("push")
@eh.wrapcommand("pull")
@eh.wrapcommand("graft")
@@ -620,8 +642,7 @@
return result
repo.__class__ = evolvingrepo
-@eh.wrapcommand("summary")
-def obssummary(orig, ui, repo, *args, **kwargs):
+def summaryhook(ui, repo):
def write(fmt, count):
s = fmt % count
if count:
@@ -629,14 +650,16 @@
else:
ui.note(s)
- ret = orig(ui, repo, *args, **kwargs)
nbunstable = len(getrevs(repo, 'unstable'))
nbbumped = len(getrevs(repo, 'bumped'))
nbdivergent = len(getrevs(repo, 'divergent'))
write('unstable: %i changesets\n', nbunstable)
write('bumped: %i changesets\n', nbbumped)
write('divergent: %i changesets\n', nbdivergent)
- return ret
+
+@eh.extsetup
+def obssummarysetup(ui):
+ cmdutil.summaryhooks.add('evolve', summaryhook)
#####################################################################
@@ -660,7 +683,6 @@
except KeyError:
pass # rebase not found
-
#####################################################################
### Old Evolve extension content ###
#####################################################################
@@ -766,7 +788,10 @@
try:
rebase = extensions.find('rebase')
# dummy state to trick rebase node
- assert orig.p2().rev() == node.nullrev, 'no support yet'
+ if not orig.p2().rev() == node.nullrev:
+ raise util.Abort(
+ 'no support for evolution merge changesets yet',
+ hint="Redo the merge a use `hg prune` to obsolete the old one")
destbookmarks = repo.nodebookmarks(dest.node())
cmdutil.duplicatecopies(repo, orig.node(), dest.node())
nodesrc = orig.node()
@@ -835,6 +860,167 @@
_('record the specified user in metadata'), _('USER')),
]
+@command('debugrecordpruneparents', [], '')
+def cmddebugrecordpruneparents(ui, repo):
+ """add parents data to prune markers when possible
+
+ This commands search the repo for prune markers without parent information.
+ If the pruned node is locally known, a new markers with parent data is
+ created."""
+ 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]
+ meta = obsolete.decodemeta(mark[3])
+ for i, p in enumerate(ctx.parents(), 1):
+ meta['p%i' % i] = p.hex()
+ before = len(store._all)
+ store.create(tr, mark[0], mark[1], mark[2], meta)
+ 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:
+ if tr is not None:
+ tr.release()
+ lockmod.release(lock, wlock)
+
+@command('debugobsstorestat', [], '')
+def cmddebugobsstorestat(ui, repo):
+ """print statistic about obsolescence markers in the repo"""
+ 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
+ metatotallenght = 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
+ metatotallenght += len(mark[3])
+ meta = obsolete.decodemeta(mark[3])
+ for key in meta:
+ metakeys.setdefault(key, 0)
+ metakeys[key] += 1
+ 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()
+ nodes.add(mark[0])
+ nodes.update(mark[1])
+ 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
+ # same with parent data
+ nodes.update(parents)
+ c = (set(nodes), set([mark]))
+ toproceed = set(nodes)
+ while toproceed:
+ n = toproceed.pop()
+ other = pclustersmap.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
+ pclustersmap[on] = other
+ c = other
+ pclustersmap[n] = c
+
+ # 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('average meta length: %9i\n'
+ % (metatotallenght/len(store._all)))
+ 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)
@command('^evolve|stabilize|solve',
[('n', 'dry-run', False, 'do not perform actions, print what to be done'),
@@ -925,31 +1111,53 @@
ui.write_err(_('no troubled changesets\n'))
return 1
+ def progresscb():
+ if allopt:
+ ui.progress('evolve', seen, unit='changesets', total=count)
+ seen = 1
+ count = allopt and _counttroubled(ui, repo) or 1
+
while tr is not None:
- result = _evolveany(ui, repo, tr, dryrunopt)
+ progresscb()
+ result = _evolveany(ui, repo, tr, dryrunopt, progresscb=progresscb)
+ progresscb()
+ seen += 1
if not allopt:
return result
+ progresscb()
tr = _picknexttroubled(ui, repo, anyopt or allopt)
+ if allopt:
+ ui.progress('evolve', None)
-def _evolveany(ui, repo, tr, dryrunopt):
+
+def _evolveany(ui, repo, tr, dryrunopt, progresscb):
repo = repo.unfiltered()
tr = repo[tr.rev()]
cmdutil.bailifchanged(repo)
troubles = tr.troubles()
if 'unstable' in troubles:
- return _solveunstable(ui, repo, tr, dryrunopt)
+ return _solveunstable(ui, repo, tr, dryrunopt, progresscb)
elif 'bumped' in troubles:
- return _solvebumped(ui, repo, tr, dryrunopt)
+ return _solvebumped(ui, repo, tr, dryrunopt, progresscb)
elif 'divergent' in troubles:
repo = repo.unfiltered()
tr = repo[tr.rev()]
- return _solvedivergent(ui, repo, tr, dryrunopt)
+ return _solvedivergent(ui, repo, tr, dryrunopt, progresscb)
else:
assert False # WHAT? unknown troubles
-def _picknexttroubled(ui, repo, pickany=False):
+def _counttroubled(ui, repo):
+ """Count the amount of troubled changesets"""
+ troubled = set()
+ troubled.update(getrevs(repo, 'unstable'))
+ troubled.update(getrevs(repo, 'bumped'))
+ troubled.update(getrevs(repo, 'divergent'))
+ return len(troubled)
+
+def _picknexttroubled(ui, repo, pickany=False, progresscb=None):
"""Pick a the next trouble changeset to solve"""
+ if progresscb: progresscb()
tr = _stabilizableunstable(repo, repo['.'])
if tr is None:
wdp = repo['.']
@@ -988,7 +1196,7 @@
return child
return None
-def _solveunstable(ui, repo, orig, dryrun=False):
+def _solveunstable(ui, repo, orig, dryrun=False, progresscb=None):
"""Stabilize a unstable changeset"""
obs = orig.parents()[0]
if not obs.obsolete():
@@ -1019,11 +1227,13 @@
repo.ui.status(_('atop:'))
if not ui.quiet:
displayer.show(target)
+ if progresscb: progresscb()
todo = 'hg rebase -r %s -d %s\n' % (orig, target)
if dryrun:
repo.ui.write(todo)
else:
repo.ui.note(todo)
+ if progresscb: progresscb()
lock = repo.lock()
try:
relocate(repo, orig, target)
@@ -1035,7 +1245,7 @@
finally:
lock.release()
-def _solvebumped(ui, repo, bumped, dryrun=False):
+def _solvebumped(ui, repo, bumped, dryrun=False, progresscb=None):
"""Stabilize a bumped changeset"""
# For now we deny bumped merge
if len(bumped.parents()) > 1:
@@ -1061,6 +1271,7 @@
repo.ui.write('hg revert --all --rev %s;\n' % bumped)
repo.ui.write('hg commit --msg "bumped update to %s"')
return 0
+ if progresscb: progresscb()
wlock = repo.wlock()
try:
newid = tmpctx = None
@@ -1124,7 +1335,7 @@
else:
phases.retractboundary(repo, bumped.phase(), [newid])
createmarkers(repo, [(tmpctx, (repo[newid],))],
- flag=latediff)
+ flag=obsolete.bumpedfix)
bmupdate(newid)
tr.close()
repo.ui.status(_('commited as %s\n') % node.short(newid))
@@ -1137,17 +1348,40 @@
finally:
wlock.release()
-def _solvedivergent(ui, repo, divergent, dryrun=False):
+def _solvedivergent(ui, repo, divergent, dryrun=False, progresscb=None):
base, others = divergentdata(divergent)
if len(others) > 1:
- raise util.Abort("We do not handle split yet")
+ othersstr = "[%s]" % (','.join([str(i) for i in others]))
+ hint = ("changeset %d is divergent with a changeset that got splitted "
+ "| into multiple ones:\n[%s]\n"
+ "| This is not handled by automatic evolution yet\n"
+ "| You have to fallback to manual handling with commands as:\n"
+ "| - hg touch -D\n"
+ "| - hg prune\n"
+ "| \n"
+ "| You should contact your local evolution Guru for help.\n"
+ % (divergent, othersstr))
+ raise util.Abort("We do not handle divergence with split yet",
+ hint='')
other = others[0]
if divergent.phase() <= phases.public:
- raise util.Abort("We can't resolve this conflict from the public side")
+ raise util.Abort("We can't resolve this conflict from the public side",
+ hint="%s is public, try from %s" % (divergent, other))
if len(other.parents()) > 1:
- raise util.Abort("divergent changeset can't be a merge (yet)")
+ raise util.Abort("divergent changeset can't be a merge (yet)",
+ hint="You have to fallback to solving this by hand...\n"
+ "| This probably mean to redo the merge and use "
+ "| `hg prune` to kill older version.")
if other.p1() not in divergent.parents():
- raise util.Abort("parents are not common (not handled yet)")
+ raise util.Abort("parents are not common (not handled yet)",
+ hint="| %(d)s, %(o)s are not based on the same changeset."
+ "| With the current state of its implementation, "
+ "| evolve does not work in that case.\n"
+ "| rebase one of them next to the other and run "
+ "| this command again.\n"
+ "| - either: hg rebase -dest 'p1(%(d)s)' -r %(o)s"
+ "| - or: hg rebase -dest 'p1(%(d)s)' -r %(o)s"
+ % {'d': divergent, 'o': other})
displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
ui.status(_('merge:'))
@@ -1177,6 +1411,7 @@
repo.ui.status(_('updating to "local" conflict\n'))
hg.update(repo, divergent.rev())
repo.ui.note(_('merging divergent changeset\n'))
+ if progresscb: progresscb()
stats = merge.update(repo,
other.node(),
branchmerge=True,
@@ -1197,6 +1432,7 @@
/!\ * hg ci -m "same message as the amended changeset" => new cset Y
/!\ * hg kill -n Y W Z
""")
+ if progresscb: progresscb()
tr = repo.transaction('stabilize-divergent')
try:
repo.dirstate.setparents(divergent.node(), node.nullid)
@@ -1332,6 +1568,7 @@
[('n', 'new', [], _("successor changeset (DEPRECATED)")),
('s', 'succ', [], _("successor changeset")),
('r', 'rev', [], _("revisions to prune")),
+ ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")),
('B', 'bookmark', '', _("remove revs only reachable from given"
" bookmark"))] + metadataopts,
_('[OPTION] [-r] REV...'))
@@ -1347,13 +1584,19 @@
When the working directory parent is pruned the repository is updated to a
non obsolete parents.
- you can use the ``--succ`` option to informs mercurial that a newer version
+ You can use the ``--succ`` option to informs mercurial that a newer version
of the pruned changeset exists.
+
+ You can use the ``--biject`` option to specify a 1-1 (bijection) between
+ revisions to prune and successor changesets. This option may be removed in
+ a future release (with the functionality absored automatically).
+
"""
revs = set(scmutil.revrange(repo, list(revs) + opts.get('rev')))
succs = opts['new'] + opts['succ']
bookmark = opts.get('bookmark')
metadata = _getmetadata(**opts)
+ biject = opts.get('biject')
if bookmark:
marks,revs = _reachablefrombookmark(repo, revs, bookmark)
@@ -1383,11 +1626,20 @@
# defines successors changesets
sucs = tuple(repo[n] for n in sortedrevs(succs))
- if len(sucs) > 1 and len(precs) > 1:
+ if not biject and len(sucs) > 1 and len(precs) > 1:
msg = "Can't use multiple successors for multiple precursors"
raise util.Abort(msg)
+
+ if biject and len(sucs) != len(precs):
+ msg = "Can't use %d successors for %d precursors" % (len(sucs), len(precs))
+ raise util.Abort(msg)
+
+ relations = [(p, sucs) for p in precs]
+ if biject:
+ relations = [(p, (s,)) for p, s in zip(precs, sucs)]
+
# create markers
- createmarkers(repo, [(p, sucs) for p in precs], metadata=metadata)
+ createmarkers(repo, relations, metadata=metadata)
# informs that changeset have been pruned
ui.status(_('%i changesets pruned\n') % len(precs))
@@ -1779,3 +2031,105 @@
entry[1].append(('O', 'old-obsolete', False,
_("make graft obsoletes its source")))
+#####################################################################
+### Obsolescence marker exchange experimenation ###
+#####################################################################
+
+@command('debugobsoleterelevant',
+ [],
+ 'REVSET')
+def debugobsoleterelevant(ui, repo, *revsets):
+ """print allobsolescence marker relevant to a set of revision"""
+ nodes = [ctx.node() for ctx in repo.set('%lr', revsets)]
+ markers = repo.obsstore.relevantmarkers(nodes)
+ for rawmarker in sorted(markers):
+ marker = obsolete.marker(repo, rawmarker)
+ cmdutil.showmarker(ui, marker)
+
+@eh.addattr(obsolete.obsstore, 'relevantmarkers')
+def relevantmarkers(self, nodes):
+ """return a set of all obsolescence marker relevant to a set of node.
+
+ "relevant" to a set of node mean:
+
+ - marker that use this changeset as successors
+ - prune marker of direct children on this changeset.
+ - recursive application of the two rules on precursors of these markers
+
+ It a set so you cannot rely on order"""
+ seennodes = set(nodes)
+ seenmarkers = set()
+ pendingnodes = set(nodes)
+ precursorsmarkers = self.precursors
+ prunedchildren = self.prunedchildren
+ while pendingnodes:
+ direct = set()
+ for current in pendingnodes:
+ direct.update(precursorsmarkers.get(current, ()))
+ direct.update(prunedchildren.get(current, ()))
+ direct -= seenmarkers
+ pendingnodes = set([m[0] for m in direct])
+ seenmarkers |= direct
+ pendingnodes -= seennodes
+ seennodes |= pendingnodes
+ return seenmarkers
+
+
+_pushkeyescape = getattr(obsolete, '_pushkeyescape', None)
+if _pushkeyescape is None:
+ def _pushkeyescape(markers):
+ """encode markers into a dict suitable for pushkey exchange
+
+ - binary data are base86 encoded
+ - splited in chunk less than 5300 bytes"""
+ parts = []
+ currentlen = _maxpayload * 2 # ensure we create a new part
+ for marker in markers:
+ nextdata = _encodeonemarker(marker)
+ if (len(nextdata) + currentlen > _maxpayload):
+ currentpart = []
+ currentlen = 0
+ parts.append(currentpart)
+ currentpart.append(nextdata)
+ currentlen += len(nextdata)
+ keys = {}
+ for idx, part in enumerate(reversed(parts)):
+ data = ''.join([_pack('>B', _fmversion)] + part)
+ keys['dump%i' % idx] = base85.b85encode(data)
+ return keys
+
+
+
+@eh.wrapfunction(exchange, '_pushobsolete')
+def _pushobsolete(orig, pushop):
+ """utility function to push obsolete markers to a remote"""
+ pushop.ui.debug('try to push obsolete markers to remote\n')
+ repo = pushop.repo
+ remote = pushop.remote
+ unfi = repo.unfiltered()
+ if (obsolete._enabled and repo.obsstore and
+ 'obsolete' in remote.listkeys('namespaces')):
+ repo.ui.status("OBSEXC: computing relevant nodes\n")
+ nodes = [ctx.node() for ctx in unfi.set('::%ln', pushop.commonheads)]
+ repo.ui.status("OBSEXC: computing markers relevant to %i nodes\n"
+ % len(nodes))
+ markers = repo.obsstore.relevantmarkers(nodes)
+ rslts = []
+ repo.ui.status("OBSEXC: encoding %i markers\n" % len(markers))
+ remotedata = obsolete._pushkeyescape(markers).items()
+ totalbytes = sum(len(d) for k,d in remotedata)
+ sentbytes = 0
+ repo.ui.status("OBSEXC: sending %i pushkey payload (%i bytes)\n"
+ % (len(remotedata), totalbytes))
+ for key, data in remotedata:
+ repo.ui.progress('OBSEXC', sentbytes, item=key, unit="bytes",
+ total=totalbytes)
+ rslts.append(remote.pushkey('obsolete', key, '', data))
+ sentbytes += len(data)
+ repo.ui.progress('OBSEXC', sentbytes, item=key, unit="bytes",
+ total=totalbytes)
+ repo.ui.progress('OBSEXC', None)
+ if [r for r in rslts if not r]:
+ msg = _('failed to push some obsolete markers!\n')
+ repo.ui.warn(msg)
+ repo.ui.status("OBSEXC: DONE\n")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/hgfastobs.py Mon Mar 03 19:28:43 2014 -0800
@@ -0,0 +1,124 @@
+"""Extension to try and speed up transfer of obsolete markers.
+
+Mercurial 2.6 transfers obsolete markers in the dumbest way possible:
+it simply transfers all of them to the server on every
+operation. While this /works/, it's not ideal because it's a large
+amount of extra data for users to pull down (1.9M for the 17k obsolete
+markers in hg-crew as of this writing in late July 2013). It's also
+frustrating because this transfer takes a nontrivial amount of time.
+
+You can specify a strategy with the config knob
+obsolete.syncstrategy. Current strategies are "stock" and
+"boxfill". Default strategy is presently boxfill.
+
+:stock: use the default strategy of mercurial explaned above
+
+:boxfill: transmit obsolete markers which list any of transmitted changesets as
+ a successor (transitively), as well as any kill markers for dead
+ nodes descended from any of the precursors of outgoing.missing.
+
+TODO(durin42): consider better names for sync strategies.
+"""
+import sys
+
+from mercurial import base85
+from mercurial import commands
+from mercurial import extensions
+from mercurial import node
+from mercurial import obsolete
+from mercurial import exchange
+from mercurial import revset
+from mercurial.i18n import _
+
+_strategies = {
+ 'stock': exchange._pushobsolete,
+ }
+
+def _strategy(name, default=False):
+ def inner(func):
+ _strategies[name] = func
+ if default:
+ _strategies[None] = func
+ return func
+ return inner
+
+def _pushobsoletewrapper(orig, pushop):
+ stratfn = _strategies[pushop.repo.ui.config('obsolete', 'syncstrategy')]
+ return stratfn(pushop)
+
+extensions.wrapfunction(exchange, '_pushobsolete', _pushobsoletewrapper)
+
+def _precursors(repo, s):
+ """Precursor of a changeset"""
+ cs = set()
+ nm = repo.changelog.nodemap
+ markerbysubj = repo.obsstore.precursors
+ for r in s:
+ for p in markerbysubj.get(repo[r].node(), ()):
+ pr = nm.get(p[0])
+ if pr is not None:
+ cs.add(pr)
+ return cs
+
+def _revsetprecursors(repo, subset, x):
+ s = revset.getset(repo, revset.baseset(range(len(repo))), x)
+ cs = _precursors(repo, s)
+ return revset.baseset([r for r in subset if r in cs])
+
+revset.symbols['_fastobs_precursors'] = _revsetprecursors
+
+
+@_strategy('boxfill', default=True)
+def boxfill(pushop):
+ """The "fill in the box" strategy from the 2.6 sprint.
+
+ See the notes[0] from the 2.6 sprint for what "fill in the box"
+ means here. It's a fairly subtle algorithm, which may have
+ surprising behavior at times, but was the least-bad option
+ proposed at the sprint.
+
+ [0]: https://bitbucket.org/durin42/2.6sprint-notes/src/tip/mercurial26-obsstore-rev.1398.txt
+ """
+ repo = pushop.repo
+ remote = pushop.remote
+ outgoing = pushop.outgoing
+ urepo = pushop.repo.unfiltered()
+ # need to collect obsolete markers which list any of
+ # outgoing.missing as a successor (transitively), as well as any
+ # kill markers for dead nodes descended from any of the precursors
+ # of outgoing.missing.
+ boxedges = urepo.revs(
+ '(descendants(_fastobs_precursors(%ln)) or '
+ ' descendants(%ln)) and hidden()',
+ outgoing.missing, outgoing.missing)
+ transmit = []
+ for node in outgoing.missing:
+ transmit.extend(obsolete.precursormarkers(urepo[node]))
+ for rev in boxedges:
+ transmit.extend(obsolete.successormarkers(urepo[rev]))
+ transmit = list(set(transmit))
+ xmit, total = len(transmit), len(repo.obsstore._all)
+ repo.ui.status(
+ 'boxpush: about to transmit %d obsolete markers (%d markers total)\n'
+ % (xmit, total))
+ parts, size, chunk = [], 0, 0
+ def transmitmarks():
+ repo.ui.note(
+ 'boxpush: sending a chunk of obsolete markers\n')
+ data = ''.join([obsolete._pack('>B', obsolete._fmversion)] + parts)
+ remote.pushkey('obsolete', 'dump-%d' % chunk, '',
+ base85.b85encode(data))
+
+ for marker in transmit:
+ enc = obsolete._encodeonemarker(_markertuple(marker))
+ parts.append(enc)
+ size += len(enc)
+ if size > obsolete._maxpayload:
+ transmitmarks()
+ parts, size = [], 0
+ chunk += 1
+ if parts:
+ transmitmarks()
+
+def _markertuple(marker):
+ return marker._data
--- a/hgext/qsync.py Mon Mar 03 19:27:42 2014 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,266 +0,0 @@
-# Copyright 2011 Logilab SA <contact@logilab.fr>
-"""synchronize patches queues and evolving changesets"""
-
-import re
-from cStringIO import StringIO
-import json
-
-from mercurial.i18n import _
-from mercurial import commands
-from mercurial import patch
-from mercurial import util
-from mercurial.node import nullid, hex, short, bin
-from mercurial import cmdutil
-from mercurial import hg
-from mercurial import scmutil
-from mercurial import error
-from mercurial import extensions
-from mercurial import phases
-from mercurial import obsolete
-
-### old compat code
-#############################
-
-BRANCHNAME="qsubmit2"
-
-### new command
-#############################
-cmdtable = {}
-command = cmdutil.command(cmdtable)
-
-@command('^qsync|sync',
- [
- ('a', 'review-all', False, _('mark all touched patches ready for review (no editor)')),
- ],
- '')
-def cmdsync(ui, repo, **opts):
- '''Export draft changeset as mq patch in a mq patches repository commit.
-
- This command get all changesets in draft phase and create an mq changeset:
-
- * on a "qsubmit2" branch (based on the last changeset)
-
- * one patch per draft changeset
-
- * a series files listing all generated patch
-
- * qsubmitdata holding useful information
-
- It does use obsolete relation to update patches that already existing in the qsubmit2 branch.
-
- Already existing patch which became public, draft or got killed are remove from the mq repo.
-
- Patch name are generated using the summary line for changeset description.
-
- .. warning:: Series files is ordered topologically. So two series with
- interleaved changeset will appear interleaved.
- '''
-
- review = 'edit'
- if opts['review_all']:
- review = 'all'
- mqrepo = repo.mq.qrepo()
- if mqrepo is None:
- raise util.Abort('No patches repository')
-
- try:
- parent = mqrepo[BRANCHNAME]
- except error.RepoLookupError:
- parent = initqsubmit(mqrepo)
- store, data, touched = fillstore(repo, parent)
- try:
- if not touched:
- raise util.Abort('Nothing changed')
- files = ['qsubmitdata', 'series'] + touched
- # mark some as ready for review
- message = 'qsubmit commit\n\n'
- review_list = []
- applied_list = []
- if review:
- olddata = get_old_data(parent)
- oldfiles = dict([(name, bin(ctxhex)) for ctxhex, name in olddata])
-
- for patch_name in touched:
- try:
- store.getfile(patch_name)
- review_list.append(patch_name)
- except IOError:
- oldnode = oldfiles[patch_name]
- newnodes = obsolete.successorssets(repo, oldnode)
- if newnodes:
- newnodes = [n for n in newnodes if n and n[0] in repo] # remove killing
- if not newnodes:
- # changeset has been killed (eg. reject)
- pass
- else:
- assert len(newnodes) == 1 # conflict!!!
- newnode = newnodes[0]
- assert len(newnode) == 1 # split unsupported for now
- newnode = list(newnode)[0]
- # XXX unmanaged case where a cs is obsoleted by an unavailable one
- #if newnode.node() not in repo.changelog.nodemap:
- # raise util.Abort('%s is obsoleted by an unknown node %s'% (oldnode, newnode))
- ctx = repo[newnode]
- if ctx.phase() == phases.public:
- # applied
- applied_list.append(patch_name)
- elif ctx.phase() == phases.secret:
- # already exported changeset is now secret
- repo.ui.warn("An already exported changeset is now secret!!!")
- else:
- # draft
- assert False, "Should be exported"
-
- if review:
- if applied_list:
- message += '\n'.join('* applied %s' % x for x in applied_list) + '\n'
- if review_list:
- message += '\n'.join('* %s ready for review' % x for x in review_list) + '\n'
- memctx = patch.makememctx(mqrepo, (parent.node(), nullid),
- message,
- None,
- None,
- parent.branch(), files, store,
- editor=None)
- if review == 'edit':
- memctx._text = cmdutil.commitforceeditor(mqrepo, memctx, [])
- mqrepo.savecommitmessage(memctx.description())
- n = memctx.commit()
- finally:
- store.close()
- return 0
-
-
-def makename(ctx):
- """create a patch name form a changeset"""
- descsummary = ctx.description().splitlines()[0]
- descsummary = re.sub(r'\s+', '_', descsummary)
- descsummary = re.sub(r'\W+', '', descsummary)
- if len(descsummary) > 45:
- descsummary = descsummary[:42] + '.'
- return '%s-%s.diff' % (ctx.branch().upper(), descsummary)
-
-
-def get_old_data(mqctx):
- """read qsubmit data to fetch previous export data
-
- get old data from the content of an mq commit"""
- try:
- old_data = mqctx['qsubmitdata']
- return json.loads(old_data.data())
- except error.LookupError:
- return []
-
-def get_current_data(repo):
- """Return what would be exported if no previous data exists"""
- data = []
- for ctx in repo.set('draft() - (obsolete() + merge())'):
- name = makename(ctx)
- data.append([ctx.hex(), makename(ctx)])
- merges = repo.revs('draft() and merge()')
- if merges:
- repo.ui.warn('ignoring %i merge\n' % len(merges))
- return data
-
-
-def patchmq(repo, store, olddata, newdata):
- """export the mq patches and return all useful data to be exported"""
- finaldata = []
- touched = set()
- currentdrafts = set(d[0] for d in newdata)
- usednew = set()
- usedold = set()
- evolve = extensions.find('evolve')
- for oldhex, oldname in olddata:
- if oldhex in usedold:
- continue # no duplicate
- usedold.add(oldhex)
- oldname = str(oldname)
- oldnode = bin(oldhex)
- newnodes = obsolete.successorssets(repo, oldnode)
- if newnodes:
- newnodes = [n for n in newnodes if n and n[0] in repo] # remove killing
- if len(newnodes) > 1:
- newnodes = [short(nodes[0]) for nodes in newnodes]
- raise util.Abort('%s have more than one newer version: %s'% (oldname, newnodes))
- if newnodes:
- # else, changeset have been killed
- newnode = list(newnodes)[0][0]
- ctx = repo[newnode]
- if ctx.hex() != oldhex and ctx.phase():
- fp = StringIO()
- cmdutil.export(repo, [ctx.rev()], fp=fp)
- data = fp.getvalue()
- store.setfile(oldname, data, (None, None))
- finaldata.append([ctx.hex(), oldname])
- usednew.add(ctx.hex())
- touched.add(oldname)
- continue
- if oldhex in currentdrafts:
- # else changeset is now public or secret
- finaldata.append([oldhex, oldname])
- usednew.add(ctx.hex())
- continue
- touched.add(oldname)
-
- for newhex, newname in newdata:
- if newhex in usednew:
- continue
- newnode = bin(newhex)
- ctx = repo[newnode]
- fp = StringIO()
- cmdutil.export(repo, [ctx.rev()], fp=fp)
- data = fp.getvalue()
- store.setfile(newname, data, (None, None))
- finaldata.append([ctx.hex(), newname])
- touched.add(newname)
- # sort by branchrev number
- finaldata.sort(key=lambda x: sort_key(repo[x[0]]))
- # sort touched too (ease review list)
- stouched = [f[1] for f in finaldata if f[1] in touched]
- stouched += [x for x in touched if x not in stouched]
- return finaldata, stouched
-
-def sort_key(ctx):
- """ctx sort key: (branch, rev)"""
- return (ctx.branch(), ctx.rev())
-
-
-def fillstore(repo, basemqctx):
- """fill store with patch data"""
- olddata = get_old_data(basemqctx)
- newdata = get_current_data(repo)
- store = patch.filestore()
- try:
- data, touched = patchmq(repo, store, olddata, newdata)
- # put all name in the series
- series ='\n'.join(d[1] for d in data) + '\n'
- store.setfile('series', series, (False, False))
-
- # export data to ease futur work
- store.setfile('qsubmitdata', json.dumps(data, indent=True),
- (False, False))
- except:
- store.close()
- raise
- return store, data, touched
-
-
-def initqsubmit(mqrepo):
- """create initial qsubmit branch"""
- store = patch.filestore()
- try:
- files = set()
- store.setfile('DO-NOT-EDIT-THIS-WORKING-COPY-BY-HAND', 'WE WARNED YOU!', (False, False))
- store.setfile('.hgignore', '^status$\n', (False, False))
- memctx = patch.makememctx(mqrepo, (nullid, nullid),
- 'qsubmit init',
- None,
- None,
- BRANCHNAME, ('.hgignore',), store,
- editor=None)
- mqrepo.savecommitmessage(memctx.description())
- n = memctx.commit()
- finally:
- store.close()
- return mqrepo[n]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-boxpush.t Mon Mar 03 19:28:43 2014 -0800
@@ -0,0 +1,44 @@
+ $ fastobs="$TESTDIR"/../hgext/hgfastobs.py
+ $ echo 'from mercurial import obsolete ; obsolete._enabled = True' > enableobs.py
+ $ cat >> $HGRCPATH <<EOF
+ > [obsolete]
+ > syncstrategy = boxfill
+ > [extensions]
+ > EOF
+ $ echo "enable-obsolete = $PWD/enableobs.py" >> $HGRCPATH
+ $ echo "fastobs = $fastobs" >> $HGRCPATH
+
+ $ hg init alice
+ $ hg init bob
+ $ hg init trent
+ $ cd alice
+ $ echo a > a
+ $ hg addr && hg ci -m 'add a'
+ adding a
+ $ echo aa >> a
+ $ hg ci -m 'edit a'
+ $ echo aa > a
+ $ hg ci --amend -m 'edit a'
+ $ hg debugobsolete
+ e772e827cd64564621e7e5af15c9f848e3b92c8e efa8cd969bc37e6a1330c29f4234fe9e9be681b3 0 {'date': '* 0', 'user': 'test'} (glob)
+ 5ccfcbc00f2a19cd7affedce5ff087e68e67c6cc 0 {'date': '* 0', 'user': 'test'} (glob)
+ $ hg push ../trent
+ pushing to ../trent
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 1 files
+ boxpush: about to transmit 2 obsolete markers (2 markers total)
+ $ cd ../bob
+ $ hg pull ../trent
+ pulling from ../trent
+ requesting all changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 1 files
+ (run 'hg update' to get a working copy)
+ $ hg debugobsolete | sort
+ 5ccfcbc00f2a19cd7affedce5ff087e68e67c6cc 0 {'date': '* 0', 'user': 'test'} (glob)
+ e772e827cd64564621e7e5af15c9f848e3b92c8e efa8cd969bc37e6a1330c29f4234fe9e9be681b3 0 {'date': '* 0', 'user': 'test'} (glob)
--- a/tests/test-corrupt.t Mon Mar 03 19:27:42 2014 -0800
+++ b/tests/test-corrupt.t Mon Mar 03 19:28:43 2014 -0800
@@ -111,6 +111,11 @@
adding manifests
adding file changes
added 1 changesets with 2 changes to 2 files
+ OBSEXC: computing relevant nodes
+ OBSEXC: computing markers relevant to 4 nodes
+ OBSEXC: encoding 2 markers
+ OBSEXC: sending 1 pushkey payload (184 bytes)
+ OBSEXC: DONE
$ hg -R ../other verify
checking changesets
checking manifests
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-drop.t Mon Mar 03 19:28:43 2014 -0800
@@ -0,0 +1,267 @@
+
+ $ cat >> $HGRCPATH <<EOF
+ > [extensions]
+ > hgext.rebase=
+ > hgext.graphlog=
+ > EOF
+ $ echo "drophack=$(echo $(dirname $TESTDIR))/hgext/drophack.py" >> $HGRCPATH
+ $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+ $ mkcommit() {
+ > echo "$1" > "$1"
+ > hg add "$1"
+ > hg ci -m "add $1"
+ > }
+ $ summary() {
+ > echo ============ graph ==============
+ > hg log -G
+ > echo ============ hidden =============
+ > hg log --hidden -G
+ > echo ============ obsmark ============
+ > hg debugobsolete
+ > }
+
+
+ $ hg init repo
+ $ cd repo
+ $ mkcommit base
+
+drop a single changeset without any rewrite
+================================================
+
+
+ $ mkcommit simple-single
+ $ summary
+ ============ graph ==============
+ @ changeset: 1:d4e7845543ff
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add simple-single
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 1:d4e7845543ff
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add simple-single
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+ $ hg drop .
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ working directory now at b4952fcf48cf
+ search obsmarker: wall * comb * user * sys * (glob)
+ 0 obsmarkers found
+ search nodes: wall * comb * user * sys * (glob)
+ 1 nodes found
+ saved backup bundle to $TESTTMP/repo/.hg/strip-backup/d4e7845543ff-drophack.hg
+ strip nodes: wall * comb * user * sys * (glob)
+ $ summary
+ ============ graph ==============
+ @ changeset: 0:b4952fcf48cf
+ tag: tip
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 0:b4952fcf48cf
+ tag: tip
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+
+Try to drop a changeset with children
+================================================
+
+ $ mkcommit parent
+ $ mkcommit child
+ $ summary
+ ============ graph ==============
+ @ changeset: 2:34b6c051bf1f
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 2:34b6c051bf1f
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+ $ hg drop 1
+ cannot drop revision with children (no-eol)
+ [1]
+ $ summary
+ ============ graph ==============
+ @ changeset: 2:34b6c051bf1f
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 2:34b6c051bf1f
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+
+Try to drop a public changeset
+================================================
+
+ $ hg phase --public 2
+ $ hg drop 2
+ cannot drop public revision (no-eol)
+ [1]
+
+
+Try to drop a changeset with rewrite
+================================================
+
+ $ hg phase --force --draft 2
+ $ echo babar >> child
+ $ hg commit --amend
+ $ summary
+ ============ graph ==============
+ @ changeset: 4:a2c06c884bfe
+ | tag: tip
+ | parent: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 4:a2c06c884bfe
+ | tag: tip
+ | parent: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ | x changeset: 3:87ea30a976fd
+ | | user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: temporary amend commit for 34b6c051bf1f
+ | |
+ | x changeset: 2:34b6c051bf1f
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add child
+ |
+ o changeset: 1:19509a42b0d0
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+ 34b6c051bf1f78db6aef400776de5cb964470207 a2c06c884bfe53d3840026248bd8a7eafa152df8 0 {'date': '* *', 'user': 'test'} (glob)
+ 87ea30a976fdf235bf096f04899cb02a903873e2 0 {'date': '* *', 'user': 'test'} (glob)
+ $ hg drop .
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ working directory now at 19509a42b0d0
+ search obsmarker: wall * comb * user * sys * (glob)
+ 1 obsmarkers found
+ search nodes: wall * comb * user * sys * (glob)
+ 2 nodes found
+ strip obsmarker: wall * comb * user * sys * (glob)
+ saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-drophack.hg (glob)
+ strip nodes: wall * comb * user * sys * (glob)
+ $ summary
+ ============ graph ==============
+ @ changeset: 1:19509a42b0d0
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ hidden =============
+ @ changeset: 1:19509a42b0d0
+ | tag: tip
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add parent
+ |
+ o changeset: 0:b4952fcf48cf
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add base
+
+ ============ obsmark ============
+ 87ea30a976fdf235bf096f04899cb02a903873e2 0 {'date': '* *', 'user': 'test'} (glob)
--- a/tests/test-evolve.t Mon Mar 03 19:27:42 2014 -0800
+++ b/tests/test-evolve.t Mon Mar 03 19:28:43 2014 -0800
@@ -645,3 +645,30 @@
4 : add 4 - test
5 : add 3 - test
11 : add 1 - test
+
+Test obsstore stat
+
+ $ hg debugobsstorestat
+ markers total: 10
+ for known precursors: 10
+ with parents data: 0
+ markers with no successors: 0
+ 1 successors: 10
+ 2 successors: 0
+ more than 2 successors: 0
+ average meta length: 27
+ available keys:
+ date: 10
+ user: 10
+ disconnected clusters: 1
+ any known node: 1
+ smallest length: 10
+ longer length: 10
+ median length: 10
+ mean length: 10
+ using parents data: 1
+ any known node: 1
+ smallest length: 10
+ longer length: 10
+ median length: 10
+ mean length: 10
--- a/tests/test-obsolete.t Mon Mar 03 19:27:42 2014 -0800
+++ b/tests/test-obsolete.t Mon Mar 03 19:28:43 2014 -0800
@@ -181,6 +181,11 @@
adding manifests
adding file changes
added 5 changesets with 5 changes to 5 files (+1 heads)
+ OBSEXC: computing relevant nodes
+ OBSEXC: computing markers relevant to 5 nodes
+ OBSEXC: encoding 2 markers
+ OBSEXC: sending 1 pushkey payload (154 bytes)
+ OBSEXC: DONE
$ hg -R ../other-new verify
checking changesets
checking manifests
@@ -234,6 +239,11 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
+ OBSEXC: computing relevant nodes
+ OBSEXC: computing markers relevant to 5 nodes
+ OBSEXC: encoding 3 markers
+ OBSEXC: sending 1 pushkey payload (230 bytes)
+ OBSEXC: DONE
$ qlog -R ../other-new
5
- 95de7fc6918d
@@ -255,6 +265,11 @@
pushing to ../other-new
searching for changes
no changes found
+ OBSEXC: computing relevant nodes
+ OBSEXC: computing markers relevant to 5 nodes
+ OBSEXC: encoding 3 markers
+ OBSEXC: sending 1 pushkey payload (230 bytes)
+ OBSEXC: DONE
[1]
$ hg up --hidden -q .^ # 3
@@ -526,6 +541,11 @@
adding manifests
adding file changes
added 2 changesets with 1 changes to [12] files (re)
+ OBSEXC: computing relevant nodes
+ OBSEXC: computing markers relevant to 5 nodes
+ OBSEXC: encoding 7 markers
+ OBSEXC: sending 1 pushkey payload (565 bytes)
+ OBSEXC: DONE
$ hg up -q 10
$ mkcommit "obsol_d'''"
created new head
@@ -537,6 +557,11 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
+ OBSEXC: computing relevant nodes
+ OBSEXC: computing markers relevant to 5 nodes
+ OBSEXC: encoding 8 markers
+ OBSEXC: sending 1 pushkey payload (642 bytes)
+ OBSEXC: DONE
$ cd ..
check bumped detection
@@ -637,7 +662,7 @@
#no produced by 2.3
33d458d86621f3186c40bfccd77652f4a122743e 3734a65252e69ddcced85901647a4f335d40de1e 0 {'date': '* *', 'user': 'test'} (glob)
-Check conflict detection
+Check divergence detection
$ hg up 9468a5f5d8b2 # add obsol_d''
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -650,7 +675,7 @@
update: (2|9|11) new changesets, (3|9|10) branch heads \(merge\) (re)
bumped: 1 changesets
$ hg debugobsolete `getid a7a6f2b5d8a5` `getid 50f11e5e3a63`
- $ hg log -r 'conflicting()'
+ $ hg log -r 'divergent()'
changeset: 12:6db5e282cb91
parent: 10:2033b4e49474
user: test
@@ -678,3 +703,141 @@
date: Thu Jan 01 00:00:00 1970 +0000
summary: add c
+
+Check import reports new unstable changeset:
+
+ $ hg up --hidden 2
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ working directory parent is obsolete!
+ $ hg export 9468a5f5d8b2 | hg import -
+ applying patch from stdin
+ 1 new unstable changesets
+
+
+Relevant marker computation
+==============================
+
+ $ hg log -G --hidden
+ @ changeset: 17:a5f7a21fe7bc
+ | tag: tip
+ | parent: 2:4538525df7e2
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add obsol_d''
+ |
+ | o changeset: 16:50f11e5e3a63
+ | | parent: 11:9468a5f5d8b2
+ | | user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: add obsolet_conflicting_d
+ | |
+ | | o changeset: 15:705ab2a6b72e
+ | | | parent: 10:2033b4e49474
+ | | | user: test
+ | | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | | summary: add f
+ | | |
+ | | | x changeset: 14:33d458d86621
+ | | | | user: test
+ | | | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | | | summary: temporary amend commit for 0b1b6dd009c0
+ | | | |
+ | | | x changeset: 13:0b1b6dd009c0
+ | | |/ parent: 10:2033b4e49474
+ | | | user: test
+ | | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | | summary: add f
+ | | |
+ | | | o changeset: 12:6db5e282cb91
+ | | |/ parent: 10:2033b4e49474
+ | | | user: test
+ | | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | | summary: add obsol_d'''
+ | | |
+ | o | changeset: 11:9468a5f5d8b2
+ | |/ user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: add obsol_d''
+ | |
+ | o changeset: 10:2033b4e49474
+ | | parent: 4:725c380fe99b
+ | | user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: add obsol_c
+ | |
+ | | x changeset: 9:83b5778897ad
+ | | parent: -1:000000000000
+ | | user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: add toto
+ | |
+ | | x changeset: 8:159dfc9fa5d3
+ | | | parent: 3:0d3f46688ccc
+ | | | user: test
+ | | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | | summary: add obsol_d''
+ | | |
+ | | | x changeset: 7:909a0fb57e5d
+ | | |/ parent: 3:0d3f46688ccc
+ | | | user: test
+ | | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | | summary: add obsol_d'
+ | | |
+ | | | x changeset: 6:95de7fc6918d
+ | | |/ parent: 3:0d3f46688ccc
+ | | | user: test
+ | | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | | summary: add obsol_d
+ | | |
+ | | | x changeset: 5:a7a6f2b5d8a5
+ | | |/ parent: 3:0d3f46688ccc
+ | | | user: test
+ | | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | | summary: add d
+ | | |
+ | o | changeset: 4:725c380fe99b
+ | | | parent: 1:7c3bad9141dc
+ | | | user: test
+ | | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | | summary: add obsol_c'
+ | | |
+ | | x changeset: 3:0d3f46688ccc
+ | |/ parent: 1:7c3bad9141dc
+ | | user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: add obsol_c
+ | |
+ x | changeset: 2:4538525df7e2
+ |/ user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add c
+ |
+ o changeset: 1:7c3bad9141dc
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: add b
+ |
+ o changeset: 0:1f0dee641bb7
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add a
+
+
+Simple rewrite
+
+ $ hg --hidden debugobsoleterelevant 3
+ 4538525df7e2b9f09423636c61ef63a4cb872a2d 0d3f46688ccc6e756c7e96cf64c391c411309597 0 {'date': '', 'user': 'test'}
+
+simple rewrite with a prune attached to it
+
+ $ hg debugobsoleterelevant 15
+ 0b1b6dd009c037985363e2290a0b579819f659db 705ab2a6b72e2cd86edb799ebe15f2695f86143e 0 {'date': '* *', 'user': 'test'} (glob)
+ 33d458d86621f3186c40bfccd77652f4a122743e 0 {'date': '* *', 'p1': '0b1b6dd009c037985363e2290a0b579819f659db', 'user': 'test'} (glob)
+
+Transitive rewrite
+
+ $ hg --hidden debugobsoleterelevant 8
+ 909a0fb57e5d909f353d89e394ffd7e0890fec88 159dfc9fa5d334d7e03a0aecfc7f7ab4c3431fea 0 {'date': '', 'user': 'test'}
+ 95de7fc6918dea4c9c8d5382f50649794b474c4a 909a0fb57e5d909f353d89e394ffd7e0890fec88 0 {'date': '', 'user': 'test'}
+ a7a6f2b5d8a54b81bc7aa2fba2934ad6d700a79e 95de7fc6918dea4c9c8d5382f50649794b474c4a 0 {'date': '', 'user': 'test'}
+
--- a/tests/test-prune.t Mon Mar 03 19:27:42 2014 -0800
+++ b/tests/test-prune.t Mon Mar 03 19:28:43 2014 -0800
@@ -43,7 +43,7 @@
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
working directory now at 47d2a3944de8
$ hg debugobsolete
- 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', 'user': 'blah'} (glob)
+ 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', ('p1': '47d2a3944de8b013de3be9578e8e344ea2e6c097', )?'user': 'blah'} (re)
prune leaving unstability behind
@@ -51,8 +51,8 @@
1 changesets pruned
2 new unstable changesets
$ hg debugobsolete
- 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', 'user': 'blah'} (glob)
- 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '*', 'user': 'test'} (glob)
+ 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', ('p1': '47d2a3944de8b013de3be9578e8e344ea2e6c097', )?'user': 'blah'} (re)
+ 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '\d+ \d+', ('p1': '1f0dee641bb7258c56bd60e93edfa2405381c41e', )?'user': 'test'} (re)
pruning multiple changeset at once
@@ -61,10 +61,10 @@
0 files updated, 0 files merged, 3 files removed, 0 files unresolved
working directory now at 1f0dee641bb7
$ hg debugobsolete
- 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', 'user': 'blah'} (glob)
- 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '*', 'user': 'test'} (glob)
- 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '*', 'user': 'test'} (glob)
- 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '*', 'user': 'test'} (glob)
+ 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', ('p1': '47d2a3944de8b013de3be9578e8e344ea2e6c097', )?'user': 'blah'} (re)
+ 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '\d+ \d+', ('p1': '1f0dee641bb7258c56bd60e93edfa2405381c41e', )?'user': 'test'} (re)
+ 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '\d+ \d+', ('p1': '7c3bad9141dcb46ff89abf5f61856facd56e476c', )?'user': 'test'} (re)
+ 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '\d+ \d+', ('p1': '4538525df7e2b9f09423636c61ef63a4cb872a2d', )?'user': 'test'} (re)
cannot prune public changesets
@@ -73,10 +73,10 @@
(see "hg help phases" for details)
[255]
$ hg debugobsolete
- 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', 'user': 'blah'} (glob)
- 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '*', 'user': 'test'} (glob)
- 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '*', 'user': 'test'} (glob)
- 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '*', 'user': 'test'} (glob)
+ 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', ('p1': '47d2a3944de8b013de3be9578e8e344ea2e6c097', )?'user': 'blah'} (re)
+ 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '\d+ \d+', ('p1': '1f0dee641bb7258c56bd60e93edfa2405381c41e', )?'user': 'test'} (re)
+ 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '\d+ \d+', ('p1': '7c3bad9141dcb46ff89abf5f61856facd56e476c', )?'user': 'test'} (re)
+ 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '\d+ \d+', ('p1': '4538525df7e2b9f09423636c61ef63a4cb872a2d', )?'user': 'test'} (re)
Check successors addition
----------------------------
@@ -118,11 +118,11 @@
$ hg prune 'desc("add ee")' -s 'desc("add nE")'
1 changesets pruned
$ hg debugobsolete
- 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', 'user': 'blah'} (glob)
- 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '*', 'user': 'test'} (glob)
- 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '*', 'user': 'test'} (glob)
- 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '*', 'user': 'test'} (glob)
- bb5e90a7ea1f3b4b38b23150a4a597b6146d70ef 6e8148413dd541855b72a920a90c06fca127c7e7 0 {'date': '*', 'user': 'test'} (glob)
+ 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', ('p1': '47d2a3944de8b013de3be9578e8e344ea2e6c097', )?'user': 'blah'} (re)
+ 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '\d+ \d+', ('p1': '1f0dee641bb7258c56bd60e93edfa2405381c41e', )?'user': 'test'} (re)
+ 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '\d+ \d+', ('p1': '7c3bad9141dcb46ff89abf5f61856facd56e476c', )?'user': 'test'} (re)
+ 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '\d+ \d+', ('p1': '4538525df7e2b9f09423636c61ef63a4cb872a2d', )?'user': 'test'} (re)
+ bb5e90a7ea1f3b4b38b23150a4a597b6146d70ef 6e8148413dd541855b72a920a90c06fca127c7e7 0 {'date': '\d+ \d+', 'user': 'test'} (re)
$ hg log -G
@ 12:6e8148413dd5[] (stable/draft) add nE
|
@@ -146,10 +146,10 @@
$ hg prune 'desc("add dd")' -s 'desc("add nD")' -s 'desc("add nC")'
1 changesets pruned
$ hg debugobsolete
- 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', 'user': 'blah'} (glob)
- 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '*', 'user': 'test'} (glob)
- 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '*', 'user': 'test'} (glob)
- 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '*', 'user': 'test'} (glob)
+ 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', ('p1': '47d2a3944de8b013de3be9578e8e344ea2e6c097', )?'user': 'blah'} (re)
+ 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '\d+ \d+', ('p1': '1f0dee641bb7258c56bd60e93edfa2405381c41e', )?'user': 'test'} (re)
+ 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '\d+ \d+', ('p1': '7c3bad9141dcb46ff89abf5f61856facd56e476c', )?'user': 'test'} (re)
+ 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '\d+ \d+', ('p1': '4538525df7e2b9f09423636c61ef63a4cb872a2d', )?'user': 'test'} (re)
bb5e90a7ea1f3b4b38b23150a4a597b6146d70ef 6e8148413dd541855b72a920a90c06fca127c7e7 0 {'date': '*', 'user': 'test'} (glob)
00ded550b1e28bba454bd34cec1269d22cf3ef25 aa96dc3f04c2c2341fe6880aeb6dc9fbffff9ef9 8ee176ff1d4b2034ce51e3efc579c2de346b631d 0 {'date': '**', 'user': 'test'} (glob)
$ hg log -G
@@ -174,10 +174,10 @@
abort: Can't use multiple successors for multiple precursors
[255]
$ hg debugobsolete
- 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', 'user': 'blah'} (glob)
- 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '*', 'user': 'test'} (glob)
- 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '*', 'user': 'test'} (glob)
- 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '*', 'user': 'test'} (glob)
+ 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', ('p1': '47d2a3944de8b013de3be9578e8e344ea2e6c097', )?'user': 'blah'} (re)
+ 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '\d+ \d+', ('p1': '1f0dee641bb7258c56bd60e93edfa2405381c41e', )?'user': 'test'} (re)
+ 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '\d+ \d+', ('p1': '7c3bad9141dcb46ff89abf5f61856facd56e476c', )?'user': 'test'} (re)
+ 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '\d+ \d+', ('p1': '4538525df7e2b9f09423636c61ef63a4cb872a2d', )?'user': 'test'} (re)
bb5e90a7ea1f3b4b38b23150a4a597b6146d70ef 6e8148413dd541855b72a920a90c06fca127c7e7 0 {'date': '*', 'user': 'test'} (glob)
00ded550b1e28bba454bd34cec1269d22cf3ef25 aa96dc3f04c2c2341fe6880aeb6dc9fbffff9ef9 8ee176ff1d4b2034ce51e3efc579c2de346b631d 0 {'date': '**', 'user': 'test'} (glob)
@@ -186,15 +186,39 @@
$ hg prune 'desc("add cc")' 'desc("add bb")' -s 'desc("add nB")'
2 changesets pruned
$ hg debugobsolete
- 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', 'user': 'blah'} (glob)
- 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '*', 'user': 'test'} (glob)
- 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '*', 'user': 'test'} (glob)
- 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '*', 'user': 'test'} (glob)
+ 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', ('p1': '47d2a3944de8b013de3be9578e8e344ea2e6c097', )?'user': 'blah'} (re)
+ 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '\d+ \d+', ('p1': '1f0dee641bb7258c56bd60e93edfa2405381c41e', )?'user': 'test'} (re)
+ 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '\d+ \d+', ('p1': '7c3bad9141dcb46ff89abf5f61856facd56e476c', )?'user': 'test'} (re)
+ 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '\d+ \d+', ('p1': '4538525df7e2b9f09423636c61ef63a4cb872a2d', )?'user': 'test'} (re)
bb5e90a7ea1f3b4b38b23150a4a597b6146d70ef 6e8148413dd541855b72a920a90c06fca127c7e7 0 {'date': '*', 'user': 'test'} (glob)
00ded550b1e28bba454bd34cec1269d22cf3ef25 aa96dc3f04c2c2341fe6880aeb6dc9fbffff9ef9 8ee176ff1d4b2034ce51e3efc579c2de346b631d 0 {'date': '**', 'user': 'test'} (glob)
814c38b95e72dfe2cbf675b1649ea9d780c89a80 6f6f25e4f748d8f7571777e6e168aedf50350ce8 0 {'date': '*', 'user': 'test'} (glob)
354011cd103f58bbbd9091a3cee6d6a6bd0dddf7 6f6f25e4f748d8f7571777e6e168aedf50350ce8 0 {'date': '*', 'user': 'test'} (glob)
+two old, two new with --biject
+
+ $ hg up 0
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ $ mkcommit n1
+ created new head
+ $ mkcommit n2
+
+ $ hg prune 'desc("add n1")::desc("add n2")' -s 'desc("add nD")::desc("add nE")' --biject
+ 2 changesets pruned
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ working directory now at 1f0dee641bb7
+ $ hg debugobsolete
+ 9d206ffc875e1bc304590549be293be36821e66c 0 {'date': '314064000 0', ('p1': '47d2a3944de8b013de3be9578e8e344ea2e6c097', )?'user': 'blah'} (re)
+ 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {'date': '\d+ \d+', ('p1': '1f0dee641bb7258c56bd60e93edfa2405381c41e', )?'user': 'test'} (re)
+ 4538525df7e2b9f09423636c61ef63a4cb872a2d 0 {'date': '\d+ \d+', ('p1': '7c3bad9141dcb46ff89abf5f61856facd56e476c', )?'user': 'test'} (re)
+ 47d2a3944de8b013de3be9578e8e344ea2e6c097 0 {'date': '\d+ \d+', ('p1': '4538525df7e2b9f09423636c61ef63a4cb872a2d', )?'user': 'test'} (re)
+ bb5e90a7ea1f3b4b38b23150a4a597b6146d70ef 6e8148413dd541855b72a920a90c06fca127c7e7 0 {'date': '*', 'user': 'test'} (glob)
+ 00ded550b1e28bba454bd34cec1269d22cf3ef25 aa96dc3f04c2c2341fe6880aeb6dc9fbffff9ef9 8ee176ff1d4b2034ce51e3efc579c2de346b631d 0 {'date': '**', 'user': 'test'} (glob)
+ 814c38b95e72dfe2cbf675b1649ea9d780c89a80 6f6f25e4f748d8f7571777e6e168aedf50350ce8 0 {'date': '* *', 'user': 'test'} (glob)
+ 354011cd103f58bbbd9091a3cee6d6a6bd0dddf7 6f6f25e4f748d8f7571777e6e168aedf50350ce8 0 {'date': '* *', 'user': 'test'} (glob)
+ cb7f8f706a6532967b98cf8583a81baab79a0fa7 8ee176ff1d4b2034ce51e3efc579c2de346b631d 0 {'date': '* *', 'user': 'test'} (glob)
+ 21b6f2f1cece8c10326e575dd38239189d467190 6e8148413dd541855b72a920a90c06fca127c7e7 0 {'date': '* *', 'user': 'test'} (glob)
+
test hg prune -B bookmark
yoinked from test-mq-strip.t
@@ -232,3 +256,28 @@
abort: unknown revision '2702dd0c91e7'!
[255]
+ $ hg debugobsstorestat
+ markers total: 4
+ for known precursors: 4
+ with parents data: [04] (re)
+ markers with no successors: 4
+ 1 successors: 0
+ 2 successors: 0
+ more than 2 successors: 0
+ average meta length: (27|71) (re)
+ available keys:
+ date: 4
+ p1: [04] (re)
+ user: 4
+ disconnected clusters: 4
+ any known node: 4
+ smallest length: 1
+ longer length: 1
+ median length: 1
+ mean length: 1
+ using parents data: [42] (re)
+ any known node: 4
+ smallest length: 1
+ longer length: [13] (re)
+ median length: [13] (re)
+ mean length: [12] (re)
--- a/tests/test-qsync.t Mon Mar 03 19:27:42 2014 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,239 +0,0 @@
- $ cat >> $HGRCPATH <<EOF
- > [defaults]
- > amend=-d "0 0"
- > [web]
- > push_ssl = false
- > allow_push = *
- > [phases]
- > publish = False
- > [alias]
- > qlog = log --template='{rev} - {node|short} {desc} ({phase})\n'
- > mqlog = log --mq --template='{rev} - {desc}\n'
- > [diff]
- > git = 1
- > unified = 0
- > [extensions]
- > hgext.rebase=
- > hgext.graphlog=
- > hgext.mq=
- > EOF
- $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
- $ echo "qsync=$(echo $(dirname $TESTDIR))/hgext/qsync.py" >> $HGRCPATH
- $ mkcommit() {
- > echo "$1" > "$1"
- > hg add "$1"
- > hg ci -m "add $1"
- > }
-
-basic sync
-
- $ hg init local
- $ cd local
- $ hg qinit -c
- $ hg qci -m "initial commit"
- $ mkcommit a
- $ mkcommit b
- $ hg qlog
- 1 - 7c3bad9141dc add b (draft)
- 0 - 1f0dee641bb7 add a (draft)
- $ hg qsync -a
- $ hg mqlog
- 2 - qsubmit commit
-
- * DEFAULT-add_a.diff ready for review
- * DEFAULT-add_b.diff ready for review
- 1 - qsubmit init
- 0 - initial commit
-
-basic sync II
-
- $ hg init local
- $ cd local
- $ hg qinit -c
- $ hg qci -m "initial commit"
- $ mkcommit a
- $ mkcommit b
- $ hg qlog
- 1 - 7c3bad9141dc add b (draft)
- 0 - 1f0dee641bb7 add a (draft)
- $ hg qsync -a
- $ hg mqlog
- 2 - qsubmit commit
-
- * DEFAULT-add_a.diff ready for review
- * DEFAULT-add_b.diff ready for review
- 1 - qsubmit init
- 0 - initial commit
-
- $ echo "b" >> b
- $ hg amend
- $ hg qsync -a
- $ hg mqlog
- 3 - qsubmit commit
-
- * DEFAULT-add_b.diff ready for review
- 2 - qsubmit commit
-
- * DEFAULT-add_a.diff ready for review
- * DEFAULT-add_b.diff ready for review
- 1 - qsubmit init
- 0 - initial commit
-
- $ hg up -r 0
- 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- $ echo "a" >> a
- $ hg amend
- 1 new unstable changesets
- $ hg graft -O 3
- grafting revision 3
- $ hg qsync -a
- $ hg mqlog
- 4 - qsubmit commit
-
- * DEFAULT-add_a.diff ready for review
- * DEFAULT-add_b.diff ready for review
- 3 - qsubmit commit
-
- * DEFAULT-add_b.diff ready for review
- 2 - qsubmit commit
-
- * DEFAULT-add_a.diff ready for review
- * DEFAULT-add_b.diff ready for review
- 1 - qsubmit init
- 0 - initial commit
-
-sync with published changeset
-
- $ hg init local
- $ cd local
- $ hg qinit -c
- $ hg qci -m "initial commit"
- $ mkcommit a
- $ mkcommit b
- $ hg qlog
- 1 - 7c3bad9141dc add b (draft)
- 0 - 1f0dee641bb7 add a (draft)
- $ hg qsync -a
- $ hg mqlog
- 2 - qsubmit commit
-
- * DEFAULT-add_a.diff ready for review
- * DEFAULT-add_b.diff ready for review
- 1 - qsubmit init
- 0 - initial commit
-
- $ hg phase -p 0
- $ hg qsync -a
- $ hg mqlog
- 3 - qsubmit commit
-
- * applied DEFAULT-add_a.diff
- 2 - qsubmit commit
-
- * DEFAULT-add_a.diff ready for review
- * DEFAULT-add_b.diff ready for review
- 1 - qsubmit init
- 0 - initial commit
-
- $ mkcommit c
- $ mkcommit d
- $ hg qsync -a
- $ hg mqlog
- 4 - qsubmit commit
-
- * DEFAULT-add_c.diff ready for review
- * DEFAULT-add_d.diff ready for review
- 3 - qsubmit commit
-
- * applied DEFAULT-add_a.diff
- 2 - qsubmit commit
-
- * DEFAULT-add_a.diff ready for review
- * DEFAULT-add_b.diff ready for review
- 1 - qsubmit init
- 0 - initial commit
-
- $ cd ..
- $ hg qclone -U local local2
- $ cd local2
- $ hg qlog
- 3 - 47d2a3944de8 add d (draft)
- 2 - 4538525df7e2 add c (draft)
- 1 - 7c3bad9141dc add b (draft)
- 0 - 1f0dee641bb7 add a (public)
- $ hg strip -n 1 --no-backup
- $ hg up
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ hg up --mq 4
- 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ hg qseries
- DEFAULT-add_b.diff
- DEFAULT-add_c.diff
- DEFAULT-add_d.diff
- $ hg qpush
- applying DEFAULT-add_b.diff
- now at: DEFAULT-add_b.diff
- $ hg qfinish -a
- $ hg phase -p .
- $ hg qci -m "applied DEFAULT-add_b.diff"
- $ cd ../local
- $ hg pull ../local2
- pulling from ../local2
- searching for changes
- no changes found
- $ hg pull --mq ../local2/.hg/patches
- pulling from ../local2/.hg/patches
- 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 qlog
- 3 - 47d2a3944de8 add d (draft)
- 2 - 4538525df7e2 add c (draft)
- 1 - 7c3bad9141dc add b (public)
- 0 - 1f0dee641bb7 add a (public)
- $ hg mqlog -l 1
- 5 - applied DEFAULT-add_b.diff
- $ hg status --mq --rev tip:-2
- M series
- A DEFAULT-add_b.diff
- $ hg qsync -a
- $ hg status --mq --rev tip:-2
- M qsubmitdata
- $ hg mqlog -l 1
- 6 - qsubmit commit
-
- * applied DEFAULT-add_b.diff
- $ hg qsync -a
- abort: Nothing changed
- [255]
-
-mixed sync
-
- $ hg init local
- $ cd local
- $ hg qinit -c
- $ mkcommit a
- $ mkcommit b
- $ hg qlog
- 1 - 7c3bad9141dc add b (draft)
- 0 - 1f0dee641bb7 add a (draft)
- $ hg qsync -a
- $ hg mqlog
- 1 - qsubmit commit
-
- * DEFAULT-add_a.diff ready for review
- * DEFAULT-add_b.diff ready for review
- 0 - qsubmit init
- $ hg phase -p 0
- $ echo "b" >> b
- $ hg amend
- $ hg qsync -a
- $ hg mqlog -l 1
- 2 - qsubmit commit
-
- * applied DEFAULT-add_a.diff
- * DEFAULT-add_b.diff ready for review
-
--- a/tests/test-stabilize-result.t Mon Mar 03 19:27:42 2014 -0800
+++ b/tests/test-stabilize-result.t Mon Mar 03 19:28:43 2014 -0800
@@ -171,7 +171,7 @@
o 0:07f494440405@default(public) bk:[] adda
-Stabilize conflicting changesets with same parent
+Stabilize divergenent changesets with same parent
=================================================
$ rm a.orig
@@ -291,7 +291,7 @@
+conflict
+babar
-Check conflicting during conflicting resolution
+Check conflict during divergence resolution
-------------------------------------------------
$ hg up --hidden 15
--- a/tests/test-tutorial.t Mon Mar 03 19:27:42 2014 -0800
+++ b/tests/test-tutorial.t Mon Mar 03 19:28:43 2014 -0800
@@ -402,6 +402,11 @@
adding manifests
adding file changes
added 3 changesets with 3 changes to 1 files
+ OBSEXC: computing relevant nodes
+ OBSEXC: computing markers relevant to 5 nodes
+ OBSEXC: encoding 6 markers
+ OBSEXC: sending 1 pushkey payload (609 bytes)
+ OBSEXC: DONE
for simplicity sake we get the bathroom change in line again
@@ -712,6 +717,11 @@
adding manifests
adding file changes
added 2 changesets with 2 changes to 1 files (+1 heads)
+ OBSEXC: computing relevant nodes
+ OBSEXC: computing markers relevant to 7 nodes
+ OBSEXC: encoding 10 markers
+ OBSEXC: sending 1 pushkey payload (1004 bytes)
+ OBSEXC: DONE
remote get a warning that current working directory is based on an obsolete changeset
@@ -826,7 +836,7 @@
Handling Divergent amend
----------------------------------------------
-We can detect that multiple diverging/conflicting amendments have been made.
+We can detect that multiple diverging amendments have been made.
The `evolve` command can solve this situation. But all corner case are not
handled now.