Commits

Pierre-Yves David  committed 7c40faf Merge

merge 2.4 code back into default.

  • Participants
  • Parent commits e7b533b, 5527e4f

Comments (0)

Files changed (4)

 Changelog
 ==================
 
+2.4.0 -- 2012-10-26
+
+- compat with mercurial 2.4
+
 1.1.0 -- 2012-10-26
 
 - fix troubles creation reporting from rebase

File hgext/evolve.py

     - 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'
 
 
 
 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
         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 <name> 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                           ###
 ### 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):
     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]
             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')
     """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')
     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]
 
 
 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)
 # - 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
-
-        <relations> must be an iterable of (<old>, (<new>, ...)) 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
 ### Troubled revset symbol
 
 @eh.revset('troubled')
-def revsetbumped(repo, subset, x):
+def revsettroubled(repo, subset, x):
     """``troubled()``
     Changesets with troubles.
     """
     """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])
     """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, ()):
     """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]:
     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, ()):
     """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]
 # 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):
     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]
 
 
             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)
 ### 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):
     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
 

File tests/test-obsolete.t

   $ 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
   $ 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
   $ 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]
 
   $ 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
   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)
 
   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

File tests/test-tutorial.t

   $ 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]