~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2005, 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1 by mbp at sourcefrog
import from baz patch-364
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1 by mbp at sourcefrog
import from baz patch-364
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1 by mbp at sourcefrog
import from baz patch-364
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1 by mbp at sourcefrog
import from baz patch-364
16
1335 by Martin Pool
doc
17
# TODO: Check ancestries are correct for every revision: includes
18
# every committed so far, and in a reasonable order.
19
1347 by Martin Pool
- refactor check code into method object
20
# TODO: Also check non-mainline revisions mentioned as parents.
21
22
# TODO: Check for extra files in the control directory.
23
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
24
# TODO: Check revision, inventory and entry objects have all
1348 by Martin Pool
- more refactoring of check code
25
# required fields.
26
1185.16.101 by mbp at sourcefrog
todo
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.
1347 by Martin Pool
- refactor check code into method object
29
1616.1.5 by Martin Pool
Cleanup and document some check code
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
4332.3.2 by Robert Collins
Extract repository access in WorkingTree._check to be data driven, adding a new _get_check_refs method to support this.
35
"""Checking of bzr objects.
36
37
check_refs is a concept used for optimising check. Objects that depend on other
38
objects (e.g. tree on repository) can list the objects they would be requesting
39
so that when the dependent object is checked, matches can be pulled out and
40
evaluated in-line rather than re-reading the same data many times.
41
check_refs are tuples (kind, value). Currently defined kinds are:
4332.3.5 by Robert Collins
Add Branch._get_check_refs.
42
* 'trees', where value is a revid and the looked up objects are revision trees.
43
* 'lefthand-distance', where value is a revid and the looked up objects are the
44
  distance along the lefthand path to NULL for that revid.
45
* 'revision-existence', where value is a revid, and the result is True or False
46
  indicating that the revision was found/not found.
4332.3.2 by Robert Collins
Extract repository access in WorkingTree._check to be data driven, adding a new _get_check_refs method to support this.
47
"""
48
3015.3.8 by Daniel Watkins
Added _scan_for_branches.
49
from bzrlib import errors, osutils
2745.6.16 by Aaron Bentley
Update from review
50
from bzrlib import repository as _mod_repository
2745.6.47 by Andrew Bennetts
Move check_parents out of VersionedFile.
51
from bzrlib import revision
3015.3.2 by Daniel Watkins
Check.check now takes a path rather than a branch.
52
from bzrlib.branch import Branch
3015.3.40 by Daniel Watkins
Modified bzrlib.check.check_dwim to use bzrlib.bzrdir.BzrDir.open_containing_tree_branch_or_repository.
53
from bzrlib.bzrdir import BzrDir
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
54
from bzrlib.errors import BzrCheckError
3015.3.3 by Daniel Watkins
Added _check_repository.
55
from bzrlib.repository import Repository
4332.3.15 by Robert Collins
Keep an ancestors dict in check rather than recreating one multiple times.
56
from bzrlib.revision import NULL_REVISION
3015.3.59 by Daniel Watkins
Further tweaks as requested on-list.
57
from bzrlib.symbol_versioning import deprecated_function, deprecated_in
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
58
from bzrlib.trace import note
1104 by Martin Pool
- Add a simple UIFactory
59
import bzrlib.ui
3015.3.11 by Daniel Watkins
Move WT checking from builtins to check.
60
from bzrlib.workingtree import WorkingTree
1104 by Martin Pool
- Add a simple UIFactory
61
1347 by Martin Pool
- refactor check code into method object
62
class Check(object):
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
63
    """Check a repository"""
1449 by Robert Collins
teach check about ghosts
64
1616.1.5 by Martin Pool
Cleanup and document some check code
65
    # The Check object interacts with InventoryEntry.check, etc.
66
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
67
    def __init__(self, repository, check_repo=True):
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
68
        self.repository = repository
1383 by Martin Pool
- untabify only
69
        self.checked_rev_cnt = 0
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
70
        self.ghosts = set()
1449 by Robert Collins
teach check about ghosts
71
        self.missing_parent_links = {}
1348 by Martin Pool
- more refactoring of check code
72
        self.missing_inventory_sha_cnt = 0
73
        self.missing_revision_cnt = 0
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
74
        self.checked_weaves = set()
2988.1.8 by Robert Collins
Change check and reconcile to use the new _generate_text_key_index rather
75
        self.unreferenced_versions = set()
2745.6.33 by Andrew Bennetts
Add VersionedFile.check_parents, and use it instead of find_bad_ancestors in reconcile.
76
        self.inconsistent_parents = []
4145.2.1 by Ian Clatworthy
faster check
77
        self.rich_roots = repository.supports_rich_root()
78
        self.text_key_references = {}
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
79
        self.check_repo = check_repo
80
        self.other_results = []
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
81
        # Plain text lines to include in the report
82
        self._report_items = []
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
83
        # Keys we are looking for; may be large and need spilling to disk.
84
        # key->(type(revision/inventory/text/signature/map), sha1, first-referer)
85
        self.pending_keys = {}
4332.3.15 by Robert Collins
Keep an ancestors dict in check rather than recreating one multiple times.
86
        # Ancestors map for all of revisions being checked; while large helper
87
        # functions we call would create it anyway, so better to have once and
88
        # keep.
89
        self.ancestors = {}
676 by Martin Pool
- lock branch while checking
90
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
91
    def check(self, callback_refs=None, check_repo=True):
92
        if callback_refs is None:
93
            callback_refs = {}
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
94
        self.repository.lock_read()
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
95
        self.progress = bzrlib.ui.ui_factory.nested_progress_bar()
1449 by Robert Collins
teach check about ghosts
96
        try:
4332.3.20 by Robert Collins
Cleanup progress reporting for check to go left to right once and only once.
97
            self.progress.update('check', 0, 4)
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
98
            if self.check_repo:
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
99
                self.progress.update('checking revisions', 0)
100
                self.check_revisions()
4332.3.29 by Robert Collins
Better descriptions.
101
                self.progress.update('checking commit contents', 1)
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
102
                self.repository._check_inventories(self)
4332.3.29 by Robert Collins
Better descriptions.
103
                self.progress.update('checking file graphs', 2)
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
104
                # check_weaves is done after the revision scan so that
105
                # revision index is known to be valid.
106
                self.check_weaves()
4332.3.20 by Robert Collins
Cleanup progress reporting for check to go left to right once and only once.
107
            self.progress.update('checking branches and trees', 3)
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
108
            if callback_refs:
109
                repo = self.repository
110
                # calculate all refs, and callback the objects requesting them.
111
                refs = {}
112
                wanting_items = set()
113
                # Current crude version calculates everything and calls
114
                # everything at once. Doing a queue and popping as things are
115
                # satisfied would be cheaper on memory [but few people have
116
                # huge numbers of working trees today. TODO: fix before
117
                # landing].
118
                distances = set()
119
                existences = set()
120
                for ref, wantlist in callback_refs.iteritems():
121
                    wanting_items.update(wantlist)
122
                    kind, value = ref
123
                    if kind == 'trees':
124
                        refs[ref] = repo.revision_tree(value)
125
                    elif kind == 'lefthand-distance':
126
                        distances.add(value)
127
                    elif kind == 'revision-existence':
128
                        existences.add(value)
129
                    else:
130
                        raise AssertionError(
131
                            'unknown ref kind for ref %s' % ref)
132
                node_distances = repo.get_graph().find_lefthand_distances(distances)
133
                for key, distance in node_distances.iteritems():
134
                    refs[('lefthand-distance', key)] = distance
135
                    if key in existences and distance > 0:
136
                        refs[('revision-existence', key)] = True
137
                        existences.remove(key)
138
                parent_map = repo.get_graph().get_parent_map(existences)
139
                for key in parent_map:
140
                    refs[('revision-existence', key)] = True
141
                    existences.remove(key)
142
                for key in existences:
143
                    refs[('revision-existence', key)] = False
144
                for item in wanting_items:
145
                    if isinstance(item, WorkingTree):
146
                        item._check(refs)
147
                    if isinstance(item, Branch):
148
                        self.other_results.append(item.check(refs))
1185.35.34 by Aaron Bentley
Made bzr check for stored revisions missing from ancestry
149
        finally:
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
150
            self.progress.finished()
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
151
            self.repository.unlock()
