comparison mercurial/verify.py @ 2802:fdc232d8a193

Move repo.verify
author Matt Mackall <mpm@selenic.com>
date Mon, 07 Aug 2006 16:27:09 -0500
parents
children f3b939444c72
comparison
equal deleted inserted replaced
2801:81d7db1aa0fb 2802:fdc232d8a193
1 # verify.py - repository integrity checking for Mercurial
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from node import *
9 from i18n import gettext as _
10 import revlog, mdiff
11
12 def verify(repo):
13 filelinkrevs = {}
14 filenodes = {}
15 changesets = revisions = files = 0
16 errors = [0]
17 warnings = [0]
18 neededmanifests = {}
19
20 def err(msg):
21 repo.ui.warn(msg + "\n")
22 errors[0] += 1
23
24 def warn(msg):
25 repo.ui.warn(msg + "\n")
26 warnings[0] += 1
27
28 def checksize(obj, name):
29 d = obj.checksize()
30 if d[0]:
31 err(_("%s data length off by %d bytes") % (name, d[0]))
32 if d[1]:
33 err(_("%s index contains %d extra bytes") % (name, d[1]))
34
35 def checkversion(obj, name):
36 if obj.version != revlog.REVLOGV0:
37 if not revlogv1:
38 warn(_("warning: `%s' uses revlog format 1") % name)
39 elif revlogv1:
40 warn(_("warning: `%s' uses revlog format 0") % name)
41
42 revlogv1 = repo.revlogversion != revlog.REVLOGV0
43 if repo.ui.verbose or revlogv1 != repo.revlogv1:
44 repo.ui.status(_("repository uses revlog format %d\n") %
45 (revlogv1 and 1 or 0))
46
47 seen = {}
48 repo.ui.status(_("checking changesets\n"))
49 checksize(repo.changelog, "changelog")
50
51 for i in range(repo.changelog.count()):
52 changesets += 1
53 n = repo.changelog.node(i)
54 l = repo.changelog.linkrev(n)
55 if l != i:
56 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
57 if n in seen:
58 err(_("duplicate changeset at revision %d") % i)
59 seen[n] = 1
60
61 for p in repo.changelog.parents(n):
62 if p not in repo.changelog.nodemap:
63 err(_("changeset %s has unknown parent %s") %
64 (short(n), short(p)))
65 try:
66 changes = repo.changelog.read(n)
67 except KeyboardInterrupt:
68 repo.ui.warn(_("interrupted"))
69 raise
70 except Exception, inst:
71 err(_("unpacking changeset %s: %s") % (short(n), inst))
72 continue
73
74 neededmanifests[changes[0]] = n
75
76 for f in changes[3]:
77 filelinkrevs.setdefault(f, []).append(i)
78
79 seen = {}
80 repo.ui.status(_("checking manifests\n"))
81 checkversion(repo.manifest, "manifest")
82 checksize(repo.manifest, "manifest")
83
84 for i in range(repo.manifest.count()):
85 n = repo.manifest.node(i)
86 l = repo.manifest.linkrev(n)
87
88 if l < 0 or l >= repo.changelog.count():
89 err(_("bad manifest link (%d) at revision %d") % (l, i))
90
91 if n in neededmanifests:
92 del neededmanifests[n]
93
94 if n in seen:
95 err(_("duplicate manifest at revision %d") % i)
96
97 seen[n] = 1
98
99 for p in repo.manifest.parents(n):
100 if p not in repo.manifest.nodemap:
101 err(_("manifest %s has unknown parent %s") %
102 (short(n), short(p)))
103
104 try:
105 delta = mdiff.patchtext(repo.manifest.delta(n))
106 except KeyboardInterrupt:
107 repo.ui.warn(_("interrupted"))
108 raise
109 except Exception, inst:
110 err(_("unpacking manifest %s: %s") % (short(n), inst))
111 continue
112
113 try:
114 ff = [ l.split('\0') for l in delta.splitlines() ]
115 for f, fn in ff:
116 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
117 except (ValueError, TypeError), inst:
118 err(_("broken delta in manifest %s: %s") % (short(n), inst))
119
120 repo.ui.status(_("crosschecking files in changesets and manifests\n"))
121
122 for m, c in neededmanifests.items():
123 err(_("Changeset %s refers to unknown manifest %s") %
124 (short(m), short(c)))
125 del neededmanifests
126
127 for f in filenodes:
128 if f not in filelinkrevs:
129 err(_("file %s in manifest but not in changesets") % f)
130
131 for f in filelinkrevs:
132 if f not in filenodes:
133 err(_("file %s in changeset but not in manifest") % f)
134
135 repo.ui.status(_("checking files\n"))
136 ff = filenodes.keys()
137 ff.sort()
138 for f in ff:
139 if f == "/dev/null":
140 continue
141 files += 1
142 if not f:
143 err(_("file without name in manifest %s") % short(n))
144 continue
145 fl = repo.file(f)
146 checkversion(fl, f)
147 checksize(fl, f)
148
149 nodes = {nullid: 1}
150 seen = {}
151 for i in range(fl.count()):
152 revisions += 1
153 n = fl.node(i)
154
155 if n in seen:
156 err(_("%s: duplicate revision %d") % (f, i))
157 if n not in filenodes[f]:
158 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
159 else:
160 del filenodes[f][n]
161
162 flr = fl.linkrev(n)
163 if flr not in filelinkrevs.get(f, []):
164 err(_("%s:%s points to unexpected changeset %d")
165 % (f, short(n), flr))
166 else:
167 filelinkrevs[f].remove(flr)
168
169 # verify contents
170 try:
171 t = fl.read(n)
172 except KeyboardInterrupt:
173 repo.ui.warn(_("interrupted"))
174 raise
175 except Exception, inst:
176 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
177
178 # verify parents
179 (p1, p2) = fl.parents(n)
180 if p1 not in nodes:
181 err(_("file %s:%s unknown parent 1 %s") %
182 (f, short(n), short(p1)))
183 if p2 not in nodes:
184 err(_("file %s:%s unknown parent 2 %s") %
185 (f, short(n), short(p1)))
186 nodes[n] = 1
187
188 # cross-check
189 for node in filenodes[f]:
190 err(_("node %s in manifests not in %s") % (hex(node), f))
191
192 repo.ui.status(_("%d files, %d changesets, %d total revisions\n") %
193 (files, changesets, revisions))
194
195 if warnings[0]:
196 repo.ui.warn(_("%d warnings encountered!\n") % warnings[0])
197 if errors[0]:
198 repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
199 return 1
200