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):
88
106
if callback_refs is None:
89
107
callback_refs = {}
90
108
self.repository.lock_read()
91
self.progress = bzrlib.ui.ui_factory.nested_progress_bar()
109
self.progress = ui.ui_factory.nested_progress_bar()
93
self.progress.update('check', 0, 4)
111
self.progress.update(gettext('check'), 0, 4)
94
112
if self.check_repo:
95
self.progress.update('checking revisions', 0)
113
self.progress.update(gettext('checking revisions'), 0)
96
114
self.check_revisions()
97
self.progress.update('checking commit contents', 1)
115
self.progress.update(gettext('checking commit contents'), 1)
98
116
self.repository._check_inventories(self)
99
self.progress.update('checking file graphs', 2)
117
self.progress.update(gettext('checking file graphs'), 2)
100
118
# check_weaves is done after the revision scan so that
101
119
# revision index is known to be valid.
102
120
self.check_weaves()
103
self.progress.update('checking branches and trees', 3)
121
self.progress.update(gettext('checking branches and trees'), 3)
104
122
if callback_refs:
105
123
repo = self.repository
106
124
# calculate all refs, and callback the objects requesting them.
187
205
result.report_results(verbose)
189
207
def _report_repo_results(self, verbose):
190
note('checked repository %s format %s',
208
note(gettext('checked repository {0} format {1}').format(
191
209
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))
210
self.repository._format))
211
note(gettext('%6d revisions'), self.checked_rev_cnt)
212
note(gettext('%6d file-ids'), len(self.checked_weaves))
196
note('%6d unreferenced text versions',
214
note(gettext('%6d unreferenced text versions'),
197
215
len(self.unreferenced_versions))
198
216
if verbose and len(self.unreferenced_versions):
199
217
for file_id, revision_id in self.unreferenced_versions:
200
note('unreferenced version: {%s} in %s', revision_id,
218
note(gettext('unreferenced version: {{{0}}} in {1}').format(revision_id,
202
220
if self.missing_inventory_sha_cnt:
203
note('%6d revisions are missing inventory_sha1',
221
note(gettext('%6d revisions are missing inventory_sha1'),
204
222
self.missing_inventory_sha_cnt)
205
223
if self.missing_revision_cnt:
206
note('%6d revisions are mentioned but not present',
224
note(gettext('%6d revisions are mentioned but not present'),
207
225
self.missing_revision_cnt)
208
226
if len(self.ghosts):
209
note('%6d ghost revisions', len(self.ghosts))
227
note(gettext('%6d ghost revisions'), len(self.ghosts))
211
229
for ghost in self.ghosts:
212
230
note(' %s', ghost)
213
231
if len(self.missing_parent_links):
214
note('%6d revisions missing parents in ancestry',
232
note(gettext('%6d revisions missing parents in ancestry'),
215
233
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)
235
for link, linkers in viewitems(self.missing_parent_links):
236
note(gettext(' %s should be in the ancestry for:'), link)
219
237
for linker in linkers:
220
238
note(' * %s', linker)
221
239
if len(self.inconsistent_parents):
222
note('%6d inconsistent parents', len(self.inconsistent_parents))
240
note(gettext('%6d inconsistent parents'), len(self.inconsistent_parents))
224
242
for info in self.inconsistent_parents:
225
243
revision_id, file_id, found_parents, correct_parents = info
226
note(' * %s version %s has parents %r '
228
% (file_id, revision_id, found_parents,
244
note(gettext(' * {0} version {1} has parents {2!r} '
245
'but should have {3!r}').format(
246
file_id, revision_id, found_parents,
229
247
correct_parents))
230
248
if self.revs_with_bad_parents_in_index:
231
note('%6d revisions have incorrect parents in the revision index',
250
'%6d revisions have incorrect parents in the revision index'),
232
251
len(self.revs_with_bad_parents_in_index))
234
253
for item in self.revs_with_bad_parents_in_index:
235
254
revision_id, index_parents, actual_parents = item
237
' %s has wrong parents in index: '
239
revision_id, index_parents, actual_parents)
256
' {0} has wrong parents in index: '
257
'{1!r} should be {2!r}').format(
258
revision_id, index_parents, actual_parents))
240
259
for item in self._report_items:
304
323
text_key_references=self.text_key_references,
305
324
ancestors=self.ancestors)
306
325
storebar.update('file-graph', 1)
307
result = weave_checker.check_file_version_parents(
326
wrongs, unused_versions = weave_checker.check_file_version_parents(
308
327
self.repository.texts)
309
328
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:
329
for text_key, (stored_parents, correct_parents) in viewitems(wrongs):
313
330
# XXX not ready for id join/split operations.
314
331
weave_id = text_key[0]
315
332
revision_id = text_key[-1]
328
345
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
348
def scan_branch(branch, needed_refs, to_unlock):
365
349
"""Scan a branch for refs.
442
426
scan_branch(branch, needed_refs, to_unlock)
443
427
if do_branch and not branches:
444
note("No branch found at specified location.")
428
note(gettext("No branch found at specified location."))
445
429
if do_tree and base_tree is None and not saw_tree:
446
note("No working tree found at specified location.")
430
note(gettext("No working tree found at specified location."))
447
431
if do_repo or do_branch or do_tree:
449
note("Checking repository at '%s'."
433
note(gettext("Checking repository at '%s'.")
450
434
% (repo.user_url,))
451
435
result = repo.check(None, callback_refs=needed_refs,
452
436
check_repo=do_repo)
453
437
result.report_results(verbose)
456
note("No working tree found at specified location.")
440
note(gettext("No working tree found at specified location."))
458
note("No branch found at specified location.")
442
note(gettext("No branch found at specified location."))
460
note("No repository found at specified location.")
444
note(gettext("No repository found at specified location."))
462
446
for thing in to_unlock: