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.
164
182
def check_revisions(self):
165
183
"""Scan revisions, checking data directly available as we go."""
166
revision_iterator = self.repository._iter_revisions(None)
184
revision_iterator = self.repository.iter_revisions(
185
self.repository.all_revision_ids())
167
186
revision_iterator = self._check_revisions(revision_iterator)
168
187
# We read the all revisions here:
169
188
# - doing this allows later code to depend on the revision index.
170
189
# - we can fill out existence flags at this point
171
190
# - we can read the revision inventory sha at this point
172
191
# - we can check properties and serialisers etc.
173
if not self.repository.revision_graph_can_have_wrong_parents():
192
if not self.repository._format.revision_graph_can_have_wrong_parents:
174
193
# The check against the index isn't needed.
175
194
self.revs_with_bad_parents_in_index = None
176
195
for thing in revision_iterator:
187
206
result.report_results(verbose)
189
208
def _report_repo_results(self, verbose):
190
note('checked repository %s format %s',
209
note(gettext('checked repository {0} format {1}').format(
191
210
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))
211
self.repository._format))
212
note(gettext('%6d revisions'), self.checked_rev_cnt)
213
note(gettext('%6d file-ids'), len(self.checked_weaves))
196
note('%6d unreferenced text versions',
215
note(gettext('%6d unreferenced text versions'),
197
216
len(self.unreferenced_versions))
198
217
if verbose and len(self.unreferenced_versions):
199
218
for file_id, revision_id in self.unreferenced_versions:
200
note('unreferenced version: {%s} in %s', revision_id,
219
note(gettext('unreferenced version: {{{0}}} in {1}').format(revision_id,
202
221
if self.missing_inventory_sha_cnt:
203
note('%6d revisions are missing inventory_sha1',
222
note(gettext('%6d revisions are missing inventory_sha1'),
204
223
self.missing_inventory_sha_cnt)
205
224
if self.missing_revision_cnt:
206
note('%6d revisions are mentioned but not present',
225
note(gettext('%6d revisions are mentioned but not present'),
207
226
self.missing_revision_cnt)
208
227
if len(self.ghosts):
209
note('%6d ghost revisions', len(self.ghosts))
228
note(gettext('%6d ghost revisions'), len(self.ghosts))
211
230
for ghost in self.ghosts:
212
231
note(' %s', ghost)
213
232
if len(self.missing_parent_links):
214
note('%6d revisions missing parents in ancestry',
233
note(gettext('%6d revisions missing parents in ancestry'),
215
234
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)
236
for link, linkers in viewitems(self.missing_parent_links):
237
note(gettext(' %s should be in the ancestry for:'), link)
219
238
for linker in linkers:
220
239
note(' * %s', linker)
221
240
if len(self.inconsistent_parents):
222
note('%6d inconsistent parents', len(self.inconsistent_parents))
241
note(gettext('%6d inconsistent parents'), len(self.inconsistent_parents))
224
243
for info in self.inconsistent_parents:
225
244
revision_id, file_id, found_parents, correct_parents = info
226
note(' * %s version %s has parents %r '
228
% (file_id, revision_id, found_parents,
245
note(gettext(' * {0} version {1} has parents {2!r} '
246
'but should have {3!r}').format(
247
file_id, revision_id, found_parents,
229
248
correct_parents))
230
249
if self.revs_with_bad_parents_in_index:
231
note('%6d revisions have incorrect parents in the revision index',
251
'%6d revisions have incorrect parents in the revision index'),
232
252
len(self.revs_with_bad_parents_in_index))
234
254
for item in self.revs_with_bad_parents_in_index:
235
255
revision_id, index_parents, actual_parents = item
237
' %s has wrong parents in index: '
239
revision_id, index_parents, actual_parents)
257
' {0} has wrong parents in index: '
258
'{1!r} should be {2!r}').format(
259
revision_id, index_parents, actual_parents))
240
260
for item in self._report_items:
304
324
text_key_references=self.text_key_references,
305
325
ancestors=self.ancestors)
306
326
storebar.update('file-graph', 1)
307
result = weave_checker.check_file_version_parents(
327
wrongs, unused_versions = weave_checker.check_file_version_parents(
308
328
self.repository.texts)
309
329
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:
330
for text_key, (stored_parents, correct_parents) in viewitems(wrongs):
313
331
# XXX not ready for id join/split operations.
314
332
weave_id = text_key[0]
315
333
revision_id = text_key[-1]
328
346
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
349
def scan_branch(branch, needed_refs, to_unlock):
365
350
"""Scan a branch for refs.
442
427
scan_branch(branch, needed_refs, to_unlock)
443
428
if do_branch and not branches:
444
note("No branch found at specified location.")
429
note(gettext("No branch found at specified location."))
445
430
if do_tree and base_tree is None and not saw_tree:
446
note("No working tree found at specified location.")
431
note(gettext("No working tree found at specified location."))
447
432
if do_repo or do_branch or do_tree:
449
note("Checking repository at '%s'."
434
note(gettext("Checking repository at '%s'.")
450
435
% (repo.user_url,))
451
436
result = repo.check(None, callback_refs=needed_refs,
452
437
check_repo=do_repo)
453
438
result.report_results(verbose)
456
note("No working tree found at specified location.")
441
note(gettext("No working tree found at specified location."))
458
note("No branch found at specified location.")
443
note(gettext("No branch found at specified location."))
460
note("No repository found at specified location.")
445
note(gettext("No repository found at specified location."))
462
447
for thing in to_unlock: