/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: Martin Pool
  • Date: 2005-06-11 01:55:29 UTC
  • Revision ID: mbp@sourcefrog.net-20050611015529-26a9c988699de34f
- help formatting fix from ndim

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
17
 
18
18
 
19
 
def _update_store_entry(obj, obj_id, branch, store_name, store):
20
 
    """This is just a meta-function, which handles both revision entries
21
 
    and inventory entries.
22
 
    """
23
 
    from bzrlib.trace import mutter
24
 
    import tempfile, os, errno
25
 
    from osutils import rename
26
 
    obj_tmp = tempfile.TemporaryFile()
27
 
    obj.write_xml(obj_tmp)
28
 
    obj_tmp.seek(0)
29
 
 
30
 
    tmpfd, tmp_path = tempfile.mkstemp(prefix=obj_id, suffix='.gz',
31
 
        dir=branch.controlfilename(store_name))
32
 
    os.close(tmpfd)
33
 
    try:
34
 
        orig_obj_path = branch.controlfilename([store_name, obj_id+'.gz'])
35
 
        # Remove the old entry out of the way
36
 
        rename(orig_obj_path, tmp_path)
37
 
        try:
38
 
            # TODO: We may need to handle the case where the old
39
 
            # entry was not compressed (and thus did not end with .gz)
40
 
 
41
 
            store.add(obj_tmp, obj_id) # Add the new one
42
 
            os.remove(tmp_path) # Remove the old name
43
 
            mutter('    Updated %s entry {%s}' % (store_name, obj_id))
44
 
        except:
45
 
            # On any exception, restore the old entry
46
 
            rename(tmp_path, orig_obj_path)
47
 
            raise
48
 
    finally:
49
 
        if os.path.exists(tmp_path):
50
 
            # Unfortunately, the next command might throw
51
 
            # an exception, which will mask a previous exception.
52
 
            os.remove(tmp_path)
53
 
        obj_tmp.close()
54
 
 
55
 
def _update_revision_entry(rev, branch):
56
 
    """After updating the values in a revision, make sure to
57
 
    write out the data, but try to do it in an atomic manner.
58
 
 
59
 
    :param rev:    The Revision object to store
60
 
    :param branch: The Branch object where this Revision is to be stored.
61
 
    """
62
 
    _update_store_entry(rev, rev.revision_id, branch,
63
 
            'revision-store', branch.revision_store)
64
 
 
65
 
def _update_inventory_entry(inv, inv_id, branch):
66
 
    """When an inventory has been modified (such as by adding a unique tree root)
67
 
    this atomically re-generates the file.
68
 
 
69
 
    :param inv:     The Inventory
70
 
    :param inv_id:  The inventory id for this inventory
71
 
    :param branch:  The Branch where this entry will be stored.
72
 
    """
73
 
    _update_store_entry(inv, inv_id, branch,
74
 
            'inventory-store', branch.inventory_store)
75
 
 
76
 
def check(branch):
 
19
 
 
20
 
 
21
def check(branch, update=False):
77
22
    """Run consistency checks on a branch.
78
23
 
79
 
    TODO: Also check non-mainline revisions mentioned as parents.
80
 
 
81
 
    TODO: Check for extra files in the control directory.
 
24
    If update is True, for revisions missing certain information
 
25
    (right now this is inventory_sha1 and revision_sha1),
 
26
    update them to include the appropriate values.
82
27
    """
 
28
    import sys
 
29
 
83
30
    from bzrlib.trace import mutter
84
31
    from bzrlib.errors import BzrCheckError
85
32
    from bzrlib.osutils import fingerprint_file
86
33
    from bzrlib.progress import ProgressBar
87
 
    from bzrlib.inventory import ROOT_ID
88
 
    from bzrlib.branch import gen_root_id
89
 
 
90
 
    branch.lock_read()
91
 
 
92
 
    try:
93
 
        pb = ProgressBar(show_spinner=True)
94
 
        last_rev_id = None
95
 
 
96
 
        missing_inventory_sha_cnt = 0
97
 
        missing_revision_sha_cnt = 0
98
 
 
99
 
        history = branch.revision_history()
100
 
        revno = 0
101
 
        revcount = len(history)
102
 
        mismatch_inv_id = []
103
 
 
104
 
        # for all texts checked, text_id -> sha1
105
 
        checked_texts = {}
106
 
 
107
 
        for rev_id in history:
108
 
            revno += 1
109
 
            pb.update('checking revision', revno, revcount)
110
 
            mutter('    revision {%s}' % rev_id)
