##// END OF EJS Templates
verify: document the `checkentry` method...
marmoute -
r42036:00c9fde7 default
parent child Browse files
Show More
@@ -1,465 +1,484 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 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11
11
12 from .i18n import _
12 from .i18n import _
13 from .node import (
13 from .node import (
14 nullid,
14 nullid,
15 short,
15 short,
16 )
16 )
17
17
18 from . import (
18 from . import (
19 error,
19 error,
20 pycompat,
20 pycompat,
21 revlog,
21 revlog,
22 util,
22 util,
23 )
23 )
24
24
25 def verify(repo):
25 def verify(repo):
26 with repo.lock():
26 with repo.lock():
27 return verifier(repo).verify()
27 return verifier(repo).verify()
28
28
29 def _normpath(f):
29 def _normpath(f):
30 # under hg < 2.4, convert didn't sanitize paths properly, so a
30 # under hg < 2.4, convert didn't sanitize paths properly, so a
31 # converted repo may contain repeated slashes
31 # converted repo may contain repeated slashes
32 while '//' in f:
32 while '//' in f:
33 f = f.replace('//', '/')
33 f = f.replace('//', '/')
34 return f
34 return f
35
35
36 class verifier(object):
36 class verifier(object):
37 def __init__(self, repo):
37 def __init__(self, repo):
38 self.repo = repo.unfiltered()
38 self.repo = repo.unfiltered()
39 self.ui = repo.ui
39 self.ui = repo.ui
40 self.match = repo.narrowmatch()
40 self.match = repo.narrowmatch()
41 self.badrevs = set()
41 self.badrevs = set()
42 self.errors = 0
42 self.errors = 0
43 self.warnings = 0
43 self.warnings = 0
44 self.havecl = len(repo.changelog) > 0
44 self.havecl = len(repo.changelog) > 0
45 self.havemf = len(repo.manifestlog.getstorage(b'')) > 0
45 self.havemf = len(repo.manifestlog.getstorage(b'')) > 0
46 self.revlogv1 = repo.changelog.version != revlog.REVLOGV0
46 self.revlogv1 = repo.changelog.version != revlog.REVLOGV0
47 self.lrugetctx = util.lrucachefunc(repo.__getitem__)
47 self.lrugetctx = util.lrucachefunc(repo.__getitem__)
48 self.refersmf = False
48 self.refersmf = False
49 self.fncachewarned = False
49 self.fncachewarned = False
50 # developer config: verify.skipflags
50 # developer config: verify.skipflags
51 self.skipflags = repo.ui.configint('verify', 'skipflags')
51 self.skipflags = repo.ui.configint('verify', 'skipflags')
52 self.warnorphanstorefiles = True
52 self.warnorphanstorefiles = True
53
53
54 def _warn(self, msg):
54 def _warn(self, msg):
55 """record a "warning" level issue"""
55 """record a "warning" level issue"""
56 self.ui.warn(msg + "\n")
56 self.ui.warn(msg + "\n")
57 self.warnings += 1
57 self.warnings += 1
58
58
59 def _err(self, linkrev, msg, filename=None):
59 def _err(self, linkrev, msg, filename=None):
60 """record a "error" level issue"""
60 """record a "error" level issue"""
61 if linkrev is not None:
61 if linkrev is not None:
62 self.badrevs.add(linkrev)
62 self.badrevs.add(linkrev)
63 linkrev = "%d" % linkrev
63 linkrev = "%d" % linkrev
64 else:
64 else:
65 linkrev = '?'
65 linkrev = '?'
66 msg = "%s: %s" % (linkrev, msg)
66 msg = "%s: %s" % (linkrev, msg)
67 if filename:
67 if filename:
68 msg = "%s@%s" % (filename, msg)
68 msg = "%s@%s" % (filename, msg)
69 self.ui.warn(" " + msg + "\n")
69 self.ui.warn(" " + msg + "\n")
70 self.errors += 1
70 self.errors += 1
71
71
72 def _exc(self, linkrev, msg, inst, filename=None):
72 def _exc(self, linkrev, msg, inst, filename=None):
73 """record exception raised during the verify process"""
73 """record exception raised during the verify process"""
74 fmsg = pycompat.bytestr(inst)
74 fmsg = pycompat.bytestr(inst)
75 if not fmsg:
75 if not fmsg:
76 fmsg = pycompat.byterepr(inst)
76 fmsg = pycompat.byterepr(inst)
77 self._err(linkrev, "%s: %s" % (msg, fmsg), filename)
77 self._err(linkrev, "%s: %s" % (msg, fmsg), filename)
78
78
79 def checklog(self, obj, name, linkrev):
79 def checklog(self, obj, name, linkrev):
80 if not len(obj) and (self.havecl or self.havemf):
80 if not len(obj) and (self.havecl or self.havemf):
81 self._err(linkrev, _("empty or missing %s") % name)
81 self._err(linkrev, _("empty or missing %s") % name)
82 return
82 return
83
83
84 d = obj.checksize()
84 d = obj.checksize()
85 if d[0]:
85 if d[0]:
86 self.err(None, _("data length off by %d bytes") % d[0], name)
86 self.err(None, _("data length off by %d bytes") % d[0], name)
87 if d[1]:
87 if d[1]:
88 self.err(None, _("index contains %d extra bytes") % d[1], name)
88 self.err(None, _("index contains %d extra bytes") % d[1], name)
89
89
90 if obj.version != revlog.REVLOGV0:
90 if obj.version != revlog.REVLOGV0:
91 if not self.revlogv1:
91 if not self.revlogv1:
92 self._warn(_("warning: `%s' uses revlog format 1") % name)
92 self._warn(_("warning: `%s' uses revlog format 1") % name)
93 elif self.revlogv1:
93 elif self.revlogv1:
94 self._warn(_("warning: `%s' uses revlog format 0") % name)
94 self._warn(_("warning: `%s' uses revlog format 0") % name)
95
95
96 def checkentry(self, obj, i, node, seen, linkrevs, f):
96 def checkentry(self, obj, i, node, seen, linkrevs, f):
97 """verify a single revlog entry
98
99 arguments are:
100 - obj: the source revlog
101 - i: the revision number
102 - node: the revision node id
103 - seen: nodes previously seen for this revlog
104 - linkrevs: [changelog-revisions] introducing "node"
105 - f: string label ("changelog", "manifest", or filename)
106
107 Performs the following checks:
108 - linkrev points to an existing changelog revision,
109 - linkrev points to a changelog revision that introduces this revision,
110 - linkrev points to the lowest of these changesets,
111 - both parents exist in the revlog,
112 - the revision is not duplicated.
113
114 Return the linkrev of the revision (or None for changelog's revisions).
115 """
97 lr = obj.linkrev(obj.rev(node))
116 lr = obj.linkrev(obj.rev(node))
98 if lr < 0 or (self.havecl and lr not in linkrevs):
117 if lr < 0 or (self.havecl and lr not in linkrevs):
99 if lr < 0 or lr >= len(self.repo.changelog):
118 if lr < 0 or lr >= len(self.repo.changelog):
100 msg = _("rev %d points to nonexistent changeset %d")
119 msg = _("rev %d points to nonexistent changeset %d")
101 else:
120 else:
102 msg = _("rev %d points to unexpected changeset %d")
121 msg = _("rev %d points to unexpected changeset %d")
103 self._err(None, msg % (i, lr), f)
122 self._err(None, msg % (i, lr), f)
104 if linkrevs:
123 if linkrevs:
105 if f and len(linkrevs) > 1:
124 if f and len(linkrevs) > 1:
106 try:
125 try:
107 # attempt to filter down to real linkrevs
126 # attempt to filter down to real linkrevs
108 linkrevs = [l for l in linkrevs
127 linkrevs = [l for l in linkrevs
109 if self.lrugetctx(l)[f].filenode() == node]
128 if self.lrugetctx(l)[f].filenode() == node]
110 except Exception:
129 except Exception:
111 pass
130 pass
112 self._warn(_(" (expected %s)") % " ".join
131 self._warn(_(" (expected %s)") % " ".join
113 (map(pycompat.bytestr, linkrevs)))
132 (map(pycompat.bytestr, linkrevs)))
114 lr = None # can't be trusted
133 lr = None # can't be trusted
115
134
116 try:
135 try:
117 p1, p2 = obj.parents(node)
136 p1, p2 = obj.parents(node)
118 if p1 not in seen and p1 != nullid:
137 if p1 not in seen and p1 != nullid:
119 self._err(lr, _("unknown parent 1 %s of %s") %
138 self._err(lr, _("unknown parent 1 %s of %s") %
120 (short(p1), short(node)), f)
139 (short(p1), short(node)), f)
121 if p2 not in seen and p2 != nullid:
140 if p2 not in seen and p2 != nullid:
122 self._err(lr, _("unknown parent 2 %s of %s") %
141 self._err(lr, _("unknown parent 2 %s of %s") %
123 (short(p2), short(node)), f)
142 (short(p2), short(node)), f)
124 except Exception as inst:
143 except Exception as inst:
125 self._exc(lr, _("checking parents of %s") % short(node), inst, f)
144 self._exc(lr, _("checking parents of %s") % short(node), inst, f)
126
145
127 if node in seen:
146 if node in seen:
128 self._err(lr, _("duplicate revision %d (%d)") % (i, seen[node]), f)
147 self._err(lr, _("duplicate revision %d (%d)") % (i, seen[node]), f)
129 seen[node] = i
148 seen[node] = i
130 return lr
149 return lr
131
150
132 def verify(self):
151 def verify(self):
133 """verify the content of the Mercurial repository
152 """verify the content of the Mercurial repository
134
153
135 This method run all verifications, displaying issues as they are found.
154 This method run all verifications, displaying issues as they are found.
136
155
137 return 1 if any error have been encountered, 0 otherwise."""
156 return 1 if any error have been encountered, 0 otherwise."""
138 # initial validation and generic report
157 # initial validation and generic report
139 repo = self.repo
158 repo = self.repo
140 ui = repo.ui
159 ui = repo.ui
141 if not repo.url().startswith('file:'):
160 if not repo.url().startswith('file:'):
142 raise error.Abort(_("cannot verify bundle or remote repos"))
161 raise error.Abort(_("cannot verify bundle or remote repos"))
143
162
144 if os.path.exists(repo.sjoin("journal")):
163 if os.path.exists(repo.sjoin("journal")):
145 ui.warn(_("abandoned transaction found - run hg recover\n"))
164 ui.warn(_("abandoned transaction found - run hg recover\n"))
146
165
147 if ui.verbose or not self.revlogv1:
166 if ui.verbose or not self.revlogv1:
148 ui.status(_("repository uses revlog format %d\n") %
167 ui.status(_("repository uses revlog format %d\n") %
149 (self.revlogv1 and 1 or 0))
168 (self.revlogv1 and 1 or 0))
150
169
151 # data verification
170 # data verification
152 mflinkrevs, filelinkrevs = self._verifychangelog()
171 mflinkrevs, filelinkrevs = self._verifychangelog()
153 filenodes = self._verifymanifest(mflinkrevs)
172 filenodes = self._verifymanifest(mflinkrevs)
154 del mflinkrevs
173 del mflinkrevs
155 self._crosscheckfiles(filelinkrevs, filenodes)
174 self._crosscheckfiles(filelinkrevs, filenodes)
156 totalfiles, filerevisions = self._verifyfiles(filenodes, filelinkrevs)
175 totalfiles, filerevisions = self._verifyfiles(filenodes, filelinkrevs)
157
176
158 # final report
177 # final report
159 ui.status(_("checked %d changesets with %d changes to %d files\n") %
178 ui.status(_("checked %d changesets with %d changes to %d files\n") %
160 (len(repo.changelog), filerevisions, totalfiles))
179 (len(repo.changelog), filerevisions, totalfiles))
161 if self.warnings:
180 if self.warnings:
162 ui.warn(_("%d warnings encountered!\n") % self.warnings)
181 ui.warn(_("%d warnings encountered!\n") % self.warnings)
163 if self.fncachewarned:
182 if self.fncachewarned:
164 ui.warn(_('hint: run "hg debugrebuildfncache" to recover from '
183 ui.warn(_('hint: run "hg debugrebuildfncache" to recover from '
165 'corrupt fncache\n'))
184 'corrupt fncache\n'))
166 if self.errors:
185 if self.errors:
167 ui.warn(_("%d integrity errors encountered!\n") % self.errors)
186 ui.warn(_("%d integrity errors encountered!\n") % self.errors)
168 if self.badrevs:
187 if self.badrevs:
169 ui.warn(_("(first damaged changeset appears to be %d)\n")
188 ui.warn(_("(first damaged changeset appears to be %d)\n")
170 % min(self.badrevs))
189 % min(self.badrevs))
171 return 1
190 return 1
172 return 0
191 return 0
173
192
174 def _verifychangelog(self):
193 def _verifychangelog(self):
175 ui = self.ui
194 ui = self.ui
176 repo = self.repo
195 repo = self.repo
177 match = self.match
196 match = self.match
178 cl = repo.changelog
197 cl = repo.changelog
179
198
180 ui.status(_("checking changesets\n"))
199 ui.status(_("checking changesets\n"))
181 mflinkrevs = {}
200 mflinkrevs = {}
182 filelinkrevs = {}
201 filelinkrevs = {}
183 seen = {}
202 seen = {}
184 self.checklog(cl, "changelog", 0)
203 self.checklog(cl, "changelog", 0)
185 progress = ui.makeprogress(_('checking'), unit=_('changesets'),
204 progress = ui.makeprogress(_('checking'), unit=_('changesets'),
186 total=len(repo))
205 total=len(repo))
187 for i in repo:
206 for i in repo:
188 progress.update(i)
207 progress.update(i)
189 n = cl.node(i)
208 n = cl.node(i)
190 self.checkentry(cl, i, n, seen, [i], "changelog")
209 self.checkentry(cl, i, n, seen, [i], "changelog")
191
210
192 try:
211 try:
193 changes = cl.read(n)
212 changes = cl.read(n)
194 if changes[0] != nullid:
213 if changes[0] != nullid:
195 mflinkrevs.setdefault(changes[0], []).append(i)
214 mflinkrevs.setdefault(changes[0], []).append(i)
196 self.refersmf = True
215 self.refersmf = True
197 for f in changes[3]:
216 for f in changes[3]:
198 if match(f):
217 if match(f):
199 filelinkrevs.setdefault(_normpath(f), []).append(i)
218 filelinkrevs.setdefault(_normpath(f), []).append(i)
200 except Exception as inst:
219 except Exception as inst:
201 self.refersmf = True
220 self.refersmf = True
202 self._exc(i, _("unpacking changeset %s") % short(n), inst)
221 self._exc(i, _("unpacking changeset %s") % short(n), inst)
203 progress.complete()
222 progress.complete()
204 return mflinkrevs, filelinkrevs
223 return mflinkrevs, filelinkrevs
205
224
206 def _verifymanifest(self, mflinkrevs, dir="", storefiles=None,
225 def _verifymanifest(self, mflinkrevs, dir="", storefiles=None,
207 subdirprogress=None):
226 subdirprogress=None):
208 repo = self.repo
227 repo = self.repo
209 ui = self.ui
228 ui = self.ui
210 match = self.match
229 match = self.match
211 mfl = self.repo.manifestlog
230 mfl = self.repo.manifestlog
212 mf = mfl.getstorage(dir)
231 mf = mfl.getstorage(dir)
213
232
214 if not dir:
233 if not dir:
215 self.ui.status(_("checking manifests\n"))
234 self.ui.status(_("checking manifests\n"))
216
235
217 filenodes = {}
236 filenodes = {}
218 subdirnodes = {}
237 subdirnodes = {}
219 seen = {}
238 seen = {}
220 label = "manifest"
239 label = "manifest"
221 if dir:
240 if dir:
222 label = dir
241 label = dir
223 revlogfiles = mf.files()
242 revlogfiles = mf.files()
224 storefiles.difference_update(revlogfiles)
243 storefiles.difference_update(revlogfiles)
225 if subdirprogress: # should be true since we're in a subdirectory
244 if subdirprogress: # should be true since we're in a subdirectory
226 subdirprogress.increment()
245 subdirprogress.increment()
227 if self.refersmf:
246 if self.refersmf:
228 # Do not check manifest if there are only changelog entries with
247 # Do not check manifest if there are only changelog entries with
229 # null manifests.
248 # null manifests.
230 self.checklog(mf, label, 0)
249 self.checklog(mf, label, 0)
231 progress = ui.makeprogress(_('checking'), unit=_('manifests'),
250 progress = ui.makeprogress(_('checking'), unit=_('manifests'),
232 total=len(mf))
251 total=len(mf))
233 for i in mf:
252 for i in mf:
234 if not dir:
253 if not dir:
235 progress.update(i)
254 progress.update(i)
236 n = mf.node(i)
255 n = mf.node(i)
237 lr = self.checkentry(mf, i, n, seen, mflinkrevs.get(n, []), label)
256 lr = self.checkentry(mf, i, n, seen, mflinkrevs.get(n, []), label)
238 if n in mflinkrevs:
257 if n in mflinkrevs:
239 del mflinkrevs[n]
258 del mflinkrevs[n]
240 elif dir:
259 elif dir:
241 self._err(lr, _("%s not in parent-directory manifest") %
260 self._err(lr, _("%s not in parent-directory manifest") %
242 short(n), label)
261 short(n), label)
243 else:
262 else:
244 self._err(lr, _("%s not in changesets") % short(n), label)
263 self._err(lr, _("%s not in changesets") % short(n), label)
245
264
246 try:
265 try:
247 mfdelta = mfl.get(dir, n).readdelta(shallow=True)
266 mfdelta = mfl.get(dir, n).readdelta(shallow=True)
248 for f, fn, fl in mfdelta.iterentries():
267 for f, fn, fl in mfdelta.iterentries():
249 if not f:
268 if not f:
250 self._err(lr, _("entry without name in manifest"))
269 self._err(lr, _("entry without name in manifest"))
251 elif f == "/dev/null": # ignore this in very old repos
270 elif f == "/dev/null": # ignore this in very old repos
252 continue
271 continue
253 fullpath = dir + _normpath(f)
272 fullpath = dir + _normpath(f)
254 if fl == 't':
273 if fl == 't':
255 if not match.visitdir(fullpath):
274 if not match.visitdir(fullpath):
256 continue
275 continue
257 subdirnodes.setdefault(fullpath + '/', {}).setdefault(
276 subdirnodes.setdefault(fullpath + '/', {}).setdefault(
258 fn, []).append(lr)
277 fn, []).append(lr)
259 else:
278 else:
260 if not match(fullpath):
279 if not match(fullpath):
261 continue
280 continue
262 filenodes.setdefault(fullpath, {}).setdefault(fn, lr)
281 filenodes.setdefault(fullpath, {}).setdefault(fn, lr)
263 except Exception as inst:
282 except Exception as inst:
264 self._exc(lr, _("reading delta %s") % short(n), inst, label)
283 self._exc(lr, _("reading delta %s") % short(n), inst, label)
265 if not dir:
284 if not dir:
266 progress.complete()
285 progress.complete()
267
286
268 if self.havemf:
287 if self.havemf:
269 for c, m in sorted([(c, m) for m in mflinkrevs
288 for c, m in sorted([(c, m) for m in mflinkrevs
270 for c in mflinkrevs[m]]):
289 for c in mflinkrevs[m]]):
271 if dir:
290 if dir:
272 self._err(c, _("parent-directory manifest refers to unknown"
291 self._err(c, _("parent-directory manifest refers to unknown"
273 " revision %s") % short(m), label)
292 " revision %s") % short(m), label)
274 else:
293 else:
275 self._err(c, _("changeset refers to unknown revision %s") %
294 self._err(c, _("changeset refers to unknown revision %s") %
276 short(m), label)
295 short(m), label)
277
296
278 if not dir and subdirnodes:
297 if not dir and subdirnodes:
279 self.ui.status(_("checking directory manifests\n"))
298 self.ui.status(_("checking directory manifests\n"))
280 storefiles = set()
299 storefiles = set()
281 subdirs = set()
300 subdirs = set()
282 revlogv1 = self.revlogv1
301 revlogv1 = self.revlogv1
283 for f, f2, size in repo.store.datafiles():
302 for f, f2, size in repo.store.datafiles():
284 if not f:
303 if not f:
285 self._err(None, _("cannot decode filename '%s'") % f2)
304 self._err(None, _("cannot decode filename '%s'") % f2)
286 elif (size > 0 or not revlogv1) and f.startswith('meta/'):
305 elif (size > 0 or not revlogv1) and f.startswith('meta/'):
287 storefiles.add(_normpath(f))
306 storefiles.add(_normpath(f))
288 subdirs.add(os.path.dirname(f))
307 subdirs.add(os.path.dirname(f))
289 subdirprogress = ui.makeprogress(_('checking'), unit=_('manifests'),
308 subdirprogress = ui.makeprogress(_('checking'), unit=_('manifests'),
290 total=len(subdirs))
309 total=len(subdirs))
291
310
292 for subdir, linkrevs in subdirnodes.iteritems():
311 for subdir, linkrevs in subdirnodes.iteritems():
293 subdirfilenodes = self._verifymanifest(linkrevs, subdir, storefiles,
312 subdirfilenodes = self._verifymanifest(linkrevs, subdir, storefiles,
294 subdirprogress)
313 subdirprogress)
295 for f, onefilenodes in subdirfilenodes.iteritems():
314 for f, onefilenodes in subdirfilenodes.iteritems():
296 filenodes.setdefault(f, {}).update(onefilenodes)
315 filenodes.setdefault(f, {}).update(onefilenodes)
297
316
298 if not dir and subdirnodes:
317 if not dir and subdirnodes:
299 subdirprogress.complete()
318 subdirprogress.complete()
300 if self.warnorphanstorefiles:
319 if self.warnorphanstorefiles:
301 for f in sorted(storefiles):
320 for f in sorted(storefiles):
302 self._warn(_("warning: orphan data file '%s'") % f)
321 self._warn(_("warning: orphan data file '%s'") % f)
303
322
304 return filenodes
323 return filenodes
305
324
306 def _crosscheckfiles(self, filelinkrevs, filenodes):
325 def _crosscheckfiles(self, filelinkrevs, filenodes):
307 repo = self.repo
326 repo = self.repo
308 ui = self.ui
327 ui = self.ui
309 ui.status(_("crosschecking files in changesets and manifests\n"))
328 ui.status(_("crosschecking files in changesets and manifests\n"))
310
329
311 total = len(filelinkrevs) + len(filenodes)
330 total = len(filelinkrevs) + len(filenodes)
312 progress = ui.makeprogress(_('crosschecking'), unit=_('files'),
331 progress = ui.makeprogress(_('crosschecking'), unit=_('files'),
313 total=total)
332 total=total)
314 if self.havemf:
333 if self.havemf:
315 for f in sorted(filelinkrevs):
334 for f in sorted(filelinkrevs):
316 progress.increment()
335 progress.increment()
317 if f not in filenodes:
336 if f not in filenodes:
318 lr = filelinkrevs[f][0]
337 lr = filelinkrevs[f][0]
319 self._err(lr, _("in changeset but not in manifest"), f)
338 self._err(lr, _("in changeset but not in manifest"), f)
320
339
321 if self.havecl:
340 if self.havecl:
322 for f in sorted(filenodes):
341 for f in sorted(filenodes):
323 progress.increment()
342 progress.increment()
324 if f not in filelinkrevs:
343 if f not in filelinkrevs:
325 try:
344 try:
326 fl = repo.file(f)
345 fl = repo.file(f)
327 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
346 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
328 except Exception:
347 except Exception:
329 lr = None
348 lr = None
330 self._err(lr, _("in manifest but not in changeset"), f)
349 self._err(lr, _("in manifest but not in changeset"), f)
331
350
332 progress.complete()
351 progress.complete()
333
352
334 def _verifyfiles(self, filenodes, filelinkrevs):
353 def _verifyfiles(self, filenodes, filelinkrevs):
335 repo = self.repo
354 repo = self.repo
336 ui = self.ui
355 ui = self.ui
337 lrugetctx = self.lrugetctx
356 lrugetctx = self.lrugetctx
338 revlogv1 = self.revlogv1
357 revlogv1 = self.revlogv1
339 havemf = self.havemf
358 havemf = self.havemf
340 ui.status(_("checking files\n"))
359 ui.status(_("checking files\n"))
341
360
342 storefiles = set()
361 storefiles = set()
343 for f, f2, size in repo.store.datafiles():
362 for f, f2, size in repo.store.datafiles():
344 if not f:
363 if not f:
345 self._err(None, _("cannot decode filename '%s'") % f2)
364 self._err(None, _("cannot decode filename '%s'") % f2)
346 elif (size > 0 or not revlogv1) and f.startswith('data/'):
365 elif (size > 0 or not revlogv1) and f.startswith('data/'):
347 storefiles.add(_normpath(f))
366 storefiles.add(_normpath(f))
348
367
349 state = {
368 state = {
350 # TODO this assumes revlog storage for changelog.
369 # TODO this assumes revlog storage for changelog.
351 'expectedversion': self.repo.changelog.version & 0xFFFF,
370 'expectedversion': self.repo.changelog.version & 0xFFFF,
352 'skipflags': self.skipflags,
371 'skipflags': self.skipflags,
353 # experimental config: censor.policy
372 # experimental config: censor.policy
354 'erroroncensored': ui.config('censor', 'policy') == 'abort',
373 'erroroncensored': ui.config('censor', 'policy') == 'abort',
355 }
374 }
356
375
357 files = sorted(set(filenodes) | set(filelinkrevs))
376 files = sorted(set(filenodes) | set(filelinkrevs))
358 revisions = 0
377 revisions = 0
359 progress = ui.makeprogress(_('checking'), unit=_('files'),
378 progress = ui.makeprogress(_('checking'), unit=_('files'),
360 total=len(files))
379 total=len(files))
361 for i, f in enumerate(files):
380 for i, f in enumerate(files):
362 progress.update(i, item=f)
381 progress.update(i, item=f)
363 try:
382 try:
364 linkrevs = filelinkrevs[f]
383 linkrevs = filelinkrevs[f]
365 except KeyError:
384 except KeyError:
366 # in manifest but not in changelog
385 # in manifest but not in changelog
367 linkrevs = []
386 linkrevs = []
368
387
369 if linkrevs:
388 if linkrevs:
370 lr = linkrevs[0]
389 lr = linkrevs[0]
371 else:
390 else:
372 lr = None
391 lr = None
373
392
374 try:
393 try:
375 fl = repo.file(f)
394 fl = repo.file(f)
376 except error.StorageError as e:
395 except error.StorageError as e:
377 self._err(lr, _("broken revlog! (%s)") % e, f)
396 self._err(lr, _("broken revlog! (%s)") % e, f)
378 continue
397 continue
379
398
380 for ff in fl.files():
399 for ff in fl.files():
381 try:
400 try:
382 storefiles.remove(ff)
401 storefiles.remove(ff)
383 except KeyError:
402 except KeyError:
384 if self.warnorphanstorefiles:
403 if self.warnorphanstorefiles:
385 self._warn(_(" warning: revlog '%s' not in fncache!") %
404 self._warn(_(" warning: revlog '%s' not in fncache!") %
386 ff)
405 ff)
387 self.fncachewarned = True
406 self.fncachewarned = True
388
407
389 if not len(fl) and (self.havecl or self.havemf):
408 if not len(fl) and (self.havecl or self.havemf):
390 self._err(lr, _("empty or missing %s") % f)
409 self._err(lr, _("empty or missing %s") % f)
391 else:
410 else:
392 # Guard against implementations not setting this.
411 # Guard against implementations not setting this.
393 state['skipread'] = set()
412 state['skipread'] = set()
394 for problem in fl.verifyintegrity(state):
413 for problem in fl.verifyintegrity(state):
395 if problem.node is not None:
414 if problem.node is not None:
396 linkrev = fl.linkrev(fl.rev(problem.node))
415 linkrev = fl.linkrev(fl.rev(problem.node))
397 else:
416 else:
398 linkrev = None
417 linkrev = None
399
418
400 if problem.warning:
419 if problem.warning:
401 self._warn(problem.warning)
420 self._warn(problem.warning)
402 elif problem.error:
421 elif problem.error:
403 self._err(linkrev if linkrev is not None else lr,
422 self._err(linkrev if linkrev is not None else lr,
404 problem.error, f)
423 problem.error, f)
405 else:
424 else:
406 raise error.ProgrammingError(
425 raise error.ProgrammingError(
407 'problem instance does not set warning or error '
426 'problem instance does not set warning or error '
408 'attribute: %s' % problem.msg)
427 'attribute: %s' % problem.msg)
409
428
410 seen = {}
429 seen = {}
411 for i in fl:
430 for i in fl:
412 revisions += 1
431 revisions += 1
413 n = fl.node(i)
432 n = fl.node(i)
414 lr = self.checkentry(fl, i, n, seen, linkrevs, f)
433 lr = self.checkentry(fl, i, n, seen, linkrevs, f)
415 if f in filenodes:
434 if f in filenodes:
416 if havemf and n not in filenodes[f]:
435 if havemf and n not in filenodes[f]:
417 self._err(lr, _("%s not in manifests") % (short(n)), f)
436 self._err(lr, _("%s not in manifests") % (short(n)), f)
418 else:
437 else:
419 del filenodes[f][n]
438 del filenodes[f][n]
420
439
421 if n in state['skipread']:
440 if n in state['skipread']:
422 continue
441 continue
423
442
424 # check renames
443 # check renames
425 try:
444 try:
426 # This requires resolving fulltext (at least on revlogs). We
445 # This requires resolving fulltext (at least on revlogs). We
427 # may want ``verifyintegrity()`` to pass a set of nodes with
446 # may want ``verifyintegrity()`` to pass a set of nodes with
428 # rename metadata as an optimization.
447 # rename metadata as an optimization.
429 rp = fl.renamed(n)
448 rp = fl.renamed(n)
430 if rp:
449 if rp:
431 if lr is not None and ui.verbose:
450 if lr is not None and ui.verbose:
432 ctx = lrugetctx(lr)
451 ctx = lrugetctx(lr)
433 if not any(rp[0] in pctx for pctx in ctx.parents()):
452 if not any(rp[0] in pctx for pctx in ctx.parents()):
434 self._warn(_("warning: copy source of '%s' not"
453 self._warn(_("warning: copy source of '%s' not"
435 " in parents of %s") % (f, ctx))
454 " in parents of %s") % (f, ctx))
436 fl2 = repo.file(rp[0])
455 fl2 = repo.file(rp[0])
437 if not len(fl2):
456 if not len(fl2):
438 self._err(lr,
457 self._err(lr,
439 _("empty or missing copy source revlog "
458 _("empty or missing copy source revlog "
440 "%s:%s") % (rp[0],
459 "%s:%s") % (rp[0],
441 short(rp[1])),
460 short(rp[1])),
442 f)
461 f)
443 elif rp[1] == nullid:
462 elif rp[1] == nullid:
444 ui.note(_("warning: %s@%s: copy source"
463 ui.note(_("warning: %s@%s: copy source"
445 " revision is nullid %s:%s\n")
464 " revision is nullid %s:%s\n")
446 % (f, lr, rp[0], short(rp[1])))
465 % (f, lr, rp[0], short(rp[1])))
447 else:
466 else:
448 fl2.rev(rp[1])
467 fl2.rev(rp[1])
449 except Exception as inst:
468 except Exception as inst:
450 self._exc(lr, _("checking rename of %s") % short(n),
469 self._exc(lr, _("checking rename of %s") % short(n),
451 inst, f)
470 inst, f)
452
471
453 # cross-check
472 # cross-check
454 if f in filenodes:
473 if f in filenodes:
455 fns = [(v, k) for k, v in filenodes[f].iteritems()]
474 fns = [(v, k) for k, v in filenodes[f].iteritems()]
456 for lr, node in sorted(fns):
475 for lr, node in sorted(fns):
457 self._err(lr, _("manifest refers to unknown revision %s") %
476 self._err(lr, _("manifest refers to unknown revision %s") %
458 short(node), f)
477 short(node), f)
459 progress.complete()
478 progress.complete()
460
479
461 if self.warnorphanstorefiles:
480 if self.warnorphanstorefiles:
462 for f in sorted(storefiles):
481 for f in sorted(storefiles):
463 self._warn(_("warning: orphan data file '%s'") % f)
482 self._warn(_("warning: orphan data file '%s'") % f)
464
483
465 return len(files), revisions
484 return len(files), revisions
General Comments 0
You need to be logged in to leave comments. Login now