# HG changeset patch # User Pierre-Yves David # Date 1393903723 28800 # Node ID 0b6af104fd78158e46101af925e3b8366bb38652 # Parent c2bf0eb727f1172e614d8f577a134865fbdfb19a# Parent fcdd9b8c970b82764c07e5dbca6cab89ddfdabbe merge with stable diff -r fcdd9b8c970b -r 0b6af104fd78 README --- 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 diff -r fcdd9b8c970b -r 0b6af104fd78 hgext/drophack.py --- /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 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. diff -r fcdd9b8c970b -r 0b6af104fd78 hgext/evolve.py --- 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") diff -r fcdd9b8c970b -r 0b6af104fd78 hgext/hgfastobs.py --- /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 diff -r fcdd9b8c970b -r 0b6af104fd78 hgext/qsync.py --- 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 -"""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] diff -r fcdd9b8c970b -r 0b6af104fd78 tests/test-boxpush.t --- /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 < [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) diff -r fcdd9b8c970b -r 0b6af104fd78 tests/test-corrupt.t --- 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 diff -r fcdd9b8c970b -r 0b6af104fd78 tests/test-drop.t --- /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 < [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) diff -r fcdd9b8c970b -r 0b6af104fd78 tests/test-evolve.t --- 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 diff -r fcdd9b8c970b -r 0b6af104fd78 tests/test-obsolete.t --- 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'} + diff -r fcdd9b8c970b -r 0b6af104fd78 tests/test-prune.t --- 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) diff -r fcdd9b8c970b -r 0b6af104fd78 tests/test-qsync.t --- 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 < [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 - diff -r fcdd9b8c970b -r 0b6af104fd78 tests/test-stabilize-result.t --- 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 diff -r fcdd9b8c970b -r 0b6af104fd78 tests/test-tutorial.t --- 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.