--- a/hgext/obsolete.py Thu Aug 23 18:21:18 2012 +0200
+++ b/hgext/obsolete.py Fri Aug 24 10:44:23 2012 +0200
@@ -5,41 +5,13 @@
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-"""Introduce the Obsolete concept to mercurial
-
-General concept
-===============
-
-This extension introduces the *obsolete* concept. The relation
-``<changeset B> obsoletes <changeset A>`` denotes that ``<changeset B>``
-is a new version of ``<changeset A>``.
-
-The *obsolete* relations act as an history **orthogonal** to the regular
-changesets history. Regular changesets history versions files. *Obsolete*
-relations version changesets.
+"""Deprecated extension that formely introduces "Changeset Obsolescence".
-:obsolete: a changeset that has been replaced by another one.
-:unstable: a changeset that is not obsolete but has an obsolete ancestor.
-:suspended: an obsolete changeset with unstable descendants.
-:extinct: an obsolete changeset without unstable descendants.
- (subject to garbage collection)
-
-Another name for unstable could be out of sync.
-
+This concept is now partially in Mercurial core (starting with mercurial 2.3). The remaining logic have been grouped with the evolve extension.
-Usage and Feature
-=================
-
-
-New commands
-------------
-
-Note that rebased changesets are now marked obsolete instead of being stripped.
-
+Some code cemains in this extensions to detect and convert prehistoric format of obsolete marker than early user may have create. Keep it enabled if you were such user.
"""
-
-
from mercurial import util
try:
@@ -50,1061 +22,11 @@
raise util.Abort('Obsolete extension requires Mercurial 2.3 (or later)')
import sys
-from mercurial.i18n import _
-from mercurial import cmdutil
-from mercurial import commands
-from mercurial import context
-from mercurial import discovery
-from mercurial import error
-from mercurial import extensions
-from mercurial import localrepo
-from mercurial import merge
-from mercurial import phases
-from mercurial import revset
-from mercurial import scmutil
-from mercurial import templatekw
-from mercurial.node import bin, short, nullid
-
-# This extension contains the following code
-#
-# - Extension Helper code
-# - Obsolescence cache
-# - ...
-# - Older format compat
-
-
-
-#####################################################################
-### Extension helper ###
-#####################################################################
-
-class exthelper(object):
- """Helper for modular extension setup
-
- A single helper should be instanciated for each extension. Helper
- methods are then used as decorator for various purpose.
-
- All decorators return the original function and may be chained.
- """
-
- def __init__(self):
- self._uicallables = []
- self._extcallables = []
- self._repocallables = []
- self._revsetsymbols = []
- self._templatekws = []
- self._commandwrappers = []
- self._extcommandwrappers = []
- self._functionwrappers = []
- self._duckpunchers = []
-
- def final_uisetup(self, ui):
- """Method to be used as the extension uisetup
-
- The following operations belong here:
-
- - Changes to ui.__class__ . The ui object that will be used to run the
- command has not yet been created. Changes made here will affect ui
- objects created after this, and in particular the ui that will be
- passed to runcommand
- - Command wraps (extensions.wrapcommand)
- - Changes that need to be visible to other extensions: because
- initialization occurs in phases (all extensions run uisetup, then all
- run extsetup), a change made here will be visible to other extensions
- during extsetup
- - Monkeypatch or wrap function (extensions.wrapfunction) of dispatch
- module members
- - Setup of pre-* and post-* hooks
- - pushkey setup
- """
- for cont, funcname, func in self._duckpunchers:
- setattr(cont, funcname, func)
- for command, wrapper in self._commandwrappers:
- extensions.wrapcommand(commands.table, command, wrapper)
- for cont, funcname, wrapper in self._functionwrappers:
- extensions.wrapfunction(cont, funcname, wrapper)
- for c in self._uicallables:
- c(ui)
-
- def final_extsetup(self, ui):
- """Method to be used as a the extension extsetup
-
- The following operations belong here:
-
- - Changes depending on the status of other extensions. (if
- extensions.find('mq'))
- - Add a global option to all commands
- - Register revset functions
- """
- knownexts = {}
- for name, symbol in self._revsetsymbols:
- revset.symbols[name] = symbol
- for name, kw in self._templatekws:
- templatekw.keywords[name] = kw
- for ext, command, wrapper in self._extcommandwrappers:
- if ext not in knownexts:
- e = extensions.find(ext)
- if e is None:
- raise util.Abort('extension %s not found' % ext)
- knownexts[ext] = e.cmdtable
- extensions.wrapcommand(knownexts[ext], commands, wrapper)
- for c in self._extcallables:
- c(ui)
-
- def final_reposetup(self, ui, repo):
- """Method to be used as a the extension reposetup
-
- The following operations belong here:
-
- - All hooks but pre-* and post-*
- - Modify configuration variables
- - Changes to repo.__class__, repo.dirstate.__class__
- """
- for c in self._repocallables:
- c(ui, repo)
-
- def uisetup(self, call):
- """Decorated function will be executed during uisetup
-
- example::
-
- @eh.uisetup
- def setupbabar(ui):
- print 'this is uisetup!'
- """
- self._uicallables.append(call)
- return call
-
- def extsetup(self, call):
- """Decorated function will be executed during extsetup
-
- example::
-
- @eh.extsetup
- def setupcelestine(ui):
- print 'this is extsetup!'
- """
- self._uicallables.append(call)
- return call
-
- def reposetup(self, call):
- """Decorated function will be executed during reposetup
-
- example::
-
- @eh.reposetup
- def setupzephir(ui, repo):
- print 'this is reposetup!'
- """
- self._repocallables.append(call)
- return call
-
- def revset(self, symbolname):
- """Decorated function is a revset symbol
-
- The name of the symbol must be given as the decorator argument.
- The symbol is added during `extsetup`.
-
- example::
-
- @eh.revset('hidden')
- def revsetbabar(repo, subset, x):
- args = revset.getargs(x, 0, 0, 'babar accept no argument')
- return [r for r in subset if 'babar' in repo[r].description()]
- """
- def dec(symbol):
- self._revsetsymbols.append((symbolname, symbol))
- return symbol
- return dec
-
-
- def templatekw(self, keywordname):
- """Decorated function is a revset keyword
-
- The name of the keyword must be given as the decorator argument.
- The symbol is added during `extsetup`.
-
- example::
-
- @eh.templatekw('babar')
- def kwbabar(ctx):
- return 'babar'
- """
- def dec(keyword):
- self._templatekws.append((keywordname, keyword))
- return keyword
- return dec
-
- def wrapcommand(self, command, extension=None):
- """Decorated function is a command wrapper
-
- The name of the command must be given as the decorator argument.
- The wrapping is installed during `uisetup`.
-
- If the second option `extension` argument is provided, the wrapping
- will be applied in the extension commandtable. This argument must be a
- string that will be searched using `extension.find` if not found and
- Abort error is raised. If the wrapping applies to an extension, it is
- installed during `extsetup`
-
- example::
-
- @eh.wrapcommand('summary')
- def wrapsummary(orig, ui, repo, *args, **kwargs):
- ui.note('Barry!')
- return orig(ui, repo, *args, **kwargs)
-
- """
- def dec(wrapper):
- if extension is None:
- self._commandwrappers.append((command, wrapper))
- else:
- self._extcommandwrappers.append((extension, command, wrapper))
- return wrapper
- return dec
-
- def wrapfunction(self, container, funcname):
- """Decorated function is a function wrapper
-
- This function takes two arguments, the container and the name of the
- function to wrap. The wrapping is performed during `uisetup`.
- (there is no extension support)
-
- example::
-
- @eh.function(discovery, 'checkheads')
- def wrapfunction(orig, *args, **kwargs):
- ui.note('His head smashed in and his heart cut out')
- return orig(*args, **kwargs)
- """
- def dec(wrapper):
- self._functionwrappers.append((container, funcname, wrapper))
- return wrapper
- return dec
-
- def addattr(self, container, funcname):
- """Decorated function is to be added to the container
-
- This function takes two arguments, the container and the name of the
- function to wrap. The wrapping is performed during `uisetup`.
-
- example::
-
- @eh.function(context.changectx, 'babar')
- def babar(ctx):
- return 'babar' in ctx.description
- """
- def dec(func):
- self._duckpunchers.append((container, funcname, func))
- return func
- return dec
-
-eh = exthelper()
-uisetup = eh.final_uisetup
-extsetup = eh.final_extsetup
-reposetup = eh.final_reposetup
-
-#####################################################################
-### Obsolescence Caching Logic ###
-#####################################################################
-
-# 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 store meaningful set of revision related to obsolescence
-# (obsolete, unstabletble ...
-#
-# Here is:
-#
-# - Computation of meaningful set,
-# - 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
-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]
-
-#####################################################################
-### Complete troubles computation logic ###
-#####################################################################
-
-# there is two kind of trouble not handled by core right now:
-# - latecomer: (successors for public changeset)
-# - conflicting: (two changeset try to succeed to the same precursors)
-#
-# This section add support for those two addition trouble
-#
-# - Cache computation
-# - revset and ctx method
-# - push warning
-
-### Cache computation
-latediff = 1 # flag to prevent taking late comer fix into account
-
-@cachefor('latecomer')
-def _computelatecomerset(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('conflicting')
-def _computeconflictingset(repo):
- """the set of rev trying to obsolete public revision"""
- conflicting = set()
- obsstore = repo.obsstore
- newermap = {}
- for ctx in repo.set('(not public()) - obsolete()'):
- prec = obsstore.successors.get(ctx.node(), ())
- toprocess = set(prec)
- while toprocess:
- prec = toprocess.pop()[0]
- if prec not in newermap:
- newermap[prec] = newerversion(repo, prec)
- newer = [n for n in newermap[prec] if n] # filter kill
- if len(newer) > 1:
- conflicting.add(ctx.rev())
- break
- toprocess.update(obsstore.successors.get(prec, ()))
- return conflicting
-
-### changectx method
-
-@eh.addattr(context.changectx, 'latecomer')
-def latecomer(ctx):
- """is the changeset latecomer (Try to succeed to public change)"""
- if ctx.node() is None:
- return False
- return ctx.rev() in getobscache(ctx._repo, 'latecomer')
-
-@eh.addattr(context.changectx, 'conflicting')
-def conflicting(ctx):
- """is the changeset conflicting (Try to succeed to public change)"""
- if ctx.node() is None:
- return False
- return ctx.rev() in getobscache(ctx._repo, 'conflicting')
-
-### revset symbol
-
-@eh.revset('latecomer')
-def revsetlatecomer(repo, subset, x):
- """``latecomer()``
- Changesets marked as successors of public changesets.
- """
- args = revset.getargs(x, 0, 0, 'latecomer takes no arguments')
- lates = getobscache(repo, 'latecomer')
- return [r for r in subset if r in lates]
-
-@eh.revset('conflicting')
-def revsetconflicting(repo, subset, x):
- """``conflicting()``
- Changesets marked as successors of a same changeset.
- """
- args = revset.getargs(x, 0, 0, 'conflicting takes no arguments')
- conf = getobscache(repo, 'conflicting')
- return [r for r in subset if r in conf]
-
-
-### Discovery wrapping
-
-@eh.wrapfunction(discovery, 'checkheads')
-def wrapcheckheads(orig, repo, remote, outgoing, *args, **kwargs):
- """wrap mercurial.discovery.checkheads
-
- * prevent latecomer and unstable 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.latecomer():
- raise util.Abort(_("push includes a latecomer changeset: %s!")
- % ctx)
- if ctx.conflicting():
- raise util.Abort(_("push includes a conflicting changeset: %s!")
- % ctx)
- return orig(repo, remote, outgoing, *args, **kwargs)
+import json
-#####################################################################
-### Filter extinct changeset from common operation ###
-#####################################################################
-
-@eh.wrapfunction(merge, 'update')
-def wrapmergeupdate(orig, repo, node, *args, **kwargs):
- """ensure we don't automatically update on hidden changeset"""
- if node is None:
- # tip of current branch
- branch = repo[None].branch()
- node = repo.revs('last((.:: and branch(%s)) - hidden())', branch)[0]
- return orig(repo, node, *args, **kwargs)
-
-@eh.wrapfunction(localrepo.localrepository, 'branchtip')
-def obsbranchtip(orig, repo, branch):
- """ensure "stable" reference does not end on a hidden changeset"""
- result = ()
- heads = repo.branchmap().get(branch, ())
- if heads:
- result = list(repo.set('last(heads(branch(%n) - hidden()))', heads[0]))
- if not result:
- raise error.RepoLookupError(_("unknown branch '%s'") % branch)
- return result[0].node()
-
-
-#####################################################################
-### Additional Utilities ###
-#####################################################################
-
-# This section contains a lot of small utility function and method
-
-# - Function to create markers
-# - useful alias pstatus and pdiff (should probably go in evolve)
-# - "troubles" method on changectx
-# - function to travel throught the obsolescence graph
-# - function to find useful changeset to stabilize
-
-### Marker Create
-
-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()
-
-
-### Useful alias
-
-@eh.uisetup
-def _installalias(ui):
- if ui.config('alias', 'pstatus', None) is None:
- ui.setconfig('alias', 'pstatus', 'status --rev .^')
- if ui.config('alias', 'pdiff', None) is None:
- ui.setconfig('alias', 'pdiff', 'diff --rev .^')
- if ui.config('alias', 'olog', None) is None:
- ui.setconfig('alias', 'olog', "log -r 'precursors(.)' --hidden")
-
-# - "troubles" method on changectx
-
-@eh.addattr(context.changectx, 'troubles')
-def troubles(ctx):
- """Return a tuple listing all the troubles that affect a changeset
-
- Troubles may be "unstable", "latecomer" or "conflicting".
- """
- troubles = []
- if ctx.unstable():
- troubles.append('unstable')
- if ctx.latecomer():
- troubles.append('latecomer')
- if ctx.conflicting():
- troubles.append('conflicting')
- return tuple(troubles)
-
-### Troubled revset symbol
-
-@eh.revset('troubled')
-def revsetlatecomer(repo, subset, x):
- """``troubled()``
- Changesets with troubles.
- """
- _ = revset.getargs(x, 0, 0, 'troubled takes no arguments')
- return list(repo.revs('%ld and (unstable() + latecomer() + conflicting())',
- subset))
-
-
-### Obsolescence graph
-
-# XXX SOME MAJOR CLEAN UP TO DO HERE XXX
-
-def _precursors(repo, s):
- """Precursor of a changeset"""
- cs = set()
- nm = repo.changelog.nodemap
- markerbysubj = repo.obsstore.successors
- 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 _allprecursors(repo, s): # XXX we need a better naming
- """transitive precursors of a subset"""
- toproceed = [repo[r].node() for r in s]
- seen = set()
- allsubjects = repo.obsstore.successors
- while toproceed:
- nc = toproceed.pop()
- for mark in allsubjects.get(nc, ()):
- np = mark[0]
- if np not in seen:
- seen.add(np)
- toproceed.append(np)
- nm = repo.changelog.nodemap
- cs = set()
- for p in seen:
- pr = nm.get(p)
- if pr is not None:
- cs.add(pr)
- return cs
-
-def _successors(repo, s):
- """Successors of a changeset"""
- cs = set()
- nm = repo.changelog.nodemap
- markerbyobj = repo.obsstore.precursors
- for r in s:
- for p in markerbyobj.get(repo[r].node(), ()):
- for sub in p[1]:
- sr = nm.get(sub)
- if sr is not None:
- cs.add(sr)
- return cs
-
-def _allsuccessors(repo, s, haltonflags=0): # XXX we need a better naming
- """transitive successors of a subset
-
- haltonflags allows to provide flags which prevent the evaluation of a
- marker. """
- toproceed = [repo[r].node() for r in s]
- seen = set()
- allobjects = repo.obsstore.precursors
- while toproceed:
- nc = toproceed.pop()
- for mark in allobjects.get(nc, ()):
- if mark[2] & haltonflags:
- continue
- for sub in mark[1]:
- if sub == nullid:
- continue # should not be here!
- if sub not in seen:
- seen.add(sub)
- toproceed.append(sub)
- nm = repo.changelog.nodemap
- cs = set()
- for s in seen:
- sr = nm.get(s)
- if sr is not None:
- cs.add(sr)
- return cs
-
-
-
-def newerversion(repo, obs):
- """Return the newer version of an obsolete changeset"""
- toproceed = set([(obs,)])
- # XXX known optimization available
- newer = set()
- objectrels = repo.obsstore.precursors
- while toproceed:
- current = toproceed.pop()
- assert len(current) <= 1, 'splitting not handled yet. %r' % current
- current = [n for n in current if n != nullid]
- if current:
- n, = current
- if n in objectrels:
- markers = objectrels[n]
- for mark in markers:
- toproceed.add(tuple(mark[1]))
- else:
- newer.add(tuple(current))
- else:
- newer.add(())
- return sorted(newer)
-
-
-#####################################################################
-### Extending revset and template ###
-#####################################################################
-
-# this section add several useful revset symbol not yet in core.
-# they are subject to changes
-
-### hidden revset is not in core yet
-
-@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):
- """``suspended()``
- Obsolete changesets with non-obsolete descendants.
- """
- args = revset.getargs(x, 0, 0, 'suspended takes no arguments')
- suspended = getobscache(repo, 'suspended')
- return [r for r in subset if r in suspended]
-
-
-@eh.revset('precursors')
-def revsetprecursors(repo, subset, x):
- """``precursors(set)``
- Immediate precursors of changesets in set.
- """
- s = revset.getset(repo, range(len(repo)), x)
- cs = _precursors(repo, s)
- return [r for r in subset if r in cs]
-
-
-@eh.revset('allprecursors')
-def revsetallprecursors(repo, subset, x):
- """``allprecursors(set)``
- Transitive precursors of changesets in set.
- """
- s = revset.getset(repo, range(len(repo)), x)
- cs = _allprecursors(repo, s)
- return [r for r in subset if r in cs]
-
-
-@eh.revset('successors')
-def revsetsuccessors(repo, subset, x):
- """``successors(set)``
- Immediate successors of changesets in set.
- """
- s = revset.getset(repo, range(len(repo)), x)
- cs = _successors(repo, s)
- return [r for r in subset if r in cs]
-
-@eh.revset('allsuccessors')
-def revsetallsuccessors(repo, subset, x):
- """``allsuccessors(set)``
- Transitive successors of changesets in set.
- """
- s = revset.getset(repo, range(len(repo)), x)
- cs = _allsuccessors(repo, s)
- return [r for r in subset if r in cs]
-
-### template keywords
-# XXX it does not handle troubles well :-/
-
-@eh.templatekw('obsolete')
-def obsoletekw(repo, ctx, templ, **args):
- """:obsolete: String. The obsolescence level of the node, could be
- ``stable``, ``unstable``, ``suspended`` or ``extinct``.
- """
- rev = ctx.rev()
- if ctx.obsolete():
- if ctx.extinct():
- return 'extinct'
- else:
- return 'suspended'
- elif ctx.unstable():
- return 'unstable'
- return 'stable'
-
-#####################################################################
-### Various trouble warning ###
-#####################################################################
-
-# This section take care of issue warning to the user when troubles appear
-
-@eh.wrapcommand("update")
-@eh.wrapcommand("pull")
-def wrapmayobsoletewc(origfn, ui, repo, *args, **opts):
- """Warn that the working directory parent is an obsolete changeset"""
- res = origfn(ui, repo, *args, **opts)
- if repo['.'].obsolete():
- ui.warn(_('Working directory parent is obsolete\n'))
- return res
-
-# XXX this could wrap transaction code
-# XXX (but this is a bit a layer violation)
-@eh.wrapcommand("commit")
-@eh.wrapcommand("push")
-@eh.wrapcommand("pull")
-@eh.wrapcommand("graft")
-@eh.wrapcommand("phase")
-@eh.wrapcommand("unbundle")
-def warnobserrors(orig, ui, repo, *args, **kwargs):
- """display warning is the command resulted in more instable changeset"""
- priorunstables = len(repo.revs('unstable()'))
- priorlatecomers = len(repo.revs('latecomer()'))
- priorconflictings = len(repo.revs('conflicting()'))
- try:
- return orig(ui, repo, *args, **kwargs)
- finally:
- newunstables = len(repo.revs('unstable()')) - priorunstables
- newlatecomers = len(repo.revs('latecomer()')) - priorlatecomers
- newconflictings = len(repo.revs('conflicting()')) - priorconflictings
- if newunstables > 0:
- ui.warn(_('%i new unstable changesets\n') % newunstables)
- if newlatecomers > 0:
- ui.warn(_('%i new latecomer changesets\n') % newlatecomers)
- if newconflictings > 0:
- ui.warn(_('%i new conflicting changesets\n') % newconflictings)
-
-@eh.reposetup
-def _repostabilizesetup(ui, repo):
- """Add a hint for "hg stabilize" when troubles make push fails
- """
- if not repo.local():
- return
-
- opush = repo.push
-
- class stabilizerrepo(repo.__class__):
- def push(self, remote, *args, **opts):
- """wrapper around pull that pull obsolete relation"""
- try:
- result = opush(remote, *args, **opts)
- except util.Abort, ex:
- hint = _("use 'hg stabilize' to get a stable history "
- "or --force to ignore warnings")
- if (len(ex.args) >= 1
- and ex.args[0].startswith('push includes ')
- and ex.hint is None):
- ex.hint = hint
- raise
- return result
- repo.__class__ = stabilizerrepo
-
-@eh.wrapcommand("summary")
-def obssummary(orig, ui, repo, *args, **kwargs):
- ret = orig(ui, repo, *args, **kwargs)
- nbunstable = len(getobscache(repo, 'unstable'))
- nblatecomer = len(getobscache(repo, 'latecomer'))
- nbconflicting = len(getobscache(repo, 'unstable'))
- if nbunstable:
- ui.write('unstable: %i changesets\n' % nbunstable)
- else:
- ui.note('unstable: 0 changesets\n')
- if nblatecomer:
- ui.write('latecomer: %i changesets\n' % nblatecomer)
- else:
- ui.note('latecomer: 0 changesets\n')
- if nbconflicting:
- ui.write('conflicting: %i changesets\n' % nbconflicting)
- else:
- ui.note('conflicting: 0 changesets\n')
- return ret
-
-
-#####################################################################
-### 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.
-
-@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 repo._rebasekeep 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):
- # warning about more obsolete
- try:
- rebase = extensions.find('rebase')
- if rebase:
- entry = extensions.wrapcommand(rebase.cmdtable, 'rebase', warnobserrors)
- extensions.wrapfunction(rebase, 'buildstate', buildstate)
- extensions.wrapfunction(rebase, 'defineparents', defineparents)
- extensions.wrapfunction(rebase, 'concludenode', concludenode)
- extensions.wrapcommand(rebase.cmdtable, "rebase", cmdrebase)
- except KeyError:
- pass # rebase not found
+from mercurial import cmdutil
+from mercurial import error
+from mercurial.node import bin, nullid
#####################################################################
@@ -1114,10 +36,8 @@
# Code related to detection and management of older legacy format never
# handled by core
-import json
-@eh.reposetup
-def _checkoldobsolete(ui, repo):
+def reposetup(ui, repo):
"""Detect that a repo still contains some old obsolete format
"""
if not repo.local():