~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:
5891.1.2 by Andrew Bennetts
Fix a bunch of docstring formatting nits, making pydoctor a bit happier.
42
4332.3.5 by Robert Collins
Add Branch._get_check_refs.
43
* 'trees', where value is a revid and the looked up objects are revision trees.
44
* 'lefthand-distance', where value is a revid and the looked up objects are the
45
  distance along the lefthand path to NULL for that revid.
46
* 'revision-existence', where value is a revid, and the result is True or False
47
  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.
48
"""
49
5582.10.8 by Jelmer Vernooij
More fixes.
50
from bzrlib import (
51
    errors,
52
    ui,
53
    )
3015.3.2 by Daniel Watkins
Check.check now takes a path rather than a branch.
54
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.
55
from bzrlib.bzrdir import BzrDir
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
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
57
from bzrlib.trace import note
3015.3.11 by Daniel Watkins
Move WT checking from builtins to check.
58
from bzrlib.workingtree import WorkingTree
1104 by Martin Pool
- Add a simple UIFactory
59
5850.1.3 by Jelmer Vernooij
Add VersionedFileCheck.
60
1347 by Martin Pool
- refactor check code into method object
61
class Check(object):
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
62
    """Check a repository"""
1449 by Robert Collins
teach check about ghosts
63
5850.1.3 by Jelmer Vernooij
Add VersionedFileCheck.
64
    def __init__(self, repository, check_repo=True):
65
        self.repository = repository
66
67
    def report_results(self, verbose):
68
        raise NotImplementedError(self.report_results)
69
70
71
class VersionedFileCheck(Check):
72
    """Check a versioned file repository"""
73
1616.1.5 by Martin Pool
Cleanup and document some check code
74
    # The Check object interacts with InventoryEntry.check, etc.
75
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
76
    def __init__(self, repository, check_repo=True):
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
77
        self.repository = repository
1383 by Martin Pool
- untabify only
78
        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.
79
        self.ghosts = set()
1449 by Robert Collins
teach check about ghosts
80
        self.missing_parent_links = {}
1348 by Martin Pool
- more refactoring of check code
81
        self.missing_inventory_sha_cnt = 0
82
        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.
83
        self.checked_weaves = set()
2988.1.8 by Robert Collins
Change check and reconcile to use the new _generate_text_key_index rather
84
        self.unreferenced_versions = set()
2745.6.33 by Andrew Bennetts
Add VersionedFile.check_parents, and use it instead of find_bad_ancestors in reconcile.
85
        self.inconsistent_parents = []
4145.2.1 by Ian Clatworthy
faster check
86
        self.rich_roots = repository.supports_rich_root()
87
        self.text_key_references = {}
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
88
        self.check_repo = check_repo
89
        self.other_results = []
4332.3.17 by Robert Collins
Check revisions as we cross check the revision index, rather than in a separate pass.
90
        # Plain text lines to include in the report
91
        self._report_items = []
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
92
        # Keys we are looking for; may be large and need spilling to disk.
93
        # key->(type(revision/inventory/text/signature/map), sha1, first-referer)
94
        self.pending_keys = {}
4332.3.15 by Robert Collins
Keep an ancestors dict in check rather than recreating one multiple times.
95
        # Ancestors map for all of revisions being checked; while large helper
96
        # functions we call would create it anyway, so better to have once and
97
        # keep.
98
        self.ancestors = {}
676 by Martin Pool
- lock branch while checking
99
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
100
    def check(self, callback_refs=None, check_repo=True):
101
        if callback_refs is None:
102
            callback_refs = {}
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
103
        self.repository.lock_read()
5582.10.8 by Jelmer Vernooij
More fixes.
104
        self.progress = ui.ui_factory.nested_progress_bar()
1449 by Robert Collins
teach check about ghosts
105
        try:
4332.3.20 by Robert Collins
Cleanup progress reporting for check to go left to right once and only once.
106
            self.progress.update('check', 0, 4)
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
107
            if self.check_repo:
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
108
                self.progress.update('checking revisions', 0)
109
                self.check_revisions()
4332.3.29 by Robert Collins
Better descriptions.
110
                self.progress.update('checking commit contents', 1)
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
111
                self.repository._check_inventories(self)
4332.3.29 by Robert Collins
Better descriptions.
112
                self.progress.update('checking file graphs', 2)
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
113
                # check_weaves is done after the revision scan so that
114
                # revision index is known to be valid.
115
                self.check_weaves()
4332.3.20 by Robert Collins
Cleanup progress reporting for check to go left to right once and only once.
116
            self.progress.update('checking branches and trees', 3)
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
117
            if callback_refs:
118
                repo = self.repository
119
                # calculate all refs, and callback the objects requesting them.
120
                refs = {}
121
                wanting_items = set()
122
                # Current crude version calculates everything and calls
123
                # everything at once. Doing a queue and popping as things are
124
                # satisfied would be cheaper on memory [but few people have
125
                # huge numbers of working trees today. TODO: fix before
126
                # landing].
127
                distances = set()
128
                existences = set()
129
                for ref, wantlist in callback_refs.iteritems():
130
                    wanting_items.update(wantlist)
131
                    kind, value = ref
132
                    if kind == 'trees':
133
                        refs[ref] = repo.revision_tree(value)
134
                    elif kind == 'lefthand-distance':
135
                        distances.add(value)
136
                    elif kind == 'revision-existence':
137
                        existences.add(value)
138
                    else:
139
                        raise AssertionError(
140
                            'unknown ref kind for ref %s' % ref)
141
                node_distances = repo.get_graph().find_lefthand_distances(distances)
142
                for key, distance in node_distances.iteritems():
143
                    refs[('lefthand-distance', key)] = distance
144
                    if key in existences and distance > 0:
145
                        refs[('revision-existence', key)] = True
146
                        existences.remove(key)
147
                parent_map = repo.get_graph().get_parent_map(existences)
148
                for key in parent_map:
149
                    refs[('revision-existence', key)] = True
150
                    existences.remove(key)
151
                for key in existences:
152
                    refs[('revision-existence', key)] = False
153
                for item in wanting_items:
154
                    if isinstance(item, WorkingTree):
155
                        item._check(refs)
156
                    if isinstance(item, Branch):
157
                        self.other_results.append(item.check(refs))
1185.35.34 by Aaron Bentley
Made bzr check for stored revisions missing from ancestry
158
        finally:
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
159
            self.progress.finished()
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
160
            self.repository.unlock()
1449 by Robert Collins
teach check about ghosts
161
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
162
    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.
163
        """Check revision objects by decorating a generator.