111
 
            rev = branch.get_revision(rev_id)
112
 
            if rev.revision_id != rev_id:
113
 
                raise BzrCheckError('wrong internal revision id in revision {%s}'
114
 
                                    % rev_id)
115
 
 
116
 
            # check the previous history entry is a parent of this entry
117
 
            if rev.parents:
118
 
                if last_rev_id is None:
119
 
                    raise BzrCheckError("revision {%s} has %d parents, but is the "
120
 
                                        "start of the branch"
121
 
                                        % (rev_id, len(rev.parents)))
122
 
                for prr in rev.parents:
123
 
                    if prr.revision_id == last_rev_id:
124
 
                        break
125
 
                else:
126
 
                    raise BzrCheckError("previous revision {%s} not listed among "
127
 
                                        "parents of {%s}"
128
 
                                        % (last_rev_id, rev_id))
129
 
 
130
 
                for prr in rev.parents:
131
 
                    if prr.revision_sha1 is None:
132
 
                        missing_revision_sha_cnt += 1
133
 
                        continue
134
 
                    prid = prr.revision_id
135
 
                    actual_sha = branch.get_revision_sha1(prid)
136
 
                    if prr.revision_sha1 != actual_sha:
137
 
                        raise BzrCheckError("mismatched revision sha1 for "
138
 
                                            "parent {%s} of {%s}: %s vs %s"
139
 
                                            % (prid, rev_id,
140
 
                                               prr.revision_sha1, actual_sha))
141
 
            elif last_rev_id:
142
 
                raise BzrCheckError("revision {%s} has no parents listed but preceded "
143
 
                                    "by {%s}"
144
 
                                    % (rev_id, last_rev_id))
145
 
 
146
 
            if rev.inventory_id != rev_id:
147
 
                mismatch_inv_id.append(rev_id)
148
 
 
149
 
            ## TODO: Check all the required fields are present on the revision.
150
 
 
151
 
            if rev.inventory_sha1:
152
 
                inv_sha1 = branch.get_inventory_sha1(rev.inventory_id)
153
 
                if inv_sha1 != rev.inventory_sha1:
154
 
                    raise BzrCheckError('Inventory sha1 hash doesn\'t match'
155
 
                        ' value in revision {%s}' % rev_id)
156
 
            else:
157
 
                missing_inventory_sha_cnt += 1
158
 
                mutter("no inventory_sha1 on revision {%s}" % rev_id)
159
 
 
160
 
            inv = branch.get_inventory(rev.inventory_id)
161
 
            seen_ids = {}
162
 
            seen_names = {}
163
 
 
164
 
            ## p('revision %d/%d file ids' % (revno, revcount))
165
 
            for file_id in inv:
166
 
                if file_id in seen_ids:
167
 
                    raise BzrCheckError('duplicated file_id {%s} '
168
 
                                        'in inventory for revision {%s}'
169
 
                                        % (file_id, rev_id))
170
 
                seen_ids[file_id] = True
171
 
 
172
 
            i = 0
173
 
            for file_id in inv:
174
 
                i += 1
175
 
                if i & 31 == 0:
176
 
                    pb.tick()
177
 
 
178
 
                ie = inv[file_id]
179
 
 
180
 
                if ie.parent_id != None:
181
 
                    if ie.parent_id not in seen_ids:
182
 
                        raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
183
 
                                % (ie.parent_id, rev_id))
184
 
 
185
 
                if ie.kind == 'file':
186
 
                    if ie.text_id in checked_texts:
187
 
                        fp = checked_texts[ie.text_id]
188
 
                    else:
189
 
                        if not ie.text_id in branch.text_store:
190
 
                            raise BzrCheckError('text {%s} not in text_store' % ie.text_id)
191
 
 
192
 
                        tf = branch.text_store[ie.text_id]
193
 
                        fp = fingerprint_file(tf)
194
 
                        checked_texts[ie.text_id] = fp
195
 
 
196
 
                    if ie.text_size != fp['size']:
197
 
                        raise BzrCheckError('text {%s} wrong size' % ie.text_id)
198
 
                    if ie.text_sha1 != fp['sha1']:
199
 
                        raise BzrCheckError('text {%s} wrong sha1' % ie.text_id)
200
 
                elif ie.kind == 'directory':
201
 
                    if ie.text_sha1 != None or ie.text_size != None or ie.text_id != None:
202
 
                        raise BzrCheckError('directory {%s} has text in revision {%s}'
203
 
                                % (file_id, rev_id))
204
 
 
205
 
            pb.tick()
