156
161
self.progress.finished()
157
162
self.repository.unlock()
164
def check_revisions(self, revisions_iterator):
165
"""Check revision objects by decorating a generator.
167
:param revisions_iterator: An iterator of(revid, Revision-or-None).
168
:return: A generator of the contents of revisions_iterator.
170
self.planned_revisions = set()
171
for revid, revision in revisions_iterator:
172
yield revid, revision
173
self._check_one_rev(revid, revision)
159
175
def check_revision_graph(self):
176
revision_iterator = self.repository._iter_revisions(None)
177
revision_iterator = self.check_revisions(revision_iterator)
178
# We read the all revisions here:
179
# - doing this allows later code to depend on the revision index.
180
# - we can fill out existence flags at this point
181
# - we can read the revision inventory sha at this point
182
# - we can check properties and serialisers etc.
160
183
if not self.repository.revision_graph_can_have_wrong_parents():
161
# This check is not necessary.
184
# The check against the index isn't needed.
162
185
self.revs_with_bad_parents_in_index = None
164
bad_revisions = self.repository._find_inconsistent_revision_parents()
165
self.revs_with_bad_parents_in_index = list(bad_revisions)
186
for thing in revision_iterator:
189
bad_revisions = self.repository._find_inconsistent_revision_parents(
191
self.revs_with_bad_parents_in_index = list(bad_revisions)
167
193
def plan_revisions(self):
168
194
repository = self.repository
188
214
note('%6d file-ids', len(self.checked_weaves))
189
215
note('%6d unique file texts', self.checked_text_cnt)
190
216
note('%6d repeated file texts', self.repeated_text_cnt)
191
note('%6d unreferenced text versions',
192
len(self.unreferenced_versions))
218
note('%6d unreferenced text versions',
219
len(self.unreferenced_versions))
220
if verbose and len(self.unreferenced_versions):
221
for file_id, revision_id in self.unreferenced_versions:
222
log_error('unreferenced version: {%s} in %s', revision_id,
193
224
if self.missing_inventory_sha_cnt:
194
225
note('%6d revisions are missing inventory_sha1',
195
226
self.missing_inventory_sha_cnt)
232
259
' %s has wrong parents in index: '
233
260
'%r should be %r',
234
261
revision_id, index_parents, actual_parents)
236
def check_one_rev(self, rev_id):
237
"""Check one revision.
239
rev_id - the one to check
262
for item in self._report_items:
265
def _check_one_rev(self, rev_id, rev):
266
"""Cross-check one revision.
268
:param rev_id: A revision id to check.
269
:param rev: A revision or None to indicate a missing revision.
241
rev = self.repository.get_revision(rev_id)
243
271
if rev.revision_id != rev_id:
244
raise BzrCheckError('wrong internal revision id in revision {%s}'
272
self._report_items.append(
273
'Mismatched internal revid {%s} and index revid {%s}' % (
274
rev.revision_id, rev_id))
275
rev_id = rev.revision_id
276
# Check this revision tree etc, and count as seen when we encounter a
278
self.planned_revisions.add(rev_id)
280
self.ghosts.discard(rev_id)
281
# Count all parents as ghosts if we haven't seen them yet.
247
282
for parent in rev.parent_ids:
248
283
if not parent in self.planned_revisions:
249
# rev has a parent we didn't know about.
250
missing_links = self.missing_parent_links.get(parent, [])
251
missing_links.append(rev_id)
252
self.missing_parent_links[parent] = missing_links
253
# list based so somewhat slow,
254
# TODO have a planned_revisions list and set.
255
if self.repository.has_revision(parent):
256
missing_ancestry = self.repository.get_ancestry(parent)
257
for missing in missing_ancestry:
258
if (missing is not None
259
and missing not in self.planned_revisions):
260
self.planned_revisions.append(missing)
262
self.ghosts.append(rev_id)
284
self.ghosts.add(parent)
264
286
self.ancestors[rev_id] = tuple(rev.parent_ids) or (NULL_REVISION,)
287
# If the revision has an inventory sha, we want to cross check it later.
265
288
if rev.inventory_sha1:
266
# Loopback - this is currently circular logic as the
267
# knit get_inventory_sha1 call returns rev.inventory_sha1.
268
# Repository.py's get_inventory_sha1 should instead return
269
# inventories.get_record_stream([(revid,)]).next().sha1 or
271
inv_sha1 = self.repository.get_inventory_sha1(rev_id)
272
if inv_sha1 != rev.inventory_sha1:
273
raise BzrCheckError('Inventory sha1 hash doesn\'t match'
274
' value in revision {%s}' % rev_id)
275
self._check_revision_tree(rev_id)
289
self.add_sha_check(rev_id, ('inventories', rev_id),
276
291
self.checked_rev_cnt += 1
293
def add_sha_check(self, referer, key, sha1):
294
"""Add a reference to a sha1 to be cross checked against a key.
296
:param referer: The referer that expects key to have sha1.
297
:param key: A storage key e.g. ('texts', 'foo@bar-20040504-1234')
298
:param sha1: A hex sha1.
300
existing = self.expected_sha1.get(key)
302
if sha1 != existing[0]:
303
self._report_items.append('Multiple expected sha1s for %s. {%s}'
304
' expects {%s}, {%s} expects {%s}', (
305
key, referer, sha1, existing[1], existing[0]))
307
self.expected_sha1[key] = (sha1, referer)
278
309
def check_weaves(self):
279
310
"""Check all the weaves we can get our hands on.