164
165
        :param revisions_iterator: An iterator of(revid, Revision-or-None).
166
        :return: A generator of the contents of revisions_iterator.
167
        """
168
        self.planned_revisions = set()
169
        for revid, revision in revisions_iterator:
170
            yield revid, revision
171
            self._check_one_rev(revid, revision)
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
172
        # Flatten the revisions we found to guarantee consistent later
173
        # iteration.
4332.3.21 by Robert Collins
Clearer code.
174
        self.planned_revisions = list(self.planned_revisions)
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
175
        # 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.
176
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
177
    def check_revisions(self):
178
        """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.
179
        revision_iterator = self.repository._iter_revisions(None)
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
180
        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.
181
        # We read the all revisions here:
182
        # - doing this allows later code to depend on the revision index.
183
        # - we can fill out existence flags at this point
184
        # - we can read the revision inventory sha at this point
185
        # - we can check properties and serialisers etc.
5766.1.1 by Jelmer Vernooij
Make revision-graph-can-have-wrong-parents a repository format attribute rather than a repository method.
186
        if not self.repository._format.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.
187
            # 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.
188
            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.
189
            for thing in revision_iterator:
190
                pass
191
        else:
192
            bad_revisions = self.repository._find_inconsistent_revision_parents(
193
                revision_iterator)
194
            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.
195
1449 by Robert Collins
teach check about ghosts
196
    def report_results(self, verbose):
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
197
        if self.check_repo:
198
            self._report_repo_results(verbose)
199
        for result in self.other_results:
200
            result.report_results(verbose)
201
202
    def _report_repo_results(self, verbose):
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
203
        note('checked repository %s format %s',
5158.6.10 by Martin Pool
Update more code to use user_transport when it should
204
            self.repository.user_url,
205
            self.repository._format)
1365 by Martin Pool
- try to avoid checking texts repeatedly
206
        note('%6d revisions', self.checked_rev_cnt)
2745.6.47 by Andrew Bennetts
Move check_parents out of VersionedFile.
207
        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.
208
        if verbose:
209
            note('%6d unreferenced text versions',
210
                len(self.unreferenced_versions))
211
        if verbose and len(self.unreferenced_versions):
212
                for file_id, revision_id in self.unreferenced_versions:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
213
                    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.
214
                        file_id)
1348 by Martin Pool
- more refactoring of check code
215
        if self.missing_inventory_sha_cnt:
1449 by Robert Collins
teach check about ghosts
216
            note('%6d revisions are missing inventory_sha1',
1383 by Martin Pool
- untabify only
217
                 self.missing_inventory_sha_cnt)
1348 by Martin Pool
- more refactoring of check code
218
        if self.missing_revision_cnt:
1449 by Robert Collins
teach check about ghosts
219
            note('%6d revisions are mentioned but not present',
1383 by Martin Pool
- untabify only
220
                 self.missing_revision_cnt)
1449 by Robert Collins
teach check about ghosts
221
        if len(self.ghosts):
222
            note('%6d ghost revisions', len(self.ghosts))
223
            if verbose:
224
                for ghost in self.ghosts:
225
                    note('      %s', ghost)
226
        if len(self.missing_parent_links):
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
227
            note('%6d revisions missing parents in ancestry',
1449 by Robert Collins
teach check about ghosts
228
                 len(self.missing_parent_links))
229
            if verbose:
230
                for link, linkers in self.missing_parent_links.items():
231
                    note('      %s should be in the ancestry for:', link)
232
                    for linker in linkers:
233
                        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.
234
        if len(self.inconsistent_parents):
235
            note('%6d inconsistent parents', len(self.inconsistent_parents))
236
            if verbose:
237
                for info in self.inconsistent_parents:
238
                    revision_id, file_id, found_parents, correct_parents = info
239
                    note('      * %s version %s has parents %r '
240
                         'but should have %r'
241
                         % (file_id, revision_id, found_parents,
242
                             correct_parents))
2819.2.3 by Andrew Bennetts
Add test that repo.check will report on wrong parents in the revision graph.
243
        if self.revs_with_bad_parents_in_index:
244
            note('%6d revisions have incorrect parents in the revision index',
245
                 len(self.revs_with_bad_parents_in_index))
246
            if verbose:
247
                for item in self.revs_with_bad_parents_in_index:
248
                    revision_id, index_parents, actual_parents = item
249
                    note(
250
                        '       %s has wrong parents in index: '
251
                        '%r should be %r',
252
                        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.
253
        for item in self._report_items:
254
            note(item)
255
256
    def _check_one_rev(self, rev_id, rev):
257
        """Cross-check one revision.
258
259
        :param rev_id: A revision id to check.
260
        :param rev: A revision or None to indicate a missing revision.
1383 by Martin Pool
- untabify only
261
        """
262
        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.
263
            self._report_items.append(
264
                'Mismatched internal revid {%s} and index revid {%s}' % (
265
                rev.revision_id, rev_id))
266
            rev_id = rev.revision_id
267
        # Check this revision tree etc, and count as seen when we encounter a
268
        # reference to it.
269
        self.planned_revisions.add(rev_id)
270
        # It is not a ghost
271
        self.ghosts.discard(rev_id)
272
        # 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
273
        for parent in rev.parent_ids:
274
            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.
275
                self.ghosts.add(parent)
276
        
4332.3.15 by Robert Collins
Keep an ancestors dict in check rather than recreating one multiple times.
277
        self.ancestors[rev_id] = tuple(rev.parent_ids) or (NULL_REVISION,)
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
278
        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.
279
            rev.inventory_sha1)
1362 by Martin Pool
- keep track of number of checked revisions
280
        self.checked_rev_cnt += 1