1449 by Robert Collins
teach check about ghosts
152
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
153
    def _check_revisions(self, revisions_iterator):
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
154
        """Check revision objects by decorating a generator.
155
156
        :param revisions_iterator: An iterator of(revid, Revision-or-None).
157
        :return: A generator of the contents of revisions_iterator.
158
        """
159
        self.planned_revisions = set()
160
        for revid, revision in revisions_iterator:
161
            yield revid, revision
162
            self._check_one_rev(revid, revision)
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
163
        # Flatten the revisions we found to guarantee consistent later
164
        # iteration.
4332.3.21 by Robert Collins
Clearer code.
165
        self.planned_revisions = list(self.planned_revisions)
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
166
        # TODO: extract digital signatures as items to callback on too.
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
167
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
168
    def check_revisions(self):
169
        """Scan revisions, checking data directly available as we go."""
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
170
        revision_iterator = self.repository._iter_revisions(None)
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
171
        revision_iterator = self._check_revisions(revision_iterator)
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
172
        # We read the all revisions here:
173
        # - doing this allows later code to depend on the revision index.
174
        # - we can fill out existence flags at this point
175
        # - we can read the revision inventory sha at this point
176
        # - we can check properties and serialisers etc.
2819.2.4 by Andrew Bennetts
Add a 'revision_graph_can_have_wrong_parents' method to repository.
177
        if not self.repository.revision_graph_can_have_wrong_parents():
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
178
            # The check against the index isn't needed.
2819.2.3 by Andrew Bennetts
Add test that repo.check will report on wrong parents in the revision graph.
179
            self.revs_with_bad_parents_in_index = None
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
180
            for thing in revision_iterator:
181
                pass
182
        else:
183
            bad_revisions = self.repository._find_inconsistent_revision_parents(
184
                revision_iterator)
185
            self.revs_with_bad_parents_in_index = list(bad_revisions)
2819.2.3 by Andrew Bennetts
Add test that repo.check will report on wrong parents in the revision graph.
186
1449 by Robert Collins
teach check about ghosts
187
    def report_results(self, verbose):
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
188
        if self.check_repo:
189
            self._report_repo_results(verbose)
190
        for result in self.other_results:
191
            result.report_results(verbose)
192
193
    def _report_repo_results(self, verbose):
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
194
        note('checked repository %s format %s',
195
             self.repository.bzrdir.root_transport,
196
             self.repository._format)
1365 by Martin Pool
- try to avoid checking texts repeatedly
197
        note('%6d revisions', self.checked_rev_cnt)
2745.6.47 by Andrew Bennetts
Move check_parents out of VersionedFile.
198
        note('%6d file-ids', len(self.checked_weaves))
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
199
        if verbose:
200
            note('%6d unreferenced text versions',
201
                len(self.unreferenced_versions))
202
        if verbose and len(self.unreferenced_versions):
203
                for file_id, revision_id in self.unreferenced_versions:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
204
                    note('unreferenced version: {%s} in %s', revision_id,
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
205
                        file_id)
1348 by Martin Pool
- more refactoring of check code
206
        if self.missing_inventory_sha_cnt:
1449 by Robert Collins
teach check about ghosts
207
            note('%6d revisions are missing inventory_sha1',
1383 by Martin Pool
- untabify only
208
                 self.missing_inventory_sha_cnt)
1348 by Martin Pool
- more refactoring of check code
209
        if self.missing_revision_cnt:
1449 by Robert Collins
teach check about ghosts
210
            note('%6d revisions are mentioned but not present',
1383 by Martin Pool
- untabify only
211
                 self.missing_revision_cnt)
1449 by Robert Collins
teach check about ghosts
212
        if len(self.ghosts):
213
            note('%6d ghost revisions', len(self.ghosts))
214
            if verbose:
215
                for ghost in self.ghosts:
216
                    note('      %s', ghost)
217
        if len(self.missing_parent_links):
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
218
            note('%6d revisions missing parents in ancestry',
1449 by Robert Collins
teach check about ghosts
219
                 len(self.missing_parent_links))
220
            if verbose:
221
                for link, linkers in self.missing_parent_links.items():
222
                    note('      %s should be in the ancestry for:', link)
223
                    for linker in linkers:
224
                        note('       * %s', linker)
