author | Matt Mackall <mpm@selenic.com> |
Thu, 22 Mar 2007 23:37:44 -0500 | |
changeset 4270 | cd7b36b7869c |
parent 4267 | b11a2fb59cf5 |
child 4395 | 47ef2d228375 |
permissions | -rw-r--r-- |
2802 | 1 |
# verify.py - repository integrity checking for Mercurial |
2 |
# |
|
3 |
# Copyright 2006 Matt Mackall <mpm@selenic.com> |
|
4 |
# |
|
5 |
# This software may be used and distributed according to the terms |
|
6 |
# of the GNU General Public License, incorporated herein by reference. |
|
7 |
||
8 |
from node import * |
|
3893 | 9 |
from i18n import _ |
2802 | 10 |
import revlog, mdiff |
11 |
||
12 |
def verify(repo): |
|
13 |
filelinkrevs = {} |
|
14 |
filenodes = {} |
|
15 |
changesets = revisions = files = 0 |
|
16 |
errors = [0] |
|
17 |
warnings = [0] |
|
18 |
neededmanifests = {} |
|
19 |
||
20 |
def err(msg): |
|
21 |
repo.ui.warn(msg + "\n") |
|
22 |
errors[0] += 1 |
|
23 |
||
24 |
def warn(msg): |
|
25 |
repo.ui.warn(msg + "\n") |
|
26 |
warnings[0] += 1 |
|
27 |
||
28 |
def checksize(obj, name): |
|
29 |
d = obj.checksize() |
|
30 |
if d[0]: |
|
31 |
err(_("%s data length off by %d bytes") % (name, d[0])) |
|
32 |
if d[1]: |
|
33 |
err(_("%s index contains %d extra bytes") % (name, d[1])) |
|
34 |
||
35 |
def checkversion(obj, name): |
|
36 |
if obj.version != revlog.REVLOGV0: |
|
37 |
if not revlogv1: |
|
38 |
warn(_("warning: `%s' uses revlog format 1") % name) |
|
39 |
elif revlogv1: |
|
40 |
warn(_("warning: `%s' uses revlog format 0") % name) |
|
41 |
||
4267
b11a2fb59cf5
revlog: simplify revlog version handling
Matt Mackall <mpm@selenic.com>
parents:
3893
diff
changeset
|
42 |
revlogv1 = repo.changelog.version != revlog.REVLOGV0 |
b11a2fb59cf5
revlog: simplify revlog version handling
Matt Mackall <mpm@selenic.com>
parents:
3893
diff
changeset
|
43 |
if repo.ui.verbose or not revlogv1: |
2802 | 44 |
repo.ui.status(_("repository uses revlog format %d\n") % |
45 |
(revlogv1 and 1 or 0)) |
|
46 |
||
47 |
seen = {} |
|
48 |
repo.ui.status(_("checking changesets\n")) |
|
49 |
checksize(repo.changelog, "changelog") |
|
50 |
||
3468
0e68608bd11d
use xrange instead of range
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
3189
diff
changeset
|
51 |
for i in xrange(repo.changelog.count()): |
2802 | 52 |
changesets += 1 |
53 |
n = repo.changelog.node(i) |
|
54 |
l = repo.changelog.linkrev(n) |
|
55 |
if l != i: |
|
56 |
err(_("incorrect link (%d) for changeset revision %d") %(l, i)) |
|
57 |
if n in seen: |
|
58 |
err(_("duplicate changeset at revision %d") % i) |
|
59 |
seen[n] = 1 |
|
60 |
||
61 |
for p in repo.changelog.parents(n): |
|
62 |
if p not in repo.changelog.nodemap: |
|
63 |
err(_("changeset %s has unknown parent %s") % |
|
64 |
(short(n), short(p))) |
|
65 |
try: |
|
66 |
changes = repo.changelog.read(n) |
|
67 |
except KeyboardInterrupt: |
|
68 |
repo.ui.warn(_("interrupted")) |
|
69 |
raise |
|
70 |
except Exception, inst: |
|
71 |
err(_("unpacking changeset %s: %s") % (short(n), inst)) |
|
72 |
continue |
|
73 |
||
74 |
neededmanifests[changes[0]] = n |
|
75 |
||
76 |
for f in changes[3]: |
|
77 |
filelinkrevs.setdefault(f, []).append(i) |
|
78 |
||
79 |
seen = {} |
|
80 |
repo.ui.status(_("checking manifests\n")) |
|
81 |
checkversion(repo.manifest, "manifest") |
|
82 |
checksize(repo.manifest, "manifest") |
|
83 |
||
3468
0e68608bd11d
use xrange instead of range
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
3189
diff
changeset
|
84 |
for i in xrange(repo.manifest.count()): |
2802 | 85 |
n = repo.manifest.node(i) |
86 |
l = repo.manifest.linkrev(n) |
|
87 |
||
88 |
if l < 0 or l >= repo.changelog.count(): |
|
89 |
err(_("bad manifest link (%d) at revision %d") % (l, i)) |
|
90 |
||
91 |
if n in neededmanifests: |
|
92 |
del neededmanifests[n] |
|
93 |
||
94 |
if n in seen: |
|
95 |
err(_("duplicate manifest at revision %d") % i) |
|
96 |
||
97 |
seen[n] = 1 |
|
98 |
||
99 |
for p in repo.manifest.parents(n): |
|
100 |
if p not in repo.manifest.nodemap: |
|
101 |
err(_("manifest %s has unknown parent %s") % |
|
102 |
(short(n), short(p))) |
|
103 |
||
104 |
try: |
|
3189
f3b939444c72
Abstract manifest block parsing.
Brendan Cully <brendan@kublai.com>
parents:
2802
diff
changeset
|
105 |
for f, fn in repo.manifest.readdelta(n).iteritems(): |
f3b939444c72
Abstract manifest block parsing.
Brendan Cully <brendan@kublai.com>
parents:
2802
diff
changeset
|
106 |
filenodes.setdefault(f, {})[fn] = 1 |
2802 | 107 |
except KeyboardInterrupt: |
108 |
repo.ui.warn(_("interrupted")) |
|
109 |
raise |
|
110 |
except Exception, inst: |
|
3189
f3b939444c72
Abstract manifest block parsing.
Brendan Cully <brendan@kublai.com>
parents:
2802
diff
changeset
|
111 |
err(_("reading delta for manifest %s: %s") % (short(n), inst)) |
2802 | 112 |
continue |
113 |
||
114 |
repo.ui.status(_("crosschecking files in changesets and manifests\n")) |
|
115 |
||
116 |
for m, c in neededmanifests.items(): |
|
117 |
err(_("Changeset %s refers to unknown manifest %s") % |
|
118 |
(short(m), short(c))) |
|
119 |
del neededmanifests |
|
120 |
||
121 |
for f in filenodes: |
|
122 |
if f not in filelinkrevs: |
|
123 |
err(_("file %s in manifest but not in changesets") % f) |
|
124 |
||
125 |
for f in filelinkrevs: |
|
126 |
if f not in filenodes: |
|
127 |
err(_("file %s in changeset but not in manifest") % f) |
|
128 |
||
129 |
repo.ui.status(_("checking files\n")) |
|
130 |
ff = filenodes.keys() |
|
131 |
ff.sort() |
|
132 |
for f in ff: |
|
133 |
if f == "/dev/null": |
|
134 |
continue |
|
135 |
files += 1 |
|
136 |
if not f: |
|
137 |
err(_("file without name in manifest %s") % short(n)) |
|
138 |
continue |
|
139 |
fl = repo.file(f) |
|
140 |
checkversion(fl, f) |
|
141 |
checksize(fl, f) |
|
142 |
||
143 |
nodes = {nullid: 1} |
|
144 |
seen = {} |
|
3468
0e68608bd11d
use xrange instead of range
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
3189
diff
changeset
|
145 |
for i in xrange(fl.count()): |
2802 | 146 |
revisions += 1 |
147 |
n = fl.node(i) |
|
148 |
||
149 |
if n in seen: |
|
150 |
err(_("%s: duplicate revision %d") % (f, i)) |
|
151 |
if n not in filenodes[f]: |
|
152 |
err(_("%s: %d:%s not in manifests") % (f, i, short(n))) |
|
153 |
else: |
|
154 |
del filenodes[f][n] |
|
155 |
||
156 |
flr = fl.linkrev(n) |
|
157 |
if flr not in filelinkrevs.get(f, []): |
|
158 |
err(_("%s:%s points to unexpected changeset %d") |
|
159 |
% (f, short(n), flr)) |
|
160 |
else: |
|
161 |
filelinkrevs[f].remove(flr) |
|
162 |
||
163 |
# verify contents |
|
164 |
try: |
|
165 |
t = fl.read(n) |
|
166 |
except KeyboardInterrupt: |
|
167 |
repo.ui.warn(_("interrupted")) |
|
168 |
raise |
|
169 |
except Exception, inst: |
|
170 |
err(_("unpacking file %s %s: %s") % (f, short(n), inst)) |
|
171 |
||
172 |
# verify parents |
|
173 |
(p1, p2) = fl.parents(n) |
|
174 |
if p1 not in nodes: |
|
175 |
err(_("file %s:%s unknown parent 1 %s") % |
|
176 |
(f, short(n), short(p1))) |
|
177 |
if p2 not in nodes: |
|
178 |
err(_("file %s:%s unknown parent 2 %s") % |
|
179 |
(f, short(n), short(p1))) |
|
180 |
nodes[n] = 1 |
|
181 |
||
3745
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
182 |
# check renames |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
183 |
try: |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
184 |
rp = fl.renamed(n) |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
185 |
if rp: |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
186 |
fl2 = repo.file(rp[0]) |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
187 |
rev = fl2.rev(rp[1]) |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
188 |
except KeyboardInterrupt: |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
189 |
repo.ui.warn(_("interrupted")) |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
190 |
raise |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
191 |
except Exception, inst: |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
192 |
err(_("checking rename on file %s %s: %s") % (f, short(n), inst)) |
d626fc9e3985
verify: add rename link checking
Matt Mackall <mpm@selenic.com>
parents:
3468
diff
changeset
|
193 |
|
2802 | 194 |
# cross-check |
195 |
for node in filenodes[f]: |
|
196 |
err(_("node %s in manifests not in %s") % (hex(node), f)) |
|
197 |
||
198 |
repo.ui.status(_("%d files, %d changesets, %d total revisions\n") % |
|
199 |
(files, changesets, revisions)) |
|
200 |
||
201 |
if warnings[0]: |
|
202 |
repo.ui.warn(_("%d warnings encountered!\n") % warnings[0]) |
|
203 |
if errors[0]: |
|
204 |
repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0]) |
|
205 |
return 1 |
|
206 |