~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/check.py

- bump version to 0.1rc2

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# along with this program; if not, write to the Free Software
16
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
17
 
18
 
 
19
 
 
20
 
######################################################################
21
 
# consistency checks
22
 
 
23
 
import sys
24
 
from sets import Set
25
 
 
26
 
import bzrlib
27
 
from trace import mutter
28
 
from errors import bailout
29
 
import osutils
30
 
 
31
 
def check(branch, progress=True):
32
 
    out = sys.stdout
33
 
 
34
 
    if progress:
35
 
        def p(m):
36
 
            mutter('checking ' + m)
37
 
            out.write('\rchecking: %-50.50s' % m)
38
 
            out.flush()
39
 
    else:
40
 
        def p(m):
41
 
            mutter('checking ' + m)
42
 
 
43
 
    p('history of %r' % branch.base)
44
 
    last_ptr = None
45
 
    checked_revs = Set()
46
 
    
47
 
    history = branch.revision_history()
48
 
    revno = 0
49
 
    revcount = len(history)
50
 
 
51
 
    checked_texts = {}
52
 
    
53
 
    for rid in history:
54
 
        revno += 1
55
 
        p('revision %d/%d' % (revno, revcount))
56
 
        mutter('    revision {%s}' % rid)
57
 
        rev = branch.get_revision(rid)
58
 
        if rev.revision_id != rid:
59
 
            bailout('wrong internal revision id in revision {%s}' % rid)
60
 
        if rev.precursor != last_ptr:
61
 
            bailout('mismatched precursor in revision {%s}' % rid)
62
 
        last_ptr = rid
63
 
        if rid in checked_revs:
64
 
            bailout('repeated revision {%s}' % rid)
65
 
        checked_revs.add(rid)
66
 
 
67
 
        ## TODO: Check all the required fields are present on the revision.
68
 
 
69
 
        inv = branch.get_inventory(rev.inventory_id)
70
 
        seen_ids = Set()
71
 
        seen_names = Set()
72
 
 
73
 
        p('revision %d/%d file ids' % (revno, revcount))
 
18
# TODO: Check ancestries are correct for every revision: includes
 
19
# every committed so far, and in a reasonable order.
 
20
 
 
21
# TODO: Also check non-mainline revisions mentioned as parents.
 
22
 
 
23
# TODO: Check for extra files in the control directory.
 
24
 
 
25
# TODO: Check revision, inventory and entry objects have all 
 
26
# required fields.
 
27
 
 
28
 
 
29
import bzrlib.ui
 
30
from bzrlib.trace import note, warning
 
31
from bzrlib.osutils import rename, sha_string, fingerprint_file
 
32
from bzrlib.trace import mutter
 
33
from bzrlib.errors import BzrCheckError, NoSuchRevision
 
34
from bzrlib.inventory import ROOT_ID
 
35
from bzrlib.branch import gen_root_id
 
36
 
 
37
 
 
38
class Check(object):
 
39
    """Check a branch"""
 
40
    def __init__(self, branch):
 
41
        self.branch = branch
 
42
        branch.lock_read()
 
43
        try:
 
44
            branch.weave_store.enable_cache = True
 
45
            branch.control_weaves.enable_cache = True
 
46
            self.run()
 
47
        finally:
 
48
            branch.unlock()
 
49
            branch.weave_store.enable_cache = False
 
50
            branch.control_weaves.enable_cache = False
 
51
 
 
52
 
 
53
    def run(self):
 
54
        branch = self.branch
 
55
 
 
56
 
 
57
        self.checked_text_cnt = 0
 
58
        self.checked_rev_cnt = 0
 
59
        self.repeated_text_cnt = 0
 
60
        self.missing_inventory_sha_cnt = 0
 
61
        self.missing_revision_cnt = 0
 
62
        # maps (file-id, version) -> sha1
 
63
        self.checked_texts = {}
 
64
 
 
65
        history = branch.revision_history()
 
66
        revno = 0
 
67
        revcount = len(history)
 
68
 
 
69
        last_rev_id = None
 
70
        self.progress = bzrlib.ui.ui_factory.progress_bar()
 
71
        for rev_id in history:
 
72
            self.progress.update('checking revision', revno, revcount)
 
73
            revno += 1
 
74
            self.check_one_rev(rev_id, last_rev_id)
 
75
            last_rev_id = rev_id
 
76
        self.progress.clear()
 
77
        self.report_results()
 
78
 
 
79
 
 
80
    def report_results(self):
 
81
        note('checked branch %s format %d',
 
82
             self.branch.base, 
 
83
             self.branch._branch_format)
 
84
 
 
85
        note('%6d revisions', self.checked_rev_cnt)
 
86
        note('%6d unique file texts', self.checked_text_cnt)
 
87
        note('%6d repeated file texts', self.repeated_text_cnt)
 
88
        if self.missing_inventory_sha_cnt:
 
89
            note('%d revisions are missing inventory_sha1',
 
90
                 self.missing_inventory_sha_cnt)
 
91
        if self.missing_revision_cnt:
 
92
            note('%d revisions are mentioned but not present',
 
93
                 self.missing_revision_cnt)
 
