2007-06-25 10:26:44 +04:00
|
|
|
# repair.py - functions for repository repair for mercurial
|
|
|
|
#
|
|
|
|
# Copyright 2005, 2006 Chris Mason <mason@suse.com>
|
|
|
|
# Copyright 2007 Matt Mackall
|
|
|
|
#
|
2009-04-26 03:08:54 +04:00
|
|
|
# This software may be used and distributed according to the terms of the
|
2010-01-20 07:20:08 +03:00
|
|
|
# GNU General Public License version 2 or any later version.
|
2007-06-25 10:26:44 +04:00
|
|
|
|
2011-11-07 16:20:22 +04:00
|
|
|
from mercurial import changegroup, bookmarks, phases
|
2011-04-30 15:59:14 +04:00
|
|
|
from mercurial.node import short
|
|
|
|
from mercurial.i18n import _
|
2009-04-28 19:40:46 +04:00
|
|
|
import os
|
2007-06-25 10:26:44 +04:00
|
|
|
|
2011-10-30 21:10:09 +04:00
|
|
|
def _bundle(repo, bases, heads, node, suffix, compress=True):
|
2008-01-19 23:01:16 +03:00
|
|
|
"""create a bundle with the specified revisions as a backup"""
|
2011-10-30 21:10:09 +04:00
|
|
|
cg = repo.changegroupsubset(bases, heads, 'strip')
|
2008-01-19 23:01:16 +03:00
|
|
|
backupdir = repo.join("strip-backup")
|
|
|
|
if not os.path.isdir(backupdir):
|
|
|
|
os.mkdir(backupdir)
|
2010-06-11 00:23:31 +04:00
|
|
|
name = os.path.join(backupdir, "%s-%s.hg" % (short(node), suffix))
|
2010-08-12 11:53:23 +04:00
|
|
|
if compress:
|
|
|
|
bundletype = "HG10BZ"
|
|
|
|
else:
|
|
|
|
bundletype = "HG10UN"
|
|
|
|
return changegroup.writebundle(cg, name, bundletype)
|
2008-01-19 23:01:16 +03:00
|
|
|
|
2008-01-19 23:01:16 +03:00
|
|
|
def _collectfiles(repo, striprev):
|
|
|
|
"""find out the filelogs affected by the strip"""
|
2009-05-17 05:38:03 +04:00
|
|
|
files = set()
|
2008-01-19 23:01:16 +03:00
|
|
|
|
2008-06-26 23:35:50 +04:00
|
|
|
for x in xrange(striprev, len(repo)):
|
2009-05-17 18:55:51 +04:00
|
|
|
files.update(repo[x].files())
|
2008-01-19 23:01:16 +03:00
|
|
|
|
2009-05-17 05:38:03 +04:00
|
|
|
return sorted(files)
|
2008-01-19 23:01:16 +03:00
|
|
|
|
2011-03-20 02:50:22 +03:00
|
|
|
def _collectbrokencsets(repo, files, striprev):
|
|
|
|
"""return the changesets which will be broken by the truncation"""
|
2011-03-21 03:43:28 +03:00
|
|
|
s = set()
|
2011-03-20 02:50:22 +03:00
|
|
|
def collectone(revlog):
|
2011-03-21 04:21:19 +03:00
|
|
|
links = (revlog.linkrev(i) for i in revlog)
|
2008-01-19 23:01:16 +03:00
|
|
|
# find the truncation point of the revlog
|
2011-03-21 03:43:28 +03:00
|
|
|
for lrev in links:
|
2011-03-20 02:50:22 +03:00
|
|
|
if lrev >= striprev:
|
2008-01-19 23:01:16 +03:00
|
|
|
break
|
2011-03-21 03:43:28 +03:00
|
|
|
# see if any revision after this point has a linkrev
|
|
|
|
# less than striprev (those will be broken by strip)
|
|
|
|
for lrev in links:
|
2011-03-20 02:50:22 +03:00
|
|
|
if lrev < striprev:
|
2011-03-24 01:33:14 +03:00
|
|
|
s.add(lrev)
|
2008-01-19 23:01:16 +03:00
|
|
|
|
2011-03-21 03:43:28 +03:00
|
|
|
collectone(repo.manifest)
|
2008-01-19 23:01:16 +03:00
|
|
|
for fname in files:
|
2011-03-21 03:43:28 +03:00
|
|
|
collectone(repo.file(fname))
|
|
|
|
|
|
|
|
return s
|
2008-01-19 23:01:16 +03:00
|
|
|
|
2008-01-19 23:01:16 +03:00
|
|
|
def strip(ui, repo, node, backup="all"):
|
2008-01-19 23:01:16 +03:00
|
|
|
cl = repo.changelog
|
2007-06-25 10:26:44 +04:00
|
|
|
# TODO delete the undo files, and handle undo of merge sets
|
2008-01-19 23:01:16 +03:00
|
|
|
striprev = cl.rev(node)
|
2007-06-25 10:26:44 +04:00
|
|
|
|
2010-08-12 11:53:23 +04:00
|
|
|
keeppartialbundle = backup == 'strip'
|
|
|
|
|
2008-02-20 01:20:10 +03:00
|
|
|
# Some revisions with rev > striprev may not be descendants of striprev.
|
|
|
|
# We have to find these revisions and put them in a bundle, so that
|
|
|
|
# we can restore them after the truncations.
|
|
|
|
# To create the bundle we use repo.changegroupsubset which requires
|
|
|
|
# the list of heads and bases of the set of interesting revisions.
|
|
|
|
# (head = revision in the set that has no descendant in the set;
|
|
|
|
# base = revision in the set that has no ancestor in the set)
|
2011-03-20 02:50:22 +03:00
|
|
|
tostrip = set(cl.descendants(striprev))
|
|
|
|
tostrip.add(striprev)
|
|
|
|
|
|
|
|
files = _collectfiles(repo, striprev)
|
2011-03-21 03:43:28 +03:00
|
|
|
saverevs = _collectbrokencsets(repo, files, striprev)
|
2011-03-20 02:50:22 +03:00
|
|
|
|
|
|
|
# compute heads
|
|
|
|
saveheads = set(saverevs)
|
2008-06-26 23:35:50 +04:00
|
|
|
for r in xrange(striprev + 1, len(cl)):
|
2011-03-20 02:50:22 +03:00
|
|
|
if r not in tostrip:
|
|
|
|
saverevs.add(r)
|
|
|
|
saveheads.difference_update(cl.parentrevs(r))
|
2009-05-17 05:38:03 +04:00
|
|
|
saveheads.add(r)
|
2011-03-20 02:50:22 +03:00
|
|
|
saveheads = [cl.node(r) for r in saveheads]
|
|
|
|
|
2011-10-30 21:10:09 +04:00
|
|
|
# compute base nodes
|
|
|
|
if saverevs:
|
|
|
|
descendants = set(cl.descendants(*saverevs))
|
|
|
|
saverevs.difference_update(descendants)
|
|
|
|
savebases = [cl.node(r) for r in saverevs]
|
2008-02-20 01:20:10 +03:00
|
|
|
|
2011-02-10 22:46:28 +03:00
|
|
|
bm = repo._bookmarks
|
|
|
|
updatebm = []
|
|
|
|
for m in bm:
|
|
|
|
rev = repo[bm[m]].rev()
|
|
|
|
if rev in tostrip:
|
|
|
|
updatebm.append(m)
|
|
|
|
|
2007-06-25 10:26:44 +04:00
|
|
|
# create a changegroup for all the branches we need to keep
|
2010-05-20 04:46:39 +04:00
|
|
|
backupfile = None
|
2007-06-25 10:26:44 +04:00
|
|
|
if backup == "all":
|
2011-10-30 21:10:09 +04:00
|
|
|
backupfile = _bundle(repo, [node], cl.heads(), node, 'backup')
|
2010-05-20 21:15:44 +04:00
|
|
|
repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
|
2011-10-30 21:10:09 +04:00
|
|
|
if saveheads or savebases:
|
2010-08-12 11:53:23 +04:00
|
|
|
# do not compress partial bundle if we remove it from disk later
|
2011-10-30 21:10:09 +04:00
|
|
|
chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
|
|
|
|
compress=keeppartialbundle)
|
2007-06-25 10:26:44 +04:00
|
|
|
|
2009-04-16 17:34:03 +04:00
|
|
|
mfst = repo.manifest
|
|
|
|
|
2010-04-10 02:23:35 +04:00
|
|
|
tr = repo.transaction("strip")
|
2009-04-16 17:34:03 +04:00
|
|
|
offset = len(tr.entries)
|
|
|
|
|
|
|
|
try:
|
2010-05-20 04:46:39 +04:00
|
|
|
tr.startgroup()
|
|
|
|
cl.strip(striprev, tr)
|
|
|
|
mfst.strip(striprev, tr)
|
|
|
|
for fn in files:
|
|
|
|
repo.file(fn).strip(striprev, tr)
|
|
|
|
tr.endgroup()
|
|
|
|
|
|
|
|
try:
|
|
|
|
for i in xrange(offset, len(tr.entries)):
|
|
|
|
file, troffset, ignore = tr.entries[i]
|
|
|
|
repo.sopener(file, 'a').truncate(troffset)
|
|
|
|
tr.close()
|
|
|
|
except:
|
|
|
|
tr.abort()
|
|
|
|
raise
|
|
|
|
|
2011-10-30 21:10:09 +04:00
|
|
|
if saveheads or savebases:
|
2010-05-20 22:27:28 +04:00
|
|
|
ui.note(_("adding branch\n"))
|
2010-05-20 04:46:39 +04:00
|
|
|
f = open(chgrpfile, "rb")
|
|
|
|
gen = changegroup.readbundle(f, chgrpfile)
|
2010-05-20 22:27:28 +04:00
|
|
|
if not repo.ui.verbose:
|
|
|
|
# silence internal shuffling chatter
|
|
|
|
repo.ui.pushbuffer()
|
2010-05-20 04:46:39 +04:00
|
|
|
repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
|
2010-05-20 22:27:28 +04:00
|
|
|
if not repo.ui.verbose:
|
|
|
|
repo.ui.popbuffer()
|
2010-05-20 04:46:39 +04:00
|
|
|
f.close()
|
2010-08-12 11:53:23 +04:00
|
|
|
if not keeppartialbundle:
|
2010-05-20 04:46:39 +04:00
|
|
|
os.unlink(chgrpfile)
|
2011-02-10 22:46:28 +03:00
|
|
|
|
|
|
|
for m in updatebm:
|
|
|
|
bm[m] = repo['.'].node()
|
|
|
|
bookmarks.write(repo)
|
2011-11-07 16:20:22 +04:00
|
|
|
# remove potential unknown phase
|
|
|
|
# XXX using to_strip data would be faster
|
|
|
|
phases.filterunknown(repo)
|
2009-04-16 17:34:03 +04:00
|
|
|
except:
|
2010-05-20 04:46:39 +04:00
|
|
|
if backupfile:
|
2010-07-16 16:44:30 +04:00
|
|
|
ui.warn(_("strip failed, full bundle stored in '%s'\n")
|
|
|
|
% backupfile)
|
2010-05-20 04:46:39 +04:00
|
|
|
elif saveheads:
|
2010-07-16 16:44:30 +04:00
|
|
|
ui.warn(_("strip failed, partial bundle stored in '%s'\n")
|
2010-05-20 04:46:39 +04:00
|
|
|
% chgrpfile)
|
2009-04-16 17:34:03 +04:00
|
|
|
raise
|
2007-06-25 10:26:44 +04:00
|
|
|
|
2009-07-16 18:39:41 +04:00
|
|
|
repo.destroyed()
|