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