##// END OF EJS Templates
verify: don't reimplement any()...
Martin von Zweigbergk -
r36357:a4d41ba4 default
parent child Browse files
Show More
@@ -1,491 +1,486 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 scmutil,
22 scmutil,
23 util,
23 util,
24 )
24 )
25
25
26 def verify(repo):
26 def verify(repo):
27 with repo.lock():
27 with repo.lock():
28 return verifier(repo).verify()
28 return verifier(repo).verify()
29
29
30 def _normpath(f):
30 def _normpath(f):
31 # under hg < 2.4, convert didn't sanitize paths properly, so a
31 # under hg < 2.4, convert didn't sanitize paths properly, so a
32 # converted repo may contain repeated slashes
32 # converted repo may contain repeated slashes
33 while '//' in f:
33 while '//' in f:
34 f = f.replace('//', '/')
34 f = f.replace('//', '/')
35 return f
35 return f
36
36
37 class verifier(object):
37 class verifier(object):
38 # The match argument is always None in hg core, but e.g. the narrowhg
38 # The match argument is always None in hg core, but e.g. the narrowhg
39 # extension will pass in a matcher here.
39 # extension will pass in a matcher here.
40 def __init__(self, repo, match=None):
40 def __init__(self, repo, match=None):
41 self.repo = repo.unfiltered()
41 self.repo = repo.unfiltered()
42 self.ui = repo.ui
42 self.ui = repo.ui
43 self.match = match or scmutil.matchall(repo)
43 self.match = match or scmutil.matchall(repo)
44 self.badrevs = set()
44 self.badrevs = set()
45 self.errors = 0
45 self.errors = 0
46 self.warnings = 0
46 self.warnings = 0
47 self.havecl = len(repo.changelog) > 0
47 self.havecl = len(repo.changelog) > 0
48 self.havemf = len(repo.manifestlog._revlog) > 0
48 self.havemf = len(repo.manifestlog._revlog) > 0
49 self.revlogv1 = repo.changelog.version != revlog.REVLOGV0
49 self.revlogv1 = repo.changelog.version != revlog.REVLOGV0
50 self.lrugetctx = util.lrucachefunc(repo.changectx)
50 self.lrugetctx = util.lrucachefunc(repo.changectx)
51 self.refersmf = False
51 self.refersmf = False
52 self.fncachewarned = False
52 self.fncachewarned = False
53 # developer config: verify.skipflags
53 # developer config: verify.skipflags
54 self.skipflags = repo.ui.configint('verify', 'skipflags')
54 self.skipflags = repo.ui.configint('verify', 'skipflags')
55
55
56 def warn(self, msg):
56 def warn(self, msg):
57 self.ui.warn(msg + "\n")
57 self.ui.warn(msg + "\n")
58 self.warnings += 1
58 self.warnings += 1
59
59
60 def err(self, linkrev, msg, filename=None):
60 def err(self, linkrev, msg, filename=None):
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 if not str(inst):
73 if not str(inst):
74 inst = repr(inst)
74 inst = repr(inst)
75 self.err(linkrev, "%s: %s" % (msg, inst), filename)
75 self.err(linkrev, "%s: %s" % (msg, inst), filename)
76
76
77 def checklog(self, obj, name, linkrev):
77 def checklog(self, obj, name, linkrev):
78 if not len(obj) and (self.havecl or self.havemf):
78 if not len(obj) and (self.havecl or self.havemf):
79 self.err(linkrev, _("empty or missing %s") % name)
79 self.err(linkrev, _("empty or missing %s") % name)
80 return
80 return
81
81
82 d = obj.checksize()
82 d = obj.checksize()
83 if d[0]:
83 if d[0]:
84 self.err(None, _("data length off by %d bytes") % d[0], name)
84 self.err(None, _("data length off by %d bytes") % d[0], name)
85 if d[1]:
85 if d[1]:
86 self.err(None, _("index contains %d extra bytes") % d[1], name)
86 self.err(None, _("index contains %d extra bytes") % d[1], name)
87
87
88 if obj.version != revlog.REVLOGV0:
88 if obj.version != revlog.REVLOGV0:
89 if not self.revlogv1:
89 if not self.revlogv1:
90 self.warn(_("warning: `%s' uses revlog format 1") % name)
90 self.warn(_("warning: `%s' uses revlog format 1") % name)
91 elif self.revlogv1:
91 elif self.revlogv1:
92 self.warn(_("warning: `%s' uses revlog format 0") % name)
92 self.warn(_("warning: `%s' uses revlog format 0") % name)
93
93
94 def checkentry(self, obj, i, node, seen, linkrevs, f):
94 def checkentry(self, obj, i, node, seen, linkrevs, f):
95 lr = obj.linkrev(obj.rev(node))
95 lr = obj.linkrev(obj.rev(node))
96 if lr < 0 or (self.havecl and lr not in linkrevs):
96 if lr < 0 or (self.havecl and lr not in linkrevs):
97 if lr < 0 or lr >= len(self.repo.changelog):
97 if lr < 0 or lr >= len(self.repo.changelog):
98 msg = _("rev %d points to nonexistent changeset %d")
98 msg = _("rev %d points to nonexistent changeset %d")
99 else:
99 else:
100 msg = _("rev %d points to unexpected changeset %d")
100 msg = _("rev %d points to unexpected changeset %d")
101 self.err(None, msg % (i, lr), f)
101 self.err(None, msg % (i, lr), f)
102 if linkrevs:
102 if linkrevs:
103 if f and len(linkrevs) > 1:
103 if f and len(linkrevs) > 1:
104 try:
104 try:
105 # attempt to filter down to real linkrevs
105 # attempt to filter down to real linkrevs
106 linkrevs = [l for l in linkrevs
106 linkrevs = [l for l in linkrevs
107 if self.lrugetctx(l)[f].filenode() == node]
107 if self.lrugetctx(l)[f].filenode() == node]
108 except Exception:
108 except Exception:
109 pass
109 pass
110 self.warn(_(" (expected %s)") % " ".join
110 self.warn(_(" (expected %s)") % " ".join
111 (map(pycompat.bytestr, linkrevs)))
111 (map(pycompat.bytestr, linkrevs)))
112 lr = None # can't be trusted
112 lr = None # can't be trusted
113
113
114 try:
114 try:
115 p1, p2 = obj.parents(node)
115 p1, p2 = obj.parents(node)
116 if p1 not in seen and p1 != nullid:
116 if p1 not in seen and p1 != nullid:
117 self.err(lr, _("unknown parent 1 %s of %s") %
117 self.err(lr, _("unknown parent 1 %s of %s") %
118 (short(p1), short(node)), f)
118 (short(p1), short(node)), f)
119 if p2 not in seen and p2 != nullid:
119 if p2 not in seen and p2 != nullid:
120 self.err(lr, _("unknown parent 2 %s of %s") %
120 self.err(lr, _("unknown parent 2 %s of %s") %
121 (short(p2), short(node)), f)
121 (short(p2), short(node)), f)
122 except Exception as inst:
122 except Exception as inst:
123 self.exc(lr, _("checking parents of %s") % short(node), inst, f)
123 self.exc(lr, _("checking parents of %s") % short(node), inst, f)
124
124
125 if node in seen:
125 if node in seen:
126 self.err(lr, _("duplicate revision %d (%d)") % (i, seen[node]), f)
126 self.err(lr, _("duplicate revision %d (%d)") % (i, seen[node]), f)
127 seen[node] = i
127 seen[node] = i
128 return lr
128 return lr
129
129
130 def verify(self):
130 def verify(self):
131 repo = self.repo
131 repo = self.repo
132
132
133 ui = repo.ui
133 ui = repo.ui
134
134
135 if not repo.url().startswith('file:'):
135 if not repo.url().startswith('file:'):
136 raise error.Abort(_("cannot verify bundle or remote repos"))
136 raise error.Abort(_("cannot verify bundle or remote repos"))
137
137
138 if os.path.exists(repo.sjoin("journal")):
138 if os.path.exists(repo.sjoin("journal")):
139 ui.warn(_("abandoned transaction found - run hg recover\n"))
139 ui.warn(_("abandoned transaction found - run hg recover\n"))
140
140
141 if ui.verbose or not self.revlogv1:
141 if ui.verbose or not self.revlogv1:
142 ui.status(_("repository uses revlog format %d\n") %
142 ui.status(_("repository uses revlog format %d\n") %
143 (self.revlogv1 and 1 or 0))
143 (self.revlogv1 and 1 or 0))
144
144
145 mflinkrevs, filelinkrevs = self._verifychangelog()
145 mflinkrevs, filelinkrevs = self._verifychangelog()
146
146
147 filenodes = self._verifymanifest(mflinkrevs)
147 filenodes = self._verifymanifest(mflinkrevs)
148 del mflinkrevs
148 del mflinkrevs
149
149
150 self._crosscheckfiles(filelinkrevs, filenodes)
150 self._crosscheckfiles(filelinkrevs, filenodes)
151
151
152 totalfiles, filerevisions = self._verifyfiles(filenodes, filelinkrevs)
152 totalfiles, filerevisions = self._verifyfiles(filenodes, filelinkrevs)
153
153
154 ui.status(_("%d files, %d changesets, %d total revisions\n") %
154 ui.status(_("%d files, %d changesets, %d total revisions\n") %
155 (totalfiles, len(repo.changelog), filerevisions))
155 (totalfiles, len(repo.changelog), filerevisions))
156 if self.warnings:
156 if self.warnings:
157 ui.warn(_("%d warnings encountered!\n") % self.warnings)
157 ui.warn(_("%d warnings encountered!\n") % self.warnings)
158 if self.fncachewarned:
158 if self.fncachewarned:
159 ui.warn(_('hint: run "hg debugrebuildfncache" to recover from '
159 ui.warn(_('hint: run "hg debugrebuildfncache" to recover from '
160 'corrupt fncache\n'))
160 'corrupt fncache\n'))
161 if self.errors:
161 if self.errors:
162 ui.warn(_("%d integrity errors encountered!\n") % self.errors)
162 ui.warn(_("%d integrity errors encountered!\n") % self.errors)
163 if self.badrevs:
163 if self.badrevs:
164 ui.warn(_("(first damaged changeset appears to be %d)\n")
164 ui.warn(_("(first damaged changeset appears to be %d)\n")
165 % min(self.badrevs))
165 % min(self.badrevs))
166 return 1
166 return 1
167
167
168 def _verifychangelog(self):
168 def _verifychangelog(self):
169 ui = self.ui
169 ui = self.ui
170 repo = self.repo
170 repo = self.repo
171 match = self.match
171 match = self.match
172 cl = repo.changelog
172 cl = repo.changelog
173
173
174 ui.status(_("checking changesets\n"))
174 ui.status(_("checking changesets\n"))
175 mflinkrevs = {}
175 mflinkrevs = {}
176 filelinkrevs = {}
176 filelinkrevs = {}
177 seen = {}
177 seen = {}
178 self.checklog(cl, "changelog", 0)
178 self.checklog(cl, "changelog", 0)
179 total = len(repo)
179 total = len(repo)
180 for i in repo:
180 for i in repo:
181 ui.progress(_('checking'), i, total=total, unit=_('changesets'))
181 ui.progress(_('checking'), i, total=total, unit=_('changesets'))
182 n = cl.node(i)
182 n = cl.node(i)
183 self.checkentry(cl, i, n, seen, [i], "changelog")
183 self.checkentry(cl, i, n, seen, [i], "changelog")
184
184
185 try:
185 try:
186 changes = cl.read(n)
186 changes = cl.read(n)
187 if changes[0] != nullid:
187 if changes[0] != nullid:
188 mflinkrevs.setdefault(changes[0], []).append(i)
188 mflinkrevs.setdefault(changes[0], []).append(i)
189 self.refersmf = True
189 self.refersmf = True
190 for f in changes[3]:
190 for f in changes[3]:
191 if match(f):
191 if match(f):
192 filelinkrevs.setdefault(_normpath(f), []).append(i)
192 filelinkrevs.setdefault(_normpath(f), []).append(i)
193 except Exception as inst:
193 except Exception as inst:
194 self.refersmf = True
194 self.refersmf = True
195 self.exc(i, _("unpacking changeset %s") % short(n), inst)
195 self.exc(i, _("unpacking changeset %s") % short(n), inst)
196 ui.progress(_('checking'), None)
196 ui.progress(_('checking'), None)
197 return mflinkrevs, filelinkrevs
197 return mflinkrevs, filelinkrevs
198
198
199 def _verifymanifest(self, mflinkrevs, dir="", storefiles=None,
199 def _verifymanifest(self, mflinkrevs, dir="", storefiles=None,
200 progress=None):
200 progress=None):
201 repo = self.repo
201 repo = self.repo
202 ui = self.ui
202 ui = self.ui
203 match = self.match
203 match = self.match
204 mfl = self.repo.manifestlog
204 mfl = self.repo.manifestlog
205 mf = mfl._revlog.dirlog(dir)
205 mf = mfl._revlog.dirlog(dir)
206
206
207 if not dir:
207 if not dir:
208 self.ui.status(_("checking manifests\n"))
208 self.ui.status(_("checking manifests\n"))
209
209
210 filenodes = {}
210 filenodes = {}
211 subdirnodes = {}
211 subdirnodes = {}
212 seen = {}
212 seen = {}
213 label = "manifest"
213 label = "manifest"
214 if dir:
214 if dir:
215 label = dir
215 label = dir
216 revlogfiles = mf.files()
216 revlogfiles = mf.files()
217 storefiles.difference_update(revlogfiles)
217 storefiles.difference_update(revlogfiles)
218 if progress: # should be true since we're in a subdirectory
218 if progress: # should be true since we're in a subdirectory
219 progress()
219 progress()
220 if self.refersmf:
220 if self.refersmf:
221 # Do not check manifest if there are only changelog entries with
221 # Do not check manifest if there are only changelog entries with
222 # null manifests.
222 # null manifests.
223 self.checklog(mf, label, 0)
223 self.checklog(mf, label, 0)
224 total = len(mf)
224 total = len(mf)
225 for i in mf:
225 for i in mf:
226 if not dir:
226 if not dir:
227 ui.progress(_('checking'), i, total=total, unit=_('manifests'))
227 ui.progress(_('checking'), i, total=total, unit=_('manifests'))
228 n = mf.node(i)
228 n = mf.node(i)
229 lr = self.checkentry(mf, i, n, seen, mflinkrevs.get(n, []), label)
229 lr = self.checkentry(mf, i, n, seen, mflinkrevs.get(n, []), label)
230 if n in mflinkrevs:
230 if n in mflinkrevs:
231 del mflinkrevs[n]
231 del mflinkrevs[n]
232 elif dir:
232 elif dir:
233 self.err(lr, _("%s not in parent-directory manifest") %
233 self.err(lr, _("%s not in parent-directory manifest") %
234 short(n), label)
234 short(n), label)
235 else:
235 else:
236 self.err(lr, _("%s not in changesets") % short(n), label)
236 self.err(lr, _("%s not in changesets") % short(n), label)
237
237
238 try:
238 try:
239 mfdelta = mfl.get(dir, n).readdelta(shallow=True)
239 mfdelta = mfl.get(dir, n).readdelta(shallow=True)
240 for f, fn, fl in mfdelta.iterentries():
240 for f, fn, fl in mfdelta.iterentries():
241 if not f:
241 if not f:
242 self.err(lr, _("entry without name in manifest"))
242 self.err(lr, _("entry without name in manifest"))
243 elif f == "/dev/null": # ignore this in very old repos
243 elif f == "/dev/null": # ignore this in very old repos
244 continue
244 continue
245 fullpath = dir + _normpath(f)
245 fullpath = dir + _normpath(f)
246 if fl == 't':
246 if fl == 't':
247 if not match.visitdir(fullpath):
247 if not match.visitdir(fullpath):
248 continue
248 continue
249 subdirnodes.setdefault(fullpath + '/', {}).setdefault(
249 subdirnodes.setdefault(fullpath + '/', {}).setdefault(
250 fn, []).append(lr)
250 fn, []).append(lr)
251 else:
251 else:
252 if not match(fullpath):
252 if not match(fullpath):
253 continue
253 continue
254 filenodes.setdefault(fullpath, {}).setdefault(fn, lr)
254 filenodes.setdefault(fullpath, {}).setdefault(fn, lr)
255 except Exception as inst:
255 except Exception as inst:
256 self.exc(lr, _("reading delta %s") % short(n), inst, label)
256 self.exc(lr, _("reading delta %s") % short(n), inst, label)
257 if not dir:
257 if not dir:
258 ui.progress(_('checking'), None)
258 ui.progress(_('checking'), None)
259
259
260 if self.havemf:
260 if self.havemf:
261 for c, m in sorted([(c, m) for m in mflinkrevs
261 for c, m in sorted([(c, m) for m in mflinkrevs
262 for c in mflinkrevs[m]]):
262 for c in mflinkrevs[m]]):
263 if dir:
263 if dir:
264 self.err(c, _("parent-directory manifest refers to unknown "
264 self.err(c, _("parent-directory manifest refers to unknown "
265 "revision %s") % short(m), label)
265 "revision %s") % short(m), label)
266 else:
266 else:
267 self.err(c, _("changeset refers to unknown revision %s") %
267 self.err(c, _("changeset refers to unknown revision %s") %
268 short(m), label)
268 short(m), label)
269
269
270 if not dir and subdirnodes:
270 if not dir and subdirnodes:
271 self.ui.status(_("checking directory manifests\n"))
271 self.ui.status(_("checking directory manifests\n"))
272 storefiles = set()
272 storefiles = set()
273 subdirs = set()
273 subdirs = set()
274 revlogv1 = self.revlogv1
274 revlogv1 = self.revlogv1
275 for f, f2, size in repo.store.datafiles():
275 for f, f2, size in repo.store.datafiles():
276 if not f:
276 if not f:
277 self.err(None, _("cannot decode filename '%s'") % f2)
277 self.err(None, _("cannot decode filename '%s'") % f2)
278 elif (size > 0 or not revlogv1) and f.startswith('meta/'):
278 elif (size > 0 or not revlogv1) and f.startswith('meta/'):
279 storefiles.add(_normpath(f))
279 storefiles.add(_normpath(f))
280 subdirs.add(os.path.dirname(f))
280 subdirs.add(os.path.dirname(f))
281 subdircount = len(subdirs)
281 subdircount = len(subdirs)
282 currentsubdir = [0]
282 currentsubdir = [0]
283 def progress():
283 def progress():
284 currentsubdir[0] += 1
284 currentsubdir[0] += 1
285 ui.progress(_('checking'), currentsubdir[0], total=subdircount,
285 ui.progress(_('checking'), currentsubdir[0], total=subdircount,
286 unit=_('manifests'))
286 unit=_('manifests'))
287
287
288 for subdir, linkrevs in subdirnodes.iteritems():
288 for subdir, linkrevs in subdirnodes.iteritems():
289 subdirfilenodes = self._verifymanifest(linkrevs, subdir, storefiles,
289 subdirfilenodes = self._verifymanifest(linkrevs, subdir, storefiles,
290 progress)
290 progress)
291 for f, onefilenodes in subdirfilenodes.iteritems():
291 for f, onefilenodes in subdirfilenodes.iteritems():
292 filenodes.setdefault(f, {}).update(onefilenodes)
292 filenodes.setdefault(f, {}).update(onefilenodes)
293
293
294 if not dir and subdirnodes:
294 if not dir and subdirnodes:
295 ui.progress(_('checking'), None)
295 ui.progress(_('checking'), None)
296 for f in sorted(storefiles):
296 for f in sorted(storefiles):
297 self.warn(_("warning: orphan revlog '%s'") % f)
297 self.warn(_("warning: orphan revlog '%s'") % f)
298
298
299 return filenodes
299 return filenodes
300
300
301 def _crosscheckfiles(self, filelinkrevs, filenodes):
301 def _crosscheckfiles(self, filelinkrevs, filenodes):
302 repo = self.repo
302 repo = self.repo
303 ui = self.ui
303 ui = self.ui
304 ui.status(_("crosschecking files in changesets and manifests\n"))
304 ui.status(_("crosschecking files in changesets and manifests\n"))
305
305
306 total = len(filelinkrevs) + len(filenodes)
306 total = len(filelinkrevs) + len(filenodes)
307 count = 0
307 count = 0
308 if self.havemf:
308 if self.havemf:
309 for f in sorted(filelinkrevs):
309 for f in sorted(filelinkrevs):
310 count += 1
310 count += 1
311 ui.progress(_('crosschecking'), count, total=total)
311 ui.progress(_('crosschecking'), count, total=total)
312 if f not in filenodes:
312 if f not in filenodes:
313 lr = filelinkrevs[f][0]
313 lr = filelinkrevs[f][0]
314 self.err(lr, _("in changeset but not in manifest"), f)
314 self.err(lr, _("in changeset but not in manifest"), f)
315
315
316 if self.havecl:
316 if self.havecl:
317 for f in sorted(filenodes):
317 for f in sorted(filenodes):
318 count += 1
318 count += 1
319 ui.progress(_('crosschecking'), count, total=total)
319 ui.progress(_('crosschecking'), count, total=total)
320 if f not in filelinkrevs:
320 if f not in filelinkrevs:
321 try:
321 try:
322 fl = repo.file(f)
322 fl = repo.file(f)
323 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
323 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
324 except Exception:
324 except Exception:
325 lr = None
325 lr = None
326 self.err(lr, _("in manifest but not in changeset"), f)
326 self.err(lr, _("in manifest but not in changeset"), f)
327
327
328 ui.progress(_('crosschecking'), None)
328 ui.progress(_('crosschecking'), None)
329
329
330 def _verifyfiles(self, filenodes, filelinkrevs):
330 def _verifyfiles(self, filenodes, filelinkrevs):
331 repo = self.repo
331 repo = self.repo
332 ui = self.ui
332 ui = self.ui
333 lrugetctx = self.lrugetctx
333 lrugetctx = self.lrugetctx
334 revlogv1 = self.revlogv1
334 revlogv1 = self.revlogv1
335 havemf = self.havemf
335 havemf = self.havemf
336 ui.status(_("checking files\n"))
336 ui.status(_("checking files\n"))
337
337
338 storefiles = set()
338 storefiles = set()
339 for f, f2, size in repo.store.datafiles():
339 for f, f2, size in repo.store.datafiles():
340 if not f:
340 if not f:
341 self.err(None, _("cannot decode filename '%s'") % f2)
341 self.err(None, _("cannot decode filename '%s'") % f2)
342 elif (size > 0 or not revlogv1) and f.startswith('data/'):
342 elif (size > 0 or not revlogv1) and f.startswith('data/'):
343 storefiles.add(_normpath(f))
343 storefiles.add(_normpath(f))
344
344
345 files = sorted(set(filenodes) | set(filelinkrevs))
345 files = sorted(set(filenodes) | set(filelinkrevs))
346 total = len(files)
346 total = len(files)
347 revisions = 0
347 revisions = 0
348 for i, f in enumerate(files):
348 for i, f in enumerate(files):
349 ui.progress(_('checking'), i, item=f, total=total, unit=_('files'))
349 ui.progress(_('checking'), i, item=f, total=total, unit=_('files'))
350 try:
350 try:
351 linkrevs = filelinkrevs[f]
351 linkrevs = filelinkrevs[f]
352 except KeyError:
352 except KeyError:
353 # in manifest but not in changelog
353 # in manifest but not in changelog
354 linkrevs = []
354 linkrevs = []
355
355
356 if linkrevs:
356 if linkrevs:
357 lr = linkrevs[0]
357 lr = linkrevs[0]
358 else:
358 else:
359 lr = None
359 lr = None
360
360
361 try:
361 try:
362 fl = repo.file(f)
362 fl = repo.file(f)
363 except error.RevlogError as e:
363 except error.RevlogError as e:
364 self.err(lr, _("broken revlog! (%s)") % e, f)
364 self.err(lr, _("broken revlog! (%s)") % e, f)
365 continue
365 continue
366
366
367 for ff in fl.files():
367 for ff in fl.files():
368 try:
368 try:
369 storefiles.remove(ff)
369 storefiles.remove(ff)
370 except KeyError:
370 except KeyError:
371 self.warn(_(" warning: revlog '%s' not in fncache!") % ff)
371 self.warn(_(" warning: revlog '%s' not in fncache!") % ff)
372 self.fncachewarned = True
372 self.fncachewarned = True
373
373
374 self.checklog(fl, f, lr)
374 self.checklog(fl, f, lr)
375 seen = {}
375 seen = {}
376 rp = None
376 rp = None
377 for i in fl:
377 for i in fl:
378 revisions += 1
378 revisions += 1
379 n = fl.node(i)
379 n = fl.node(i)
380 lr = self.checkentry(fl, i, n, seen, linkrevs, f)
380 lr = self.checkentry(fl, i, n, seen, linkrevs, f)
381 if f in filenodes:
381 if f in filenodes:
382 if havemf and n not in filenodes[f]:
382 if havemf and n not in filenodes[f]:
383 self.err(lr, _("%s not in manifests") % (short(n)), f)
383 self.err(lr, _("%s not in manifests") % (short(n)), f)
384 else:
384 else:
385 del filenodes[f][n]
385 del filenodes[f][n]
386
386
387 # Verify contents. 4 cases to care about:
387 # Verify contents. 4 cases to care about:
388 #
388 #
389 # common: the most common case
389 # common: the most common case
390 # rename: with a rename
390 # rename: with a rename
391 # meta: file content starts with b'\1\n', the metadata
391 # meta: file content starts with b'\1\n', the metadata
392 # header defined in filelog.py, but without a rename
392 # header defined in filelog.py, but without a rename
393 # ext: content stored externally
393 # ext: content stored externally
394 #
394 #
395 # More formally, their differences are shown below:
395 # More formally, their differences are shown below:
396 #
396 #
397 # | common | rename | meta | ext
397 # | common | rename | meta | ext
398 # -------------------------------------------------------
398 # -------------------------------------------------------
399 # flags() | 0 | 0 | 0 | not 0
399 # flags() | 0 | 0 | 0 | not 0
400 # renamed() | False | True | False | ?
400 # renamed() | False | True | False | ?
401 # rawtext[0:2]=='\1\n'| False | True | True | ?
401 # rawtext[0:2]=='\1\n'| False | True | True | ?
402 #
402 #
403 # "rawtext" means the raw text stored in revlog data, which
403 # "rawtext" means the raw text stored in revlog data, which
404 # could be retrieved by "revision(rev, raw=True)". "text"
404 # could be retrieved by "revision(rev, raw=True)". "text"
405 # mentioned below is "revision(rev, raw=False)".
405 # mentioned below is "revision(rev, raw=False)".
406 #
406 #
407 # There are 3 different lengths stored physically:
407 # There are 3 different lengths stored physically:
408 # 1. L1: rawsize, stored in revlog index
408 # 1. L1: rawsize, stored in revlog index
409 # 2. L2: len(rawtext), stored in revlog data
409 # 2. L2: len(rawtext), stored in revlog data
410 # 3. L3: len(text), stored in revlog data if flags==0, or
410 # 3. L3: len(text), stored in revlog data if flags==0, or
411 # possibly somewhere else if flags!=0
411 # possibly somewhere else if flags!=0
412 #
412 #
413 # L1 should be equal to L2. L3 could be different from them.
413 # L1 should be equal to L2. L3 could be different from them.
414 # "text" may or may not affect commit hash depending on flag
414 # "text" may or may not affect commit hash depending on flag
415 # processors (see revlog.addflagprocessor).
415 # processors (see revlog.addflagprocessor).
416 #
416 #
417 # | common | rename | meta | ext
417 # | common | rename | meta | ext
418 # -------------------------------------------------
418 # -------------------------------------------------
419 # rawsize() | L1 | L1 | L1 | L1
419 # rawsize() | L1 | L1 | L1 | L1
420 # size() | L1 | L2-LM | L1(*) | L1 (?)
420 # size() | L1 | L2-LM | L1(*) | L1 (?)
421 # len(rawtext) | L2 | L2 | L2 | L2
421 # len(rawtext) | L2 | L2 | L2 | L2
422 # len(text) | L2 | L2 | L2 | L3
422 # len(text) | L2 | L2 | L2 | L3
423 # len(read()) | L2 | L2-LM | L2-LM | L3 (?)
423 # len(read()) | L2 | L2-LM | L2-LM | L3 (?)
424 #
424 #
425 # LM: length of metadata, depending on rawtext
425 # LM: length of metadata, depending on rawtext
426 # (*): not ideal, see comment in filelog.size
426 # (*): not ideal, see comment in filelog.size
427 # (?): could be "- len(meta)" if the resolved content has
427 # (?): could be "- len(meta)" if the resolved content has
428 # rename metadata
428 # rename metadata
429 #
429 #
430 # Checks needed to be done:
430 # Checks needed to be done:
431 # 1. length check: L1 == L2, in all cases.
431 # 1. length check: L1 == L2, in all cases.
432 # 2. hash check: depending on flag processor, we may need to
432 # 2. hash check: depending on flag processor, we may need to
433 # use either "text" (external), or "rawtext" (in revlog).
433 # use either "text" (external), or "rawtext" (in revlog).
434 try:
434 try:
435 skipflags = self.skipflags
435 skipflags = self.skipflags
436 if skipflags:
436 if skipflags:
437 skipflags &= fl.flags(i)
437 skipflags &= fl.flags(i)
438 if not skipflags:
438 if not skipflags:
439 fl.read(n) # side effect: read content and do checkhash
439 fl.read(n) # side effect: read content and do checkhash
440 rp = fl.renamed(n)
440 rp = fl.renamed(n)
441 # the "L1 == L2" check
441 # the "L1 == L2" check
442 l1 = fl.rawsize(i)
442 l1 = fl.rawsize(i)
443 l2 = len(fl.revision(n, raw=True))
443 l2 = len(fl.revision(n, raw=True))
444 if l1 != l2:
444 if l1 != l2:
445 self.err(lr, _("unpacked size is %s, %s expected") %
445 self.err(lr, _("unpacked size is %s, %s expected") %
446 (l2, l1), f)
446 (l2, l1), f)
447 except error.CensoredNodeError:
447 except error.CensoredNodeError:
448 # experimental config: censor.policy
448 # experimental config: censor.policy
449 if ui.config("censor", "policy") == "abort":
449 if ui.config("censor", "policy") == "abort":
450 self.err(lr, _("censored file data"), f)
450 self.err(lr, _("censored file data"), f)
451 except Exception as inst:
451 except Exception as inst:
452 self.exc(lr, _("unpacking %s") % short(n), inst, f)
452 self.exc(lr, _("unpacking %s") % short(n), inst, f)
453
453
454 # check renames
454 # check renames
455 try:
455 try:
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 found = False
459 if not any(rp[0] in pctx for pctx in ctx.parents()):
460 for pctx in ctx.parents():
461 if rp[0] in pctx:
462 found = True
463 break
464 if not found:
465 self.warn(_("warning: copy source of '%s' not"
460 self.warn(_("warning: copy source of '%s' not"
466 " in parents of %s") % (f, ctx))
461 " in parents of %s") % (f, ctx))
467 fl2 = repo.file(rp[0])
462 fl2 = repo.file(rp[0])
468 if not len(fl2):
463 if not len(fl2):
469 self.err(lr, _("empty or missing copy source "
464 self.err(lr, _("empty or missing copy source "
470 "revlog %s:%s") % (rp[0], short(rp[1])), f)
465 "revlog %s:%s") % (rp[0], short(rp[1])), f)
471 elif rp[1] == nullid:
466 elif rp[1] == nullid:
472 ui.note(_("warning: %s@%s: copy source"
467 ui.note(_("warning: %s@%s: copy source"
473 " revision is nullid %s:%s\n")
468 " revision is nullid %s:%s\n")
474 % (f, lr, rp[0], short(rp[1])))
469 % (f, lr, rp[0], short(rp[1])))
475 else:
470 else:
476 fl2.rev(rp[1])
471 fl2.rev(rp[1])
477 except Exception as inst:
472 except Exception as inst:
478 self.exc(lr, _("checking rename of %s") % short(n), inst, f)
473 self.exc(lr, _("checking rename of %s") % short(n), inst, f)
479
474
480 # cross-check
475 # cross-check
481 if f in filenodes:
476 if f in filenodes:
482 fns = [(v, k) for k, v in filenodes[f].iteritems()]
477 fns = [(v, k) for k, v in filenodes[f].iteritems()]
483 for lr, node in sorted(fns):
478 for lr, node in sorted(fns):
484 self.err(lr, _("manifest refers to unknown revision %s") %
479 self.err(lr, _("manifest refers to unknown revision %s") %
485 short(node), f)
480 short(node), f)
486 ui.progress(_('checking'), None)
481 ui.progress(_('checking'), None)
487
482
488 for f in sorted(storefiles):
483 for f in sorted(storefiles):
489 self.warn(_("warning: orphan revlog '%s'") % f)
484 self.warn(_("warning: orphan revlog '%s'") % f)
490
485
491 return len(files), revisions
486 return len(files), revisions
General Comments 0
You need to be logged in to leave comments. Login now