1
# Copyright (C) 2005, 2006 by Canonical Ltd
1
# Copyright (C) 2004, 2005 by Martin Pool
2
# Copyright (C) 2005 by Canonical Ltd
3
4
# This program is free software; you can redistribute it and/or modify
4
5
# it under the terms of the GNU General Public License as published by
24
25
# TODO: Check revision, inventory and entry objects have all
27
# TODO: Get every revision in the revision-store even if they're not
28
# 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
36
30
from bzrlib.trace import note, warning
38
32
from bzrlib.trace import mutter
39
33
from bzrlib.errors import BzrCheckError, NoSuchRevision
40
34
from bzrlib.inventory import ROOT_ID
35
from bzrlib.branch import gen_root_id
43
38
class Check(object):
44
39
"""Check a branch"""
46
# The Check object interacts with InventoryEntry.check, etc.
48
41
def __init__(self, branch):
49
42
self.branch = branch
50
self.repository = branch.repository
51
43
self.checked_text_cnt = 0
52
44
self.checked_rev_cnt = 0
55
47
self.missing_parent_links = {}
56
48
self.missing_inventory_sha_cnt = 0
57
49
self.missing_revision_cnt = 0
58
# maps (file-id, version) -> sha1; used by InventoryFile._check
50
# maps (file-id, version) -> sha1
59
51
self.checked_texts = {}
60
self.checked_weaves = {}
63
54
self.branch.lock_read()
64
self.progress = bzrlib.ui.ui_factory.nested_progress_bar()
66
self.progress.update('retrieving inventory', 0, 0)
67
# do not put in init, as it should be done with progess,
68
# and inside the lock.
69
self.inventory_weave = self.branch.repository.get_inventory_weave()
70
56
self.history = self.branch.revision_history()
71
57
if not len(self.history):
72
58
# nothing to see here
60
self.planned_revisions = self.branch.get_ancestry(self.history[-1])
61
self.planned_revisions.remove(None)
64
self.progress = bzrlib.ui.ui_factory.progress_bar()
77
65
while revno < len(self.planned_revisions):
78
66
rev_id = self.planned_revisions[revno]
79
67
self.progress.update('checking revision', revno,
80
68
len(self.planned_revisions))
82
70
self.check_one_rev(rev_id)
84
self.progress.finished()
85
73
self.branch.unlock()
87
def plan_revisions(self):
88
repository = self.branch.repository
89
self.planned_revisions = set(repository.all_revision_ids())
91
inventoried = set(self.inventory_weave.versions())
92
awol = self.planned_revisions - inventoried
94
raise BzrCheckError('Stored revisions missing from inventory'
95
'{%s}' % ','.join([f for f in awol]))
96
self.planned_revisions = list(self.planned_revisions)
98
75
def report_results(self, verbose):
99
note('checked branch %s format %s',
76
note('checked branch %s format %d',
78
self.branch._branch_format)
103
80
note('%6d revisions', self.checked_rev_cnt)
104
81
note('%6d unique file texts', self.checked_text_cnt)
105
82
note('%6d repeated file texts', self.repeated_text_cnt)
106
note('%6d weaves', len(self.checked_weaves))
107
83
if self.missing_inventory_sha_cnt:
108
84
note('%6d revisions are missing inventory_sha1',
109
85
self.missing_inventory_sha_cnt)
140
116
rev_history_position = None
141
117
last_rev_id = None
142
118
if rev_history_position:
143
rev = branch.repository.get_revision(rev_id)
119
rev = branch.get_revision(rev_id)
144
120
if rev_history_position > 0:
145
121
last_rev_id = self.history[rev_history_position - 1]
147
rev = branch.repository.get_revision(rev_id)
123
rev = branch.get_revision(rev_id)
149
125
if rev.revision_id != rev_id:
150
126
raise BzrCheckError('wrong internal revision id in revision {%s}'
153
129
# check the previous history entry is a parent of this entry
154
130
if rev.parent_ids:
131
if last_rev_id is None and rev_history_position is not None:
132
# what if the start is a ghost ? i.e. conceptually the
134
raise BzrCheckError("revision {%s} has %d parents, but is the "
135
"start of the branch"
136
% (rev_id, len(rev.parent_ids)))
155
137
if last_rev_id is not None:
156
138
for parent_id in rev.parent_ids:
157
139
if parent_id == last_rev_id:
165
147
missing_links = self.missing_parent_links.get(parent, [])
166
148
missing_links.append(rev_id)
167
149
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)
150
# list based so slow, TODO have a planned_revisions list and set.
151
if self.branch.has_revision(parent):
152
missing_ancestry = self.branch.get_ancestry(parent)
172
153
for missing in missing_ancestry:
173
154
if (missing is not None
174
155
and missing not in self.planned_revisions):
181
162
% (rev_id, last_rev_id))
183
164
if rev.inventory_sha1:
184
inv_sha1 = branch.repository.get_inventory_sha1(rev_id)
165
inv_sha1 = branch.get_inventory_sha1(rev_id)
185
166
if inv_sha1 != rev.inventory_sha1:
186
167
raise BzrCheckError('Inventory sha1 hash doesn\'t match'
187
168
' value in revision {%s}' % rev_id)
189
170
missing_inventory_sha_cnt += 1
190
mutter("no inventory_sha1 on revision {%s}", rev_id)
171
mutter("no inventory_sha1 on revision {%s}" % rev_id)
191
172
self._check_revision_tree(rev_id)
192
173
self.checked_rev_cnt += 1
194
def check_weaves(self):
195
"""Check all the weaves we can get our hands on.
199
if self.branch.repository.weave_store.listable():
200
weave_ids = list(self.branch.repository.weave_store)
201
n_weaves = len(weave_ids)
202
self.progress.update('checking weave', 0, n_weaves)
203
self.inventory_weave.check(progress_bar=self.progress)
204
for i, weave_id in enumerate(weave_ids):
205
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())
208
# No progress here, because it looks ugly.
210
self.checked_weaves[weave_id] = True
212
175
def _check_revision_tree(self, rev_id):
213
tree = self.branch.repository.revision_tree(rev_id)
176
tree = self.branch.revision_tree(rev_id)
214
177
inv = tree.inventory
216
179
for file_id in inv: