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