diff -r 1e042eeb2d1a -r 48ac58354b7b hgext/obsolete.py --- a/hgext/obsolete.py Tue Aug 07 14:38:28 2012 +0200 +++ b/hgext/obsolete.py Tue Aug 07 15:02:54 2012 +0200 @@ -79,7 +79,7 @@ ##################################################################### -### Extension helper #### +### Extension helper ### ##################################################################### class exthelper(object): @@ -307,9 +307,9 @@ extsetup = eh.final_extsetup reposetup = eh.final_reposetup - -### Patch changectx -############################# +##################################################################### +### Obsolescence Caching Logic ### +##################################################################### @eh.addattr(context.changectx, 'unstable') def unstable(ctx): @@ -326,32 +326,6 @@ return False return ctx.rev() in ctx._repo._extinctset -@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 ctx._repo._latecomerset - -@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 ctx._repo._conflictingset - - -### revset -############################# - -@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] - @eh.revset('obsolete') def revsetobsolete(repo, subset, x): """``obsolete()`` @@ -368,14 +342,6 @@ args = revset.getargs(x, 0, 0, 'unstable takes no arguments') return [r for r in subset if r in repo._unstableset] -@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') - return [r for r in subset if r in repo._suspendedset] - @eh.revset('extinct') def revsetextinct(repo, subset, x): """``extinct()`` @@ -384,21 +350,125 @@ args = revset.getargs(x, 0, 0, 'extinct takes no arguments') return [r for r in subset if r in repo._extinctset] -@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') - return [r for r in subset if r in repo._latecomerset] + +@eh.wrapfunction(phases, 'advanceboundary') +def wrapclearcache(orig, repo, *args, **kwargs): + try: + return orig(repo, *args, **kwargs) + finally: + repo._clearobsoletecache() + +@eh.reposetup +def _repocachesetup(ui, repo): + if not repo.local(): + return + + o_updatebranchcache = repo.updatebranchcache + class cachedobsolescencegrepo(repo.__class__): + + # XXX move me on obssotre + @util.propertycache + def _obsoleteset(self): + """the set of obsolete revision""" + obs = set() + nm = self.changelog.nodemap + for prec in self.obsstore.precursors: + rev = nm.get(prec) + if rev is not None: + obs.add(rev) + return obs + + # XXX move me on obssotre + @util.propertycache + def _unstableset(self): + """the set of non obsolete revision with obsolete parent""" + return set(self.revs('(obsolete()::) - obsolete()')) + + # XXX move me on obssotre + @util.propertycache + def _suspendedset(self): + """the set of obsolete parent with non obsolete descendant""" + return set(self.revs('obsolete() and obsolete()::unstable()')) + + # XXX move me on obssotre + @util.propertycache + def _extinctset(self): + """the set of obsolete parent without non obsolete descendant""" + return set(self.revs('obsolete() - obsolete()::unstable()')) + + # XXX move me on obssotre + @util.propertycache + def _latecomerset(self): + """the set of rev trying to obsolete public revision""" + query = 'allsuccessors(public()) - obsolete() - public()' + return set(self.revs(query)) -@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') - return [r for r in subset if r in repo._conflictingset] + # XXX move me on obssotre + @util.propertycache + def _conflictingset(self): + """the set of rev trying to obsolete public revision""" + conflicting = set() + obsstore = self.obsstore + newermap = {} + for ctx in self.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(self, 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 + + def _clearobsoletecache(self): + if '_obsoleteset' in vars(self): + del self._obsoleteset + self._clearunstablecache() + + def updatebranchcache(self): + o_updatebranchcache() + self._clearunstablecache() + + def _clearunstablecache(self): + if '_unstableset' in vars(self): + del self._unstableset + if '_suspendedset' in vars(self): + del self._suspendedset + if '_extinctset' in vars(self): + del self._extinctset + if '_latecomerset' in vars(self): + del self._latecomerset + if '_conflictingset' in vars(self): + del self._conflictingset + + repo.__class__ = cachedobsolescencegrepo + +##################################################################### +### Complete troubles computation logic ### +##################################################################### + +@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 ctx._repo._latecomerset + +@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 ctx._repo._conflictingset + + +##################################################################### +### Additional Utilities functions ### +##################################################################### def _precursors(repo, s): """Precursor of a changeset""" @@ -412,16 +482,6 @@ cs.add(pr) return cs -@eh.revset('obsparents') -@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] - def _allprecursors(repo, s): # XXX we need a better naming """transitive precursors of a subset""" toproceed = [repo[r].node() for r in s] @@ -442,16 +502,6 @@ cs.add(pr) return cs -@eh.revset('obsancestors') -@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] - def _successors(repo, s): """Successors of a changeset""" cs = set() @@ -465,16 +515,6 @@ cs.add(sr) return cs -@eh.revset('obschildrend') -@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] - def _allsuccessors(repo, s): # XXX we need a better naming """transitive successors of a subset""" toproceed = [repo[r].node() for r in s] @@ -495,6 +535,178 @@ cs.add(sr) return cs + +### diagnostique tools + +def unstables(repo): + """Return all unstable changeset""" + return scmutil.revrange(repo, ['obsolete():: and (not obsolete())']) + +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) + +cmdtable = {} +command = cmdutil.command(cmdtable) +@command('debugsuccessors', [], '') +def cmddebugsuccessors(ui, repo): + """dump obsolete changesets and their successors + + Each line matches an existing marker, the first identifier is the + obsolete changeset identifier, followed by it successors. + """ + lock = repo.lock() + try: + allsuccessors = repo.obsstore.precursors + for old in sorted(allsuccessors): + successors = [sorted(m[1]) for m in allsuccessors[old]] + for i, group in enumerate(sorted(successors)): + ui.write('%s' % short(old)) + for new in group: + ui.write(' %s' % short(new)) + ui.write('\n') + finally: + lock.release() + + +@eh.reposetup +def _repoobsutilsetup(ui, repo): + if not repo.local(): + return + + class obsoletingrepo(repo.__class__): + + # XXX kill me + def addobsolete(self, sub, obj): + """Add a relation marking that node is a new version of """ + assert sub != obj + if not repo[obj].phase(): + if sub is None: + self.ui.warn( + _("trying to kill immutable changeset %(obj)s\n") + % {'obj': short(obj)}) + if sub is not None: + self.ui.warn( + _("%(sub)s try to obsolete immutable changeset %(obj)s\n") + % {'sub': short(sub), 'obj': short(obj)}) + lock = self.lock() + try: + tr = self.transaction('add-obsolete') + try: + meta = { + 'date': '%i %i' % util.makedate(), + 'user': ui.username(), + } + subs = (sub == nullid) and [] or [sub] + mid = self.obsstore.create(tr, obj, subs, 0, meta) + tr.close() + self._clearobsoletecache() + return mid + finally: + tr.release() + finally: + lock.release() + + # XXX kill me + def addcollapsedobsolete(self, oldnodes, newnode): + """Mark oldnodes as collapsed into newnode.""" + # Assume oldnodes are all descendants of a single rev + rootrevs = self.revs('roots(%ln)', oldnodes) + assert len(rootrevs) == 1, rootrevs + #rootnode = self[rootrevs[0]].node() + for n in oldnodes: + self.addobsolete(newnode, n) + repo.__class__ = obsoletingrepo + +##################################################################### +### Extending revset and template ### +##################################################################### + +@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] + +## troubles + +@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') + return [r for r in subset if r in repo._suspendedset] + +@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') + return [r for r in subset if r in repo._latecomerset] + +@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') + return [r for r in subset if r in repo._conflictingset] + + +@eh.revset('obsparents') +@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('obsancestors') +@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('obschildrend') +@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('obsdescendants') @eh.revset('allsuccessors') def revsetallsuccessors(repo, subset, x): @@ -505,9 +717,7 @@ cs = _allsuccessors(repo, s) return [r for r in subset if r in cs] - ### template keywords -##################### @eh.templatekw('obsolete') def obsoletekw(repo, ctx, templ, **args): @@ -523,9 +733,121 @@ return 'unstable' return 'stable' -### Other Extension compat -############################ +##################################################################### +### Various trouble warning ### +##################################################################### + + +### Discovery wrapping + +@eh.wrapfunction(discovery, 'checkheads') +def wrapcheckheads(orig, repo, remote, outgoing, *args, **kwargs): + """wrap mercurial.discovery.checkheads + + * prevent unstability to be pushed + * patch remote to ignore obsolete heads on remote + """ + # 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) + +@eh.wrapcommand("update") +@eh.wrapcommand("pull") +def wrapmayobsoletewc(origfn, ui, repo, *args, **opts): + res = origfn(ui, repo, *args, **opts) + if repo['.'].obsolete(): + ui.warn(_('Working directory parent is obsolete\n')) + return res +@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()')) + #print orig, priorunstables + #print len(repo.revs('secret() - obsolete()')) + 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 + #print orig, newunstables + #print len(repo.revs('secret() - obsolete()')) + if newunstables > 0: + ui.warn(_('%i new unstables changesets\n') % newunstables) + if newlatecomers > 0: + ui.warn(_('%i new latecomers changesets\n') % newlatecomers) + if newconflictings > 0: + ui.warn(_('%i new conflictings changesets\n') % newconflictings) + +@eh.reposetup +def _repostabilizesetup(ui, repo): + 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 + +##################################################################### +### Other extension compat ### +##################################################################### + +### commit --amend + +@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() + repo._clearobsoletecache() + finally: + tr.release() + finally: + lock.release() + return new + +### rebase def buildstate(orig, repo, dest, rebaseset, *ags, **kws): """wrapper for rebase 's buildstate that exclude obsolete changeset""" @@ -620,316 +942,6 @@ except KeyError: pass # rebase not found -### Discovery wrapping -############################# - -@eh.wrapfunction(discovery, 'checkheads') -def wrapcheckheads(orig, repo, remote, outgoing, *args, **kwargs): - """wrap mercurial.discovery.checkheads - - * prevent unstability to be pushed - * patch remote to ignore obsolete heads on remote - """ - # 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) - -@eh.wrapfunction(phases, 'advanceboundary') -def wrapclearcache(orig, repo, *args, **kwargs): - try: - return orig(repo, *args, **kwargs) - finally: - repo._clearobsoletecache() - - -### New commands -############################# - -cmdtable = {} -command = cmdutil.command(cmdtable) - - - -@command('debugsuccessors', [], '') -def cmddebugsuccessors(ui, repo): - """dump obsolete changesets and their successors - - Each line matches an existing marker, the first identifier is the - obsolete changeset identifier, followed by it successors. - """ - lock = repo.lock() - try: - allsuccessors = repo.obsstore.precursors - for old in sorted(allsuccessors): - successors = [sorted(m[1]) for m in allsuccessors[old]] - for i, group in enumerate(sorted(successors)): - ui.write('%s' % short(old)) - for new in group: - ui.write(' %s' % short(new)) - ui.write('\n') - finally: - lock.release() - -### Altering existing command -############################# - -@eh.wrapcommand("update") -@eh.wrapcommand("pull") -def wrapmayobsoletewc(origfn, ui, repo, *args, **opts): - res = origfn(ui, repo, *args, **opts) - if repo['.'].obsolete(): - ui.warn(_('Working directory parent is obsolete\n')) - return res - -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()')) - #print orig, priorunstables - #print len(repo.revs('secret() - obsolete()')) - 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 - #print orig, newunstables - #print len(repo.revs('secret() - obsolete()')) - if newunstables > 0: - ui.warn(_('%i new unstables changesets\n') % newunstables) - if newlatecomers > 0: - ui.warn(_('%i new latecomers changesets\n') % newlatecomers) - if newconflictings > 0: - ui.warn(_('%i new conflictings changesets\n') % newconflictings) - -@eh.extsetup -def _coreobserrorwrapping(ui): - # warning about more obsolete - for cmd in ['commit', 'push', 'pull', 'graft', 'phase', 'unbundle']: - entry = extensions.wrapcommand(commands.table, cmd, warnobserrors) - -@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() - repo._clearobsoletecache() - finally: - tr.release() - finally: - lock.release() - return new - - -### diagnostique tools -############################# - -def unstables(repo): - """Return all unstable changeset""" - return scmutil.revrange(repo, ['obsolete():: and (not obsolete())']) - -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) - -### repo subclassing -############################# - -@eh.reposetup -def _reposetup(ui, repo): - if not repo.local(): - return - - opush = repo.push - o_updatebranchcache = repo.updatebranchcache - - o_hook = repo.hook - - - class obsoletingrepo(repo.__class__): - - # workaround - def hook(self, name, throw=False, **args): - if 'pushkey' in name: - args.pop('new') - args.pop('old') - return o_hook(name, throw=False, **args) - - # XXX move me on obssotre - @util.propertycache - def _obsoleteset(self): - """the set of obsolete revision""" - obs = set() - nm = self.changelog.nodemap - for prec in self.obsstore.precursors: - rev = nm.get(prec) - if rev is not None: - obs.add(rev) - return obs - - # XXX move me on obssotre - @util.propertycache - def _unstableset(self): - """the set of non obsolete revision with obsolete parent""" - return set(self.revs('(obsolete()::) - obsolete()')) - - # XXX move me on obssotre - @util.propertycache - def _suspendedset(self): - """the set of obsolete parent with non obsolete descendant""" - return set(self.revs('obsolete() and obsolete()::unstable()')) - - # XXX move me on obssotre - @util.propertycache - def _extinctset(self): - """the set of obsolete parent without non obsolete descendant""" - return set(self.revs('obsolete() - obsolete()::unstable()')) - - # XXX move me on obssotre - @util.propertycache - def _latecomerset(self): - """the set of rev trying to obsolete public revision""" - query = 'allsuccessors(public()) - obsolete() - public()' - return set(self.revs(query)) - - # XXX move me on obssotre - @util.propertycache - def _conflictingset(self): - """the set of rev trying to obsolete public revision""" - conflicting = set() - obsstore = self.obsstore - newermap = {} - for ctx in self.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(self, 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 - - def _clearobsoletecache(self): - if '_obsoleteset' in vars(self): - del self._obsoleteset - self._clearunstablecache() - - def updatebranchcache(self): - o_updatebranchcache() - self._clearunstablecache() - - def _clearunstablecache(self): - if '_unstableset' in vars(self): - del self._unstableset - if '_suspendedset' in vars(self): - del self._suspendedset - if '_extinctset' in vars(self): - del self._extinctset - if '_latecomerset' in vars(self): - del self._latecomerset - if '_conflictingset' in vars(self): - del self._conflictingset - - # XXX kill me - def addobsolete(self, sub, obj): - """Add a relation marking that node is a new version of """ - assert sub != obj - if not repo[obj].phase(): - if sub is None: - self.ui.warn( - _("trying to kill immutable changeset %(obj)s\n") - % {'obj': short(obj)}) - if sub is not None: - self.ui.warn( - _("%(sub)s try to obsolete immutable changeset %(obj)s\n") - % {'sub': short(sub), 'obj': short(obj)}) - lock = self.lock() - try: - tr = self.transaction('add-obsolete') - try: - meta = { - 'date': '%i %i' % util.makedate(), - 'user': ui.username(), - } - subs = (sub == nullid) and [] or [sub] - mid = self.obsstore.create(tr, obj, subs, 0, meta) - tr.close() - self._clearobsoletecache() - return mid - finally: - tr.release() - finally: - lock.release() - - # XXX kill me - def addcollapsedobsolete(self, oldnodes, newnode): - """Mark oldnodes as collapsed into newnode.""" - # Assume oldnodes are all descendants of a single rev - rootrevs = self.revs('roots(%ln)', oldnodes) - assert len(rootrevs) == 1, rootrevs - #rootnode = self[rootrevs[0]].node() - for n in oldnodes: - self.addobsolete(newnode, n) - - ### pull // push support - - 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__ = obsoletingrepo - ##################################################################### ### Older format management ###