1349 by Martin Pool
- more refactoring of check code
281
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
282
    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.
283
        """Add a reference to a sha1 to be cross checked against a key.
284
285
        :param referer: The referer that expects key to have sha1.
286
        :param key: A storage key e.g. ('texts', 'foo@bar-20040504-1234')
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
287
        :param kind: revision/inventory/text/map/signature
288
        :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.
289
        """
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
290
        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.
291
        if existing:
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
292
            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.
293
                self._report_items.append('Multiple expected sha1s for %s. {%s}'
294
                    ' expects {%s}, {%s} expects {%s}', (
295
                    key, referer, sha1, existing[1], existing[0]))
296
        else:
4332.3.25 by Robert Collins
Checkpointing refactoring of inventory/file checks.
297
            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.
298
1185.50.28 by John Arbash Meinel
Lots of updates for 'bzr check'
299
    def check_weaves(self):
300
        """Check all the weaves we can get our hands on.
301
        """
302
        weave_ids = []
5582.10.8 by Jelmer Vernooij
More fixes.
303
        storebar = ui.ui_factory.nested_progress_bar()
4332.3.20 by Robert Collins
Cleanup progress reporting for check to go left to right once and only once.
304
        try:
305
            self._check_weaves(storebar)
306
        finally:
307
            storebar.finished()
308
309
    def _check_weaves(self, storebar):
4332.3.28 by Robert Collins
Start checking file texts in a single pass.
310
        storebar.update('text-index', 0, 2)
311
        if self.repository._format.fast_deltas:
312
            # We haven't considered every fileid instance so far.
4332.3.30 by Robert Collins
Fix check_weaves.
313
            weave_checker = self.repository._get_versioned_file_checker(
314
                ancestors=self.ancestors)
4332.3.28 by Robert Collins
Start checking file texts in a single pass.
315
        else:
4332.3.30 by Robert Collins
Fix check_weaves.
316
            weave_checker = self.repository._get_versioned_file_checker(
4332.3.28 by Robert Collins
Start checking file texts in a single pass.
317
                text_key_references=self.text_key_references,
4332.3.30 by Robert Collins
Fix check_weaves.
318
                ancestors=self.ancestors)
319
        storebar.update('file-graph', 1)
320
        result = weave_checker.check_file_version_parents(
321
            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.
322
        self.checked_weaves = weave_checker.file_ids
323
        bad_parents, unused_versions = result
324
        bad_parents = bad_parents.items()
325
        for text_key, (stored_parents, correct_parents) in bad_parents:
326
            # XXX not ready for id join/split operations.
327
            weave_id = text_key[0]
328
            revision_id = text_key[-1]
329
            weave_parents = tuple([parent[-1] for parent in stored_parents])
330
            correct_parents = tuple([parent[-1] for parent in correct_parents])
331
            self.inconsistent_parents.append(
332
                (revision_id, weave_id, weave_parents, correct_parents))
333
        self.unreferenced_versions.update(unused_versions)
1185.50.28 by John Arbash Meinel
Lots of updates for 'bzr check'
334
4145.2.1 by Ian Clatworthy
faster check
335
    def _add_entry_to_text_key_references(self, inv, entry):
4332.3.28 by Robert Collins
Start checking file texts in a single pass.
336
        if not self.rich_roots and entry.name == '':
4145.2.1 by Ian Clatworthy
faster check
337
            return
338
        key = (entry.file_id, entry.revision)
339
        self.text_key_references.setdefault(key, False)
340
        if entry.revision == inv.revision_id:
341
            self.text_key_references[key] = True
1349 by Martin Pool
- more refactoring of check code
342
1347 by Martin Pool
- refactor check code into method object
343
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
344
def scan_branch(branch, needed_refs, to_unlock):
345
    """Scan a branch for refs.
346
347
    :param branch:  The branch to schedule for checking.
348
    :param needed_refs: Refs we are accumulating.
349
    :param to_unlock: The unlock list accumulating.
350
    """
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
351
    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.
352
    branch.lock_read()
353
    to_unlock.append(branch)
354
    branch_refs = branch._get_check_refs()
355
    for ref in branch_refs:
356
        reflist = needed_refs.setdefault(ref, [])
357
        reflist.append(branch)
358
359
360
def scan_tree(base_tree, tree, needed_refs, to_unlock):
361
    """Scan a tree for refs.
362
363
    :param base_tree: The original tree check opened, used to detect duplicate
364
        tree checks.
365
    :param tree:  The tree to schedule for checking.
366
    :param needed_refs: Refs we are accumulating.
367
    :param to_unlock: The unlock list accumulating.
368
    """
369
    if base_tree is not None and tree.basedir == base_tree.basedir:
370
        return
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
371
    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.
372
    tree.lock_read()
373
    to_unlock.append(tree)
374
    tree_refs = tree._get_check_refs()
375
    for ref in tree_refs:
376
        reflist = needed_refs.setdefault(ref, [])
377
        reflist.append(tree)
378
379
3015.4.5 by Daniel Watkins
Each option selects only the specific thing to be checked.
380
def check_dwim(path, verbose, do_branch=False, do_repo=False, do_tree=False):
4332.3.35 by Robert Collins
Fix failing tests.
381
    """Check multiple objects.
382
383
    If errors occur they are accumulated and reported as far as possible, and
384
    an exception raised at the end of the process.
385
    """
3015.4.16 by Daniel Watkins
Added implementation of error reporting when objects are missing.
386
    try:
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
387
        base_tree, branch, repo, relpath = \
3015.4.11 by Daniel Watkins
Fixed long line.
388
                        BzrDir.open_containing_tree_branch_or_repository(path)
3015.4.16 by Daniel Watkins
Added implementation of error reporting when objects are missing.
389
    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.
390
        base_tree = branch = repo = None
3015.3.23 by Daniel Watkins
Abstracted discovery of elements away.
391
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
392
    to_unlock = []
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
393
    needed_refs= {}
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
394
    try:
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
395
        if base_tree is not None:
396
            # If the tree is a lightweight checkout we won't see it in
397
            # repo.find_branches - add now.
398
            if do_tree:
399
                scan_tree(None, base_tree, needed_refs, to_unlock)
400
            branch = base_tree.branch
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
401
        if branch is not None:
402
            # We have a branch
403
            if repo is None:
404
                # The branch is in a shared repository
405
                repo = branch.repository
406
        if repo is not None:
407
            repo.lock_read()
408
            to_unlock.append(repo)
409
            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.
410
            saw_tree = False
411
            if do_branch or do_tree:
412
                for branch in branches:
413
                    if do_tree:
414
                        try:
415
                            tree = branch.bzrdir.open_workingtree()
416
                            saw_tree = True
417
                        except (errors.NotLocalUrl, errors.NoWorkingTree):
418
                            pass
419
                        else:
420
                            scan_tree(base_tree, tree, needed_refs, to_unlock)
421
                    if do_branch:
422
                        scan_branch(branch, needed_refs, to_unlock)
423
            if do_branch and not branches:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
424
                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.
425
            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
426
                note("No working tree found at specified location.")
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
427
            if do_repo or do_branch or do_tree:
428
                if do_repo:
429
                    note("Checking repository at '%s'."
5158.6.9 by Martin Pool
Simplify various code to use user_url
430
                         % (repo.user_url,))
4332.3.11 by Robert Collins
Move tree and back callbacks into the repository check core.
431
                result = repo.check(None, callback_refs=needed_refs,
432
                    check_repo=do_repo)
3015.4.3 by Daniel Watkins
Implemented CLI options.
433
                result.report_results(verbose)
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
434
        else:
4332.3.10 by Robert Collins
Invert control of check so that trees and branches are checked by calling back into them.
435
            if do_tree:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
436
                note("No working tree found at specified location.")
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
437
            if do_branch:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
438
                note("No branch found at specified location.")
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
439
            if do_repo:
4695.5.4 by Martin Pool
Remove uses of trace.log_error in check
440
                note("No repository found at specified location.")
4332.3.9 by Robert Collins
Less lock thrashing in check.py.
441
    finally:
442
        for thing in to_unlock:
443
            thing.unlock()