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