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