repair.py
151 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 | ||
from node import * | ||||
Matt Mackall
|
r4702 | |||
Alexis S. L. Carvalho
|
r5905 | def _limitheads(cl, stoprev): | ||
"""return the list of all revs >= stoprev that have no children""" | ||||
seen = {} | ||||
heads = [] | ||||
Matt Mackall
|
r4702 | |||
Alexis S. L. Carvalho
|
r5905 | for r in xrange(cl.count() - 1, stoprev - 1, -1): | ||
if r not in seen: | ||||
heads.append(r) | ||||
for p in cl.parentrevs(r): | ||||
seen[p] = 1 | ||||
return heads | ||||
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 | |||
Alexis S. L. Carvalho
|
r5905 | for x in xrange(striprev, repo.changelog.count()): | ||
for name in repo.changectx(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 = [] | ||||
startrev = count = revlog.count() | ||||
# 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 | pp = cl.parents(node) | ||
striprev = cl.rev(node) | ||||
Matt Mackall
|
r4702 | |||
# save is a list of all the branches we are truncating away | ||||
# that we actually want to keep. changegroup will be used | ||||
# to preserve them and add them back after the truncate | ||||
saveheads = [] | ||||
savebases = {} | ||||
Alexis S. L. Carvalho
|
r5905 | heads = [cl.node(r) for r in _limitheads(cl, striprev)] | ||
Matt Mackall
|
r4702 | seen = {} | ||
# search through all the heads, finding those where the revision | ||||
# we want to strip away is an ancestor. Also look for merges | ||||
# that might be turned into new heads by the strip. | ||||
while heads: | ||||
h = heads.pop() | ||||
n = h | ||||
while True: | ||||
seen[n] = 1 | ||||
Alexis S. L. Carvalho
|
r5901 | pp = cl.parents(n) | ||
Alexis S. L. Carvalho
|
r5899 | if pp[1] != nullid: | ||
Matt Mackall
|
r4702 | for p in pp: | ||
Alexis S. L. Carvalho
|
r5901 | if cl.rev(p) > striprev and p not in seen: | ||
Matt Mackall
|
r4702 | heads.append(p) | ||
Alexis S. L. Carvalho
|
r5899 | if pp[0] == nullid: | ||
Matt Mackall
|
r4702 | break | ||
Alexis S. L. Carvalho
|
r5901 | if cl.rev(pp[0]) < striprev: | ||
Matt Mackall
|
r4702 | break | ||
n = pp[0] | ||||
Alexis S. L. Carvalho
|
r5900 | if n == node: | ||
Matt Mackall
|
r4702 | break | ||
Alexis S. L. Carvalho
|
r5901 | r = cl.reachable(h, node) | ||
Alexis S. L. Carvalho
|
r5900 | if node not in r: | ||
Matt Mackall
|
r4702 | saveheads.append(h) | ||
for x in r: | ||||
Alexis S. L. Carvalho
|
r5901 | if cl.rev(x) > striprev: | ||
Matt Mackall
|
r4702 | savebases[x] = 1 | ||
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: | ||
chgrpfile = _bundle(repo, savebases.keys(), saveheads, node, 'temp', | ||||
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) | ||||