##// END OF EJS Templates
add a fix for issue 1175...
add a fix for issue 1175 If we copy a file followed by an update, it's possible for the parent manifest to no longer contain the source file of the copy, which could cause commit to fail. If this happens, we search backwares from the first parent to find the most likely original revision.

File last commit:

r6762:f67d1468 default
r6875:0d714a48 default
Show More
verify.py
281 lines | 9.1 KiB | text/x-python | PythonLexer
Matt Mackall
Move repo.verify
r2778 # verify.py - repository integrity checking for Mercurial
#
Thomas Arendsen Hein
Updated copyright notices and add "and others" to "hg version"
r4635 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
Matt Mackall
Move repo.verify
r2778 #
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
Joel Rosdahl
Expand import * to allow Pyflakes to find problems
r6211 from node import nullid, short
Matt Mackall
Simplify i18n imports
r3891 from i18n import _
Matt Mackall
remove unneeded imports of mdiff
r5175 import revlog
Matt Mackall
Move repo.verify
r2778
def verify(repo):
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 lock = repo.lock()
try:
return _verify(repo)
finally:
del lock
def _verify(repo):
Matt Mackall
Move repo.verify
r2778 filelinkrevs = {}
filenodes = {}
changesets = revisions = files = 0
Matt Mackall
verify: report first bad changeset...
r5313 firstbad = [None]
Matt Mackall
Move repo.verify
r2778 errors = [0]
warnings = [0]
neededmanifests = {}
Matt Mackall
verify: report first bad changeset...
r5313 def err(linkrev, msg, filename=None):
if linkrev != None:
if firstbad[0] != None:
firstbad[0] = min(firstbad[0], linkrev)
else:
firstbad[0] = linkrev
else:
linkrev = "?"
msg = "%s: %s" % (linkrev, msg)
if filename:
msg = "%s@%s" % (filename, msg)
repo.ui.warn(" " + msg + "\n")
Matt Mackall
Move repo.verify
r2778 errors[0] += 1
def warn(msg):
repo.ui.warn(msg + "\n")
warnings[0] += 1
def checksize(obj, name):
d = obj.checksize()
if d[0]:
Matt Mackall
verify: report first bad changeset...
r5313 err(None, _("data length off by %d bytes") % d[0], name)
Matt Mackall
Move repo.verify
r2778 if d[1]:
Matt Mackall
verify: report first bad changeset...
r5313 err(None, _("index contains %d extra bytes") % d[1], name)
Matt Mackall
Move repo.verify
r2778
def checkversion(obj, name):
if obj.version != revlog.REVLOGV0:
if not revlogv1:
warn(_("warning: `%s' uses revlog format 1") % name)
elif revlogv1:
warn(_("warning: `%s' uses revlog format 0") % name)
Matt Mackall
revlog: simplify revlog version handling...
r4258 revlogv1 = repo.changelog.version != revlog.REVLOGV0
if repo.ui.verbose or not revlogv1:
Matt Mackall
Move repo.verify
r2778 repo.ui.status(_("repository uses revlog format %d\n") %
(revlogv1 and 1 or 0))
Matt Mackall
verify: improve handling of empty or missing files...
r5541 havecl = havemf = 1
Matt Mackall
Move repo.verify
r2778 seen = {}
repo.ui.status(_("checking changesets\n"))
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if repo.changelog.count() == 0 and repo.manifest.count() > 1:
havecl = 0
err(0, _("empty or missing 00changelog.i"))
else:
checksize(repo.changelog, "changelog")
Matt Mackall
Move repo.verify
r2778
Benoit Boissinot
use xrange instead of range
r3473 for i in xrange(repo.changelog.count()):
Matt Mackall
Move repo.verify
r2778 changesets += 1
n = repo.changelog.node(i)
l = repo.changelog.linkrev(n)
if l != i:
Matt Mackall
verify: report first bad changeset...
r5313 err(i, _("incorrect link (%d) for changeset") %(l))
Matt Mackall
Move repo.verify
r2778 if n in seen:
Matt Mackall
verify: report first bad changeset...
r5313 err(i, _("duplicates changeset at revision %d") % seen[n])
seen[n] = i
Matt Mackall
Move repo.verify
r2778
for p in repo.changelog.parents(n):
if p not in repo.changelog.nodemap:
Matt Mackall
verify: report first bad changeset...
r5313 err(i, _("changeset has unknown parent %s") % short(p))
Matt Mackall
Move repo.verify
r2778 try:
changes = repo.changelog.read(n)
except KeyboardInterrupt:
repo.ui.warn(_("interrupted"))
raise
except Exception, inst:
Matt Mackall
verify: report first bad changeset...
r5313 err(i, _("unpacking changeset: %s") % inst)
Matt Mackall
Move repo.verify
r2778 continue
Matt Mackall
verify: report first bad changeset...
r5313 if changes[0] not in neededmanifests:
neededmanifests[changes[0]] = i
Matt Mackall
Move repo.verify
r2778
for f in changes[3]:
filelinkrevs.setdefault(f, []).append(i)
seen = {}
repo.ui.status(_("checking manifests\n"))
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if repo.changelog.count() > 0 and repo.manifest.count() == 0:
havemf = 0
err(0, _("empty or missing 00manifest.i"))
else:
checkversion(repo.manifest, "manifest")
checksize(repo.manifest, "manifest")
Matt Mackall
Move repo.verify
r2778
Benoit Boissinot
use xrange instead of range
r3473 for i in xrange(repo.manifest.count()):
Matt Mackall
Move repo.verify
r2778 n = repo.manifest.node(i)
l = repo.manifest.linkrev(n)
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if l < 0 or (havecl and l >= repo.changelog.count()):
Matt Mackall
verify: report first bad changeset...
r5313 err(None, _("bad link (%d) at manifest revision %d") % (l, i))
Matt Mackall
Move repo.verify
r2778
if n in neededmanifests:
del neededmanifests[n]
if n in seen:
Matt Mackall
verify: report first bad changeset...
r5313 err(l, _("duplicates manifest from %d") % seen[n])
Matt Mackall
Move repo.verify
r2778
Matt Mackall
verify: report first bad changeset...
r5313 seen[n] = l
Matt Mackall
Move repo.verify
r2778
for p in repo.manifest.parents(n):
if p not in repo.manifest.nodemap:
Matt Mackall
verify: report first bad changeset...
r5313 err(l, _("manifest has unknown parent %s") % short(p))
Matt Mackall
Move repo.verify
r2778
try:
Brendan Cully
Abstract manifest block parsing.
r3196 for f, fn in repo.manifest.readdelta(n).iteritems():
Matt Mackall
verify: report first bad changeset...
r5313 fns = filenodes.setdefault(f, {})
if fn not in fns:
fns[fn] = n
Matt Mackall
Move repo.verify
r2778 except KeyboardInterrupt:
repo.ui.warn(_("interrupted"))
raise
except Exception, inst:
Matt Mackall
verify: report first bad changeset...
r5313 err(l, _("reading manifest delta: %s") % inst)
Matt Mackall
Move repo.verify
r2778 continue
repo.ui.status(_("crosschecking files in changesets and manifests\n"))
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if havemf > 0:
nm = [(c, m) for m, c in neededmanifests.items()]
nm.sort()
for c, m in nm:
err(c, _("changeset refers to unknown manifest %s") % short(m))
del neededmanifests, nm
Matt Mackall
Move repo.verify
r2778
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if havecl:
fl = filenodes.keys()
fl.sort()
for f in fl:
if f not in filelinkrevs:
lrs = [repo.manifest.linkrev(n) for n in filenodes[f]]
lrs.sort()
err(lrs[0], _("in manifest but not in changeset"), f)
del fl
Matt Mackall
Move repo.verify
r2778
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if havemf:
fl = filelinkrevs.keys()
fl.sort()
for f in fl:
if f not in filenodes:
lr = filelinkrevs[f][0]
err(lr, _("in changeset but not in manifest"), f)
del fl
Matt Mackall
Move repo.verify
r2778
repo.ui.status(_("checking files\n"))
Matt Mackall
verify: improve handling of empty or missing files...
r5541 ff = dict.fromkeys(filenodes.keys() + filelinkrevs.keys()).keys()
Matt Mackall
Move repo.verify
r2778 ff.sort()
for f in ff:
if f == "/dev/null":
continue
files += 1
if not f:
Matt Mackall
verify: improve handling of empty or missing files...
r5541 lr = filelinkrevs[f][0]
err(lr, _("file without name in manifest"))
Matt Mackall
Move repo.verify
r2778 continue
fl = repo.file(f)
checkversion(fl, f)
checksize(fl, f)
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if fl.count() == 0:
err(filelinkrevs[f][0], _("empty or missing revlog"), f)
continue
Matt Mackall
verify: report first bad changeset...
r5313 seen = {}
Matt Mackall
Move repo.verify
r2778 nodes = {nullid: 1}
Benoit Boissinot
use xrange instead of range
r3473 for i in xrange(fl.count()):
Matt Mackall
Move repo.verify
r2778 revisions += 1
n = fl.node(i)
Matt Mackall
verify: report first bad changeset...
r5313 flr = fl.linkrev(n)
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if flr < 0 or (havecl and flr not in filelinkrevs.get(f, [])):
Matt Mackall
verify: report first bad changeset...
r5313 if flr < 0 or flr >= repo.changelog.count():
err(None, _("rev %d point to nonexistent changeset %d")
% (i, flr), f)
else:
err(None, _("rev %d points to unexpected changeset %d")
% (i, flr), f)
if f in filelinkrevs:
warn(_(" (expected %s)") % filelinkrevs[f][0])
flr = None # can't be trusted
else:
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if havecl:
filelinkrevs[f].remove(flr)
Matt Mackall
Move repo.verify
r2778
if n in seen:
Matt Mackall
verify: report first bad changeset...
r5313 err(flr, _("duplicate revision %d") % i, f)
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if f in filenodes:
if havemf and n not in filenodes[f]:
err(flr, _("%s not in manifests") % (short(n)), f)
else:
del filenodes[f][n]
Matt Mackall
Move repo.verify
r2778
# verify contents
try:
t = fl.read(n)
except KeyboardInterrupt:
repo.ui.warn(_("interrupted"))
raise
except Exception, inst:
Matt Mackall
verify: report first bad changeset...
r5313 err(flr, _("unpacking %s: %s") % (short(n), inst), f)
Matt Mackall
Move repo.verify
r2778
# verify parents
Matt Mackall
verify: report first bad changeset...
r5313 try:
(p1, p2) = fl.parents(n)
if p1 not in nodes:
err(flr, _("unknown parent 1 %s of %s") %
(short(p1), short(n)), f)
if p2 not in nodes:
err(flr, _("unknown parent 2 %s of %s") %
(short(p2), short(p1)), f)
except KeyboardInterrupt:
repo.ui.warn(_("interrupted"))
raise
except Exception, inst:
err(flr, _("checking parents of %s: %s") % (short(n), inst), f)
Matt Mackall
Move repo.verify
r2778 nodes[n] = 1
Matt Mackall
verify: add rename link checking
r3744 # check renames
try:
rp = fl.renamed(n)
if rp:
fl2 = repo.file(rp[0])
Patrick Mezard
verify: check copy source revlog and nodeid
r6534 if fl2.count() == 0:
err(flr, _("empty or missing copy source revlog %s:%s")
% (rp[0], short(rp[1])), f)
elif rp[1] == nullid:
err(flr, _("copy source revision is nullid %s:%s")
% (rp[0], short(rp[1])), f)
else:
rev = fl2.rev(rp[1])
Matt Mackall
verify: add rename link checking
r3744 except KeyboardInterrupt:
repo.ui.warn(_("interrupted"))
raise
except Exception, inst:
Matt Mackall
verify: report first bad changeset...
r5313 err(flr, _("checking rename of %s: %s") %
(short(n), inst), f)
Matt Mackall
verify: add rename link checking
r3744
Matt Mackall
Move repo.verify
r2778 # cross-check
Matt Mackall
verify: improve handling of empty or missing files...
r5541 if f in filenodes:
fns = [(repo.manifest.linkrev(filenodes[f][n]), n)
for n in filenodes[f]]
fns.sort()
for lr, node in fns:
err(lr, _("%s in manifests not found") % short(node), f)
Matt Mackall
Move repo.verify
r2778
repo.ui.status(_("%d files, %d changesets, %d total revisions\n") %
(files, changesets, revisions))
if warnings[0]:
repo.ui.warn(_("%d warnings encountered!\n") % warnings[0])
if errors[0]:
repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
Matt Mackall
verify: report first bad changeset...
r5313 if firstbad[0]:
repo.ui.warn(_("(first damaged changeset appears to be %d)\n")
% firstbad[0])
Matt Mackall
Move repo.verify
r2778 return 1