1
# Copyright (C) 2005, 2006 Canonical Ltd
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.
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.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
# TODO: Perhaps have a way to record errors other than by raising exceptions;
18
# would perhaps be enough to accumulate exception objects in a list without
19
# raising them. If there's more than one exception it'd be good to see them
22
"""Checking of bzr objects.
24
check_refs is a concept used for optimising check. Objects that depend on other
25
objects (e.g. tree on repository) can list the objects they would be requesting
26
so that when the dependent object is checked, matches can be pulled out and
27
evaluated in-line rather than re-reading the same data many times.
28
check_refs are tuples (kind, value). Currently defined kinds are:
30
* 'trees', where value is a revid and the looked up objects are revision trees.
31
* 'lefthand-distance', where value is a revid and the looked up objects are the
32
distance along the lefthand path to NULL for that revid.
33
* 'revision-existence', where value is a revid, and the result is True or False
34
indicating that the revision was found/not found.
37
from __future__ import absolute_import
44
from .controldir import ControlDir
45
from .trace import note
46
from .i18n import gettext
50
"""Check a repository"""
52
def __init__(self, repository, check_repo=True):
53
self.repository = repository
55
def report_results(self, verbose):
56
raise NotImplementedError(self.report_results)
59
def scan_branch(branch, needed_refs, exit_stack):
60
"""Scan a branch for refs.
62
:param branch: The branch to schedule for checking.
63
:param needed_refs: Refs we are accumulating.
64
:param exit_stack: The exit stack accumulating.
66
note(gettext("Checking branch at '%s'.") % (branch.base,))
67
exit_stack.enter_context(branch.lock_read())
68
branch_refs = branch._get_check_refs()
69
for ref in branch_refs:
70
reflist = needed_refs.setdefault(ref, [])
71
reflist.append(branch)
74
def scan_tree(base_tree, tree, needed_refs, exit_stack):
75
"""Scan a tree for refs.
77
:param base_tree: The original tree check opened, used to detect duplicate
79
:param tree: The tree to schedule for checking.
80
:param needed_refs: Refs we are accumulating.
81
:param exit_stack: The exit stack accumulating.
83
if base_tree is not None and tree.basedir == base_tree.basedir:
85
note(gettext("Checking working tree at '%s'.") % (tree.basedir,))
86
exit_stack.enter_context(tree.lock_read())
87
tree_refs = tree._get_check_refs()
89
reflist = needed_refs.setdefault(ref, [])
93
def check_dwim(path, verbose, do_branch=False, do_repo=False, do_tree=False):
94
"""Check multiple objects.
96
If errors occur they are accumulated and reported as far as possible, and
97
an exception raised at the end of the process.
100
base_tree, branch, repo, relpath = \
101
ControlDir.open_containing_tree_branch_or_repository(path)
102
except errors.NotBranchError:
103
base_tree = branch = repo = None
105
with contextlib.ExitStack() as exit_stack:
107
if base_tree is not None:
108
# If the tree is a lightweight checkout we won't see it in
109
# repo.find_branches - add now.
111
scan_tree(None, base_tree, needed_refs, exit_stack)
112
branch = base_tree.branch
113
if branch is not None:
116
# The branch is in a shared repository
117
repo = branch.repository
119
exit_stack.enter_context(repo.lock_read())
120
branches = list(repo.find_branches(using=True))
122
if do_branch or do_tree:
123
for branch in branches:
126
tree = branch.controldir.open_workingtree()
128
except (errors.NotLocalUrl, errors.NoWorkingTree):
131
scan_tree(base_tree, tree, needed_refs, exit_stack)
133
scan_branch(branch, needed_refs, exit_stack)
134
if do_branch and not branches:
135
note(gettext("No branch found at specified location."))
136
if do_tree and base_tree is None and not saw_tree:
137
note(gettext("No working tree found at specified location."))
138
if do_repo or do_branch or do_tree:
140
note(gettext("Checking repository at '%s'.")
142
result = repo.check(None, callback_refs=needed_refs,
144
result.report_results(verbose)
147
note(gettext("No working tree found at specified location."))
149
note(gettext("No branch found at specified location."))
151
note(gettext("No repository found at specified location."))