94
 
 
95
 
 
96
    def check_one_rev(self, rev_id, last_rev_id):
 
97
        """Check one revision.
 
98
 
 
99
        rev_id - the one to check
 
100
 
 
101
        last_rev_id - the previous one on the mainline, if any.
 
102
        """
 
103
 
 
104
        # mutter('    revision {%s}' % rev_id)
 
105
        branch = self.branch
 
106
        rev = branch.get_revision(rev_id)
 
107
        if rev.revision_id != rev_id:
 
108
            raise BzrCheckError('wrong internal revision id in revision {%s}'
 
109
                                % rev_id)
 
110
 
 
111
        # check the previous history entry is a parent of this entry
 
112
        if rev.parent_ids:
 
113
            if last_rev_id is None:
 
114
                raise BzrCheckError("revision {%s} has %d parents, but is the "
 
115
                                    "start of the branch"
 
116
                                    % (rev_id, len(rev.parent_ids)))
 
117
            for parent_id in rev.parent_ids:
 
118
                if parent_id == last_rev_id:
 
119
                    break
 
120
            else:
 
121
                raise BzrCheckError("previous revision {%s} not listed among "
 
122
                                    "parents of {%s}"
 
123
                                    % (last_rev_id, rev_id))
 
124
        elif last_rev_id:
 
125
            raise BzrCheckError("revision {%s} has no parents listed "
 
126
                                "but preceded by {%s}"
 
127
                                % (rev_id, last_rev_id))
 
128
 
 
129
        if rev.inventory_sha1:
 
130
            inv_sha1 = branch.get_inventory_sha1(rev_id)
 
131
            if inv_sha1 != rev.inventory_sha1:
 
132
                raise BzrCheckError('Inventory sha1 hash doesn\'t match'
 
133
                    ' value in revision {%s}' % rev_id)
 
134
        else:
 
135
            missing_inventory_sha_cnt += 1
 
136
            mutter("no inventory_sha1 on revision {%s}" % rev_id)
 
137
        self._check_revision_tree(rev_id)
 
138
        self.checked_rev_cnt += 1
 
139
 
 
140
    def _check_revision_tree(self, rev_id):
 
141
        tree = self.branch.revision_tree(rev_id)
 
142
        inv = tree.inventory
 
143
        seen_ids = {}
74
144
        for file_id in inv:
75
145
            if file_id in seen_ids:
76
 
                bailout('duplicated file_id {%s} in inventory for revision {%s}'
77
 
                        % (file_id, revid))
78
 
            seen_ids.add(file_id)
79
 
 
80
 
        i = 0
81
 
        len_inv = len(inv)
 
146
                raise BzrCheckError('duplicated file_id {%s} '
 
147
                                    'in inventory for revision {%s}'
 
148
                                    % (file_id, rev_id))
 
149
            seen_ids[file_id] = True
82
150
        for file_id in inv:
83
 
            i += 1
84
 
            if (i % 100) == 0:
85
 
                p('revision %d/%d file text %d/%d' % (revno, revcount, i, len_inv))
86
 
 
87
151
            ie = inv[file_id]
88
 
 
89
 
            if ie.parent_id != None:
90
 
                if ie.parent_id not in seen_ids:
91
 
                    bailout('missing parent {%s} in inventory for revision {%s}'
92
 
                            % (ie.parent_id, revid))
93
 
 
94
 
            if ie.kind == 'file':
95
 
                if ie.text_id in checked_texts:
96
 
                    fp = checked_texts[ie.text_id]
97
 
                else:
98
 
                    if not ie.text_id in branch.text_store:
99
 
                        bailout('text {%s} not in text_store' % ie.text_id)
100
 
 
101
 
                    tf = branch.text_store[ie.text_id]
102
 
                    fp = osutils.fingerprint_file(tf)
103
 
                    checked_texts[ie.text_id] = fp
104
 
 
105
 
                if ie.text_size != fp['size']:
106
 
                    bailout('text {%s} wrong size' % ie.text_id)
107
 
                if ie.text_sha1 != fp['sha1']:
108
 
                    bailout('text {%s} wrong sha1' % ie.text_id)
109
 
            elif ie.kind == 'directory':
110
 
                if ie.text_sha1 != None or ie.text_size != None or ie.text_id != None:
111
 
                    bailout('directory {%s} has text in revision {%s}'
112
 
                            % (file_id, revid))
113
 
 
114
 
        p('revision %d/%d file paths' % (revno, revcount))
 
152
            ie.check(self, rev_id, inv, tree)
 
153
        seen_names = {}
115
154
        for path, ie in inv.iter_entries():
116
155
            if path in seen_names:
117
 
                bailout('duplicated path %r in inventory for revision {%s}' % (path, revid))
118
 
            seen_names.add(path)
119
 
 
120
 
 
121
 
    p('done')
122
 
    if progress:
123
 
        print 
124
 
 
125
 
 
 
156
                raise BzrCheckError('duplicated path %s '
 
157
                                    'in inventory for revision {%s}'
 
158
                                    % (path, rev_id))
 
159
            seen_names[path] = True
 
160
 
 
161
 
 
162
def check(branch):
 
163
    """Run consistency checks on a branch."""
 
164
    Check(branch)