/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/check.py

  • Committer: John Arbash Meinel
  • Date: 2008-08-18 22:34:21 UTC
  • mto: (3606.5.6 1.6)
  • mto: This revision was merged to the branch mainline in revision 3641.
  • Revision ID: john@arbash-meinel.com-20080818223421-todjny24vj4faj4t
Add tests for the fetching behavior.

The proper parameter passed is 'unordered' add an assert for it, and
fix callers that were passing 'unsorted' instead.
Add tests that we make the right get_record_stream call based
on the value of _fetch_uses_deltas.
Fix the fetch request for signatures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
# TODO: Check ancestries are correct for every revision: includes
 
18
# every committed so far, and in a reasonable order.
 
19
 
 
20
# TODO: Also check non-mainline revisions mentioned as parents.
 
21
 
 
22
# TODO: Check for extra files in the control directory.
 
23
 
 
24
# TODO: Check revision, inventory and entry objects have all 
 
25
# required fields.
 
26
 
 
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.
 
29
 
 
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
 
33
# all.
 
34
 
 
35
from bzrlib import errors, osutils
 
36
from bzrlib import repository as _mod_repository
 
37
from bzrlib import revision
 
38
from bzrlib.branch import Branch
 
39
from bzrlib.bzrdir import BzrDir
 
40
from bzrlib.errors import BzrCheckError
 
41
from bzrlib.repository import Repository
 
42
from bzrlib.symbol_versioning import deprecated_function, deprecated_in
 
43
from bzrlib.trace import log_error, note
 
44
import bzrlib.ui
 
45
from bzrlib.workingtree import WorkingTree
 
46
 
 
47
class Check(object):
 
48
    """Check a repository"""
 
49
 
 
50
    # The Check object interacts with InventoryEntry.check, etc.
 
51
 
 
52
    def __init__(self, repository):
 
53
        self.repository = repository
 
54
        self.checked_text_cnt = 0
 
55
        self.checked_rev_cnt = 0
 
56
        self.ghosts = []
 
57
        self.repeated_text_cnt = 0
 
58
        self.missing_parent_links = {}
 
59
        self.missing_inventory_sha_cnt = 0
 
60
        self.missing_revision_cnt = 0
 
61
        # maps (file-id, version) -> sha1; used by InventoryFile._check
 
62
        self.checked_texts = {}
 
63
        self.checked_weaves = set()
 
64
        self.unreferenced_versions = set()
 
65
        self.inconsistent_parents = []
 
66
 
 
67
    def check(self):
 
68
        self.repository.lock_read()
 
69
        self.progress = bzrlib.ui.ui_factory.nested_progress_bar()
 
70
        try:
 
71
            self.progress.update('retrieving inventory', 0, 2)
 
72
            # do not put in init, as it should be done with progess,
 
73
            # and inside the lock.
 
74
            self.inventory_weave = self.repository.inventories
 
75
            self.progress.update('checking revision graph', 1)
 
76
            self.check_revision_graph()
 
77
            self.plan_revisions()
 
78
            revno = 0
 
79
            while revno < len(self.planned_revisions):
 
80
                rev_id = self.planned_revisions[revno]
 
81
                self.progress.update('checking revision', revno,
 
82
                                     len(self.planned_revisions))
 
83
                revno += 1
 
84
                self.check_one_rev(rev_id)
 
85
            # check_weaves is done after the revision scan so that
 
86
            # revision index is known to be valid.
 
87
            self.check_weaves()
 
88
        finally:
 
89
            self.progress.finished()
 
90
            self.repository.unlock()
 
91
 
 
92
    def check_revision_graph(self):
 
93
        if not self.repository.revision_graph_can_have_wrong_parents():
 
94
            # This check is not necessary.
 
95
            self.revs_with_bad_parents_in_index = None
 
96
            return
 
97
        bad_revisions = self.repository._find_inconsistent_revision_parents()
 
98
        self.revs_with_bad_parents_in_index = list(bad_revisions)
 
99
 
 
100
    def plan_revisions(self):
 
101
        repository = self.repository
 
102
        self.planned_revisions = repository.all_revision_ids()
 
103
        self.progress.clear()
 
104
        inventoried = set(key[-1] for key in self.inventory_weave.keys())
 
105
        awol = set(self.planned_revisions) - inventoried
 
106
        if len(awol) > 0:
 
107
            raise BzrCheckError('Stored revisions missing from inventory'
 
108
                '{%s}' % ','.join([f for f in awol]))
 
109
 
 
110
    def report_results(self, verbose):
 
111
        note('checked repository %s format %s',
 
112
             self.repository.bzrdir.root_transport,
 
113
             self.repository._format)
 