206
 
            for path, ie in inv.iter_entries():
207
 
                if path in seen_names:
208
 
                    raise BzrCheckError('duplicated path %s '
209
 
                                        'in inventory for revision {%s}'
210
 
                                        % (path, rev_id))
 
34
    
 
35
    out = sys.stdout
 
36
 
 
37
    pb = ProgressBar(show_spinner=True)
 
38
    last_ptr = None
 
39
    checked_revs = {}
 
40
 
 
41
    missing_inventory_sha_cnt = 0
 
42
    
 
43
    history = branch.revision_history()
 
44
    revno = 0
 
45
    revcount = len(history)
 
46
 
 
47
    checked_texts = {}
 
48
 
 
49
    updated_revisions = []
 
50
 
 
51
    # Set to True in the case that the previous revision entry
 
52
    # was updated, since this will affect future revision entries
 
53
    updated_previous_revision = False
 
54
    
 
55
    for rid in history:
 
56
        revno += 1
 
57
        pb.update('checking revision', revno, revcount)
 
58
        mutter('    revision {%s}' % rid)
 
59
        rev = branch.get_revision(rid)
 
60
        if rev.revision_id != rid:
 
61
            raise BzrCheckError('wrong internal revision id in revision {%s}' % rid)
 
62
        if rev.precursor != last_ptr:
 
63
            raise BzrCheckError('mismatched precursor in revision {%s}' % rid)
 
64
        last_ptr = rid
 
65
        if rid in checked_revs:
 
66
            raise BzrCheckError('repeated revision {%s}' % rid)
 
67
        checked_revs[rid] = True
 
68
 
 
69
        ## TODO: Check all the required fields are present on the revision.
 
70
 
 
71
        updated = False
 
72
        if rev.inventory_sha1:
 
73
            #mutter('    checking inventory hash {%s}' % rev.inventory_sha1)
 
74
            inv_sha1 = branch.get_inventory_sha1(rev.inventory_id)
 
75
            if inv_sha1 != rev.inventory_sha1:
 
76
                raise BzrCheckError('Inventory sha1 hash doesn\'t match'
 
77
                    ' value in revision {%s}' % rid)
 
78
        elif update:
 
79
            inv_sha1 = branch.get_inventory_sha1(rev.inventory_id)
 
80
            rev.inventory_sha1 = inv_sha1
 
81
            updated = True
 
82
        else:
 
83
            missing_inventory_sha_cnt += 1
 
84
            mutter("no inventory_sha1 on revision {%s}" % rid)
 
85
 
 
86
        if rev.precursor:
 
87
            if rev.precursor_sha1:
 
88
                precursor_sha1 = branch.get_revision_sha1(rev.precursor)
 
89
                if updated_previous_revision: 
 
90
                    # we don't expect the hashes to match, because
 
91
                    # we had to modify the previous revision_history entry.
 
92
                    rev.precursor_sha1 = precursor_sha1
 
93
                    updated = True
 
94
                else:
 
95
                    #mutter('    checking precursor hash {%s}' % rev.precursor_sha1)
 
96
                    if rev.precursor_sha1 != precursor_sha1:
 
97
                        raise BzrCheckError('Precursor sha1 hash doesn\'t match'
 
98
                            ' value in revision {%s}' % rid)
 
99
            elif update:
 
100
                precursor_sha1 = branch.get_revision_sha1(rev.precursor)
 
101
                rev.precursor_sha1 = precursor_sha1
 
102
                updated = True
 
103
 
 
104
        if updated:
 
105
            updated_previous_revision = True
 
106
            # We had to update this revision entries hashes
 
107
            # Now we need to write out a new value
 
108
            # This is a little bit invasive, since we are *rewriting* a
 
109
            # revision entry. I'm not supremely happy about it, but
 
110
            # there should be *some* way of making old entries have
 
111
            # the full meta information.
 
112
            import tempfile, os, errno
 
113
            rev_tmp = tempfile.TemporaryFile()
 
114
            rev.write_xml(rev_tmp)
 
115
            rev_tmp.seek(0)
 
116
 
 
117
            tmpfd, tmp_path = tempfile.mkstemp(prefix=rid, suffix='.gz',
 
118
                dir=branch.controlfilename('revision-store'))
 
119
            os.close(tmpfd)
 
120
            def special_rename(p1, p2):
 
121
                if sys.platform == 'win32':
 
122
                    try:
 
123
                        os.remove(p2)
 
124
                    except OSError, e:
 
125
                        if e.errno != e.ENOENT:
 