2745.6.39 by Andrew Bennetts
Use scenario in test_check too, and make check actually report inconsistent parents to the end user.
225
        if len(self.inconsistent_parents):
226
            note('%6d inconsistent parents', len(self.inconsistent_parents))
227
            if verbose:
228
                for info in self.inconsistent_parents:
229
                    revision_id, file_id, found_parents, correct_parents = info
230
                    note('      * %s version %s has parents %r '
231
                         'but should have %r'
232
                         % (file_id, revision_id, found_parents,
233
                             correct_parents))
2819.2.3 by Andrew Bennetts
Add test that repo.check will report on wrong parents in the revision graph.
234
        if self.revs_with_bad_parents_in_index:
235
            note('%6d revisions have incorrect parents in the revision index',
236
                 len(self.revs_with_bad_parents_in_index))
237
            if verbose:
238
                for item in self.revs_with_bad_parents_in_index:
239
                    revision_id, index_parents, actual_parents = item
240
                    note(
241
                        '       %s has wrong parents in index: '
242
                        '%r should be %r',
243
                        revision_id, index_parents, actual_parents)
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
244
        for item in self._report_items:
245
            note(item)
246
247
    def _check_one_rev(self, rev_id, rev):
248
        """Cross-check one revision.
249
250
        :param rev_id: A revision id to check.
251
        :param rev: A revision or None to indicate a missing revision.
1383 by Martin Pool
- untabify only
252
        """
253
        if rev.revision_id != rev_id:
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
254
            self._report_items.append(
255
                'Mismatched internal revid {%s} and index revid {%s}' % (
256
                rev.revision_id, rev_id))
257
            rev_id = rev.revision_id
258
        # Check this revision tree etc, and count as seen when we encounter a
259
        # reference to it.
260
        self.planned_revisions.add(rev_id)
261
        # It is not a ghost
262
        self.ghosts.discard(rev_id)
263
        # Count all parents as ghosts if we haven't seen them yet.
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
264
        for parent in rev.parent_ids:
265
            if not parent in self.planned_revisions:
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
266
                self.ghosts.add(parent)
267
        
4332.3.15 by Robert Collins
Keep an ancestors dict in check rather than recreating one multiple times.
268
        self.ancestors[rev_id] = tuple(rev.parent_ids) or (NULL_REVISION,)
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
269
        self.add_pending_item(rev_id, ('inventories', rev_id), 'inventory',
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
270
            rev.inventory_sha1)
1362 by Martin Pool
- keep track of number of checked revisions
271
        self.checked_rev_cnt += 1
1349 by Martin Pool
- more refactoring of check code
272
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
273
    def add_pending_item(self, referer, key, kind, sha1):
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
274
        """Add a reference to a sha1 to be cross checked against a key.
275
276
        :param referer: The referer that expects key to have sha1.
277
        :param key: A storage key e.g. ('texts', 'foo@bar-20040504-1234')
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
278
        :param kind: revision/inventory/text/map/signature
279
        :param sha1: A hex sha1 or None if no sha1 is known.
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
280
        """
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
281
        existing = self.pending_keys.get(key)
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
282
        if existing:
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
283
            if sha1 != existing[1]:
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
284
                self._report_items.append('Multiple expected sha1s for %s. {%s}'
285
                    ' expects {%s}, {%s} expects {%s}', (
286
                    key, referer, sha1, existing[1], existing[0]))
287
        else:
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
288
            self.pending_keys[key] = (kind, sha1, referer)
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
289
1185.50.28 by John Arbash Meinel
Lots of updates for 'bzr check'
290
    def check_weaves(self):
291
        """Check all the weaves we can get our hands on.
292
        """
293
        weave_ids = []
4332.3.20 by Robert Collins
Cleanup progress reporting for check to go left to right once and only once.
294
        storebar = bzrlib.ui.ui_factory.nested_progress_bar()
295
        try:
296
            self._check_weaves(storebar)
297
        finally:
298
            storebar.finished()
299
300
    def _check_weaves(self, storebar):
4332.3.28 by Robert Collins
Start checking file texts in a single pass.
301
        storebar.update('text-index', 0, 2)
302
        if self.repository._format.fast_deltas:
303
            # We haven't considered every fileid instance so far.
