##// END OF EJS Templates
verify: check copy source revlog and nodeid
Patrick Mezard -
r6534:9b35a9f3 default
parent child Browse files
Show More
@@ -1,274 +1,281 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 nullid, short
8 from node import nullid, short
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 havecl = havemf = 1
66 seen = {}
66 seen = {}
67 repo.ui.status(_("checking changesets\n"))
67 repo.ui.status(_("checking changesets\n"))
68 if repo.changelog.count() == 0 and repo.manifest.count() > 1:
68 if repo.changelog.count() == 0 and repo.manifest.count() > 1:
69 havecl = 0
69 havecl = 0
70 err(0, _("empty or missing 00changelog.i"))
70 err(0, _("empty or missing 00changelog.i"))
71 else:
71 else:
72 checksize(repo.changelog, "changelog")
72 checksize(repo.changelog, "changelog")
73
73
74 for i in xrange(repo.changelog.count()):
74 for i in xrange(repo.changelog.count()):
75 changesets += 1
75 changesets += 1
76 n = repo.changelog.node(i)
76 n = repo.changelog.node(i)
77 l = repo.changelog.linkrev(n)
77 l = repo.changelog.linkrev(n)
78 if l != i:
78 if l != i:
79 err(i, _("incorrect link (%d) for changeset") %(l))
79 err(i, _("incorrect link (%d) for changeset") %(l))
80 if n in seen:
80 if n in seen:
81 err(i, _("duplicates changeset at revision %d") % seen[n])
81 err(i, _("duplicates changeset at revision %d") % seen[n])
82 seen[n] = i
82 seen[n] = i
83
83
84 for p in repo.changelog.parents(n):
84 for p in repo.changelog.parents(n):
85 if p not in repo.changelog.nodemap:
85 if p not in repo.changelog.nodemap:
86 err(i, _("changeset has unknown parent %s") % short(p))
86 err(i, _("changeset has unknown parent %s") % short(p))
87 try:
87 try:
88 changes = repo.changelog.read(n)
88 changes = repo.changelog.read(n)
89 except KeyboardInterrupt:
89 except KeyboardInterrupt:
90 repo.ui.warn(_("interrupted"))
90 repo.ui.warn(_("interrupted"))
91 raise
91 raise
92 except Exception, inst:
92 except Exception, inst:
93 err(i, _("unpacking changeset: %s") % inst)
93 err(i, _("unpacking changeset: %s") % inst)
94 continue
94 continue
95
95
96 if changes[0] not in neededmanifests:
96 if changes[0] not in neededmanifests:
97 neededmanifests[changes[0]] = i
97 neededmanifests[changes[0]] = i
98
98
99 for f in changes[3]:
99 for f in changes[3]:
100 filelinkrevs.setdefault(f, []).append(i)
100 filelinkrevs.setdefault(f, []).append(i)
101
101
102 seen = {}
102 seen = {}
103 repo.ui.status(_("checking manifests\n"))
103 repo.ui.status(_("checking manifests\n"))
104 if repo.changelog.count() > 0 and repo.manifest.count() == 0:
104 if repo.changelog.count() > 0 and repo.manifest.count() == 0:
105 havemf = 0
105 havemf = 0
106 err(0, _("empty or missing 00manifest.i"))
106 err(0, _("empty or missing 00manifest.i"))
107 else:
107 else:
108 checkversion(repo.manifest, "manifest")
108 checkversion(repo.manifest, "manifest")
109 checksize(repo.manifest, "manifest")
109 checksize(repo.manifest, "manifest")
110
110
111 for i in xrange(repo.manifest.count()):
111 for i in xrange(repo.manifest.count()):
112 n = repo.manifest.node(i)
112 n = repo.manifest.node(i)
113 l = repo.manifest.linkrev(n)
113 l = repo.manifest.linkrev(n)
114
114
115 if l < 0 or (havecl and l >= repo.changelog.count()):
115 if l < 0 or (havecl and l >= repo.changelog.count()):
116 err(None, _("bad link (%d) at manifest revision %d") % (l, i))
116 err(None, _("bad link (%d) at manifest revision %d") % (l, i))
117
117
118 if n in neededmanifests:
118 if n in neededmanifests:
119 del neededmanifests[n]
119 del neededmanifests[n]
120
120
121 if n in seen:
121 if n in seen:
122 err(l, _("duplicates manifest from %d") % seen[n])
122 err(l, _("duplicates manifest from %d") % seen[n])
123
123
124 seen[n] = l
124 seen[n] = l
125
125
126 for p in repo.manifest.parents(n):
126 for p in repo.manifest.parents(n):
127 if p not in repo.manifest.nodemap:
127 if p not in repo.manifest.nodemap:
128 err(l, _("manifest has unknown parent %s") % short(p))
128 err(l, _("manifest has unknown parent %s") % short(p))
129
129
130 try:
130 try:
131 for f, fn in repo.manifest.readdelta(n).iteritems():
131 for f, fn in repo.manifest.readdelta(n).iteritems():
132 fns = filenodes.setdefault(f, {})
132 fns = filenodes.setdefault(f, {})
133 if fn not in fns:
133 if fn not in fns:
134 fns[fn] = n
134 fns[fn] = n
135 except KeyboardInterrupt:
135 except KeyboardInterrupt:
136 repo.ui.warn(_("interrupted"))
136 repo.ui.warn(_("interrupted"))
137 raise
137 raise
138 except Exception, inst:
138 except Exception, inst:
139 err(l, _("reading manifest delta: %s") % inst)
139 err(l, _("reading manifest delta: %s") % inst)
140 continue
140 continue
141
141
142 repo.ui.status(_("crosschecking files in changesets and manifests\n"))
142 repo.ui.status(_("crosschecking files in changesets and manifests\n"))
143
143
144 if havemf > 0:
144 if havemf > 0:
145 nm = [(c, m) for m, c in neededmanifests.items()]
145 nm = [(c, m) for m, c in neededmanifests.items()]
146 nm.sort()
146 nm.sort()
147 for c, m in nm:
147 for c, m in nm:
148 err(c, _("changeset refers to unknown manifest %s") % short(m))
148 err(c, _("changeset refers to unknown manifest %s") % short(m))
149 del neededmanifests, nm
149 del neededmanifests, nm
150
150
151 if havecl:
151 if havecl:
152 fl = filenodes.keys()
152 fl = filenodes.keys()
153 fl.sort()
153 fl.sort()
154 for f in fl:
154 for f in fl:
155 if f not in filelinkrevs:
155 if f not in filelinkrevs:
156 lrs = [repo.manifest.linkrev(n) for n in filenodes[f]]
156 lrs = [repo.manifest.linkrev(n) for n in filenodes[f]]
157 lrs.sort()
157 lrs.sort()
158 err(lrs[0], _("in manifest but not in changeset"), f)
158 err(lrs[0], _("in manifest but not in changeset"), f)
159 del fl
159 del fl
160
160
161 if havemf:
161 if havemf:
162 fl = filelinkrevs.keys()
162 fl = filelinkrevs.keys()
163 fl.sort()
163 fl.sort()
164 for f in fl:
164 for f in fl:
165 if f not in filenodes:
165 if f not in filenodes:
166 lr = filelinkrevs[f][0]
166 lr = filelinkrevs[f][0]
167 err(lr, _("in changeset but not in manifest"), f)
167 err(lr, _("in changeset but not in manifest"), f)
168 del fl
168 del fl
169
169
170 repo.ui.status(_("checking files\n"))
170 repo.ui.status(_("checking files\n"))
171 ff = dict.fromkeys(filenodes.keys() + filelinkrevs.keys()).keys()
171 ff = dict.fromkeys(filenodes.keys() + filelinkrevs.keys()).keys()
172 ff.sort()
172 ff.sort()
173 for f in ff:
173 for f in ff:
174 if f == "/dev/null":
174 if f == "/dev/null":
175 continue
175 continue
176 files += 1
176 files += 1
177 if not f:
177 if not f:
178 lr = filelinkrevs[f][0]
178 lr = filelinkrevs[f][0]
179 err(lr, _("file without name in manifest"))
179 err(lr, _("file without name in manifest"))
180 continue
180 continue
181 fl = repo.file(f)
181 fl = repo.file(f)
182 checkversion(fl, f)
182 checkversion(fl, f)
183 checksize(fl, f)
183 checksize(fl, f)
184
184
185 if fl.count() == 0:
185 if fl.count() == 0:
186 err(filelinkrevs[f][0], _("empty or missing revlog"), f)
186 err(filelinkrevs[f][0], _("empty or missing revlog"), f)
187 continue
187 continue
188
188
189 seen = {}
189 seen = {}
190 nodes = {nullid: 1}
190 nodes = {nullid: 1}
191 for i in xrange(fl.count()):
191 for i in xrange(fl.count()):
192 revisions += 1
192 revisions += 1
193 n = fl.node(i)
193 n = fl.node(i)
194 flr = fl.linkrev(n)
194 flr = fl.linkrev(n)
195
195
196 if flr < 0 or (havecl and flr not in filelinkrevs.get(f, [])):
196 if flr < 0 or (havecl and flr not in filelinkrevs.get(f, [])):
197 if flr < 0 or flr >= repo.changelog.count():
197 if flr < 0 or flr >= repo.changelog.count():
198 err(None, _("rev %d point to nonexistent changeset %d")
198 err(None, _("rev %d point to nonexistent changeset %d")
199 % (i, flr), f)
199 % (i, flr), f)
200 else:
200 else:
201 err(None, _("rev %d points to unexpected changeset %d")
201 err(None, _("rev %d points to unexpected changeset %d")
202 % (i, flr), f)
202 % (i, flr), f)
203 if f in filelinkrevs:
203 if f in filelinkrevs:
204 warn(_(" (expected %s)") % filelinkrevs[f][0])
204 warn(_(" (expected %s)") % filelinkrevs[f][0])
205 flr = None # can't be trusted
205 flr = None # can't be trusted
206 else:
206 else:
207 if havecl:
207 if havecl:
208 filelinkrevs[f].remove(flr)
208 filelinkrevs[f].remove(flr)
209
209
210 if n in seen:
210 if n in seen:
211 err(flr, _("duplicate revision %d") % i, f)
211 err(flr, _("duplicate revision %d") % i, f)
212 if f in filenodes:
212 if f in filenodes:
213 if havemf and n not in filenodes[f]:
213 if havemf and n not in filenodes[f]:
214 err(flr, _("%s not in manifests") % (short(n)), f)
214 err(flr, _("%s not in manifests") % (short(n)), f)
215 else:
215 else:
216 del filenodes[f][n]
216 del filenodes[f][n]
217
217
218 # verify contents
218 # verify contents
219 try:
219 try:
220 t = fl.read(n)
220 t = fl.read(n)
221 except KeyboardInterrupt:
221 except KeyboardInterrupt:
222 repo.ui.warn(_("interrupted"))
222 repo.ui.warn(_("interrupted"))
223 raise
223 raise
224 except Exception, inst:
224 except Exception, inst:
225 err(flr, _("unpacking %s: %s") % (short(n), inst), f)
225 err(flr, _("unpacking %s: %s") % (short(n), inst), f)
226
226
227 # verify parents
227 # verify parents
228 try:
228 try:
229 (p1, p2) = fl.parents(n)
229 (p1, p2) = fl.parents(n)
230 if p1 not in nodes:
230 if p1 not in nodes:
231 err(flr, _("unknown parent 1 %s of %s") %
231 err(flr, _("unknown parent 1 %s of %s") %
232 (short(p1), short(n)), f)
232 (short(p1), short(n)), f)
233 if p2 not in nodes:
233 if p2 not in nodes:
234 err(flr, _("unknown parent 2 %s of %s") %
234 err(flr, _("unknown parent 2 %s of %s") %
235 (short(p2), short(p1)), f)
235 (short(p2), short(p1)), f)
236 except KeyboardInterrupt:
236 except KeyboardInterrupt:
237 repo.ui.warn(_("interrupted"))
237 repo.ui.warn(_("interrupted"))
238 raise
238 raise
239 except Exception, inst:
239 except Exception, inst:
240 err(flr, _("checking parents of %s: %s") % (short(n), inst), f)
240 err(flr, _("checking parents of %s: %s") % (short(n), inst), f)
241 nodes[n] = 1
241 nodes[n] = 1
242
242
243 # check renames
243 # check renames
244 try:
244 try:
245 rp = fl.renamed(n)
245 rp = fl.renamed(n)
246 if rp:
246 if rp:
247 fl2 = repo.file(rp[0])
247 fl2 = repo.file(rp[0])
248 rev = fl2.rev(rp[1])
248 if fl2.count() == 0:
249 err(flr, _("empty or missing copy source revlog %s:%s")
250 % (rp[0], short(rp[1])), f)
251 elif rp[1] == nullid:
252 err(flr, _("copy source revision is nullid %s:%s")
253 % (rp[0], short(rp[1])), f)
254 else:
255 rev = fl2.rev(rp[1])
249 except KeyboardInterrupt:
256 except KeyboardInterrupt:
250 repo.ui.warn(_("interrupted"))
257 repo.ui.warn(_("interrupted"))
251 raise
258 raise
252 except Exception, inst:
259 except Exception, inst:
253 err(flr, _("checking rename of %s: %s") %
260 err(flr, _("checking rename of %s: %s") %
254 (short(n), inst), f)
261 (short(n), inst), f)
255
262
256 # cross-check
263 # cross-check
257 if f in filenodes:
264 if f in filenodes:
258 fns = [(repo.manifest.linkrev(filenodes[f][n]), n)
265 fns = [(repo.manifest.linkrev(filenodes[f][n]), n)
259 for n in filenodes[f]]
266 for n in filenodes[f]]
260 fns.sort()
267 fns.sort()
261 for lr, node in fns:
268 for lr, node in fns:
262 err(lr, _("%s in manifests not found") % short(node), f)
269 err(lr, _("%s in manifests not found") % short(node), f)
263
270
264 repo.ui.status(_("%d files, %d changesets, %d total revisions\n") %
271 repo.ui.status(_("%d files, %d changesets, %d total revisions\n") %
265 (files, changesets, revisions))
272 (files, changesets, revisions))
266
273
267 if warnings[0]:
274 if warnings[0]:
268 repo.ui.warn(_("%d warnings encountered!\n") % warnings[0])
275 repo.ui.warn(_("%d warnings encountered!\n") % warnings[0])
269 if errors[0]:
276 if errors[0]:
270 repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
277 repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
271 if firstbad[0]:
278 if firstbad[0]:
272 repo.ui.warn(_("(first damaged changeset appears to be %d)\n")
279 repo.ui.warn(_("(first damaged changeset appears to be %d)\n")
273 % firstbad[0])
280 % firstbad[0])
274 return 1
281 return 1
General Comments 0
You need to be logged in to leave comments. Login now