114
        note('%6d revisions', self.checked_rev_cnt)
 
115
        note('%6d file-ids', len(self.checked_weaves))
 
116
        note('%6d unique file texts', self.checked_text_cnt)
 
117
        note('%6d repeated file texts', self.repeated_text_cnt)
 
118
        note('%6d unreferenced text versions',
 
119
             len(self.unreferenced_versions))
 
120
        if self.missing_inventory_sha_cnt:
 
121
            note('%6d revisions are missing inventory_sha1',
 
122
                 self.missing_inventory_sha_cnt)
 
123
        if self.missing_revision_cnt:
 
124
            note('%6d revisions are mentioned but not present',
 
125
                 self.missing_revision_cnt)
 
126
        if len(self.ghosts):
 
127
            note('%6d ghost revisions', len(self.ghosts))
 
128
            if verbose:
 
129
                for ghost in self.ghosts:
 
130
                    note('      %s', ghost)
 
131
        if len(self.missing_parent_links):
 
132
            note('%6d revisions missing parents in ancestry',
 
133
                 len(self.missing_parent_links))
 
134
            if verbose:
 
135
                for link, linkers in self.missing_parent_links.items():
 
136
                    note('      %s should be in the ancestry for:', link)
 
137
                    for linker in linkers:
 
138
                        note('       * %s', linker)
 
139
            if verbose:
 
140
                for file_id, revision_id in self.unreferenced_versions:
 
141
                    log_error('unreferenced version: {%s} in %s', revision_id,
 
142
                        file_id)
 
143
        if len(self.inconsistent_parents):
 
144
            note('%6d inconsistent parents', len(self.inconsistent_parents))
 
145
            if verbose:
 
146
                for info in self.inconsistent_parents:
 
147
                    revision_id, file_id, found_parents, correct_parents = info
 
148
                    note('      * %s version %s has parents %r '
 
149
                         'but should have %r'
 
150
                         % (file_id, revision_id, found_parents,
 
151
                             correct_parents))
 
152
        if self.revs_with_bad_parents_in_index:
 
153
            note('%6d revisions have incorrect parents in the revision index',
 
154
                 len(self.revs_with_bad_parents_in_index))
 
155
            if verbose:
 
156
                for item in self.revs_with_bad_parents_in_index:
 
157
                    revision_id, index_parents, actual_parents = item
 
158
                    note(
 
159
                        '       %s has wrong parents in index: '
 
160
                        '%r should be %r',
 
161
                        revision_id, index_parents, actual_parents)
 
162
 
 
163
    def check_one_rev(self, rev_id):
 
164
        """Check one revision.
 
165
 
 
166
        rev_id - the one to check
 
167
        """
 
168
        rev = self.repository.get_revision(rev_id)
 
169
                
 
170
        if rev.revision_id != rev_id:
 
171
            raise BzrCheckError('wrong internal revision id in revision {%s}'
 
172
                                % rev_id)
 
173
 
 
174
        for parent in rev.parent_ids:
 
175
            if not parent in self.planned_revisions:
 
176
                missing_links = self.missing_parent_links.get(parent, [])
 
177
                missing_links.append(rev_id)
 
178
                self.missing_parent_links[parent] = missing_links
 
179
                # list based so somewhat slow,
 
180
                # TODO have a planned_revisions list and set.
 
181
                if self.repository.has_revision(parent):
 
182
                    missing_ancestry = self.repository.get_ancestry(parent)
 
183
                    for missing in missing_ancestry:
 
184
                        if (missing is not None 
 
185
                            and missing not in self.planned_revisions):
 
186
                            self.planned_revisions.append(missing)
 
187
                else:
 
188
                    self.ghosts.append(rev_id)
 
189
 
 
190
        if rev.inventory_sha1:
 
191
            inv_sha1 = self.repository.get_inventory_sha1(rev_id)
 
192
            if inv_sha1 != rev.inventory_sha1:
 
193
                raise BzrCheckError('Inventory sha1 hash doesn\'t match'
 
194
                    ' value in revision {%s}' % rev_id)
 
195
        self._check_revision_tree(rev_id)
 
196
        self.checked_rev_cnt += 1
 
197
 
 
198
    def check_weaves(self):
 
199
        """Check all the weaves we can get our hands on.
 
200
        """
 
201
        weave_ids = []
 
202
        self.progress.update('checking inventory', 0, 2)
 
203
        self.inventory_weave.check(progress_bar=self.progress)
 
204
        self.progress.update('checking text storage', 1, 2)
 
205
        self.repository.texts.check(progress_bar=self.progress)
 