126
                            raise
 
127
                os.rename(p1, p2)
 
128
 
 
129
            try:
 
130
                # TODO: We may need to handle the case where the old revision
 
131
                # entry was not compressed (and thus did not end with .gz)
 
132
 
 
133
                # Remove the old revision entry out of the way
 
134
                rev_path = branch.controlfilename(['revision-store', rid+'.gz'])
 
135
                special_rename(rev_path, tmp_path)
 
136
                branch.revision_store.add(rev_tmp, rid) # Add the new one
 
137
                os.remove(tmp_path) # Remove the old name
 
138
                mutter('    Updated revision entry {%s}' % rid)
 
139
            except:
 
140
                # On any exception, restore the old entry
 
141
                special_rename(tmp_path, rev_path)
 
142
                raise
 
143
            rev_tmp.close()
 
144
            updated_revisions.append(rid)
 
145
        else:
 
146
            updated_previous_revision = False
 
147
            
 
148
        inv = branch.get_inventory(rev.inventory_id)
 
149
        seen_ids = {}
 
150
        seen_names = {}
 
151
 
 
152
        ## p('revision %d/%d file ids' % (revno, revcount))
 
153
        for file_id in inv:
 
154
            if file_id in seen_ids:
 
155
                raise BzrCheckError('duplicated file_id {%s} '
 
156
                                    'in inventory for revision {%s}'
 
157
                                    % (file_id, rid))
 
158
            seen_ids[file_id] = True
 
159
 
 
160
        i = 0
 
161
        len_inv = len(inv)
 
162
        for file_id in inv:
 
163
            i += 1
 
164
            if i & 31 == 0:
 
165
                pb.tick()
 
166
 
 
167
            ie = inv[file_id]
 
168
 
 
169
            if ie.parent_id != None:
 
170
                if ie.parent_id not in seen_ids:
 
171
                    raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
 
172
                            % (ie.parent_id, rid))
 
173
 
 
174
            if ie.kind == 'file':
 
175
                if ie.text_id in checked_texts:
 
176
                    fp = checked_texts[ie.text_id]
 
177
                else:
 
178
                    if not ie.text_id in branch.text_store:
 
179
                        raise BzrCheckError('text {%s} not in text_store' % ie.text_id)
 
180
 
 
181
                    tf = branch.text_store[ie.text_id]
 
182
                    fp = fingerprint_file(tf)
 
183
                    checked_texts[ie.text_id] = fp
 
184
 
 
185
                if ie.text_size != fp['size']:
 
186
                    raise BzrCheckError('text {%s} wrong size' % ie.text_id)
 
187
                if ie.text_sha1 != fp['sha1']:
 
188
                    raise BzrCheckError('text {%s} wrong sha1' % ie.text_id)
 
189
            elif ie.kind == 'directory':
 
190
                if ie.text_sha1 != None or ie.text_size != None or ie.text_id != None:
 
191
                    raise BzrCheckError('directory {%s} has text in revision {%s}'
 
192
                            % (file_id, rid))
 
193
 
 
194
        pb.tick()
 
195
        for path, ie in inv.iter_entries():
 
196
            if path in seen_names:
 
197
                raise BzrCheckError('duplicated path %r '
 
198
                                    'in inventory for revision {%s}'
 
199
                                    % (path, revid))
211
200
            seen_names[path] = True
212
 
            last_rev_id = rev_id
213
201
 
214
 
    finally:
215
 
        branch.unlock()
216
202
 
217
203
    pb.clear()
218
204
 
219
205
    print 'checked %d revisions, %d file texts' % (revcount, len(checked_texts))
220
 
    
 
206
    if updated_revisions:
 
207
        print '%d revisions updated to current format' % len(updated_revisions)
221
208
    if missing_inventory_sha_cnt:
222
209
        print '%d revisions are missing inventory_sha1' % missing_inventory_sha_cnt
223
 
 
224
 
    if missing_revision_sha_cnt:
225
 
        print '%d parent links are missing revision_sha1' % missing_revision_sha_cnt
226
 
 
227
 
    if (missing_inventory_sha_cnt
228
 
        or missing_revision_sha_cnt):
229
 
        print '  (use "bzr upgrade" to fix them)'
230
 
 
231
 
    if mismatch_inv_id:
232
 
        print '%d revisions have mismatched inventory ids:' % len(mismatch_inv_id)
233
 
        for rev_id in mismatch_inv_id:
234
 
            print '  ', rev_id
 
210
        print '  (use bzr check --update to fix them)'