27
28
# TODO: Get every revision in the revision-store even if they're not
28
29
# referenced by history and make sure they're all valid.
30
# TODO: Perhaps have a way to record errors other than by raising exceptions;
31
# would perhaps be enough to accumulate exception objects in a list without
32
# raising them. If there's more than one exception it'd be good to see them
35
from bzrlib.errors import BzrCheckError
37
from bzrlib.trace import note
32
from bzrlib.trace import note, warning
33
from bzrlib.osutils import rename, sha_string, fingerprint_file
34
from bzrlib.trace import mutter
35
from bzrlib.errors import BzrCheckError, NoSuchRevision
36
from bzrlib.inventory import ROOT_ID
39
39
class Check(object):
40
"""Check a repository"""
42
# The Check object interacts with InventoryEntry.check, etc.
44
def __init__(self, repository):
45
self.repository = repository
42
def __init__(self, branch):
44
self.repository = branch.repository
46
45
self.checked_text_cnt = 0
47
46
self.checked_rev_cnt = 0
50
49
self.missing_parent_links = {}
51
50
self.missing_inventory_sha_cnt = 0
52
51
self.missing_revision_cnt = 0
53
# maps (file-id, version) -> sha1; used by InventoryFile._check
52
# maps (file-id, version) -> sha1
54
53
self.checked_texts = {}
55
54
self.checked_weaves = {}
58
self.repository.lock_read()
59
self.progress = bzrlib.ui.ui_factory.nested_progress_bar()
57
self.branch.lock_read()
58
self.progress = bzrlib.ui.ui_factory.progress_bar()
61
60
self.progress.update('retrieving inventory', 0, 0)
62
61
# do not put in init, as it should be done with progess,
63
62
# and inside the lock.
64
self.inventory_weave = self.repository.get_inventory_weave()
63
self.inventory_weave = self.branch.repository.get_inventory_weave()
64
self.history = self.branch.revision_history()
65
if not len(self.history):
65
68
self.plan_revisions()
67
70
self.check_weaves()
73
76
self.check_one_rev(rev_id)
75
self.progress.finished()
76
self.repository.unlock()
78
81
def plan_revisions(self):
79
repository = self.repository
80
self.planned_revisions = set(repository.all_revision_ids())
82
inventoried = set(self.inventory_weave.versions())
82
repository = self.branch.repository
83
if not repository.revision_store.listable():
84
self.planned_revisions = repository.get_ancestry(self.history[-1])
85
self.planned_revisions.remove(None)
86
# FIXME progress bars should support this more nicely.
88
print ("Checking reachable history -"
89
" for a complete check use a local branch.")
92
self.planned_revisions = set(repository.revision_store)
93
inventoried = set(self.inventory_weave.names())
83
94
awol = self.planned_revisions - inventoried
85
96
raise BzrCheckError('Stored revisions missing from inventory'
87
98
self.planned_revisions = list(self.planned_revisions)
89
100
def report_results(self, verbose):
90
note('checked repository %s format %s',
91
self.repository.bzrdir.root_transport,
92
self.repository._format)
101
note('checked branch %s format %s',
93
105
note('%6d revisions', self.checked_rev_cnt)
94
106
note('%6d unique file texts', self.checked_text_cnt)
95
107
note('%6d repeated file texts', self.repeated_text_cnt)
118
130
"""Check one revision.
120
132
rev_id - the one to check
134
last_rev_id - the previous one on the mainline, if any.
122
rev = self.repository.get_revision(rev_id)
137
# mutter(' revision {%s}', rev_id)
140
rev_history_position = self.history.index(rev_id)
142
rev_history_position = None
144
if rev_history_position:
145
rev = branch.repository.get_revision(rev_id)
146
if rev_history_position > 0:
147
last_rev_id = self.history[rev_history_position - 1]
149
rev = branch.repository.get_revision(rev_id)
124
151
if rev.revision_id != rev_id:
125
152
raise BzrCheckError('wrong internal revision id in revision {%s}'
128
for parent in rev.parent_ids:
129
if not parent in self.planned_revisions:
130
missing_links = self.missing_parent_links.get(parent, [])
131
missing_links.append(rev_id)
132
self.missing_parent_links[parent] = missing_links
133
# list based so somewhat slow,
134
# TODO have a planned_revisions list and set.
135
if self.repository.has_revision(parent):
136
missing_ancestry = self.repository.get_ancestry(parent)
137
for missing in missing_ancestry:
138
if (missing is not None
139
and missing not in self.planned_revisions):
140
self.planned_revisions.append(missing)
155
# check the previous history entry is a parent of this entry
157
if last_rev_id is not None:
158
for parent_id in rev.parent_ids:
159
if parent_id == last_rev_id:
142
self.ghosts.append(rev_id)
162
raise BzrCheckError("previous revision {%s} not listed among "
164
% (last_rev_id, rev_id))
165
for parent in rev.parent_ids:
166
if not parent in self.planned_revisions:
167
missing_links = self.missing_parent_links.get(parent, [])
168
missing_links.append(rev_id)
169
self.missing_parent_links[parent] = missing_links
170
# list based so somewhat slow,
171
# TODO have a planned_revisions list and set.
172
if self.branch.has_revision(parent):
173
missing_ancestry = self.repository.get_ancestry(parent)
174
for missing in missing_ancestry:
175
if (missing is not None
176
and missing not in self.planned_revisions):
177
self.planned_revisions.append(missing)
179
self.ghosts.append(rev_id)
181
raise BzrCheckError("revision {%s} has no parents listed "
182
"but preceded by {%s}"
183
% (rev_id, last_rev_id))
144
185
if rev.inventory_sha1:
145
inv_sha1 = self.repository.get_inventory_sha1(rev_id)
186
inv_sha1 = branch.repository.get_inventory_sha1(rev_id)
146
187
if inv_sha1 != rev.inventory_sha1:
147
188
raise BzrCheckError('Inventory sha1 hash doesn\'t match'
148
189
' value in revision {%s}' % rev_id)
191
missing_inventory_sha_cnt += 1
192
mutter("no inventory_sha1 on revision {%s}", rev_id)
149
193
self._check_revision_tree(rev_id)
150
194
self.checked_rev_cnt += 1
157
if self.repository.weave_store.listable():
158
weave_ids = list(self.repository.weave_store)
201
if self.branch.repository.weave_store.listable():
202
weave_ids = list(self.branch.repository.weave_store)
159
203
n_weaves = len(weave_ids)
160
204
self.progress.update('checking weave', 0, n_weaves)
161
205
self.inventory_weave.check(progress_bar=self.progress)
162
206
for i, weave_id in enumerate(weave_ids):
163
207
self.progress.update('checking weave', i, n_weaves)
164
w = self.repository.weave_store.get_weave(weave_id,
165
self.repository.get_transaction())
208
w = self.branch.repository.weave_store.get_weave(weave_id,
209
self.branch.repository.get_transaction())
166
210
# No progress here, because it looks ugly.
168
212
self.checked_weaves[weave_id] = True
170
214
def _check_revision_tree(self, rev_id):
171
tree = self.repository.revision_tree(rev_id)
215
tree = self.branch.repository.revision_tree(rev_id)
172
216
inv = tree.inventory
174
218
for file_id in inv:
192
236
def check(branch, verbose):
193
"""Run consistency checks on a branch.
195
Results are reported through logging.
197
:raise BzrCheckError: if there's a consistency error.
201
branch_result = branch.check()
202
repo_result = branch.repository.check([branch.last_revision()])
205
branch_result.report_results(verbose)
206
repo_result.report_results(verbose)
237
"""Run consistency checks on a branch."""
238
checker = Check(branch)
240
checker.report_results(verbose)