206
        weave_checker = self.repository._get_versioned_file_checker()
 
207
        result = weave_checker.check_file_version_parents(
 
208
            self.repository.texts, progress_bar=self.progress)
 
209
        self.checked_weaves = weave_checker.file_ids
 
210
        bad_parents, unused_versions = result
 
211
        bad_parents = bad_parents.items()
 
212
        for text_key, (stored_parents, correct_parents) in bad_parents:
 
213
            # XXX not ready for id join/split operations.
 
214
            weave_id = text_key[0]
 
215
            revision_id = text_key[-1]
 
216
            weave_parents = tuple([parent[-1] for parent in stored_parents])
 
217
            correct_parents = tuple([parent[-1] for parent in correct_parents])
 
218
            self.inconsistent_parents.append(
 
219
                (revision_id, weave_id, weave_parents, correct_parents))
 
220
        self.unreferenced_versions.update(unused_versions)
 
221
 
 
222
    def _check_revision_tree(self, rev_id):
 
223
        tree = self.repository.revision_tree(rev_id)
 
224
        inv = tree.inventory
 
225
        seen_ids = {}
 
226
        for file_id in inv:
 
227
            if file_id in seen_ids:
 
228
                raise BzrCheckError('duplicated file_id {%s} '
 
229
                                    'in inventory for revision {%s}'
 
230
                                    % (file_id, rev_id))
 
231
            seen_ids[file_id] = True
 
232
        for file_id in inv:
 
233
            ie = inv[file_id]
 
234
            ie.check(self, rev_id, inv, tree)
 
235
        seen_names = {}
 
236
        for path, ie in inv.iter_entries():
 
237
            if path in seen_names:
 
238
                raise BzrCheckError('duplicated path %s '
 
239
                                    'in inventory for revision {%s}'
 
240
                                    % (path, rev_id))
 
241
            seen_names[path] = True
 
242
 
 
243
 
 
244
@deprecated_function(deprecated_in((1,6,0)))
 
245
def check(branch, verbose):
 
246
    """Run consistency checks on a branch.
 
247
    
 
248
    Results are reported through logging.
 
249
    
 
250
    Deprecated in 1.6.  Please use check_branch instead.
 
251
 
 
252
    :raise BzrCheckError: if there's a consistency error.
 
253
    """
 
254
    check_branch(branch, verbose)
 
255
 
 
256
 
 
257
def check_branch(branch, verbose):
 
258
    """Run consistency checks on a branch.
 
259
 
 
260
    Results are reported through logging.
 
261
 
 
262
    :raise BzrCheckError: if there's a consistency error.
 
263
    """
 
264
    branch.lock_read()
 
265
    try:
 
266
        branch_result = branch.check()
 
267
    finally:
 
268
        branch.unlock()
 
269
    branch_result.report_results(verbose)
 
270
 
 
271
 
 
272
def check_dwim(path, verbose, do_branch=False, do_repo=False, do_tree=False):
 
273
    try:
 
274
        tree, branch, repo, relpath = \
 
275
                        BzrDir.open_containing_tree_branch_or_repository(path)
 
276
    except errors.NotBranchError:
 
277
        tree = branch = repo = None
 
278
 
 
279
    if do_tree:
 
280
        if tree is not None:
 
281
            note("Checking working tree at '%s'." 
 
282
                 % (tree.bzrdir.root_transport.base,))
 
283
            tree._check()
 
284
        else:
 
285
            log_error("No working tree found at specified location.")
 
286
 
 
287
    if branch is not None:
 
288
        # We have a branch
 
289
        if repo is None:
 
290
            # The branch is in a shared repository
 
291
            repo = branch.repository
 
292
        branches = [branch]
 
293
    elif repo is not None:
 
294
        branches = repo.find_branches(using=True)
 
295
 
 
296
    if repo is not None:
 
297
        repo.lock_read()
 
298
        try:
 
299
            if do_repo:
 
300
                note("Checking repository at '%s'."
 
301
                     % (repo.bzrdir.root_transport.base,))
 
302
                result = repo.check()
 
303
                result.report_results(verbose)
 
304
            if do_branch:
 
305
                if branches == []:
 
306
                    log_error("No branch found at specified location.")
 
307
                else:
 
308
                    for branch in branches:
 
309
                        note("Checking branch at '%s'."
 
310
                             % (branch.bzrdir.root_transport.base,))
 
311
                        check_branch(branch, verbose)
 
312
        finally:
 
313
            repo.unlock()
 
314
    else:
 
315
        if do_branch:
 
316
            log_error("No branch found at specified location.")
 
317
        if do_repo:
 
318
            log_error("No repository found at specified location.")