/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/check.py

  • Committer: Robert Collins
  • Date: 2007-04-19 02:27:44 UTC
  • mto: This revision was merged to the branch mainline in revision 2426.
  • Revision ID: robertc@robertcollins.net-20070419022744-pfdqz42kp1wizh43
``make docs`` now creates a man page at ``man1/bzr.1`` fixing bug 107388.
(Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
# TODO: Check ancestries are correct for every revision: includes
 
18
# every committed so far, and in a reasonable order.
 
19
 
 
20
# TODO: Also check non-mainline revisions mentioned as parents.
 
21
 
 
22
# TODO: Check for extra files in the control directory.
 
23
 
 
24
# TODO: Check revision, inventory and entry objects have all 
 
25
# required fields.
 
26
 
 
27
# TODO: Get every revision in the revision-store even if they're not
 
28
# referenced by history and make sure they're all valid.
16
29
 
17
30
# TODO: Perhaps have a way to record errors other than by raising exceptions;
18
31
# would perhaps be enough to accumulate exception objects in a list without
19
32
# raising them.  If there's more than one exception it'd be good to see them
20
33
# all.
21
34
 
22
 
"""Checking of bzr objects.
23
 
 
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:
29
 
 
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.
35
 
"""
36
 
 
37
 
import contextlib
38
 
 
39
 
from . import (
40
 
    errors,
41
 
    )
42
 
from .controldir import ControlDir
43
 
from .trace import note
44
 
from .i18n import gettext
45
 
 
 
35
from bzrlib.errors import BzrCheckError
 
36
import bzrlib.ui
 
37
from bzrlib.trace import note
46
38
 
47
39
class Check(object):
48
40
    """Check a repository"""
49
41
 
50
 
    def __init__(self, repository, check_repo=True):
 
42
    # The Check object interacts with InventoryEntry.check, etc.
 
43
 
 
44
    def __init__(self, repository):
51
45
        self.repository = repository
 
46
        self.checked_text_cnt = 0
 
47
        self.checked_rev_cnt = 0
 
48
        self.ghosts = []
 
49
        self.repeated_text_cnt = 0
 
50
        self.missing_parent_links = {}
 
51
        self.missing_inventory_sha_cnt = 0
 
52
        self.missing_revision_cnt = 0
 
53
        # maps (file-id, version) -> sha1; used by InventoryFile._check
 
54
        self.checked_texts = {}
 
55
        self.checked_weaves = {}
 
56
 
 
57
    def check(self):
 
58
        self.repository.lock_read()
 
59
        self.progress = bzrlib.ui.ui_factory.nested_progress_bar()
 
60
        try:
 
61
            self.progress.update('retrieving inventory', 0, 0)
 
62
            # do not put in init, as it should be done with progess,
 
63
            # and inside the lock.
 
64
            self.inventory_weave = self.repository.get_inventory_weave()
 
65
            self.plan_revisions()
 
66
            revno = 0
 
67
            self.check_weaves()
 
68
            while revno < len(self.planned_revisions):
 
69
                rev_id = self.planned_revisions[revno]
 
70
                self.progress.update('checking revision', revno,
 
71
                                     len(self.planned_revisions))
 
72
                revno += 1
 
73
                self.check_one_rev(rev_id)
 
74
        finally:
 
75
            self.progress.finished()
 
76
            self.repository.unlock()
 
77
 
 
78
    def plan_revisions(self):
 
79
        repository = self.repository
 
80
        self.planned_revisions = set(repository.all_revision_ids())
 
81
        self.progress.clear()
 
82
        inventoried = set(self.inventory_weave.versions())
 
83
        awol = self.planned_revisions - inventoried
 
84
        if len(awol) > 0:
 
85
            raise BzrCheckError('Stored revisions missing from inventory'
 
86
                '{%s}' % ','.join([f for f in awol]))
 
87
        self.planned_revisions = list(self.planned_revisions)
52
88
 
53
89
    def report_results(self, verbose):
54
 
        raise NotImplementedError(self.report_results)
55
 
 
56
 
 
57
 
def scan_branch(branch, needed_refs, exit_stack):
58
 
    """Scan a branch for refs.
59
 
 
60
 
    :param branch:  The branch to schedule for checking.
61
 
    :param needed_refs: Refs we are accumulating.
62
 
    :param exit_stack: The exit stack accumulating.
63
 
    """
64
 
    note(gettext("Checking branch at '%s'.") % (branch.base,))
65
 
    exit_stack.enter_context(branch.lock_read())
66
 
    branch_refs = branch._get_check_refs()
67
 
    for ref in branch_refs:
68
 
        reflist = needed_refs.setdefault(ref, [])
69
 
        reflist.append(branch)
70
 
 
71
 
 
72
 
def scan_tree(base_tree, tree, needed_refs, exit_stack):
73
 
    """Scan a tree for refs.
74
 
 
75
 
    :param base_tree: The original tree check opened, used to detect duplicate
76
 
        tree checks.
77
 
    :param tree:  The tree to schedule for checking.
78
 
    :param needed_refs: Refs we are accumulating.
79
 
    :param exit_stack: The exit stack accumulating.
80
 
    """
81
 
    if base_tree is not None and tree.basedir == base_tree.basedir:
82
 
        return
83
 
    note(gettext("Checking working tree at '%s'.") % (tree.basedir,))
84
 
    exit_stack.enter_context(tree.lock_read())
85
 
    tree_refs = tree._get_check_refs()
86
 
    for ref in tree_refs:
87
 
        reflist = needed_refs.setdefault(ref, [])
88
 
        reflist.append(tree)
89
 
 
90
 
 
91
 
def check_dwim(path, verbose, do_branch=False, do_repo=False, do_tree=False):
92
 
    """Check multiple objects.
93
 
 
94
 
    If errors occur they are accumulated and reported as far as possible, and
95
 
    an exception raised at the end of the process.
96
 
    """
 
90
        note('checked repository %s format %s',
 
91
             self.repository.bzrdir.root_transport,
 
92
             self.repository._format)
 
93
        note('%6d revisions', self.checked_rev_cnt)
 
94
        note('%6d unique file texts', self.checked_text_cnt)
 
95
        note('%6d repeated file texts', self.repeated_text_cnt)
 
96
        note('%6d weaves', len(self.checked_weaves))
 
97
        if self.missing_inventory_sha_cnt:
 
98
            note('%6d revisions are missing inventory_sha1',
 
99
                 self.missing_inventory_sha_cnt)
 
100
        if self.missing_revision_cnt:
 
101
            note('%6d revisions are mentioned but not present',
 
102
                 self.missing_revision_cnt)
 
103
        if len(self.ghosts):
 
104
            note('%6d ghost revisions', len(self.ghosts))
 
105
            if verbose:
 
106
                for ghost in self.ghosts:
 
107
                    note('      %s', ghost)
 
108
        if len(self.missing_parent_links):
 
109
            note('%6d revisions missing parents in ancestry',
 
110
                 len(self.missing_parent_links))
 
111
            if verbose:
 
112
                for link, linkers in self.missing_parent_links.items():
 
113
                    note('      %s should be in the ancestry for:', link)
 
114
                    for linker in linkers:
 
115
                        note('       * %s', linker)
 
116
 
 
117
    def check_one_rev(self, rev_id):
 
118
        """Check one revision.
 
119
 
 
120
        rev_id - the one to check
 
121
        """
 
122
        rev = self.repository.get_revision(rev_id)
 
123
                
 
124
        if rev.revision_id != rev_id:
 
125
            raise BzrCheckError('wrong internal revision id in revision {%s}'
 
126
                                % rev_id)
 
127
 
 
128
        for parent in rev.parent_ids:
 
129
            if not parent in self.planned_revisions:
 
130
                missing_links = self.missing_parent_links.get(parent, [])
 
131
                missing_links.append(rev_id)
 
132
                self.missing_parent_links[parent] = missing_links
 
133
                # list based so somewhat slow,
 
134
                # TODO have a planned_revisions list and set.
 
135
                if self.repository.has_revision(parent):
 
136
                    missing_ancestry = self.repository.get_ancestry(parent)
 
137
                    for missing in missing_ancestry:
 
138
                        if (missing is not None 
 
139
                            and missing not in self.planned_revisions):
 
140
                            self.planned_revisions.append(missing)
 
141
                else:
 
142
                    self.ghosts.append(rev_id)
 
143
 
 
144
        if rev.inventory_sha1:
 
145
            inv_sha1 = self.repository.get_inventory_sha1(rev_id)
 
146
            if inv_sha1 != rev.inventory_sha1:
 
147
                raise BzrCheckError('Inventory sha1 hash doesn\'t match'
 
148
                    ' value in revision {%s}' % rev_id)
 
149
        self._check_revision_tree(rev_id)
 
150
        self.checked_rev_cnt += 1
 
151
 
 
152
    def check_weaves(self):
 
153
        """Check all the weaves we can get our hands on.
 
154
        """
 
155
        n_weaves = 1
 
156
        weave_ids = []
 
157
        if self.repository.weave_store.listable():
 
158
            weave_ids = list(self.repository.weave_store)
 
159
            n_weaves = len(weave_ids)
 
160
        self.progress.update('checking weave', 0, n_weaves)
 
161
        self.inventory_weave.check(progress_bar=self.progress)
 
162
        for i, weave_id in enumerate(weave_ids):
 
163
            self.progress.update('checking weave', i, n_weaves)
 
164
            w = self.repository.weave_store.get_weave(weave_id,
 
165
                    self.repository.get_transaction())
 
166
            # No progress here, because it looks ugly.
 
167
            w.check()
 
168
            self.checked_weaves[weave_id] = True
 
169
 
 
170
    def _check_revision_tree(self, rev_id):
 
171
        tree = self.repository.revision_tree(rev_id)
 
172
        inv = tree.inventory
 
173
        seen_ids = {}
 
174
        for file_id in inv:
 
175
            if file_id in seen_ids:
 
176
                raise BzrCheckError('duplicated file_id {%s} '
 
177
                                    'in inventory for revision {%s}'
 
178
                                    % (file_id, rev_id))
 
179
            seen_ids[file_id] = True
 
180
        for file_id in inv:
 
181
            ie = inv[file_id]
 
182
            ie.check(self, rev_id, inv, tree)
 
183
        seen_names = {}
 
184
        for path, ie in inv.iter_entries():
 
185
            if path in seen_names:
 
186
                raise BzrCheckError('duplicated path %s '
 
187
                                    'in inventory for revision {%s}'
 
188
                                    % (path, rev_id))
 
189
            seen_names[path] = True
 
190
 
 
191
 
 
192
def check(branch, verbose):
 
193
    """Run consistency checks on a branch.
 
194
    
 
195
    Results are reported through logging.
 
196
    
 
197
    :raise BzrCheckError: if there's a consistency error.
 
198
    """
 
199
    branch.lock_read()
97
200
    try:
98
 
        base_tree, branch, repo, relpath = \
99
 
            ControlDir.open_containing_tree_branch_or_repository(path)
100
 
    except errors.NotBranchError:
101
 
        base_tree = branch = repo = None
102
 
 
103
 
    with contextlib.ExitStack() as exit_stack:
104
 
        needed_refs = {}
105
 
        if base_tree is not None:
106
 
            # If the tree is a lightweight checkout we won't see it in
107
 
            # repo.find_branches - add now.
108
 
            if do_tree:
109
 
                scan_tree(None, base_tree, needed_refs, exit_stack)
110
 
            branch = base_tree.branch
111
 
        if branch is not None:
112
 
            # We have a branch
113
 
            if repo is None:
114
 
                # The branch is in a shared repository
115
 
                repo = branch.repository
116
 
        if repo is not None:
117
 
            exit_stack.enter_context(repo.lock_read())
118
 
            branches = list(repo.find_branches(using=True))
119
 
            saw_tree = False
120
 
            if do_branch or do_tree:
121
 
                for branch in branches:
122
 
                    if do_tree:
123
 
                        try:
124
 
                            tree = branch.controldir.open_workingtree()
125
 
                            saw_tree = True
126
 
                        except (errors.NotLocalUrl, errors.NoWorkingTree):
127
 
                            pass
128
 
                        else:
129
 
                            scan_tree(base_tree, tree, needed_refs, exit_stack)
130
 
                    if do_branch:
131
 
                        scan_branch(branch, needed_refs, exit_stack)
132
 
            if do_branch and not branches:
133
 
                note(gettext("No branch found at specified location."))
134
 
            if do_tree and base_tree is None and not saw_tree:
135
 
                note(gettext("No working tree found at specified location."))
136
 
            if do_repo or do_branch or do_tree:
137
 
                if do_repo:
138
 
                    note(gettext("Checking repository at '%s'.")
139
 
                         % (repo.user_url,))
140
 
                result = repo.check(None, callback_refs=needed_refs,
141
 
                                    check_repo=do_repo)
142
 
                result.report_results(verbose)
143
 
        else:
144
 
            if do_tree:
145
 
                note(gettext("No working tree found at specified location."))
146
 
            if do_branch:
147
 
                note(gettext("No branch found at specified location."))
148
 
            if do_repo:
149
 
                note(gettext("No repository found at specified location."))
 
201
        branch_result = branch.check()
 
202
        repo_result = branch.repository.check([branch.last_revision()])
 
203
    finally:
 
204
        branch.unlock()
 
205
    branch_result.report_results(verbose)
 
206
    repo_result.report_results(verbose)