~spiv/bzr-crosscheck/repo-check

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import collections

from bzrlib.commands import Command, builtin_command_registry
from bzrlib import (
    bzrdir,
    errors,
    trace,
    )


class cmd_cross_check(Command):

    __doc__ = """Check that 2 or more repos agree data in common.

    Currently this only checks that the contents of inventories are the same in
    all repos.

    Use ``--verbose`` to get details of the differences.
    """
    
    takes_args = ['repository*']

    takes_options = ['revision', 'verbose']

    def run(self, revision=None, repository_list=None, verbose=False):
        if not repository_list or len(repository_list) < 2:
            raise errors.BzrCommandError("At least 2 repos required.")
        repos = []
        possible_transports = []
        for repo_path in repository_list:
            bd, leftover_path = bzrdir.BzrDir.open_containing(repo_path,
                    possible_transports=possible_transports)
            possible_transports.append(bd.root_transport)
            repo = bd.find_repository()
            self.add_cleanup(repo.lock_read().unlock)
            repos.append(repo)

        inv_counts = collections.defaultdict(lambda: 0)
        for repo in repos:
            for inv_key in repo.inventories.keys():
                inv_counts[inv_key] += 1
        all_inv_keys = frozenset(inv_counts)
        common_inv_keys = set()
        for inv_key, count in inv_counts.iteritems():
            if count >= 2:
                common_inv_keys.add(inv_key)
        self.outf.write('%d common inventories to check (%d seen)\n' %
                (len(common_inv_keys), len(all_inv_keys)))
        trace.mutter('common invs: %r', common_inv_keys)
        #trace.mutter('all invs: %r', all_inv_keys)

        for inv_key in common_inv_keys:
            inv_id = inv_key[-1]
            first_inv = expected_root_1 = expected_root_2 = None
            for repo in repos:
                try:
                    inv = repo.get_inventory(inv_id)
                except errors.NoSuchRevision:
                    continue
                else:
                    root_1 = inv.id_to_entry.key()
                    root_2 = inv.parent_id_basename_to_file_id.key()
                if first_inv is None:
                    first_inv = inv
                    expected_root_1 = root_1
                    expected_root_2 = root_2
                    continue
                if root_1 != expected_root_1:
                    self.outf.write(
                        'disagree id_to_entry for %s: %r %r\n'
                        % (inv_id, expected_root_1, root_1))
                    if verbose:
                        self.outf.write(first_inv.id_to_entry._dump_tree(True))
                        self.outf.write(inv.id_to_entry._dump_tree(True))
                if root_2 != expected_root_2:
                    self.outf.write(
                        'disagree parent_id_basename_to_file_id for %s: %r %r\n'
                        % (inv_id, expected_root_2, root_2))
                    if verbose:
                        self.outf.write(first_inv.parent_id_basename_to_file_id._dump_tree(True))
                        self.outf.write(inv.parent_id_basename_to_file_id._dump_tree(True))


builtin_command_registry.register(cmd_cross_check)