4332.3.30 by Robert Collins
Fix check_weaves.
304
            weave_checker = self.repository._get_versioned_file_checker(
305
                ancestors=self.ancestors)
4332.3.28 by Robert Collins
Start checking file texts in a single pass.
306
        else:
4332.3.30 by Robert Collins
Fix check_weaves.
307
            weave_checker = self.repository._get_versioned_file_checker(
4332.3.28 by Robert Collins
Start checking file texts in a single pass.
308
                text_key_references=self.text_key_references,
4332.3.30 by Robert Collins
Fix check_weaves.
309
                ancestors=self.ancestors)
310
        storebar.update('file-graph', 1)
311
        result = weave_checker.check_file_version_parents(
312
            self.repository.texts)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
313
        self.checked_weaves = weave_checker.file_ids
314
        bad_parents, unused_versions = result
315
        bad_parents = bad_parents.items()
316
        for text_key, (stored_parents, correct_parents) in bad_parents:
317
            # XXX not ready for id join/split operations.
318
            weave_id = text_key[0]
319
            revision_id = text_key[-1]
320
            weave_parents = tuple([parent[-1] for parent in stored_parents])
321
            correct_parents = tuple([parent[-1] for parent in correct_parents])
322
            self.inconsistent_parents.append(
323
                (revision_id, weave_id, weave_parents, correct_parents))
324
        self.unreferenced_versions.update(unused_versions)
1185.50.28 by John Arbash Meinel
Lots of updates for 'bzr check'
325
4145.2.1 by Ian Clatworthy
faster check
326
    def _add_entry_to_text_key_references(self, inv, entry):
4332.3.28 by Robert Collins
Start checking file texts in a single pass.
327
        if not self.rich_roots and entry.name == '':
4145.2.1 by Ian Clatworthy
faster check
328
            return
329
        key = (entry.file_id, entry.revision)
330
        self.text_key_references.setdefault(key, False)
331
        if entry.revision == inv.revision_id:
332
            self.text_key_references[key] = True
1349 by Martin Pool
- more refactoring of check code
333
1347 by Martin Pool
- refactor check code into method object
334
3015.3.59 by Daniel Watkins
Further tweaks as requested on-list.
335
@deprecated_function(deprecated_in((1,6,0)))
3015.3.35 by Daniel Watkins
Reintroduced bzrlib.check.check() with a deprecation warning.
336
def check(branch, verbose):
337
    """Run consistency checks on a branch.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
338
3015.3.35 by Daniel Watkins
Reintroduced bzrlib.check.check() with a deprecation warning.
339
    Results are reported through logging.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
340
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
341
    Deprecated in 1.6.  Please use check_dwim instead.
3015.3.37 by Daniel Watkins
Added deprecation comment to docstring.
342
3015.3.35 by Daniel Watkins
Reintroduced bzrlib.check.check() with a deprecation warning.
343
    :raise BzrCheckError: if there's a consistency error.
344
    """
345
    check_branch(branch, verbose)
346
347
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
348
@deprecated_function(deprecated_in((1,16,0)))
3015.3.7 by Daniel Watkins
Fixed failing tests.
349
def check_branch(branch, verbose):
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
350
    """Run consistency checks on a branch.
3015.3.35 by Daniel Watkins
Reintroduced bzrlib.check.check() with a deprecation warning.
351
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
352
    Results are reported through logging.
3015.3.35 by Daniel Watkins
Reintroduced bzrlib.check.check() with a deprecation warning.
353
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
354
    :raise BzrCheckError: if there's a consistency error.
355
    """
356
    branch.lock_read()
357
    try:
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
358
        needed_refs = {}
359
        for ref in branch._get_check_refs():
360
            needed_refs.setdefault(ref, []).append(branch)
361
        result = branch.repository.check([branch.last_revision()], needed_refs)
362
        branch_result = result.other_results[0]
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
363
    finally:
364
        branch.unlock()
365
    branch_result.report_results(verbose)
2745.6.47 by Andrew Bennetts
Move check_parents out of VersionedFile.
366
367
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
368
def scan_branch(branch, needed_refs, to_unlock):
369
    """Scan a branch for refs.
370
371
    :param branch:  The branch to schedule for checking.
372
    :param needed_refs: Refs we are accumulating.
373
    :param to_unlock: The unlock list accumulating.
374
    """
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
375
    note("Checking branch at '%s'." % (branch.base,))
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
376
    branch.lock_read()
