# HG changeset patch # User Pierre-Yves David # Date 1351007121 -7200 # Node ID 5527e4fe4418189e9b65e3a36413d08cd3d42d1f # Parent 7f89b31fcb268694bea8f7b14966dc22fdd01153# Parent 96482166420c413e5a912ede29a1958b18ee1d54 merge changelog update diff -r 96482166420c -r 5527e4fe4418 README --- a/README Tue Oct 23 17:44:31 2012 +0200 +++ b/README Tue Oct 23 17:45:21 2012 +0200 @@ -46,6 +46,10 @@ Changelog ================== + -- 2.0.0 + +- compat with mercurial 2.4 + -- 1.1.0 - fix troubles creation reporting from rebase diff -r 96482166420c -r 5527e4fe4418 hgext/evolve.py --- a/hgext/evolve.py Tue Oct 23 17:44:31 2012 +0200 +++ b/hgext/evolve.py Tue Oct 23 17:45:21 2012 +0200 @@ -19,7 +19,7 @@ - improves some aspect of the early implementation in 2.3 ''' -testedwith = '2.3 2.3.1 2.3.2' +testedwith = '2.4-rc' buglink = 'https://bitbucket.org/marmoute/mutable-history/issues' @@ -29,18 +29,11 @@ try: from mercurial import obsolete + getattr(obsolete, 'getrevs') # 2.4 specific if not obsolete._enabled: obsolete._enabled = True -except ImportError: - raise util.Abort('Evolve extension requires Mercurial 2.3 (or later)') - -try: - getattr(obsolete, 'getrevs') # 2.4 specific - raise util.Abort('Your version of Mercurial is too recent for this ' - 'version of evolve', - hint="upgrade your evolve") -except AttributeError: - pass +except (ImportError, AttributeError): + raise util.Abort('Evolve extension requires Mercurial 2.4 (or later)') from mercurial import bookmarks @@ -326,184 +319,10 @@ yield marker -##################################################################### -### Obsolescence Caching Logic ### -##################################################################### - -# IN CORE fb72eec7efd8 - -# Obsolescence related logic can be very slow if we don't have efficient cache. -# -# This section implements a cache mechanism that did not make it into core for -# time reason. It stores meaningful set of revisions related to obsolescence -# (obsolete, unstable, etc.) -# -# Here is: -# -# - Computation of meaningful sets -# - Cache access logic, -# - Cache invalidation logic, -# - revset and ctx using this cache. -# - - -### Computation of meaningful set -# -# Most set can be computed with "simple" revset. - -#: { set name -> function to compute this set } mapping -#: function take a single "repo" argument. -#: -#: Use the `cachefor` decorator to register new cache function -try: - cachefuncs = obsolete.cachefuncs - cachefor = obsolete.cachefor - getobscache = obsolete.getobscache - clearobscaches = obsolete.clearobscaches -except AttributeError: - cachefuncs = {} - - def cachefor(name): - """Decorator to register a function as computing the cache for a set""" - def decorator(func): - assert name not in cachefuncs - cachefuncs[name] = func - return func - return decorator - - @cachefor('obsolete') - def _computeobsoleteset(repo): - """the set of obsolete revisions""" - obs = set() - nm = repo.changelog.nodemap - for prec in repo.obsstore.precursors: - rev = nm.get(prec) - if rev is not None: - obs.add(rev) - return set(repo.revs('%ld - public()', obs)) - - @cachefor('unstable') - def _computeunstableset(repo): - """the set of non obsolete revisions with obsolete parents""" - return set(repo.revs('(obsolete()::) - obsolete()')) - - @cachefor('suspended') - def _computesuspendedset(repo): - """the set of obsolete parents with non obsolete descendants""" - return set(repo.revs('obsolete() and obsolete()::unstable()')) - - @cachefor('extinct') - def _computeextinctset(repo): - """the set of obsolete parents without non obsolete descendants""" - return set(repo.revs('obsolete() - obsolete()::unstable()')) - - @eh.wrapfunction(obsolete.obsstore, '__init__') - def _initobsstorecache(orig, obsstore, *args, **kwargs): - """add a cache attribute to obsstore""" - obsstore.caches = {} - return orig(obsstore, *args, **kwargs) - -### Cache access - - def getobscache(repo, name): - """Return the set of revision that belong to the set - - Such access may compute the set and cache it for future use""" - if not repo.obsstore: - return () - if name not in repo.obsstore.caches: - repo.obsstore.caches[name] = cachefuncs[name](repo) - return repo.obsstore.caches[name] - -### Cache clean up -# -# To be simple we need to invalidate obsolescence cache when: -# -# - new changeset is added: -# - public phase is changed -# - obsolescence marker are added -# - strip is used a repo - - - def clearobscaches(repo): - """Remove all obsolescence related cache from a repo - - This remove all cache in obsstore is the obsstore already exist on the - repo. - - (We could be smarter here)""" - if 'obsstore' in repo._filecache: - repo.obsstore.caches.clear() - - @eh.wrapfunction(localrepo.localrepository, 'addchangegroup') # new changeset - @eh.wrapfunction(phases, 'retractboundary') # phase movement - @eh.wrapfunction(phases, 'advanceboundary') # phase movement - @eh.wrapfunction(localrepo.localrepository, 'destroyed') # strip - def wrapclearcache(orig, repo, *args, **kwargs): - try: - return orig(repo, *args, **kwargs) - finally: - # we are a bit wide here - # we could restrict to: - # advanceboundary + phase==public - # retractboundary + phase==draft - clearobscaches(repo) - - @eh.wrapfunction(obsolete.obsstore, 'add') # new marker - def clearonadd(orig, obsstore, *args, **kwargs): - try: - return orig(obsstore, *args, **kwargs) - finally: - obsstore.caches.clear() - -### Use the case -# Function in core that could benefic from the cache are overwritten by cache using version - -# changectx method - - @eh.addattr(context.changectx, 'unstable') - def unstable(ctx): - """is the changeset unstable (have obsolete ancestor)""" - if ctx.node() is None: - return False - return ctx.rev() in getobscache(ctx._repo, 'unstable') - - - @eh.addattr(context.changectx, 'extinct') - def extinct(ctx): - """is the changeset extinct by other""" - if ctx.node() is None: - return False - return ctx.rev() in getobscache(ctx._repo, 'extinct') - -# revset - - @eh.revset('obsolete') - def revsetobsolete(repo, subset, x): - """``obsolete()`` - Changeset is obsolete. - """ - args = revset.getargs(x, 0, 0, 'obsolete takes no argument') - obsoletes = getobscache(repo, 'obsolete') - return [r for r in subset if r in obsoletes] - - @eh.revset('unstable') - def revsetunstable(repo, subset, x): - """``unstable()`` - Unstable changesets are non-obsolete with obsolete ancestors. - """ - args = revset.getargs(x, 0, 0, 'unstable takes no arguments') - unstables = getobscache(repo, 'unstable') - return [r for r in subset if r in unstables] - - @eh.revset('extinct') - def revsetextinct(repo, subset, x): - """``extinct()`` - Obsolete changesets with obsolete descendants only. - """ - args = revset.getargs(x, 0, 0, 'extinct takes no arguments') - extincts = getobscache(repo, 'extinct') - return [r for r in subset if r in extincts] +cachefuncs = obsolete.cachefuncs +cachefor = obsolete.cachefor +getrevs = obsolete.getrevs +clearobscaches = obsolete.clearobscaches ##################################################################### ### Complete troubles computation logic ### @@ -522,13 +341,6 @@ ### Cache computation latediff = 1 # flag to prevent taking late comer fix into account -@cachefor('bumped') -def _computebumpedset(repo): - """the set of rev trying to obsolete public revision""" - candidates = _allsuccessors(repo, repo.revs('public()'), - haltonflags=latediff) - query = '%ld - obsolete() - public()' - return set(repo.revs(query, candidates)) @cachefor('divergent') def _computedivergentset(repo): @@ -537,7 +349,7 @@ obsstore = repo.obsstore newermap = {} for ctx in repo.set('(not public()) - obsolete()'): - mark = obsstore.successors.get(ctx.node(), ()) + mark = obsstore.precursors.get(ctx.node(), ()) toprocess = set(mark) while toprocess: prec = toprocess.pop()[0] @@ -547,18 +359,15 @@ if len(newer) > 1: divergent.add(ctx.rev()) break - toprocess.update(obsstore.successors.get(prec, ())) + toprocess.update(obsstore.precursors.get(prec, ())) return divergent ### changectx method @eh.addattr(context.changectx, 'latecomer') -@eh.addattr(context.changectx, 'bumped') -def bumped(ctx): +def latecomer(ctx): """is the changeset bumped (Try to succeed to public change)""" - if ctx.node() is None: - return False - return ctx.rev() in getobscache(ctx._repo, 'bumped') + return ctx.bumped() @eh.addattr(context.changectx, 'conflicting') @eh.addattr(context.changectx, 'divergent') @@ -566,19 +375,11 @@ """is the changeset divergent (Try to succeed to public change)""" if ctx.node() is None: return False - return ctx.rev() in getobscache(ctx._repo, 'divergent') + return ctx.rev() in getrevs(ctx._repo, 'divergent') ### revset symbol -@eh.revset('latecomer') -@eh.revset('bumped') -def revsetbumped(repo, subset, x): - """``bumped()`` - Changesets marked as successors of public changesets. - """ - args = revset.getargs(x, 0, 0, 'bumped takes no arguments') - lates = getobscache(repo, 'bumped') - return [r for r in subset if r in lates] +eh.revset('latecomer')(revset.symbols['bumped']) @eh.revset('conflicting') @eh.revset('divergent') @@ -587,7 +388,7 @@ Changesets marked as successors of a same changeset. """ args = revset.getargs(x, 0, 0, 'divergent takes no arguments') - conf = getobscache(repo, 'divergent') + conf = getrevs(repo, 'divergent') return [r for r in subset if r in conf] @@ -598,16 +399,13 @@ def wrapcheckheads(orig, repo, remote, outgoing, *args, **kwargs): """wrap mercurial.discovery.checkheads - * prevent bumped and unstable to be pushed + * prevent divergent to be pushed """ # do not push instability for h in outgoing.missingheads: # Checking heads is enough, obsolete descendants are either # obsolete or unstable. ctx = repo[h] - if ctx.bumped(): - raise util.Abort(_("push includes a bumped changeset: %s!") - % ctx) if ctx.divergent(): raise util.Abort(_("push includes a divergent changeset: %s!") % ctx) @@ -659,44 +457,7 @@ # - function to travel throught the obsolescence graph # - function to find useful changeset to stabilize -### Marker Create -# NOW IN CORE f85816af6294 -try: - createmarkers = obsolete.createmarkers -except AttributeError: - def createmarkers(repo, relations, metadata=None, flag=0): - """Add obsolete markers between changeset in a repo - - must be an iterable of (, (, ...)) tuple. - `old` and `news` are changectx. - - Current user and date are used except if specified otherwise in the - metadata attribute. - - /!\ assume the repo have been locked by the user /!\ - """ - # prepare metadata - if metadata is None: - metadata = {} - if 'date' not in metadata: - metadata['date'] = '%i %i' % util.makedate() - if 'user' not in metadata: - metadata['user'] = repo.ui.username() - # check future marker - tr = repo.transaction('add-obsolescence-marker') - try: - for prec, sucs in relations: - if not prec.mutable(): - raise util.Abort("cannot obsolete immutable changeset: %s" % prec) - nprec = prec.node() - nsucs = tuple(s.node() for s in sucs) - if nprec in nsucs: - raise util.Abort("changeset %s cannot obsolete himself" % prec) - repo.obsstore.create(tr, nprec, nsucs, flag, metadata) - clearobscaches(repo) - tr.close() - finally: - tr.release() +createmarkers = obsolete.createmarkers ### Useful alias @@ -732,7 +493,7 @@ ### Troubled revset symbol @eh.revset('troubled') -def revsetbumped(repo, subset, x): +def revsettroubled(repo, subset, x): """``troubled()`` Changesets with troubles. """ @@ -749,7 +510,7 @@ """Precursor of a changeset""" cs = set() nm = repo.changelog.nodemap - markerbysubj = repo.obsstore.successors + markerbysubj = repo.obsstore.precursors for r in s: for p in markerbysubj.get(repo[r].node(), ()): pr = nm.get(p[0]) @@ -761,7 +522,7 @@ """transitive precursors of a subset""" toproceed = [repo[r].node() for r in s] seen = set() - allsubjects = repo.obsstore.successors + allsubjects = repo.obsstore.precursors while toproceed: nc = toproceed.pop() for mark in allsubjects.get(nc, ()): @@ -781,7 +542,7 @@ """Successors of a changeset""" cs = set() nm = repo.changelog.nodemap - markerbyobj = repo.obsstore.precursors + markerbyobj = repo.obsstore.successors for r in s: for p in markerbyobj.get(repo[r].node(), ()): for sub in p[1]: @@ -797,7 +558,7 @@ marker. """ toproceed = [repo[r].node() for r in s] seen = set() - allobjects = repo.obsstore.precursors + allobjects = repo.obsstore.successors while toproceed: nc = toproceed.pop() for mark in allobjects.get(nc, ()): @@ -822,7 +583,7 @@ """Return the newer version of an obsolete changeset""" # prec -> markers mapping - markersfor = repo.obsstore.precursors + markersfor = repo.obsstore.successors # Stack of node need to know the last successors set toproceed = [initialnode] @@ -944,16 +705,6 @@ # they are subject to changes -if 'hidden' not in revset.symbols: - # in 2.3+ - @eh.revset('hidden') - def revsethidden(repo, subset, x): - """``hidden()`` - Changeset is hidden. - """ - args = revset.getargs(x, 0, 0, 'hidden takes no argument') - return [r for r in subset if r in repo.hiddenrevs] - ### XXX I'm not sure this revset is useful @eh.revset('suspended') def revsetsuspended(repo, subset, x): @@ -961,7 +712,7 @@ Obsolete changesets with non-obsolete descendants. """ args = revset.getargs(x, 0, 0, 'suspended takes no arguments') - suspended = getobscache(repo, 'suspended') + suspended = getrevs(repo, 'suspended') return [r for r in subset if r in suspended] @@ -1098,9 +849,9 @@ ui.note(s) ret = orig(ui, repo, *args, **kwargs) - nbunstable = len(getobscache(repo, 'unstable')) - nbbumped = len(getobscache(repo, 'bumped')) - nbdivergent = len(getobscache(repo, 'unstable')) + nbunstable = len(getrevs(repo, 'unstable')) + nbbumped = len(getrevs(repo, 'bumped')) + nbdivergent = len(getrevs(repo, 'unstable')) write('unstable: %i changesets\n', nbunstable) write('bumped: %i changesets\n', nbbumped) write('divergent: %i changesets\n', nbdivergent) @@ -1111,134 +862,6 @@ ### Core Other extension compat ### ##################################################################### -# This section make official history rewritter create obsolete marker - - -### commit --amend -# make commit --amend create obsolete marker -# -# The precursor is still strip from the repository. - -# IN CORE 63e45aee46d4 - -if getattr(cmdutil, 'obsolete', None) is None: - @eh.wrapfunction(cmdutil, 'amend') - def wrapcmdutilamend(orig, ui, repo, commitfunc, old, *args, **kwargs): - oldnode = old.node() - new = orig(ui, repo, commitfunc, old, *args, **kwargs) - if new != oldnode: - lock = repo.lock() - try: - tr = repo.transaction('post-amend-obst') - try: - meta = { - 'date': '%i %i' % util.makedate(), - 'user': ui.username(), - } - repo.obsstore.create(tr, oldnode, [new], 0, meta) - tr.close() - clearobscaches(repo) - finally: - tr.release() - finally: - lock.release() - return new - -### rebase -# -# - ignore obsolete changeset -# - create obsolete marker *instead of* striping - -def buildstate(orig, repo, dest, rebaseset, *ags, **kws): - """wrapper for rebase 's buildstate that exclude obsolete changeset""" - - rebaseset = repo.revs('%ld - extinct()', rebaseset) - if not rebaseset: - repo.ui.warn(_('whole rebase set is extinct and ignored.\n')) - return {} - root = min(rebaseset) - if (not getattr(repo, '_rebasekeep', False) - and not repo[root].mutable()): - raise util.Abort(_("can't rebase immutable changeset %s") % repo[root], - hint=_('see hg help phases for details')) - return orig(repo, dest, rebaseset, *ags, **kws) - -def defineparents(orig, repo, rev, target, state, *args, **kwargs): - rebasestate = getattr(repo, '_rebasestate', None) - if rebasestate is not None: - repo._rebasestate = dict(state) - repo._rebasetarget = target - return orig(repo, rev, target, state, *args, **kwargs) - -def concludenode(orig, repo, rev, p1, *args, **kwargs): - """wrapper for rebase 's concludenode that set obsolete relation""" - newrev = orig(repo, rev, p1, *args, **kwargs) - rebasestate = getattr(repo, '_rebasestate', None) - if rebasestate is not None: - if newrev is not None: - nrev = repo[newrev].rev() - else: - nrev = p1 - repo._rebasestate[rev] = nrev - return newrev - -def cmdrebase(orig, ui, repo, *args, **kwargs): - - reallykeep = kwargs.get('keep', False) - kwargs = dict(kwargs) - kwargs['keep'] = True - repo._rebasekeep = reallykeep - - # We want to mark rebased revision as obsolete and set their - # replacements if any. Doing it in concludenode() prevents - # aborting the rebase, and is not called with all relevant - # revisions in --collapse case. Instead, we try to track the - # rebase state structure by sampling/updating it in - # defineparents() and concludenode(). The obsolete markers are - # added from this state after a successful call. - repo._rebasestate = {} - repo._rebasetarget = None - try: - l = repo.lock() - try: - res = orig(ui, repo, *args, **kwargs) - if not reallykeep: - # Filter nullmerge or unrebased entries - repo._rebasestate = dict(p for p in repo._rebasestate.iteritems() - if p[1] >= 0) - if not res and not kwargs.get('abort') and repo._rebasestate: - # Rebased revisions are assumed to be descendants of - # targetrev. If a source revision is mapped to targetrev - # or to another rebased revision, it must have been - # removed. - markers = [] - if kwargs.get('collapse'): - # collapse assume revision disapear because they are all - # in the created revision - newrevs = set(repo._rebasestate.values()) - newrevs.remove(repo._rebasetarget) - if newrevs: - # we create new revision. - # A single one by --collapse design - assert len(newrevs) == 1 - new = tuple(repo[n] for n in newrevs) - else: - # every body died. no new changeset created - new = (repo[repo._rebasetarget],) - for rev, newrev in sorted(repo._rebasestate.items()): - markers.append((repo[rev], new)) - else: - # no collapse assume revision disapear because they are - # contained in parent - for rev, newrev in sorted(repo._rebasestate.items()): - markers.append((repo[rev], (repo[newrev],))) - createmarkers(repo, markers) - return res - finally: - l.release() - finally: - delattr(repo, '_rebasestate') - delattr(repo, '_rebasetarget') @eh.extsetup def _rebasewrapping(ui): @@ -1246,14 +869,7 @@ try: rebase = extensions.find('rebase') if rebase: - incore = getattr(rebase, 'obsolete', None) is not None - if not incore: - extensions.wrapcommand(rebase.cmdtable, "rebase", cmdrebase) extensions.wrapcommand(rebase.cmdtable, 'rebase', warnobserrors) - if not incore: - extensions.wrapfunction(rebase, 'buildstate', buildstate) - extensions.wrapfunction(rebase, 'defineparents', defineparents) - extensions.wrapfunction(rebase, 'concludenode', concludenode) except KeyError: pass # rebase not found diff -r 96482166420c -r 5527e4fe4418 tests/test-obsolete.t --- a/tests/test-obsolete.t Tue Oct 23 17:44:31 2012 +0200 +++ b/tests/test-obsolete.t Tue Oct 23 17:45:21 2012 +0200 @@ -164,7 +164,7 @@ $ hg push ../other-new pushing to ../other-new searching for changes - abort: push includes an unstable changeset: a7a6f2b5d8a5! + abort: push includes unstable changeset: a7a6f2b5d8a5! (use 'hg evolve' to get a stable history or --force to ignore warnings) [255] $ hg push -f ../other-new @@ -217,7 +217,7 @@ $ hg push ../other-new pushing to ../other-new searching for changes - abort: push includes an unstable changeset: 95de7fc6918d! + abort: push includes unstable changeset: 95de7fc6918d! (use 'hg evolve' to get a stable history or --force to ignore warnings) [255] $ hg push ../other-new -f # use f because there is unstability @@ -562,7 +562,7 @@ $ hg push ../other-new/ pushing to ../other-new/ searching for changes - abort: push includes a bumped changeset: 6db5e282cb91! + abort: push includes bumped changeset: 6db5e282cb91! (use 'hg evolve' to get a stable history or --force to ignore warnings) [255] @@ -575,7 +575,7 @@ $ echo 42 >> f $ hg commit --amend --traceback --quiet $ hg log -G - @ changeset: 1[35]:3734a65252e6 (re) + @ changeset: 15:705ab2a6b72e | tag: tip | parent: 10:2033b4e49474 | user: test @@ -626,7 +626,7 @@ 0d3f46688ccc6e756c7e96cf64c391c411309597 2033b4e494742365851fac84d276640cbf52833e 0 {'date': '* *', 'user': 'test'} (glob) 159dfc9fa5d334d7e03a0aecfc7f7ab4c3431fea 9468a5f5d8b2c5d91e17474e95ae4791e9718fdf 0 {'date': '* *', 'user': 'test'} (glob) 9468a5f5d8b2c5d91e17474e95ae4791e9718fdf 6db5e282cb91df5c43ff1f1287c119ff83230d42 0 {'date': '', 'user': 'test'} (glob) - 0b1b6dd009c037985363e2290a0b579819f659db 3734a65252e69ddcced85901647a4f335d40de1e 0 {'date': '* *', 'user': 'test'} (glob) + 0b1b6dd009c037985363e2290a0b579819f659db 705ab2a6b72e2cd86edb799ebe15f2695f86143e 0 {'date': '* *', 'user': 'test'} (glob) #no produced by 2.3 33d458d86621f3186c40bfccd77652f4a122743e 3734a65252e69ddcced85901647a4f335d40de1e 0 {'date': '* *', 'user': 'test'} (glob) @@ -650,7 +650,7 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: add obsol_d''' - changeset: 14:50f11e5e3a63 + changeset: 16:50f11e5e3a63 tag: tip parent: 11:9468a5f5d8b2 user: test diff -r 96482166420c -r 5527e4fe4418 tests/test-tutorial.t --- a/tests/test-tutorial.t Tue Oct 23 17:44:31 2012 +0200 +++ b/tests/test-tutorial.t Tue Oct 23 17:45:21 2012 +0200 @@ -612,7 +612,7 @@ $ hg push other pushing to $TESTTMP/other searching for changes - abort: push includes an unstable changeset: 9ac5d0e790a2! + abort: push includes unstable changeset: 9ac5d0e790a2! (use 'hg evolve' to get a stable history or --force to ignore warnings) [255]