32
32
# raising them. If there's more than one exception it'd be good to see them
35
from bzrlib.errors import BzrCheckError
36
from bzrlib.trace import note, warning
37
from bzrlib.osutils import rename, sha_string, fingerprint_file
38
from bzrlib.trace import mutter
39
from bzrlib.errors import BzrCheckError, NoSuchRevision
40
from bzrlib.inventory import ROOT_ID
37
from bzrlib.trace import note
43
39
class Check(object):
40
"""Check a repository"""
46
42
# The Check object interacts with InventoryEntry.check, etc.
48
def __init__(self, branch):
50
self.repository = branch.repository
44
def __init__(self, repository):
45
self.repository = repository
51
46
self.checked_text_cnt = 0
52
47
self.checked_rev_cnt = 0
60
55
self.checked_weaves = {}
63
self.branch.lock_read()
58
self.repository.lock_read()
64
59
self.progress = bzrlib.ui.ui_factory.nested_progress_bar()
66
61
self.progress.update('retrieving inventory', 0, 0)
67
62
# do not put in init, as it should be done with progess,
68
63
# and inside the lock.
69
self.inventory_weave = self.branch.repository.get_inventory_weave()
70
self.history = self.branch.revision_history()
71
if not len(self.history):
64
self.inventory_weave = self.repository.get_inventory_weave()
74
65
self.plan_revisions()
76
67
self.check_weaves()
96
87
self.planned_revisions = list(self.planned_revisions)
98
89
def report_results(self, verbose):
99
note('checked branch %s format %s',
90
note('checked repository %s format %s',
91
self.repository.bzrdir.root_transport,
92
self.repository._format)
103
93
note('%6d revisions', self.checked_rev_cnt)
104
94
note('%6d unique file texts', self.checked_text_cnt)
105
95
note('%6d repeated file texts', self.repeated_text_cnt)
128
118
"""Check one revision.
130
120
rev_id - the one to check
132
last_rev_id - the previous one on the mainline, if any.
135
# mutter(' revision {%s}', rev_id)
138
rev_history_position = self.history.index(rev_id)
140
rev_history_position = None
142
if rev_history_position:
143
rev = branch.repository.get_revision(rev_id)
144
if rev_history_position > 0:
145
last_rev_id = self.history[rev_history_position - 1]
147
rev = branch.repository.get_revision(rev_id)
122
rev = self.repository.get_revision(rev_id)
149
124
if rev.revision_id != rev_id:
150
125
raise BzrCheckError('wrong internal revision id in revision {%s}'
153
# check the previous history entry is a parent of this entry
155
if last_rev_id is not None:
156
for parent_id in rev.parent_ids:
157
if parent_id == last_rev_id:
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)
160
raise BzrCheckError("previous revision {%s} not listed among "
162
% (last_rev_id, rev_id))
163
for parent in rev.parent_ids:
164
if not parent in self.planned_revisions:
165
missing_links = self.missing_parent_links.get(parent, [])
166
missing_links.append(rev_id)
167
self.missing_parent_links[parent] = missing_links
168
# list based so somewhat slow,
169
# TODO have a planned_revisions list and set.
170
if self.branch.repository.has_revision(parent):
171
missing_ancestry = self.repository.get_ancestry(parent)
172
for missing in missing_ancestry:
173
if (missing is not None
174
and missing not in self.planned_revisions):
175
self.planned_revisions.append(missing)
177
self.ghosts.append(rev_id)
179
raise BzrCheckError("revision {%s} has no parents listed "
180
"but preceded by {%s}"
181
% (rev_id, last_rev_id))
142
self.ghosts.append(rev_id)
183
144
if rev.inventory_sha1:
184
inv_sha1 = branch.repository.get_inventory_sha1(rev_id)
145
inv_sha1 = self.repository.get_inventory_sha1(rev_id)
185
146
if inv_sha1 != rev.inventory_sha1:
186
147
raise BzrCheckError('Inventory sha1 hash doesn\'t match'
187
148
' value in revision {%s}' % rev_id)
189
self.missing_inventory_sha_cnt += 1
190
mutter("no inventory_sha1 on revision {%s}", rev_id)
191
149
self._check_revision_tree(rev_id)
192
150
self.checked_rev_cnt += 1
199
if self.branch.repository.weave_store.listable():
200
weave_ids = list(self.branch.repository.weave_store)
157
if self.repository.weave_store.listable():
158
weave_ids = list(self.repository.weave_store)
201
159
n_weaves = len(weave_ids)
202
160
self.progress.update('checking weave', 0, n_weaves)
203
161
self.inventory_weave.check(progress_bar=self.progress)
204
162
for i, weave_id in enumerate(weave_ids):
205
163
self.progress.update('checking weave', i, n_weaves)
206
w = self.branch.repository.weave_store.get_weave(weave_id,
207
self.branch.repository.get_transaction())
164
w = self.repository.weave_store.get_weave(weave_id,
165
self.repository.get_transaction())
208
166
# No progress here, because it looks ugly.
210
168
self.checked_weaves[weave_id] = True
212
170
def _check_revision_tree(self, rev_id):
213
tree = self.branch.repository.revision_tree(rev_id)
171
tree = self.repository.revision_tree(rev_id)
214
172
inv = tree.inventory
216
174
for file_id in inv:
234
192
def check(branch, verbose):
235
"""Run consistency checks on a branch."""
236
checker = Check(branch)
238
checker.report_results(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)