Mercurial > public > mercurial-scm > hg
diff mercurial/repair.py @ 33087:fcd1c483f5ea
strip: add a delayedstrip method that works in a transaction
For long, the fact that strip does not work inside a transaction and some
code has to work with both obsstore and fallback to strip lead to duplicated
code like:
with repo.transaction():
....
if obsstore:
obsstore.createmarkers(...)
if not obsstore:
repair.strip(...)
Things get more complex when you want to call something which may call strip
under the hood. Like you cannot simply write:
with repo.transaction():
....
rebasemod.rebase(...) # may call "strip", so this doesn't work
But you do want rebase to run inside a same transaction if possible, so the
code may look like:
with repo.transaction():
....
if obsstore:
rebasemod.rebase(...)
obsstore.createmarkers(...)
if not obsstore:
rebasemod.rebase(...)
repair.strip(...)
That's ugly and error-prone. Ideally it's possible to just write:
with repo.transaction():
rebasemod.rebase(...)
saferemovenodes(...)
This patch is the first step towards that. It adds a "delayedstrip" method
to repair.py which maintains a postclose callback in the transaction object.
author | Jun Wu <quark@fb.com> |
---|---|
date | Sun, 25 Jun 2017 10:38:45 -0700 |
parents | 18c2489ac96d |
children | 208de1534ebd |
line wrap: on
line diff
--- a/mercurial/repair.py Sun Jun 25 22:30:14 2017 -0700 +++ b/mercurial/repair.py Sun Jun 25 10:38:45 2017 -0700 @@ -253,6 +253,63 @@ # extensions can use it return backupfile +def safestriproots(ui, repo, nodes): + """return list of roots of nodes where descendants are covered by nodes""" + torev = repo.unfiltered().changelog.rev + revs = set(torev(n) for n in nodes) + # tostrip = wanted - unsafe = wanted - ancestors(orphaned) + # orphaned = affected - wanted + # affected = descendants(roots(wanted)) + # wanted = revs + tostrip = set(repo.revs('%ld-(::((roots(%ld)::)-%ld))', revs, revs, revs)) + notstrip = revs - tostrip + if notstrip: + nodestr = ', '.join(sorted(short(repo[n].node()) for n in notstrip)) + ui.warn(_('warning: orphaned descendants detected, ' + 'not stripping %s\n') % nodestr) + return [c.node() for c in repo.set('roots(%ld)', tostrip)] + +class stripcallback(object): + """used as a transaction postclose callback""" + + def __init__(self, ui, repo, backup, topic): + self.ui = ui + self.repo = repo + self.backup = backup + self.topic = topic or 'backup' + self.nodelist = [] + + def addnodes(self, nodes): + self.nodelist.extend(nodes) + + def __call__(self, tr): + roots = safestriproots(self.ui, self.repo, self.nodelist) + if roots: + strip(self.ui, self.repo, roots, True, self.topic) + +def delayedstrip(ui, repo, nodelist, topic=None): + """like strip, but works inside transaction and won't strip irreverent revs + + nodelist must explicitly contain all descendants. Otherwise a warning will + be printed that some nodes are not stripped. + + Always do a backup. The last non-None "topic" will be used as the backup + topic name. The default backup topic name is "backup". + """ + tr = repo.currenttransaction() + if not tr: + nodes = safestriproots(ui, repo, nodelist) + return strip(ui, repo, nodes, True, topic) + # transaction postclose callbacks are called in alphabet order. + # use '\xff' as prefix so we are likely to be called last. + callback = tr.getpostclose('\xffstrip') + if callback is None: + callback = stripcallback(ui, repo, True, topic) + tr.addpostclose('\xffstrip', callback) + if topic: + callback.topic = topic + callback.addnodes(nodelist) + def striptrees(repo, tr, striprev, files): if 'treemanifest' in repo.requirements: # safe but unnecessary # otherwise