46
47
indicating that the revision was found/not found.
49
from bzrlib import errors
50
from bzrlib.branch import Branch
51
from bzrlib.bzrdir import BzrDir
52
from bzrlib.revision import NULL_REVISION
53
from bzrlib.symbol_versioning import deprecated_function, deprecated_in
54
from bzrlib.trace import note
56
from bzrlib.workingtree import WorkingTree
50
from __future__ import absolute_import
56
from .branch import Branch
57
from .controldir import ControlDir
58
from .revision import NULL_REVISION
62
from .trace import note
63
from .workingtree import WorkingTree
64
from .i18n import gettext
58
66
class Check(object):
59
67
"""Check a repository"""
69
def __init__(self, repository, check_repo=True):
70
self.repository = repository
72
def report_results(self, verbose):
73
raise NotImplementedError(self.report_results)
76
class VersionedFileCheck(Check):
77
"""Check a versioned file repository"""
61
79
# The Check object interacts with InventoryEntry.check, etc.
63
81
def __init__(self, repository, check_repo=True):
87
105
def check(self, callback_refs=None, check_repo=True):
88
106
if callback_refs is None:
89
107
callback_refs = {}
90
self.repository.lock_read()
91
self.progress = bzrlib.ui.ui_factory.nested_progress_bar()
93
self.progress.update('check', 0, 4)
108
with self.repository.lock_read(), ui.ui_factory.nested_progress_bar() as self.progress:
109
self.progress.update(gettext('check'), 0, 4)
94
110
if self.check_repo:
95
self.progress.update('checking revisions', 0)
111
self.progress.update(gettext('checking revisions'), 0)
96
112
self.check_revisions()
97
self.progress.update('checking commit contents', 1)
113
self.progress.update(gettext('checking commit contents'), 1)
98
114
self.repository._check_inventories(self)
99
self.progress.update('checking file graphs', 2)
115
self.progress.update(gettext('checking file graphs'), 2)
100
116
# check_weaves is done after the revision scan so that
101
117
# revision index is known to be valid.
102
118
self.check_weaves()
103
self.progress.update('checking branches and trees', 3)
119
self.progress.update(gettext('checking branches and trees'), 3)
104
120
if callback_refs:
105
121
repo = self.repository
106
122
# calculate all refs, and callback the objects requesting them.
164
177
def check_revisions(self):
165
178
"""Scan revisions, checking data directly available as we go."""
166
revision_iterator = self.repository._iter_revisions(None)
179
revision_iterator = self.repository.iter_revisions(
180
self.repository.all_revision_ids())
167
181
revision_iterator = self._check_revisions(revision_iterator)
168
182
# We read the all revisions here:
169
183
# - doing this allows later code to depend on the revision index.
170
184
# - we can fill out existence flags at this point
171
185
# - we can read the revision inventory sha at this point
172
186
# - we can check properties and serialisers etc.
173
if not self.repository.revision_graph_can_have_wrong_parents():
187
if not self.repository._format.revision_graph_can_have_wrong_parents:
174
188
# The check against the index isn't needed.
175
189
self.revs_with_bad_parents_in_index = None
176
190
for thing in revision_iterator:
187
201
result.report_results(verbose)
189
203
def _report_repo_results(self, verbose):
190
note('checked repository %s format %s',
204
note(gettext('checked repository {0} format {1}').format(
191
205
self.repository.user_url,
192
self.repository._format)
193
note('%6d revisions', self.checked_rev_cnt)
194
note('%6d file-ids', len(self.checked_weaves))
206
self.repository._format))
207
note(gettext('%6d revisions'), self.checked_rev_cnt)
208
note(gettext('%6d file-ids'), len(self.checked_weaves))
196
note('%6d unreferenced text versions',
210
note(gettext('%6d unreferenced text versions'),
197
211
len(self.unreferenced_versions))
198
212
if verbose and len(self.unreferenced_versions):
199
213
for file_id, revision_id in self.unreferenced_versions:
200
note('unreferenced version: {%s} in %s', revision_id,
214
note(gettext('unreferenced version: {{{0}}} in {1}').format(revision_id,
202
216
if self.missing_inventory_sha_cnt:
203
note('%6d revisions are missing inventory_sha1',
217
note(gettext('%6d revisions are missing inventory_sha1'),
204
218
self.missing_inventory_sha_cnt)
205
219
if self.missing_revision_cnt:
206
note('%6d revisions are mentioned but not present',
220
note(gettext('%6d revisions are mentioned but not present'),
207
221
self.missing_revision_cnt)
208
222
if len(self.ghosts):
209
note('%6d ghost revisions', len(self.ghosts))
223
note(gettext('%6d ghost revisions'), len(self.ghosts))
211
225
for ghost in self.ghosts:
212
226
note(' %s', ghost)
213
227
if len(self.missing_parent_links):
214
note('%6d revisions missing parents in ancestry',
228
note(gettext('%6d revisions missing parents in ancestry'),
215
229
len(self.missing_parent_links))
217
for link, linkers in self.missing_parent_links.items():
218
note(' %s should be in the ancestry for:', link)
231
for link, linkers in viewitems(self.missing_parent_links):
232
note(gettext(' %s should be in the ancestry for:'), link)
219
233
for linker in linkers:
220
234
note(' * %s', linker)
221
235
if len(self.inconsistent_parents):
222
note('%6d inconsistent parents', len(self.inconsistent_parents))
236
note(gettext('%6d inconsistent parents'), len(self.inconsistent_parents))
224
238
for info in self.inconsistent_parents:
225
239
revision_id, file_id, found_parents, correct_parents = info
226
note(' * %s version %s has parents %r '
228
% (file_id, revision_id, found_parents,
240
note(gettext(' * {0} version {1} has parents {2!r} '
241
'but should have {3!r}').format(
242
file_id, revision_id, found_parents,
229
243
correct_parents))
230
244
if self.revs_with_bad_parents_in_index:
231
note('%6d revisions have incorrect parents in the revision index',
246
'%6d revisions have incorrect parents in the revision index'),
232
247
len(self.revs_with_bad_parents_in_index))
234
249
for item in self.revs_with_bad_parents_in_index:
235
250
revision_id, index_parents, actual_parents = item
237
' %s has wrong parents in index: '
239
revision_id, index_parents, actual_parents)
252
' {0} has wrong parents in index: '
253
'{1!r} should be {2!r}').format(
254
revision_id, index_parents, actual_parents))
240
255
for item in self._report_items:
304
316
text_key_references=self.text_key_references,
305
317
ancestors=self.ancestors)
306
318
storebar.update('file-graph', 1)
307
result = weave_checker.check_file_version_parents(
319
wrongs, unused_versions = weave_checker.check_file_version_parents(
308
320
self.repository.texts)
309
321
self.checked_weaves = weave_checker.file_ids
310
bad_parents, unused_versions = result
311
bad_parents = bad_parents.items()
312
for text_key, (stored_parents, correct_parents) in bad_parents:
322
for text_key, (stored_parents, correct_parents) in viewitems(wrongs):
313
323
# XXX not ready for id join/split operations.
314
324
weave_id = text_key[0]
315
325
revision_id = text_key[-1]
328
338
self.text_key_references[key] = True
331
@deprecated_function(deprecated_in((1,6,0)))
332
def check(branch, verbose):
333
"""Run consistency checks on a branch.
335
Results are reported through logging.
337
Deprecated in 1.6. Please use check_dwim instead.
339
:raise BzrCheckError: if there's a consistency error.
341
check_branch(branch, verbose)
344
@deprecated_function(deprecated_in((1,16,0)))
345
def check_branch(branch, verbose):
346
"""Run consistency checks on a branch.
348
Results are reported through logging.
350
:raise BzrCheckError: if there's a consistency error.
355
for ref in branch._get_check_refs():
356
needed_refs.setdefault(ref, []).append(branch)
357
result = branch.repository.check([branch.last_revision()], needed_refs)
358
branch_result = result.other_results[0]
361
branch_result.report_results(verbose)
364
341
def scan_branch(branch, needed_refs, to_unlock):
365
342
"""Scan a branch for refs.
442
419
scan_branch(branch, needed_refs, to_unlock)
443
420
if do_branch and not branches:
444
note("No branch found at specified location.")
421
note(gettext("No branch found at specified location."))
445
422
if do_tree and base_tree is None and not saw_tree:
446
note("No working tree found at specified location.")
423
note(gettext("No working tree found at specified location."))
447
424
if do_repo or do_branch or do_tree:
449
note("Checking repository at '%s'."
426
note(gettext("Checking repository at '%s'.")
450
427
% (repo.user_url,))
451
428
result = repo.check(None, callback_refs=needed_refs,
452
429
check_repo=do_repo)
453
430
result.report_results(verbose)
456
note("No working tree found at specified location.")
433
note(gettext("No working tree found at specified location."))
458
note("No branch found at specified location.")
435
note(gettext("No branch found at specified location."))
460
note("No repository found at specified location.")
437
note(gettext("No repository found at specified location."))
462
439
for thing in to_unlock: