repair.py
135 lines
| 4.6 KiB
| text/x-python
|
PythonLexer
/ mercurial / repair.py
Matt Mackall
|
r4702 | # repair.py - functions for repository repair for mercurial | ||
# | ||||
# Copyright 2005, 2006 Chris Mason <mason@suse.com> | ||||
# Copyright 2007 Matt Mackall | ||||
# | ||||
# This software may be used and distributed according to the terms | ||||
# of the GNU General Public License, incorporated herein by reference. | ||||
Alexis S. L. Carvalho
|
r5899 | import changegroup, os | ||
Joel Rosdahl
|
r6211 | from node import nullrev, short | ||
Matt Mackall
|
r4702 | |||
Alexis S. L. Carvalho
|
r5909 | def _bundle(repo, bases, heads, node, suffix, extranodes=None): | ||
Alexis S. L. Carvalho
|
r5905 | """create a bundle with the specified revisions as a backup""" | ||
Alexis S. L. Carvalho
|
r5909 | cg = repo.changegroupsubset(bases, heads, 'strip', extranodes) | ||
Alexis S. L. Carvalho
|
r5905 | backupdir = repo.join("strip-backup") | ||
if not os.path.isdir(backupdir): | ||||
os.mkdir(backupdir) | ||||
name = os.path.join(backupdir, "%s-%s" % (short(node), suffix)) | ||||
repo.ui.warn("saving bundle to %s\n" % name) | ||||
return changegroup.writebundle(cg, name, "HG10BZ") | ||||
Matt Mackall
|
r4702 | |||
Alexis S. L. Carvalho
|
r5910 | def _collectfiles(repo, striprev): | ||
"""find out the filelogs affected by the strip""" | ||||
files = {} | ||||
Matt Mackall
|
r4702 | |||
Matt Mackall
|
r6750 | for x in xrange(striprev, len(repo)): | ||
Matt Mackall
|
r6747 | for name in repo[x].files(): | ||
Alexis S. L. Carvalho
|
r5910 | if name in files: | ||
Alexis S. L. Carvalho
|
r5905 | continue | ||
Alexis S. L. Carvalho
|
r5910 | files[name] = 1 | ||
Alexis S. L. Carvalho
|
r5902 | |||
Alexis S. L. Carvalho
|
r5910 | files = files.keys() | ||
files.sort() | ||||
return files | ||||
Alexis S. L. Carvalho
|
r5902 | |||
Alexis S. L. Carvalho
|
r5909 | def _collectextranodes(repo, files, link): | ||
"""return the nodes that have to be saved before the strip""" | ||||
def collectone(revlog): | ||||
extra = [] | ||||
Matt Mackall
|
r6750 | startrev = count = len(revlog) | ||
Alexis S. L. Carvalho
|
r5909 | # find the truncation point of the revlog | ||
for i in xrange(0, count): | ||||
node = revlog.node(i) | ||||
lrev = revlog.linkrev(node) | ||||
if lrev >= link: | ||||
startrev = i + 1 | ||||
break | ||||
# see if any revision after that point has a linkrev less than link | ||||
# (we have to manually save these guys) | ||||
for i in xrange(startrev, count): | ||||
node = revlog.node(i) | ||||
lrev = revlog.linkrev(node) | ||||
if lrev < link: | ||||
extra.append((node, cl.node(lrev))) | ||||
return extra | ||||
extranodes = {} | ||||
cl = repo.changelog | ||||
extra = collectone(repo.manifest) | ||||
if extra: | ||||
extranodes[1] = extra | ||||
for fname in files: | ||||
f = repo.file(fname) | ||||
extra = collectone(f) | ||||
if extra: | ||||
extranodes[fname] = extra | ||||
return extranodes | ||||
Alexis S. L. Carvalho
|
r5905 | def strip(ui, repo, node, backup="all"): | ||
Alexis S. L. Carvalho
|
r5901 | cl = repo.changelog | ||
Matt Mackall
|
r4702 | # TODO delete the undo files, and handle undo of merge sets | ||
Alexis S. L. Carvalho
|
r5901 | striprev = cl.rev(node) | ||
Matt Mackall
|
r4702 | |||
Alexis S. L. Carvalho
|
r6147 | # 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) | ||||
tostrip = {striprev: 1} | ||||
saveheads = {} | ||||
savebases = [] | ||||
Matt Mackall
|
r6750 | for r in xrange(striprev + 1, len(cl)): | ||
Alexis S. L. Carvalho
|
r6147 | parents = cl.parentrevs(r) | ||
if parents[0] in tostrip or parents[1] in tostrip: | ||||
# r is a descendant of striprev | ||||
tostrip[r] = 1 | ||||
# if this is a merge and one of the parents does not descend | ||||
# from striprev, mark that parent as a savehead. | ||||
if parents[1] != nullrev: | ||||
for p in parents: | ||||
if p not in tostrip and p > striprev: | ||||
saveheads[p] = 1 | ||||
else: | ||||
# if no parents of this revision will be stripped, mark it as | ||||
# a savebase | ||||
if parents[0] < striprev and parents[1] < striprev: | ||||
savebases.append(cl.node(r)) | ||||
Matt Mackall
|
r4702 | |||
Alexis S. L. Carvalho
|
r6147 | for p in parents: | ||
if p in saveheads: | ||||
del saveheads[p] | ||||
saveheads[r] = 1 | ||||
Matt Mackall
|
r4702 | |||
Alexis S. L. Carvalho
|
r6147 | saveheads = [cl.node(r) for r in saveheads] | ||
Alexis S. L. Carvalho
|
r5910 | files = _collectfiles(repo, striprev) | ||
Alexis S. L. Carvalho
|
r5909 | |||
Alexis S. L. Carvalho
|
r5910 | extranodes = _collectextranodes(repo, files, striprev) | ||
Alexis S. L. Carvalho
|
r5909 | |||
Matt Mackall
|
r4702 | # create a changegroup for all the branches we need to keep | ||
if backup == "all": | ||||
Alexis S. L. Carvalho
|
r5905 | _bundle(repo, [node], cl.heads(), node, 'backup') | ||
Alexis S. L. Carvalho
|
r5909 | if saveheads or extranodes: | ||
Alexis S. L. Carvalho
|
r6147 | chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp', | ||
Alexis S. L. Carvalho
|
r5909 | extranodes) | ||
Matt Mackall
|
r4702 | |||
Alexis S. L. Carvalho
|
r5910 | cl.strip(striprev) | ||
repo.manifest.strip(striprev) | ||||
for name in files: | ||||
f = repo.file(name) | ||||
f.strip(striprev) | ||||
Matt Mackall
|
r4702 | |||
Alexis S. L. Carvalho
|
r5909 | if saveheads or extranodes: | ||
Matt Mackall
|
r4702 | ui.status("adding branch\n") | ||
Alexis S. L. Carvalho
|
r5898 | f = open(chgrpfile, "rb") | ||
gen = changegroup.readbundle(f, chgrpfile) | ||||
Alexis S. L. Carvalho
|
r5909 | repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True) | ||
Alexis S. L. Carvalho
|
r5898 | f.close() | ||
Matt Mackall
|
r4702 | if backup != "strip": | ||
os.unlink(chgrpfile) | ||||