##// END OF EJS Templates
verify: detect manifest revs not in any changeset
Peter Arrenbrecht -
r8394:850b5a7c default
parent child Browse files
Show More
@@ -1,255 +1,257 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, 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 lock.release()
17 lock.release()
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, linkrev):
55 def checklog(obj, name, linkrev):
56 if not len(obj) and (havecl or havemf):
56 if not len(obj) and (havecl or havemf):
57 err(linkrev, _("empty or missing %s") % name)
57 err(linkrev, _("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 if lr < 0 or lr >= len(cl):
75 if lr < 0 or lr >= len(cl):
76 msg = _("rev %d points to nonexistent changeset %d")
76 msg = _("rev %d points to nonexistent changeset %d")
77 else:
77 else:
78 msg = _("rev %d points to unexpected changeset %d")
78 msg = _("rev %d points to unexpected changeset %d")
79 err(None, msg % (i, lr), f)
79 err(None, msg % (i, lr), f)
80 if linkrevs:
80 if linkrevs:
81 warn(_(" (expected %s)") % " ".join(map(str,linkrevs)))
81 warn(_(" (expected %s)") % " ".join(map(str,linkrevs)))
82 lr = None # can't be trusted
82 lr = None # can't be trusted
83
83
84 try:
84 try:
85 p1, p2 = obj.parents(node)
85 p1, p2 = obj.parents(node)
86 if p1 not in seen and p1 != nullid:
86 if p1 not in seen and p1 != nullid:
87 err(lr, _("unknown parent 1 %s of %s") %
87 err(lr, _("unknown parent 1 %s of %s") %
88 (short(p1), short(n)), f)
88 (short(p1), short(n)), f)
89 if p2 not in seen and p2 != nullid:
89 if p2 not in seen and p2 != nullid:
90 err(lr, _("unknown parent 2 %s of %s") %
90 err(lr, _("unknown parent 2 %s of %s") %
91 (short(p2), short(p1)), f)
91 (short(p2), short(p1)), f)
92 except Exception, inst:
92 except Exception, inst:
93 exc(lr, _("checking parents of %s") % short(node), inst, f)
93 exc(lr, _("checking parents of %s") % short(node), inst, f)
94
94
95 if node in seen:
95 if node in seen:
96 err(lr, _("duplicate revision %d (%d)") % (i, seen[n]), f)
96 err(lr, _("duplicate revision %d (%d)") % (i, seen[n]), f)
97 seen[n] = i
97 seen[n] = i
98 return lr
98 return lr
99
99
100 revlogv1 = cl.version != revlog.REVLOGV0
100 revlogv1 = cl.version != revlog.REVLOGV0
101 if ui.verbose or not revlogv1:
101 if ui.verbose or not revlogv1:
102 ui.status(_("repository uses revlog format %d\n") %
102 ui.status(_("repository uses revlog format %d\n") %
103 (revlogv1 and 1 or 0))
103 (revlogv1 and 1 or 0))
104
104
105 havecl = len(cl) > 0
105 havecl = len(cl) > 0
106 havemf = len(mf) > 0
106 havemf = len(mf) > 0
107
107
108 ui.status(_("checking changesets\n"))
108 ui.status(_("checking changesets\n"))
109 seen = {}
109 seen = {}
110 checklog(cl, "changelog", 0)
110 checklog(cl, "changelog", 0)
111 for i in repo:
111 for i in repo:
112 n = cl.node(i)
112 n = cl.node(i)
113 checkentry(cl, i, n, seen, [i], "changelog")
113 checkentry(cl, i, n, seen, [i], "changelog")
114
114
115 try:
115 try:
116 changes = cl.read(n)
116 changes = cl.read(n)
117 mflinkrevs.setdefault(changes[0], []).append(i)
117 mflinkrevs.setdefault(changes[0], []).append(i)
118 for f in changes[3]:
118 for f in changes[3]:
119 filelinkrevs.setdefault(f, []).append(i)
119 filelinkrevs.setdefault(f, []).append(i)
120 except Exception, inst:
120 except Exception, inst:
121 exc(i, _("unpacking changeset %s") % short(n), inst)
121 exc(i, _("unpacking changeset %s") % short(n), inst)
122
122
123 ui.status(_("checking manifests\n"))
123 ui.status(_("checking manifests\n"))
124 seen = {}
124 seen = {}
125 checklog(mf, "manifest", 0)
125 checklog(mf, "manifest", 0)
126 for i in mf:
126 for i in mf:
127 n = mf.node(i)
127 n = mf.node(i)
128 lr = checkentry(mf, i, n, seen, mflinkrevs.get(n, []), "manifest")
128 lr = checkentry(mf, i, n, seen, mflinkrevs.get(n, []), "manifest")
129 if n in mflinkrevs:
129 if n in mflinkrevs:
130 del mflinkrevs[n]
130 del mflinkrevs[n]
131 else:
132 err(lr, _("%s not in changesets") % short(n), "manifest")
131
133
132 try:
134 try:
133 for f, fn in mf.readdelta(n).iteritems():
135 for f, fn in mf.readdelta(n).iteritems():
134 if not f:
136 if not f:
135 err(lr, _("file without name in manifest"))
137 err(lr, _("file without name in manifest"))
136 elif f != "/dev/null":
138 elif f != "/dev/null":
137 fns = filenodes.setdefault(f, {})
139 fns = filenodes.setdefault(f, {})
138 if fn not in fns:
140 if fn not in fns:
139 fns[fn] = i
141 fns[fn] = i
140 except Exception, inst:
142 except Exception, inst:
141 exc(lr, _("reading manifest delta %s") % short(n), inst)
143 exc(lr, _("reading manifest delta %s") % short(n), inst)
142
144
143 ui.status(_("crosschecking files in changesets and manifests\n"))
145 ui.status(_("crosschecking files in changesets and manifests\n"))
144
146
145 if havemf:
147 if havemf:
146 for c,m in sorted([(c, m) for m in mflinkrevs for c in mflinkrevs[m]]):
148 for c,m in sorted([(c, m) for m in mflinkrevs for c in mflinkrevs[m]]):
147 err(c, _("changeset refers to unknown manifest %s") % short(m))
149 err(c, _("changeset refers to unknown manifest %s") % short(m))
148 del mflinkrevs
150 del mflinkrevs
149
151
150 for f in sorted(filelinkrevs):
152 for f in sorted(filelinkrevs):
151 if f not in filenodes:
153 if f not in filenodes:
152 lr = filelinkrevs[f][0]
154 lr = filelinkrevs[f][0]
153 err(lr, _("in changeset but not in manifest"), f)
155 err(lr, _("in changeset but not in manifest"), f)
154
156
155 if havecl:
157 if havecl:
156 for f in sorted(filenodes):
158 for f in sorted(filenodes):
157 if f not in filelinkrevs:
159 if f not in filelinkrevs:
158 try:
160 try:
159 fl = repo.file(f)
161 fl = repo.file(f)
160 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
162 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
161 except:
163 except:
162 lr = None
164 lr = None
163 err(lr, _("in manifest but not in changeset"), f)
165 err(lr, _("in manifest but not in changeset"), f)
164
166
165 ui.status(_("checking files\n"))
167 ui.status(_("checking files\n"))
166
168
167 storefiles = {}
169 storefiles = {}
168 for f, f2, size in repo.store.datafiles():
170 for f, f2, size in repo.store.datafiles():
169 if not f:
171 if not f:
170 err(None, _("cannot decode filename '%s'") % f2)
172 err(None, _("cannot decode filename '%s'") % f2)
171 elif size > 0:
173 elif size > 0:
172 storefiles[f] = True
174 storefiles[f] = True
173
175
174 files = sorted(set(filenodes) | set(filelinkrevs))
176 files = sorted(set(filenodes) | set(filelinkrevs))
175 for f in files:
177 for f in files:
176 try:
178 try:
177 linkrevs = filelinkrevs[f]
179 linkrevs = filelinkrevs[f]
178 except KeyError:
180 except KeyError:
179 # in manifest but not in changelog
181 # in manifest but not in changelog
180 linkrevs = []
182 linkrevs = []
181
183
182 if linkrevs:
184 if linkrevs:
183 lr = linkrevs[0]
185 lr = linkrevs[0]
184 else:
186 else:
185 lr = None
187 lr = None
186
188
187 try:
189 try:
188 fl = repo.file(f)
190 fl = repo.file(f)
189 except error.RevlogError, e:
191 except error.RevlogError, e:
190 err(lr, _("broken revlog! (%s)") % e, f)
192 err(lr, _("broken revlog! (%s)") % e, f)
191 continue
193 continue
192
194
193 for ff in fl.files():
195 for ff in fl.files():
194 try:
196 try:
195 del storefiles[ff]
197 del storefiles[ff]
196 except KeyError:
198 except KeyError:
197 err(lr, _("missing revlog!"), ff)
199 err(lr, _("missing revlog!"), ff)
198
200
199 checklog(fl, f, lr)
201 checklog(fl, f, lr)
200 seen = {}
202 seen = {}
201 for i in fl:
203 for i in fl:
202 revisions += 1
204 revisions += 1
203 n = fl.node(i)
205 n = fl.node(i)
204 lr = checkentry(fl, i, n, seen, linkrevs, f)
206 lr = checkentry(fl, i, n, seen, linkrevs, f)
205 if f in filenodes:
207 if f in filenodes:
206 if havemf and n not in filenodes[f]:
208 if havemf and n not in filenodes[f]:
207 err(lr, _("%s not in manifests") % (short(n)), f)
209 err(lr, _("%s not in manifests") % (short(n)), f)
208 else:
210 else:
209 del filenodes[f][n]
211 del filenodes[f][n]
210
212
211 # verify contents
213 # verify contents
212 try:
214 try:
213 t = fl.read(n)
215 t = fl.read(n)
214 rp = fl.renamed(n)
216 rp = fl.renamed(n)
215 if len(t) != fl.size(i):
217 if len(t) != fl.size(i):
216 if len(fl.revision(n)) != fl.size(i):
218 if len(fl.revision(n)) != fl.size(i):
217 err(lr, _("unpacked size is %s, %s expected") %
219 err(lr, _("unpacked size is %s, %s expected") %
218 (len(t), fl.size(i)), f)
220 (len(t), fl.size(i)), f)
219 except Exception, inst:
221 except Exception, inst:
220 exc(lr, _("unpacking %s") % short(n), inst, f)
222 exc(lr, _("unpacking %s") % short(n), inst, f)
221
223
222 # check renames
224 # check renames
223 try:
225 try:
224 if rp:
226 if rp:
225 fl2 = repo.file(rp[0])
227 fl2 = repo.file(rp[0])
226 if not len(fl2):
228 if not len(fl2):
227 err(lr, _("empty or missing copy source revlog %s:%s")
229 err(lr, _("empty or missing copy source revlog %s:%s")
228 % (rp[0], short(rp[1])), f)
230 % (rp[0], short(rp[1])), f)
229 elif rp[1] == nullid:
231 elif rp[1] == nullid:
230 warn(_("warning: %s@%s: copy source revision is nullid %s:%s")
232 warn(_("warning: %s@%s: copy source revision is nullid %s:%s")
231 % (f, lr, rp[0], short(rp[1])))
233 % (f, lr, rp[0], short(rp[1])))
232 else:
234 else:
233 fl2.rev(rp[1])
235 fl2.rev(rp[1])
234 except Exception, inst:
236 except Exception, inst:
235 exc(lr, _("checking rename of %s") % short(n), inst, f)
237 exc(lr, _("checking rename of %s") % short(n), inst, f)
236
238
237 # cross-check
239 # cross-check
238 if f in filenodes:
240 if f in filenodes:
239 fns = [(mf.linkrev(l), n) for n,l in filenodes[f].iteritems()]
241 fns = [(mf.linkrev(l), n) for n,l in filenodes[f].iteritems()]
240 for lr, node in sorted(fns):
242 for lr, node in sorted(fns):
241 err(lr, _("%s in manifests not found") % short(node), f)
243 err(lr, _("%s in manifests not found") % short(node), f)
242
244
243 for f in storefiles:
245 for f in storefiles:
244 warn(_("warning: orphan revlog '%s'") % f)
246 warn(_("warning: orphan revlog '%s'") % f)
245
247
246 ui.status(_("%d files, %d changesets, %d total revisions\n") %
248 ui.status(_("%d files, %d changesets, %d total revisions\n") %
247 (len(files), len(cl), revisions))
249 (len(files), len(cl), revisions))
248 if warnings[0]:
250 if warnings[0]:
249 ui.warn(_("%d warnings encountered!\n") % warnings[0])
251 ui.warn(_("%d warnings encountered!\n") % warnings[0])
250 if errors[0]:
252 if errors[0]:
251 ui.warn(_("%d integrity errors encountered!\n") % errors[0])
253 ui.warn(_("%d integrity errors encountered!\n") % errors[0])
252 if badrevs:
254 if badrevs:
253 ui.warn(_("(first damaged changeset appears to be %d)\n")
255 ui.warn(_("(first damaged changeset appears to be %d)\n")
254 % min(badrevs))
256 % min(badrevs))
255 return 1
257 return 1
@@ -1,86 +1,88 b''
1 % before update 0, strip 2
1 % before update 0, strip 2
2 changeset: 0:cb9a9f314b8b
2 changeset: 0:cb9a9f314b8b
3 user: test
3 user: test
4 date: Thu Jan 01 00:00:00 1970 +0000
4 date: Thu Jan 01 00:00:00 1970 +0000
5 summary: a
5 summary: a
6
6
7 saving bundle to
7 saving bundle to
8 transaction abort!
8 transaction abort!
9 failed to truncate data/b.i
9 failed to truncate data/b.i
10 rollback failed - please run hg recover
10 rollback failed - please run hg recover
11 abort: Permission denied .hg/store/data/b.i
11 abort: Permission denied .hg/store/data/b.i
12 % after update 0, strip 2
12 % after update 0, strip 2
13 checking changesets
13 checking changesets
14 checking manifests
14 checking manifests
15 crosschecking files in changesets and manifests
15 crosschecking files in changesets and manifests
16 checking files
16 checking files
17 b@?: rev 1 points to nonexistent changeset 2
17 b@?: rev 1 points to nonexistent changeset 2
18 (expected 1)
18 (expected 1)
19 b@?: 736c29771fba not in manifests
19 b@?: 736c29771fba not in manifests
20 warning: orphan revlog 'data/c.i'
20 warning: orphan revlog 'data/c.i'
21 2 files, 2 changesets, 3 total revisions
21 2 files, 2 changesets, 3 total revisions
22 2 warnings encountered!
22 2 warnings encountered!
23 2 integrity errors encountered!
23 2 integrity errors encountered!
24 % journal contents
24 % journal contents
25 00changelog.i
25 00changelog.i
26 00manifest.i
26 00manifest.i
27 data/b.i
27 data/b.i
28 data/c.i
28 data/c.i
29 rolling back interrupted transaction
29 rolling back interrupted transaction
30 checking changesets
30 checking changesets
31 checking manifests
31 checking manifests
32 crosschecking files in changesets and manifests
32 crosschecking files in changesets and manifests
33 checking files
33 checking files
34 2 files, 2 changesets, 2 total revisions
34 2 files, 2 changesets, 2 total revisions
35 % before update 0, strip 2
35 % before update 0, strip 2
36 changeset: 0:cb9a9f314b8b
36 changeset: 0:cb9a9f314b8b
37 user: test
37 user: test
38 date: Thu Jan 01 00:00:00 1970 +0000
38 date: Thu Jan 01 00:00:00 1970 +0000
39 summary: a
39 summary: a
40
40
41 abort: Permission denied .hg/store/data/b.i
41 abort: Permission denied .hg/store/data/b.i
42 % after update 0, strip 2
42 % after update 0, strip 2
43 checking changesets
43 checking changesets
44 checking manifests
44 checking manifests
45 crosschecking files in changesets and manifests
45 crosschecking files in changesets and manifests
46 checking files
46 checking files
47 3 files, 4 changesets, 4 total revisions
47 3 files, 4 changesets, 4 total revisions
48 % journal contents
48 % journal contents
49 cat: .hg/store/journal: No such file or directory
49 cat: .hg/store/journal: No such file or directory
50 % before update 0, strip 2
50 % before update 0, strip 2
51 changeset: 0:cb9a9f314b8b
51 changeset: 0:cb9a9f314b8b
52 user: test
52 user: test
53 date: Thu Jan 01 00:00:00 1970 +0000
53 date: Thu Jan 01 00:00:00 1970 +0000
54 summary: a
54 summary: a
55
55
56 saving bundle to
56 saving bundle to
57 transaction abort!
57 transaction abort!
58 failed to truncate 00manifest.i
58 failed to truncate 00manifest.i
59 rollback failed - please run hg recover
59 rollback failed - please run hg recover
60 abort: Permission denied .hg/store/00manifest.i
60 abort: Permission denied .hg/store/00manifest.i
61 % after update 0, strip 2
61 % after update 0, strip 2
62 checking changesets
62 checking changesets
63 checking manifests
63 checking manifests
64 manifest@?: rev 2 points to nonexistent changeset 2
64 manifest@?: rev 2 points to nonexistent changeset 2
65 manifest@?: 3362547cdf64 not in changesets
65 manifest@?: rev 3 points to nonexistent changeset 3
66 manifest@?: rev 3 points to nonexistent changeset 3
67 manifest@?: 265a85892ecb not in changesets
66 crosschecking files in changesets and manifests
68 crosschecking files in changesets and manifests
67 c@3: in manifest but not in changeset
69 c@3: in manifest but not in changeset
68 checking files
70 checking files
69 b@?: rev 1 points to nonexistent changeset 2
71 b@?: rev 1 points to nonexistent changeset 2
70 (expected 1)
72 (expected 1)
71 c@?: rev 0 points to nonexistent changeset 3
73 c@?: rev 0 points to nonexistent changeset 3
72 3 files, 2 changesets, 4 total revisions
74 3 files, 2 changesets, 4 total revisions
73 1 warnings encountered!
75 1 warnings encountered!
74 5 integrity errors encountered!
76 7 integrity errors encountered!
75 (first damaged changeset appears to be 3)
77 (first damaged changeset appears to be 3)
76 % journal contents
78 % journal contents
77 00changelog.i
79 00changelog.i
78 00manifest.i
80 00manifest.i
79 data/b.i
81 data/b.i
80 data/c.i
82 data/c.i
81 rolling back interrupted transaction
83 rolling back interrupted transaction
82 checking changesets
84 checking changesets
83 checking manifests
85 checking manifests
84 crosschecking files in changesets and manifests
86 crosschecking files in changesets and manifests
85 checking files
87 checking files
86 2 files, 2 changesets, 2 total revisions
88 2 files, 2 changesets, 2 total revisions
General Comments 0
You need to be logged in to leave comments. Login now