377
    to_unlock.append(branch)
378
    branch_refs = branch._get_check_refs()
379
    for ref in branch_refs:
380
        reflist = needed_refs.setdefault(ref, [])
381
        reflist.append(branch)
382
383
384
def scan_tree(base_tree, tree, needed_refs, to_unlock):
385
    """Scan a tree for refs.
386
387
    :param base_tree: The original tree check opened, used to detect duplicate
388
        tree checks.
389
    :param tree:  The tree to schedule for checking.
390
    :param needed_refs: Refs we are accumulating.
391
    :param to_unlock: The unlock list accumulating.
392
    """
393
    if base_tree is not None and tree.basedir == base_tree.basedir:
394
        return
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
395
    note("Checking working tree at '%s'." % (tree.basedir,))
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
396
    tree.lock_read()
397
    to_unlock.append(tree)
398
    tree_refs = tree._get_check_refs()
399
    for ref in tree_refs:
400
        reflist = needed_refs.setdefault(ref, [])
401
        reflist.append(tree)
402
403
3015.4.5 by Daniel Watkins
Each option selects only the specific thing to be checked.
404
def check_dwim(path, verbose, do_branch=False, do_repo=False, do_tree=False):
4332.3.35 by Robert Collins
Fix failing tests.
405
    """Check multiple objects.
406
407
    If errors occur they are accumulated and reported as far as possible, and
408
    an exception raised at the end of the process.
409
    """
3015.4.16 by Daniel Watkins
Added implementation of error reporting when objects are missing.
410
    try:
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
411
        base_tree, branch, repo, relpath = \
3015.4.11 by Daniel Watkins
Fixed long line.
412
                        BzrDir.open_containing_tree_branch_or_repository(path)
3015.4.16 by Daniel Watkins
Added implementation of error reporting when objects are missing.
413
    except errors.NotBranchError:
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
414
        base_tree = branch = repo = None
3015.3.23 by Daniel Watkins
Abstracted discovery of elements away.
415
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
416
    to_unlock = []
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
417
    needed_refs= {}
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
418
    try:
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
419
        if base_tree is not None:
420
            # If the tree is a lightweight checkout we won't see it in
421
            # repo.find_branches - add now.
422
            if do_tree:
423
                scan_tree(None, base_tree, needed_refs, to_unlock)
424
            branch = base_tree.branch
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
425
        if branch is not None:
426
            # We have a branch
427
            if repo is None:
428
                # The branch is in a shared repository
429
                repo = branch.repository
430
        if repo is not None:
431
            repo.lock_read()
432
            to_unlock.append(repo)
433
            branches = repo.find_branches(using=True)
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
434
            saw_tree = False
435
            if do_branch or do_tree:
436
                for branch in branches:
437
                    if do_tree:
438
                        try:
439
                            tree = branch.bzrdir.open_workingtree()
440
                            saw_tree = True
441
                        except (errors.NotLocalUrl, errors.NoWorkingTree):
442
                            pass
443
                        else:
444
                            scan_tree(base_tree, tree, needed_refs, to_unlock)
445
                    if do_branch:
446
                        scan_branch(branch, needed_refs, to_unlock)
447
            if do_branch and not branches:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
448
                note("No branch found at specified location.")
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
449
            if do_tree and base_tree is None and not saw_tree:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
450
                note("No working tree found at specified location.")
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
451
            if do_repo or do_branch or do_tree:
452
                if do_repo:
453
                    note("Checking repository at '%s'."
454
                         % (repo.bzrdir.root_transport.base,))
455
                result = repo.check(None, callback_refs=needed_refs,
456
                    check_repo=do_repo)
3015.4.3 by Daniel Watkins
Implemented CLI options.
457
                result.report_results(verbose)
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
458
        else:
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
459
            if do_tree:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
460
                note("No working tree found at specified location.")
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
461
            if do_branch:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
462
                note("No branch found at specified location.")
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
463
            if do_repo:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
464
                note("No repository found at specified location.")
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
465
    finally:
466
        for thing in to_unlock:
467
            thing.unlock()