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