1
# Copyright (C) 2004, 2005 by Martin Pool
2
# Copyright (C) 2005 by Canonical Ltd
1
# Copyright (C) 2005, 2006 by Canonical Ltd
4
3
# This program is free software; you can redistribute it and/or modify
5
4
# it under the terms of the GNU General Public License as published by
25
24
# 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
30
36
from bzrlib.trace import note, warning
32
38
from bzrlib.trace import mutter
33
39
from bzrlib.errors import BzrCheckError, NoSuchRevision
34
40
from bzrlib.inventory import ROOT_ID
35
from bzrlib.branch import gen_root_id
38
43
class Check(object):
39
44
"""Check a branch"""
46
# The Check object interacts with InventoryEntry.check, etc.
41
48
def __init__(self, branch):
42
49
self.branch = branch
50
self.repository = branch.repository
43
51
self.checked_text_cnt = 0
44
52
self.checked_rev_cnt = 0
47
55
self.missing_parent_links = {}
48
56
self.missing_inventory_sha_cnt = 0
49
57
self.missing_revision_cnt = 0
50
# maps (file-id, version) -> sha1
58
# maps (file-id, version) -> sha1; used by InventoryFile._check
51
59
self.checked_texts = {}
60
self.checked_weaves = {}
54
63
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()
56
70
self.history = self.branch.revision_history()
57
71
if not len(self.history):
58
72
# 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()
65
77
while revno < len(self.planned_revisions):
66
78
rev_id = self.planned_revisions[revno]
67
79
self.progress.update('checking revision', revno,
68
80
len(self.planned_revisions))
70
82
self.check_one_rev(rev_id)
84
self.progress.finished()
73
85
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)
75
98
def report_results(self, verbose):
76
note('checked branch %s format %d',
99
note('checked branch %s format %s',
78
self.branch._branch_format)
80
103
note('%6d revisions', self.checked_rev_cnt)
81
104
note('%6d unique file texts', self.checked_text_cnt)
82
105
note('%6d repeated file texts', self.repeated_text_cnt)
106
note('%6d weaves', len(self.checked_weaves))
83
107
if self.missing_inventory_sha_cnt:
84
108
note('%6d revisions are missing inventory_sha1',
85
109
self.missing_inventory_sha_cnt)
116
140
rev_history_position = None
117
141
last_rev_id = None
118
142
if rev_history_position:
119
rev = branch.get_revision(rev_id)
143
rev = branch.repository.get_revision(rev_id)
120
144
if rev_history_position > 0:
121
145
last_rev_id = self.history[rev_history_position - 1]
123
rev = branch.get_revision(rev_id)
147
rev = branch.repository.get_revision(rev_id)
125
149
if rev.revision_id != rev_id:
126
150
raise BzrCheckError('wrong internal revision id in revision {%s}'
129
153
# check the previous history entry is a parent of this entry
130
154
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)))
137
155
if last_rev_id is not None:
138
156
for parent_id in rev.parent_ids:
139
157
if parent_id == last_rev_id:
147
165
missing_links = self.missing_parent_links.get(parent, [])
148
166
missing_links.append(rev_id)
149
167
self.missing_parent_links[parent] = missing_links
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)
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)
153
172
for missing in missing_ancestry:
154
173
if (missing is not None
155
174
and missing not in self.planned_revisions):
162
181
% (rev_id, last_rev_id))
164
183
if rev.inventory_sha1:
165
inv_sha1 = branch.get_inventory_sha1(rev_id)
184
inv_sha1 = branch.repository.get_inventory_sha1(rev_id)
166
185
if inv_sha1 != rev.inventory_sha1:
167
186
raise BzrCheckError('Inventory sha1 hash doesn\'t match'
168
187
' value in revision {%s}' % rev_id)
170
189
missing_inventory_sha_cnt += 1
171
mutter("no inventory_sha1 on revision {%s}" % rev_id)
190
mutter("no inventory_sha1 on revision {%s}", rev_id)
172
191
self._check_revision_tree(rev_id)
173
192
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
175
212
def _check_revision_tree(self, rev_id):
176
tree = self.branch.revision_tree(rev_id)
213
tree = self.branch.repository.revision_tree(rev_id)
177
214
inv = tree.inventory
179
216
for file_id in inv: