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