Show More
@@ -253,6 +253,63 b' def strip(ui, repo, nodelist, backup=Tru' | |||||
253 | # extensions can use it |
|
253 | # extensions can use it | |
254 | return backupfile |
|
254 | return backupfile | |
255 |
|
255 | |||
|
256 | def safestriproots(ui, repo, nodes): | |||
|
257 | """return list of roots of nodes where descendants are covered by nodes""" | |||
|
258 | torev = repo.unfiltered().changelog.rev | |||
|
259 | revs = set(torev(n) for n in nodes) | |||
|
260 | # tostrip = wanted - unsafe = wanted - ancestors(orphaned) | |||
|
261 | # orphaned = affected - wanted | |||
|
262 | # affected = descendants(roots(wanted)) | |||
|
263 | # wanted = revs | |||
|
264 | tostrip = set(repo.revs('%ld-(::((roots(%ld)::)-%ld))', revs, revs, revs)) | |||
|
265 | notstrip = revs - tostrip | |||
|
266 | if notstrip: | |||
|
267 | nodestr = ', '.join(sorted(short(repo[n].node()) for n in notstrip)) | |||
|
268 | ui.warn(_('warning: orphaned descendants detected, ' | |||
|
269 | 'not stripping %s\n') % nodestr) | |||
|
270 | return [c.node() for c in repo.set('roots(%ld)', tostrip)] | |||
|
271 | ||||
|
272 | class stripcallback(object): | |||
|
273 | """used as a transaction postclose callback""" | |||
|
274 | ||||
|
275 | def __init__(self, ui, repo, backup, topic): | |||
|
276 | self.ui = ui | |||
|
277 | self.repo = repo | |||
|
278 | self.backup = backup | |||
|
279 | self.topic = topic or 'backup' | |||
|
280 | self.nodelist = [] | |||
|
281 | ||||
|
282 | def addnodes(self, nodes): | |||
|
283 | self.nodelist.extend(nodes) | |||
|
284 | ||||
|
285 | def __call__(self, tr): | |||
|
286 | roots = safestriproots(self.ui, self.repo, self.nodelist) | |||
|
287 | if roots: | |||
|
288 | strip(self.ui, self.repo, roots, True, self.topic) | |||
|
289 | ||||
|
290 | def delayedstrip(ui, repo, nodelist, topic=None): | |||
|
291 | """like strip, but works inside transaction and won't strip irreverent revs | |||
|
292 | ||||
|
293 | nodelist must explicitly contain all descendants. Otherwise a warning will | |||
|
294 | be printed that some nodes are not stripped. | |||
|
295 | ||||
|
296 | Always do a backup. The last non-None "topic" will be used as the backup | |||
|
297 | topic name. The default backup topic name is "backup". | |||
|
298 | """ | |||
|
299 | tr = repo.currenttransaction() | |||
|
300 | if not tr: | |||
|
301 | nodes = safestriproots(ui, repo, nodelist) | |||
|
302 | return strip(ui, repo, nodes, True, topic) | |||
|
303 | # transaction postclose callbacks are called in alphabet order. | |||
|
304 | # use '\xff' as prefix so we are likely to be called last. | |||
|
305 | callback = tr.getpostclose('\xffstrip') | |||
|
306 | if callback is None: | |||
|
307 | callback = stripcallback(ui, repo, True, topic) | |||
|
308 | tr.addpostclose('\xffstrip', callback) | |||
|
309 | if topic: | |||
|
310 | callback.topic = topic | |||
|
311 | callback.addnodes(nodelist) | |||
|
312 | ||||
256 | def striptrees(repo, tr, striprev, files): |
|
313 | def striptrees(repo, tr, striprev, files): | |
257 | if 'treemanifest' in repo.requirements: # safe but unnecessary |
|
314 | if 'treemanifest' in repo.requirements: # safe but unnecessary | |
258 | # otherwise |
|
315 | # otherwise |
@@ -412,7 +412,7 b' class transaction(object):' | |||||
412 |
|
412 | |||
413 | @active |
|
413 | @active | |
414 | def addpostclose(self, category, callback): |
|
414 | def addpostclose(self, category, callback): | |
415 |
"""add a callback to be called after the transaction |
|
415 | """add or replace a callback to be called after the transaction closed | |
416 |
|
416 | |||
417 | The transaction will be given as callback's first argument. |
|
417 | The transaction will be given as callback's first argument. | |
418 |
|
418 | |||
@@ -422,6 +422,11 b' class transaction(object):' | |||||
422 | self._postclosecallback[category] = callback |
|
422 | self._postclosecallback[category] = callback | |
423 |
|
423 | |||
424 | @active |
|
424 | @active | |
|
425 | def getpostclose(self, category): | |||
|
426 | """return a postclose callback added before, or None""" | |||
|
427 | return self._postclosecallback.get(category, None) | |||
|
428 | ||||
|
429 | @active | |||
425 | def addabort(self, category, callback): |
|
430 | def addabort(self, category, callback): | |
426 | """add a callback to be called when the transaction is aborted. |
|
431 | """add a callback to be called when the transaction is aborted. | |
427 |
|
432 |
@@ -2,6 +2,7 b'' | |||||
2 | $ echo "usegeneraldelta=yes" >> $HGRCPATH |
|
2 | $ echo "usegeneraldelta=yes" >> $HGRCPATH | |
3 | $ echo "[extensions]" >> $HGRCPATH |
|
3 | $ echo "[extensions]" >> $HGRCPATH | |
4 | $ echo "strip=" >> $HGRCPATH |
|
4 | $ echo "strip=" >> $HGRCPATH | |
|
5 | $ echo "drawdag=$TESTDIR/drawdag.py" >> $HGRCPATH | |||
5 |
|
6 | |||
6 | $ restore() { |
|
7 | $ restore() { | |
7 | > hg unbundle -q .hg/strip-backup/* |
|
8 | > hg unbundle -q .hg/strip-backup/* | |
@@ -940,4 +941,52 b' Error during post-close callback of the ' | |||||
940 | abort: boom |
|
941 | abort: boom | |
941 | [255] |
|
942 | [255] | |
942 |
|
943 | |||
|
944 | Use delayedstrip to strip inside a transaction | |||
943 |
|
945 | |||
|
946 | $ cd $TESTTMP | |||
|
947 | $ hg init delayedstrip | |||
|
948 | $ cd delayedstrip | |||
|
949 | $ hg debugdrawdag <<'EOS' | |||
|
950 | > D | |||
|
951 | > | | |||
|
952 | > C F H # Commit on top of "I", | |||
|
953 | > | |/| # Strip B+D+I+E+G+H+Z | |||
|
954 | > I B E G | |||
|
955 | > \|/ | |||
|
956 | > A Z | |||
|
957 | > EOS | |||
|
958 | ||||
|
959 | $ hg up -C I | |||
|
960 | 2 files updated, 0 files merged, 0 files removed, 0 files unresolved | |||
|
961 | $ echo 3 >> I | |||
|
962 | $ cat > $TESTTMP/delayedstrip.py <<EOF | |||
|
963 | > from mercurial import repair, commands | |||
|
964 | > def reposetup(ui, repo): | |||
|
965 | > def getnodes(expr): | |||
|
966 | > return [repo.changelog.node(r) for r in repo.revs(expr)] | |||
|
967 | > with repo.wlock(): | |||
|
968 | > with repo.lock(): | |||
|
969 | > with repo.transaction('delayedstrip'): | |||
|
970 | > repair.delayedstrip(ui, repo, getnodes('B+I+Z+D+E'), 'J') | |||
|
971 | > repair.delayedstrip(ui, repo, getnodes('G+H+Z'), 'I') | |||
|
972 | > commands.commit(ui, repo, message='J', date='0 0') | |||
|
973 | > EOF | |||
|
974 | $ hg log -r . -T '\n' --config extensions.t=$TESTTMP/delayedstrip.py | |||
|
975 | warning: orphaned descendants detected, not stripping 08ebfeb61bac, 112478962961, 7fb047a69f22 | |||
|
976 | saved backup bundle to $TESTTMP/delayedstrip/.hg/strip-backup/f585351a92f8-81fa23b0-I.hg (glob) | |||
|
977 | ||||
|
978 | $ hg log -G -T '{rev}:{node|short} {desc}' -r 'sort(all(), topo)' | |||
|
979 | @ 6:2f2d51af6205 J | |||
|
980 | | | |||
|
981 | o 3:08ebfeb61bac I | |||
|
982 | | | |||
|
983 | | o 5:64a8289d2492 F | |||
|
984 | | | | |||
|
985 | | o 2:7fb047a69f22 E | |||
|
986 | |/ | |||
|
987 | | o 4:26805aba1e60 C | |||
|
988 | | | | |||
|
989 | | o 1:112478962961 B | |||
|
990 | |/ | |||
|
991 | o 0:426bada5c675 A | |||
|
992 |
General Comments 0
You need to be logged in to leave comments.
Login now