##// END OF EJS Templates
verify: improve handling of empty or missing files...
Matt Mackall -
r5541:ceaa752f default
parent child Browse files
Show More
@@ -1,250 +1,274 b''
1 # verify.py - repository integrity checking for Mercurial
1 # verify.py - repository integrity checking for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import *
8 from node import *
9 from i18n import _
9 from i18n import _
10 import revlog
10 import revlog
11
11
12 def verify(repo):
12 def verify(repo):
13 lock = repo.lock()
13 lock = repo.lock()
14 try:
14 try:
15 return _verify(repo)
15 return _verify(repo)
16 finally:
16 finally:
17 del lock
17 del lock
18
18
19 def _verify(repo):
19 def _verify(repo):
20 filelinkrevs = {}
20 filelinkrevs = {}
21 filenodes = {}
21 filenodes = {}
22 changesets = revisions = files = 0
22 changesets = revisions = files = 0
23 firstbad = [None]
23 firstbad = [None]
24 errors = [0]
24 errors = [0]
25 warnings = [0]
25 warnings = [0]
26 neededmanifests = {}
26 neededmanifests = {}
27
27
28 def err(linkrev, msg, filename=None):
28 def err(linkrev, msg, filename=None):
29 if linkrev != None:
29 if linkrev != None:
30 if firstbad[0] != None:
30 if firstbad[0] != None:
31 firstbad[0] = min(firstbad[0], linkrev)
31 firstbad[0] = min(firstbad[0], linkrev)
32 else:
32 else:
33 firstbad[0] = linkrev
33 firstbad[0] = linkrev
34 else:
34 else:
35 linkrev = "?"
35 linkrev = "?"
36 msg = "%s: %s" % (linkrev, msg)
36 msg = "%s: %s" % (linkrev, msg)
37 if filename:
37 if filename:
38 msg = "%s@%s" % (filename, msg)
38 msg = "%s@%s" % (filename, msg)
39 repo.ui.warn(" " + msg + "\n")
39 repo.ui.warn(" " + msg + "\n")
40 errors[0] += 1
40 errors[0] += 1
41
41
42 def warn(msg):
42 def warn(msg):
43 repo.ui.warn(msg + "\n")
43 repo.ui.warn(msg + "\n")
44 warnings[0] += 1
44 warnings[0] += 1
45
45
46 def checksize(obj, name):
46 def checksize(obj, name):
47 d = obj.checksize()
47 d = obj.checksize()
48 if d[0]:
48 if d[0]:
49 err(None, _("data length off by %d bytes") % d[0], name)
49 err(None, _("data length off by %d bytes") % d[0], name)
50 if d[1]:
50 if d[1]:
51 err(None, _("index contains %d extra bytes") % d[1], name)
51 err(None, _("index contains %d extra bytes") % d[1], name)
52
52
53 def checkversion(obj, name):
53 def checkversion(obj, name):
54 if obj.version != revlog.REVLOGV0:
54 if obj.version != revlog.REVLOGV0:
55 if not revlogv1:
55 if not revlogv1:
56 warn(_("warning: `%s' uses revlog format 1") % name)
56 warn(_("warning: `%s' uses revlog format 1") % name)
57 elif revlogv1:
57 elif revlogv1:
58 warn(_("warning: `%s' uses revlog format 0") % name)
58 warn(_("warning: `%s' uses revlog format 0") % name)
59
59
60 revlogv1 = repo.changelog.version != revlog.REVLOGV0
60 revlogv1 = repo.changelog.version != revlog.REVLOGV0
61 if repo.ui.verbose or not revlogv1:
61 if repo.ui.verbose or not revlogv1:
62 repo.ui.status(_("repository uses revlog format %d\n") %
62 repo.ui.status(_("repository uses revlog format %d\n") %
63 (revlogv1 and 1 or 0))
63 (revlogv1 and 1 or 0))
64
64
65 havecl = havemf = 1
65 seen = {}
66 seen = {}
66 repo.ui.status(_("checking changesets\n"))
67 repo.ui.status(_("checking changesets\n"))
67 checksize(repo.changelog, "changelog")
68 if repo.changelog.count() == 0 and repo.manifest.count() > 1:
69 havecl = 0
70 err(0, _("empty or missing 00changelog.i"))
71 else:
72 checksize(repo.changelog, "changelog")
68
73
69 for i in xrange(repo.changelog.count()):
74 for i in xrange(repo.changelog.count()):
70 changesets += 1
75 changesets += 1
71 n = repo.changelog.node(i)
76 n = repo.changelog.node(i)
72 l = repo.changelog.linkrev(n)
77 l = repo.changelog.linkrev(n)
73 if l != i:
78 if l != i:
74 err(i, _("incorrect link (%d) for changeset") %(l))
79 err(i, _("incorrect link (%d) for changeset") %(l))
75 if n in seen:
80 if n in seen:
76 err(i, _("duplicates changeset at revision %d") % seen[n])
81 err(i, _("duplicates changeset at revision %d") % seen[n])
77 seen[n] = i
82 seen[n] = i
78
83
79 for p in repo.changelog.parents(n):
84 for p in repo.changelog.parents(n):
80 if p not in repo.changelog.nodemap:
85 if p not in repo.changelog.nodemap:
81 err(i, _("changeset has unknown parent %s") % short(p))
86 err(i, _("changeset has unknown parent %s") % short(p))
82 try:
87 try:
83 changes = repo.changelog.read(n)
88 changes = repo.changelog.read(n)
84 except KeyboardInterrupt:
89 except KeyboardInterrupt:
85 repo.ui.warn(_("interrupted"))
90 repo.ui.warn(_("interrupted"))
86 raise
91 raise
87 except Exception, inst:
92 except Exception, inst:
88 err(i, _("unpacking changeset: %s") % inst)
93 err(i, _("unpacking changeset: %s") % inst)
89 continue
94 continue
90
95
91 if changes[0] not in neededmanifests:
96 if changes[0] not in neededmanifests:
92 neededmanifests[changes[0]] = i
97 neededmanifests[changes[0]] = i
93
98
94 for f in changes[3]:
99 for f in changes[3]:
95 filelinkrevs.setdefault(f, []).append(i)
100 filelinkrevs.setdefault(f, []).append(i)
96
101
97 seen = {}
102 seen = {}
98 repo.ui.status(_("checking manifests\n"))
103 repo.ui.status(_("checking manifests\n"))
99 checkversion(repo.manifest, "manifest")
104 if repo.changelog.count() > 0 and repo.manifest.count() == 0:
100 checksize(repo.manifest, "manifest")
105 havemf = 0
106 err(0, _("empty or missing 00manifest.i"))
107 else:
108 checkversion(repo.manifest, "manifest")
109 checksize(repo.manifest, "manifest")
101
110
102 for i in xrange(repo.manifest.count()):
111 for i in xrange(repo.manifest.count()):
103 n = repo.manifest.node(i)
112 n = repo.manifest.node(i)
104 l = repo.manifest.linkrev(n)
113 l = repo.manifest.linkrev(n)
105
114
106 if l < 0 or l >= repo.changelog.count():
115 if l < 0 or (havecl and l >= repo.changelog.count()):
107 err(None, _("bad link (%d) at manifest revision %d") % (l, i))
116 err(None, _("bad link (%d) at manifest revision %d") % (l, i))
108
117
109 if n in neededmanifests:
118 if n in neededmanifests:
110 del neededmanifests[n]
119 del neededmanifests[n]
111
120
112 if n in seen:
121 if n in seen:
113 err(l, _("duplicates manifest from %d") % seen[n])
122 err(l, _("duplicates manifest from %d") % seen[n])
114
123
115 seen[n] = l
124 seen[n] = l
116
125
117 for p in repo.manifest.parents(n):
126 for p in repo.manifest.parents(n):
118 if p not in repo.manifest.nodemap:
127 if p not in repo.manifest.nodemap:
119 err(l, _("manifest has unknown parent %s") % short(p))
128 err(l, _("manifest has unknown parent %s") % short(p))
120
129
121 try:
130 try:
122 for f, fn in repo.manifest.readdelta(n).iteritems():
131 for f, fn in repo.manifest.readdelta(n).iteritems():
123 fns = filenodes.setdefault(f, {})
132 fns = filenodes.setdefault(f, {})
124 if fn not in fns:
133 if fn not in fns:
125 fns[fn] = n
134 fns[fn] = n
126 except KeyboardInterrupt:
135 except KeyboardInterrupt:
127 repo.ui.warn(_("interrupted"))
136 repo.ui.warn(_("interrupted"))
128 raise
137 raise
129 except Exception, inst:
138 except Exception, inst:
130 err(l, _("reading manifest delta: %s") % inst)
139 err(l, _("reading manifest delta: %s") % inst)
131 continue
140 continue
132
141
133 repo.ui.status(_("crosschecking files in changesets and manifests\n"))
142 repo.ui.status(_("crosschecking files in changesets and manifests\n"))
134
143
135 nm = neededmanifests.items()
144 if havemf > 0:
136 nm.sort()
145 nm = [(c, m) for m, c in neededmanifests.items()]
137 for m, c in nm:
146 nm.sort()
138 err(m, _("changeset refers to unknown manifest %s") % short(c))
147 for c, m in nm:
139 del neededmanifests, nm
148 err(c, _("changeset refers to unknown manifest %s") % short(m))
149 del neededmanifests, nm
140
150
141 for f in filenodes:
151 if havecl:
142 if f not in filelinkrevs:
152 fl = filenodes.keys()
143 lrs = [repo.manifest.linkrev(n) for n in filenodes[f]]
153 fl.sort()
144 lrs.sort()
154 for f in fl:
145 err(lrs[0], _("in manifest but not in changeset"), f)
155 if f not in filelinkrevs:
156 lrs = [repo.manifest.linkrev(n) for n in filenodes[f]]
157 lrs.sort()
158 err(lrs[0], _("in manifest but not in changeset"), f)
159 del fl
146
160
147 for f in filelinkrevs:
161 if havemf:
148 if f not in filenodes:
162 fl = filelinkrevs.keys()
149 lr = filelinkrevs[f][0]
163 fl.sort()
150 err(lr, _("in changeset but not in manifest"), f)
164 for f in fl:
165 if f not in filenodes:
166 lr = filelinkrevs[f][0]
167 err(lr, _("in changeset but not in manifest"), f)
168 del fl
151
169
152 repo.ui.status(_("checking files\n"))
170 repo.ui.status(_("checking files\n"))
153 ff = filenodes.keys()
171 ff = dict.fromkeys(filenodes.keys() + filelinkrevs.keys()).keys()
154 ff.sort()
172 ff.sort()
155 for f in ff:
173 for f in ff:
156 if f == "/dev/null":
174 if f == "/dev/null":
157 continue
175 continue
158 files += 1
176 files += 1
159 if not f:
177 if not f:
160 lr = repo.manifest.linkrev(filenodes[f][0])
178 lr = filelinkrevs[f][0]
161 err(lr, _("file without name in manifest %s") % short(ff[n]))
179 err(lr, _("file without name in manifest"))
162 continue
180 continue
163 fl = repo.file(f)
181 fl = repo.file(f)
164 checkversion(fl, f)
182 checkversion(fl, f)
165 checksize(fl, f)
183 checksize(fl, f)
166
184
185 if fl.count() == 0:
186 err(filelinkrevs[f][0], _("empty or missing revlog"), f)
187 continue
188
167 seen = {}
189 seen = {}
168 nodes = {nullid: 1}
190 nodes = {nullid: 1}
169 for i in xrange(fl.count()):
191 for i in xrange(fl.count()):
170 revisions += 1
192 revisions += 1
171 n = fl.node(i)
193 n = fl.node(i)
172 flr = fl.linkrev(n)
194 flr = fl.linkrev(n)
173
195
174 if flr not in filelinkrevs.get(f, []):
196 if flr < 0 or (havecl and flr not in filelinkrevs.get(f, [])):
175 if flr < 0 or flr >= repo.changelog.count():
197 if flr < 0 or flr >= repo.changelog.count():
176 err(None, _("rev %d point to nonexistent changeset %d")
198 err(None, _("rev %d point to nonexistent changeset %d")
177 % (i, flr), f)
199 % (i, flr), f)
178 else:
200 else:
179 err(None, _("rev %d points to unexpected changeset %d")
201 err(None, _("rev %d points to unexpected changeset %d")
180 % (i, flr), f)
202 % (i, flr), f)
181 if f in filelinkrevs:
203 if f in filelinkrevs:
182 warn(_(" (expected %s)") % filelinkrevs[f][0])
204 warn(_(" (expected %s)") % filelinkrevs[f][0])
183 flr = None # can't be trusted
205 flr = None # can't be trusted
184 else:
206 else:
185 filelinkrevs[f].remove(flr)
207 if havecl:
208 filelinkrevs[f].remove(flr)
186
209
187 if n in seen:
210 if n in seen:
188 err(flr, _("duplicate revision %d") % i, f)
211 err(flr, _("duplicate revision %d") % i, f)
189 if n not in filenodes[f]:
212 if f in filenodes:
190 err(flr, _("%s not in manifests") % (short(n)), f)
213 if havemf and n not in filenodes[f]:
191 else:
214 err(flr, _("%s not in manifests") % (short(n)), f)
192 del filenodes[f][n]
215 else:
216 del filenodes[f][n]
193
217
194 # verify contents
218 # verify contents
195 try:
219 try:
196 t = fl.read(n)
220 t = fl.read(n)
197 except KeyboardInterrupt:
221 except KeyboardInterrupt:
198 repo.ui.warn(_("interrupted"))
222 repo.ui.warn(_("interrupted"))
199 raise
223 raise
200 except Exception, inst:
224 except Exception, inst:
201 err(flr, _("unpacking %s: %s") % (short(n), inst), f)
225 err(flr, _("unpacking %s: %s") % (short(n), inst), f)
202
226
203 # verify parents
227 # verify parents
204 try:
228 try:
205 (p1, p2) = fl.parents(n)
229 (p1, p2) = fl.parents(n)
206 if p1 not in nodes:
230 if p1 not in nodes:
207 err(flr, _("unknown parent 1 %s of %s") %
231 err(flr, _("unknown parent 1 %s of %s") %
208 (short(p1), short(n)), f)
232 (short(p1), short(n)), f)
209 if p2 not in nodes:
233 if p2 not in nodes:
210 err(flr, _("unknown parent 2 %s of %s") %
234 err(flr, _("unknown parent 2 %s of %s") %
211 (short(p2), short(p1)), f)
235 (short(p2), short(p1)), f)
212 except KeyboardInterrupt:
236 except KeyboardInterrupt:
213 repo.ui.warn(_("interrupted"))
237 repo.ui.warn(_("interrupted"))
214 raise
238 raise
215 except Exception, inst:
239 except Exception, inst:
216 err(flr, _("checking parents of %s: %s") % (short(n), inst), f)
240 err(flr, _("checking parents of %s: %s") % (short(n), inst), f)
217 nodes[n] = 1
241 nodes[n] = 1
218
242
219 # check renames
243 # check renames
220 try:
244 try:
221 rp = fl.renamed(n)
245 rp = fl.renamed(n)
222 if rp:
246 if rp:
223 fl2 = repo.file(rp[0])
247 fl2 = repo.file(rp[0])
224 rev = fl2.rev(rp[1])
248 rev = fl2.rev(rp[1])
225 except KeyboardInterrupt:
249 except KeyboardInterrupt:
226 repo.ui.warn(_("interrupted"))
250 repo.ui.warn(_("interrupted"))
227 raise
251 raise
228 except Exception, inst:
252 except Exception, inst:
229 err(flr, _("checking rename of %s: %s") %
253 err(flr, _("checking rename of %s: %s") %
230 (short(n), inst), f)
254 (short(n), inst), f)
231
255
232 # cross-check
256 # cross-check
233 fns = [(repo.manifest.linkrev(filenodes[f][n]), n)
257 if f in filenodes:
234 for n in filenodes[f]]
258 fns = [(repo.manifest.linkrev(filenodes[f][n]), n)
235 fns.sort()
259 for n in filenodes[f]]
236 for lr, node in fns:
260 fns.sort()
237 err(lr, _("%s in manifests not found") % short(node), f)
261 for lr, node in fns:
262 err(lr, _("%s in manifests not found") % short(node), f)
238
263
239 repo.ui.status(_("%d files, %d changesets, %d total revisions\n") %
264 repo.ui.status(_("%d files, %d changesets, %d total revisions\n") %
240 (files, changesets, revisions))
265 (files, changesets, revisions))
241
266
242 if warnings[0]:
267 if warnings[0]:
243 repo.ui.warn(_("%d warnings encountered!\n") % warnings[0])
268 repo.ui.warn(_("%d warnings encountered!\n") % warnings[0])
244 if errors[0]:
269 if errors[0]:
245 repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
270 repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
246 if firstbad[0]:
271 if firstbad[0]:
247 repo.ui.warn(_("(first damaged changeset appears to be %d)\n")
272 repo.ui.warn(_("(first damaged changeset appears to be %d)\n")
248 % firstbad[0])
273 % firstbad[0])
249 return 1
274 return 1
250
General Comments 0
You need to be logged in to leave comments. Login now