##// END OF EJS Templates
verify: find correct first corrupted cset for missing/corrupted revlogs
Benoit Boissinot -
r7833:794def2f default
parent child Browse files
Show More
@@ -1,243 +1,244
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, util, error
10 import revlog, util, error
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 mflinkrevs = {}
20 mflinkrevs = {}
21 filelinkrevs = {}
21 filelinkrevs = {}
22 filenodes = {}
22 filenodes = {}
23 revisions = 0
23 revisions = 0
24 badrevs = {}
24 badrevs = {}
25 errors = [0]
25 errors = [0]
26 warnings = [0]
26 warnings = [0]
27 ui = repo.ui
27 ui = repo.ui
28 cl = repo.changelog
28 cl = repo.changelog
29 mf = repo.manifest
29 mf = repo.manifest
30
30
31 if not repo.cancopy():
31 if not repo.cancopy():
32 raise util.Abort(_("cannot verify bundle or remote repos"))
32 raise util.Abort(_("cannot verify bundle or remote repos"))
33
33
34 def err(linkrev, msg, filename=None):
34 def err(linkrev, msg, filename=None):
35 if linkrev != None:
35 if linkrev != None:
36 badrevs[linkrev] = True
36 badrevs[linkrev] = True
37 else:
37 else:
38 linkrev = '?'
38 linkrev = '?'
39 msg = "%s: %s" % (linkrev, msg)
39 msg = "%s: %s" % (linkrev, msg)
40 if filename:
40 if filename:
41 msg = "%s@%s" % (filename, msg)
41 msg = "%s@%s" % (filename, msg)
42 ui.warn(" " + msg + "\n")
42 ui.warn(" " + msg + "\n")
43 errors[0] += 1
43 errors[0] += 1
44
44
45 def exc(linkrev, msg, inst, filename=None):
45 def exc(linkrev, msg, inst, filename=None):
46 if isinstance(inst, KeyboardInterrupt):
46 if isinstance(inst, KeyboardInterrupt):
47 ui.warn(_("interrupted"))
47 ui.warn(_("interrupted"))
48 raise
48 raise
49 err(linkrev, "%s: %s" % (msg, inst), filename)
49 err(linkrev, "%s: %s" % (msg, inst), filename)
50
50
51 def warn(msg):
51 def warn(msg):
52 ui.warn(msg + "\n")
52 ui.warn(msg + "\n")
53 warnings[0] += 1
53 warnings[0] += 1
54
54
55 def checklog(obj, name):
55 def checklog(obj, name):
56 if not len(obj) and (havecl or havemf):
56 if not len(obj) and (havecl or havemf):
57 err(0, _("empty or missing %s") % name)
57 err(0, _("empty or missing %s") % name)
58 return
58 return
59
59
60 d = obj.checksize()
60 d = obj.checksize()
61 if d[0]:
61 if d[0]:
62 err(None, _("data length off by %d bytes") % d[0], name)
62 err(None, _("data length off by %d bytes") % d[0], name)
63 if d[1]:
63 if d[1]:
64 err(None, _("index contains %d extra bytes") % d[1], name)
64 err(None, _("index contains %d extra bytes") % d[1], name)
65
65
66 if obj.version != revlog.REVLOGV0:
66 if obj.version != revlog.REVLOGV0:
67 if not revlogv1:
67 if not revlogv1:
68 warn(_("warning: `%s' uses revlog format 1") % name)
68 warn(_("warning: `%s' uses revlog format 1") % name)
69 elif revlogv1:
69 elif revlogv1:
70 warn(_("warning: `%s' uses revlog format 0") % name)
70 warn(_("warning: `%s' uses revlog format 0") % name)
71
71
72 def checkentry(obj, i, node, seen, linkrevs, f):
72 def checkentry(obj, i, node, seen, linkrevs, f):
73 lr = obj.linkrev(obj.rev(node))
73 lr = obj.linkrev(obj.rev(node))
74 if lr < 0 or (havecl and lr not in linkrevs):
74 if lr < 0 or (havecl and lr not in linkrevs):
75 t = "unexpected"
75 t = "unexpected"
76 if lr < 0 or lr >= len(cl):
76 if lr < 0 or lr >= len(cl):
77 t = "nonexistent"
77 t = "nonexistent"
78 err(None, _("rev %d point to %s changeset %d") % (i, t, lr), f)
78 err(None, _("rev %d point to %s changeset %d") % (i, t, lr), f)
79 if linkrevs:
79 if linkrevs:
80 warn(_(" (expected %s)") % " ".join(map(str,linkrevs)))
80 warn(_(" (expected %s)") % " ".join(map(str,linkrevs)))
81 lr = None # can't be trusted
81 lr = None # can't be trusted
82
82
83 try:
83 try:
84 p1, p2 = obj.parents(node)
84 p1, p2 = obj.parents(node)
85 if p1 not in seen and p1 != nullid:
85 if p1 not in seen and p1 != nullid:
86 err(lr, _("unknown parent 1 %s of %s") %
86 err(lr, _("unknown parent 1 %s of %s") %
87 (short(p1), short(n)), f)
87 (short(p1), short(n)), f)
88 if p2 not in seen and p2 != nullid:
88 if p2 not in seen and p2 != nullid:
89 err(lr, _("unknown parent 2 %s of %s") %
89 err(lr, _("unknown parent 2 %s of %s") %
90 (short(p2), short(p1)), f)
90 (short(p2), short(p1)), f)
91 except Exception, inst:
91 except Exception, inst:
92 exc(lr, _("checking parents of %s") % short(node), inst, f)
92 exc(lr, _("checking parents of %s") % short(node), inst, f)
93
93
94 if node in seen:
94 if node in seen:
95 err(lr, _("duplicate revision %d (%d)") % (i, seen[n]), f)
95 err(lr, _("duplicate revision %d (%d)") % (i, seen[n]), f)
96 seen[n] = i
96 seen[n] = i
97 return lr
97 return lr
98
98
99 revlogv1 = cl.version != revlog.REVLOGV0
99 revlogv1 = cl.version != revlog.REVLOGV0
100 if ui.verbose or not revlogv1:
100 if ui.verbose or not revlogv1:
101 ui.status(_("repository uses revlog format %d\n") %
101 ui.status(_("repository uses revlog format %d\n") %
102 (revlogv1 and 1 or 0))
102 (revlogv1 and 1 or 0))
103
103
104 havecl = len(cl) > 0
104 havecl = len(cl) > 0
105 havemf = len(mf) > 0
105 havemf = len(mf) > 0
106
106
107 ui.status(_("checking changesets\n"))
107 ui.status(_("checking changesets\n"))
108 seen = {}
108 seen = {}
109 checklog(cl, "changelog")
109 checklog(cl, "changelog")
110 for i in repo:
110 for i in repo:
111 n = cl.node(i)
111 n = cl.node(i)
112 checkentry(cl, i, n, seen, [i], "changelog")
112 checkentry(cl, i, n, seen, [i], "changelog")
113
113
114 try:
114 try:
115 changes = cl.read(n)
115 changes = cl.read(n)
116 mflinkrevs.setdefault(changes[0], []).append(i)
116 mflinkrevs.setdefault(changes[0], []).append(i)
117 for f in changes[3]:
117 for f in changes[3]:
118 filelinkrevs.setdefault(f, []).append(i)
118 filelinkrevs.setdefault(f, []).append(i)
119 except Exception, inst:
119 except Exception, inst:
120 exc(i, _("unpacking changeset %s") % short(n), inst)
120 exc(i, _("unpacking changeset %s") % short(n), inst)
121
121
122 ui.status(_("checking manifests\n"))
122 ui.status(_("checking manifests\n"))
123 seen = {}
123 seen = {}
124 checklog(mf, "manifest")
124 checklog(mf, "manifest")
125 for i in mf:
125 for i in mf:
126 n = mf.node(i)
126 n = mf.node(i)
127 lr = checkentry(mf, i, n, seen, mflinkrevs.get(n, []), "manifest")
127 lr = checkentry(mf, i, n, seen, mflinkrevs.get(n, []), "manifest")
128 if n in mflinkrevs:
128 if n in mflinkrevs:
129 del mflinkrevs[n]
129 del mflinkrevs[n]
130
130
131 try:
131 try:
132 for f, fn in mf.readdelta(n).iteritems():
132 for f, fn in mf.readdelta(n).iteritems():
133 if not f:
133 if not f:
134 err(lr, _("file without name in manifest"))
134 err(lr, _("file without name in manifest"))
135 elif f != "/dev/null":
135 elif f != "/dev/null":
136 fns = filenodes.setdefault(f, {})
136 fns = filenodes.setdefault(f, {})
137 if fn not in fns:
137 if fn not in fns:
138 fns[fn] = i
138 fns[fn] = i
139 except Exception, inst:
139 except Exception, inst:
140 exc(lr, _("reading manifest delta %s") % short(n), inst)
140 exc(lr, _("reading manifest delta %s") % short(n), inst)
141
141
142 ui.status(_("crosschecking files in changesets and manifests\n"))
142 ui.status(_("crosschecking files in changesets and manifests\n"))
143
143
144 if havemf:
144 if havemf:
145 for c, m in util.sort([(c, m) for m in mflinkrevs for c in mflinkrevs[m]]):
145 for c, m in util.sort([(c, m) for m in mflinkrevs for c in mflinkrevs[m]]):
146 err(c, _("changeset refers to unknown manifest %s") % short(m))
146 err(c, _("changeset refers to unknown manifest %s") % short(m))
147 del mflinkrevs
147 del mflinkrevs
148
148
149 for f in util.sort(filelinkrevs):
149 for f in util.sort(filelinkrevs):
150 if f not in filenodes:
150 if f not in filenodes:
151 lr = filelinkrevs[f][0]
151 lr = filelinkrevs[f][0]
152 err(lr, _("in changeset but not in manifest"), f)
152 err(lr, _("in changeset but not in manifest"), f)
153
153
154 if havecl:
154 if havecl:
155 for f in util.sort(filenodes):
155 for f in util.sort(filenodes):
156 if f not in filelinkrevs:
156 if f not in filelinkrevs:
157 try:
157 try:
158 fl = repo.file(f)
158 fl = repo.file(f)
159 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
159 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
160 except:
160 except:
161 lr = None
161 lr = None
162 err(lr, _("in manifest but not in changeset"), f)
162 err(lr, _("in manifest but not in changeset"), f)
163
163
164 ui.status(_("checking files\n"))
164 ui.status(_("checking files\n"))
165
165
166 storefiles = {}
166 storefiles = {}
167 for f, f2, size in repo.store.datafiles():
167 for f, f2, size in repo.store.datafiles():
168 if not f:
168 if not f:
169 err(None, _("cannot decode filename '%s'") % f2)
169 err(None, _("cannot decode filename '%s'") % f2)
170 elif size > 0:
170 elif size > 0:
171 storefiles[f] = True
171 storefiles[f] = True
172
172
173 files = util.sort(util.unique(filenodes.keys() + filelinkrevs.keys()))
173 files = util.sort(util.unique(filenodes.keys() + filelinkrevs.keys()))
174 for f in files:
174 for f in files:
175 lr = filelinkrevs[f][0]
175 try:
176 try:
176 fl = repo.file(f)
177 fl = repo.file(f)
177 except error.RevlogError, e:
178 except error.RevlogError, e:
178 err(0, _("broken revlog! (%s)") % e, f)
179 err(lr, _("broken revlog! (%s)") % e, f)
179 continue
180 continue
180
181
181 for ff in fl.files():
182 for ff in fl.files():
182 try:
183 try:
183 del storefiles[ff]
184 del storefiles[ff]
184 except KeyError:
185 except KeyError:
185 err(0, _("missing revlog!"), ff)
186 err(lr, _("missing revlog!"), ff)
186
187
187 checklog(fl, f)
188 checklog(fl, f)
188 seen = {}
189 seen = {}
189 for i in fl:
190 for i in fl:
190 revisions += 1
191 revisions += 1
191 n = fl.node(i)
192 n = fl.node(i)
192 lr = checkentry(fl, i, n, seen, filelinkrevs.get(f, []), f)
193 lr = checkentry(fl, i, n, seen, filelinkrevs.get(f, []), f)
193 if f in filenodes:
194 if f in filenodes:
194 if havemf and n not in filenodes[f]:
195 if havemf and n not in filenodes[f]:
195 err(lr, _("%s not in manifests") % (short(n)), f)
196 err(lr, _("%s not in manifests") % (short(n)), f)
196 else:
197 else:
197 del filenodes[f][n]
198 del filenodes[f][n]
198
199
199 # verify contents
200 # verify contents
200 try:
201 try:
201 t = fl.read(n)
202 t = fl.read(n)
202 rp = fl.renamed(n)
203 rp = fl.renamed(n)
203 if len(t) != fl.size(i):
204 if len(t) != fl.size(i):
204 if len(fl.revision(n)) != fl.size(i):
205 if len(fl.revision(n)) != fl.size(i):
205 err(lr, _("unpacked size is %s, %s expected") %
206 err(lr, _("unpacked size is %s, %s expected") %
206 (len(t), fl.size(i)), f)
207 (len(t), fl.size(i)), f)
207 except Exception, inst:
208 except Exception, inst:
208 exc(lr, _("unpacking %s") % short(n), inst, f)
209 exc(lr, _("unpacking %s") % short(n), inst, f)
209
210
210 # check renames
211 # check renames
211 try:
212 try:
212 if rp:
213 if rp:
213 fl2 = repo.file(rp[0])
214 fl2 = repo.file(rp[0])
214 if not len(fl2):
215 if not len(fl2):
215 err(lr, _("empty or missing copy source revlog %s:%s")
216 err(lr, _("empty or missing copy source revlog %s:%s")
216 % (rp[0], short(rp[1])), f)
217 % (rp[0], short(rp[1])), f)
217 elif rp[1] == nullid:
218 elif rp[1] == nullid:
218 warn(_("warning: %s@%s: copy source revision is nullid %s:%s")
219 warn(_("warning: %s@%s: copy source revision is nullid %s:%s")
219 % (f, lr, rp[0], short(rp[1])))
220 % (f, lr, rp[0], short(rp[1])))
220 else:
221 else:
221 rev = fl2.rev(rp[1])
222 rev = fl2.rev(rp[1])
222 except Exception, inst:
223 except Exception, inst:
223 exc(lr, _("checking rename of %s") % short(n), inst, f)
224 exc(lr, _("checking rename of %s") % short(n), inst, f)
224
225
225 # cross-check
226 # cross-check
226 if f in filenodes:
227 if f in filenodes:
227 fns = [(mf.linkrev(l), n) for n,l in filenodes[f].iteritems()]
228 fns = [(mf.linkrev(l), n) for n,l in filenodes[f].iteritems()]
228 for lr, node in util.sort(fns):
229 for lr, node in util.sort(fns):
229 err(lr, _("%s in manifests not found") % short(node), f)
230 err(lr, _("%s in manifests not found") % short(node), f)
230
231
231 for f in storefiles:
232 for f in storefiles:
232 warn(_("warning: orphan revlog '%s'") % f)
233 warn(_("warning: orphan revlog '%s'") % f)
233
234
234 ui.status(_("%d files, %d changesets, %d total revisions\n") %
235 ui.status(_("%d files, %d changesets, %d total revisions\n") %
235 (len(files), len(cl), revisions))
236 (len(files), len(cl), revisions))
236 if warnings[0]:
237 if warnings[0]:
237 ui.warn(_("%d warnings encountered!\n") % warnings[0])
238 ui.warn(_("%d warnings encountered!\n") % warnings[0])
238 if errors[0]:
239 if errors[0]:
239 ui.warn(_("%d integrity errors encountered!\n") % errors[0])
240 ui.warn(_("%d integrity errors encountered!\n") % errors[0])
240 if badrevs:
241 if badrevs:
241 ui.warn(_("(first damaged changeset appears to be %d)\n")
242 ui.warn(_("(first damaged changeset appears to be %d)\n")
242 % min(badrevs))
243 % min(badrevs))
243 return 1
244 return 1
@@ -1,43 +1,43
1 % init repo1
1 % init repo1
2
2
3 % add a; ci
3 % add a; ci
4 adding a
4 adding a
5
5
6 % cat .hg/store/fncache
6 % cat .hg/store/fncache
7 data/a.i
7 data/a.i
8
8
9 % add a.i/b; ci
9 % add a.i/b; ci
10 adding a.i/b
10 adding a.i/b
11
11
12 % cat .hg/store/fncache
12 % cat .hg/store/fncache
13 data/a.i
13 data/a.i
14 data/a.i.hg/b.i
14 data/a.i.hg/b.i
15
15
16 % add a.i.hg/c; ci
16 % add a.i.hg/c; ci
17 adding a.i.hg/c
17 adding a.i.hg/c
18
18
19 % cat .hg/store/fncache
19 % cat .hg/store/fncache
20 data/a.i
20 data/a.i
21 data/a.i.hg/b.i
21 data/a.i.hg/b.i
22 data/a.i.hg.hg/c.i
22 data/a.i.hg.hg/c.i
23
23
24 % hg verify
24 % hg verify
25 checking changesets
25 checking changesets
26 checking manifests
26 checking manifests
27 crosschecking files in changesets and manifests
27 crosschecking files in changesets and manifests
28 checking files
28 checking files
29 3 files, 3 changesets, 3 total revisions
29 3 files, 3 changesets, 3 total revisions
30
30
31 % rm .hg/store/fncache
31 % rm .hg/store/fncache
32
32
33 % hg verify
33 % hg verify
34 checking changesets
34 checking changesets
35 checking manifests
35 checking manifests
36 crosschecking files in changesets and manifests
36 crosschecking files in changesets and manifests
37 checking files
37 checking files
38 data/a.i@0: missing revlog!
38 data/a.i@0: missing revlog!
39 data/a.i.hg.hg/c.i@0: missing revlog!
39 data/a.i.hg.hg/c.i@2: missing revlog!
40 data/a.i.hg/b.i@0: missing revlog!
40 data/a.i.hg/b.i@1: missing revlog!
41 3 files, 3 changesets, 3 total revisions
41 3 files, 3 changesets, 3 total revisions
42 3 integrity errors encountered!
42 3 integrity errors encountered!
43 (first damaged changeset appears to be 0)
43 (first damaged changeset appears to be 0)
General Comments 0
You need to be logged in to leave comments. Login now