Mercurial > hg
annotate mercurial/verify.py @ 3414:52617d992eed
Report branch for hg log and friends
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Tue, 17 Oct 2006 18:30:18 -0500 |
parents | f3b939444c72 |
children | 0e68608bd11d |
rev | line source |
---|---|
2778 | 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: | |
3196
f3b939444c72
Abstract manifest block parsing.
Brendan Cully <brendan@kublai.com>
parents:
2778
diff
changeset
|
105 for f, fn in repo.manifest.readdelta(n).iteritems(): |
f3b939444c72
Abstract manifest block parsing.
Brendan Cully <brendan@kublai.com>
parents:
2778
diff
changeset
|
106 filenodes.setdefault(f, {})[fn] = 1 |
2778 | 107 except KeyboardInterrupt: |
108 repo.ui.warn(_("interrupted")) | |
109 raise | |
110 except Exception, inst: | |
3196
f3b939444c72
Abstract manifest block parsing.
Brendan Cully <brendan@kublai.com>
parents:
2778
diff
changeset
|
111 err(_("reading delta for manifest %s: %s") % (short(n), inst)) |
2778 | 112 continue |
113 | |
114 repo.ui.status(_("crosschecking files in changesets and manifests\n")) | |
115 | |
116 for m, c in neededmanifests.items(): | |
117 err(_("Changeset %s refers to unknown manifest %s") % | |
118 (short(m), short(c))) | |
119 del neededmanifests | |
120 | |
121 for f in filenodes: | |
122 if f not in filelinkrevs: | |
123 err(_("file %s in manifest but not in changesets") % f) | |
124 | |
125 for f in filelinkrevs: | |
126 if f not in filenodes: | |
127 err(_("file %s in changeset but not in manifest") % f) | |
128 | |
129 repo.ui.status(_("checking files\n")) | |
130 ff = filenodes.keys() | |
131 ff.sort() | |
132 for f in ff: | |
133 if f == "/dev/null": | |
134 continue | |
135 files += 1 | |
136 if not f: | |
137 err(_("file without name in manifest %s") % short(n)) | |
138 continue | |
139 fl = repo.file(f) | |
140 checkversion(fl, f) | |
141 checksize(fl, f) | |
142 | |
143 nodes = {nullid: 1} | |
144 seen = {} | |
145 for i in range(fl.count()): | |
146 revisions += 1 | |
147 n = fl.node(i) | |
148 | |
149 if n in seen: | |
150 err(_("%s: duplicate revision %d") % (f, i)) | |
151 if n not in filenodes[f]: | |
152 err(_("%s: %d:%s not in manifests") % (f, i, short(n))) | |
153 else: | |
154 del filenodes[f][n] | |
155 | |
156 flr = fl.linkrev(n) | |
157 if flr not in filelinkrevs.get(f, []): | |
158 err(_("%s:%s points to unexpected changeset %d") | |
159 % (f, short(n), flr)) | |
160 else: | |
161 filelinkrevs[f].remove(flr) | |
162 | |
163 # verify contents | |
164 try: | |
165 t = fl.read(n) | |
166 except KeyboardInterrupt: | |
167 repo.ui.warn(_("interrupted")) | |
168 raise | |
169 except Exception, inst: | |
170 err(_("unpacking file %s %s: %s") % (f, short(n), inst)) | |
171 | |
172 # verify parents | |
173 (p1, p2) = fl.parents(n) | |
174 if p1 not in nodes: | |
175 err(_("file %s:%s unknown parent 1 %s") % | |
176 (f, short(n), short(p1))) | |
177 if p2 not in nodes: | |
178 err(_("file %s:%s unknown parent 2 %s") % | |
179 (f, short(n), short(p1))) | |
180 nodes[n] = 1 | |
181 | |
182 # cross-check | |
183 for node in filenodes[f]: | |
184 err(_("node %s in manifests not in %s") % (hex(node), f)) | |
185 | |
186 repo.ui.status(_("%d files, %d changesets, %d total revisions\n") % | |
187 (files, changesets, revisions)) | |
188 | |
189 if warnings[0]: | |
190 repo.ui.warn(_("%d warnings encountered!\n") % warnings[0]) | |
191 if errors[0]: | |
192 repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0]) | |
193 return 1 | |
194 |