~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to read_changeset.py

  • Committer: John Arbash Meinel
  • Date: 2005-07-02 19:54:42 UTC
  • mto: (0.5.85) (1185.82.1 bzr-w-changeset)
  • mto: This revision was merged to the branch mainline in revision 1738.
  • Revision ID: john@arbash-meinel.com-20050702195442-f94d66c7fd15cc39
SUCCESS, we now are able to validate the inventory XML.

Show diffs side-by-side

added added

removed removed

Lines of Context:
143
143
                    assert self.base_sha1 == rev.parents[0].revision_sha1
144
144
                self.base_sha1 = rev.parents[0].revision_sha1
145
145
 
146
 
 
147
 
 
148
146
class ChangesetReader(object):
149
147
    """This class reads in a changeset from a file, and returns
150
148
    a Changeset object, which can then be applied against a tree.
224
222
        that we can.
225
223
        """
226
224
        rev_to_sha = {}
227
 
        def add_sha(rev_id, sha1):
 
225
        inv_to_sha = {}
 
226
        def add_sha(d, rev_id, sha1):
228
227
            if rev_id is None:
229
228
                if sha1 is not None:
230
229
                    raise BzrError('A Null revision should always'
231
230
                        'have a null sha1 hash')
232
231
                return
233
 
            if rev_id in rev_to_sha:
 
232
            if rev_id in d:
234
233
                # This really should have been validated as part
235
234
                # of _validate_revisions but lets do it again
236
 
                if sha1 != rev_to_sha[rev_id]:
 
235
                if sha1 != d[rev_id]:
237
236
                    raise BzrError('** Revision %r referenced with 2 different'
238
237
                            ' sha hashes %s != %s' % (rev_id,
239
 
                                sha1, rev_to_sha[rev_id]))
 
238
                                sha1, d[rev_id]))
240
239
            else:
241
 
                rev_to_sha[rev_id] = sha1
 
240
                d[rev_id] = sha1
242
241
 
243
 
        add_sha(self.info.base, self.info.base_sha1)
 
242
        add_sha(rev_to_sha, self.info.base, self.info.base_sha1)
244
243
        # All of the contained revisions were checked
245
244
        # in _validate_revisions
246
245
        checked = {}
247
246
        for rev_info in self.info.revisions:
248
247
            checked[rev_info.rev_id] = True
249
 
            add_sha(rev_info.rev_id, rev_info.sha1)
 
248
            add_sha(rev_to_sha, rev_info.rev_id, rev_info.sha1)
250
249
                
251
250
        for rev in self.info.real_revisions:
 
251
            add_sha(inv_to_sha, rev_info.inventory_id, rev_info.inventory_sha1)
252
252
            for parent in rev.parents:
253
 
                add_sha(parent.revision_id, parent.revision_sha1)
 
253
                add_sha(rev_to_sha, parent.revision_id, parent.revision_sha1)
254
254
 
 
255
        count = 0
255
256
        missing = {}
256
257
        for rev_id, sha1 in rev_to_sha.iteritems():
257
258
            if rev_id in branch.revision_store:
258
259
                local_sha1 = branch.get_revision_sha1(rev_id)
259
260
                if sha1 != local_sha1:
260
 
                    raise BzrError('sha1 mismatch. For revision_id {%s}' 
 
261
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
261
262
                            'local: %s, cset: %s' % (rev_id, local_sha1, sha1))
 
263
                else:
 
264
                    count += 1
262
265
            elif rev_id not in checked:
263
266
                missing[rev_id] = sha1
264
267
 
 
268
        for inv_id, sha1 in inv_to_sha.iteritems():
 
269
            if inv_id in branch.inventory_store:
 
270
                local_sha1 = branch.get_inventory_sha1(inv_id)
 
271
                if sha1 != local_sha1:
 
272
                    raise BzrError('sha1 mismatch. For inventory id {%s}' 
 
273
                            'local: %s, cset: %s' % (inv_id, local_sha1, sha1))
 
274
                else:
 
275
                    count += 1
 
276
 
265
277
        if len(missing) > 0:
266
278
            # I don't know if this is an error yet
267
279
            from bzrlib.trace import warning
268
280
            warning('Not all revision hashes could be validated.'
269
281
                    ' Unable validate %d hashes' % len(missing))
270
 
 
271
 
    def _validate_inventory(self, branch, tree):
 
282
        mutter('Verified %d sha hashes for the changeset.' % count)
 
283
 
 
284
    def _create_inventory(self, tree):
 
285
        """Build up the inventory entry for the ChangesetTree.
 
286
 
 
287
        TODO: This sort of thing should probably end up part of
 
288
        ChangesetTree, but since it doesn't handle meta-information
 
289
        yet, we need to do it here. (We need the ChangesetInfo,
 
290
        specifically the text_ids)
 
291
        """
 
292
        from os.path import dirname, basename
 
293
        from bzrlib.inventory import Inventory, InventoryEntry, ROOT_ID
 
294
 
 
295
        # TODO: deal with trees having a unique ROOT_ID
 
296
        root_id = ROOT_ID
 
297
        inv = Inventory()
 
298
        for file_id in tree:
 
299
            if file_id == root_id:
 
300
                continue
 
301
            path = tree.id2path(file_id)
 
302
            parent_path = dirname(path)
 
303
            if path == '':
 
304
                parent_id = root_id
 
305
            else:
 
306
                parent_id = tree.path2id(parent_path)
 
307
 
 
308
            if self.info.text_ids.has_key(file_id):
 
309
                text_id = self.info.text_ids[file_id]
 
310
            else:
 
311
                # If we don't have the text_id in the local map
 
312
                # that means the file didn't exist in the changeset
 
313
                # so we just use the old text_id.
 
314
                text_id = tree.base_tree.inventory[file_id].text_id
 
315
            name = basename(path)
 
316
            kind = tree.get_kind(file_id)
 
317
            ie = InventoryEntry(file_id, name, kind, parent_id, text_id=text_id)
 
318
            ie.text_size, ie.text_sha1 = tree.get_size_and_sha1(file_id)
 
319
            if ie.text_size is None:
 
320
                raise BzrError('Got a text_size of None for file_id %r' % file_id)
 
321
            inv.add(ie)
 
322
        return inv
 
323
 
 
324
    def _validate_inventory(self, inv):
272
325
        """At this point we should have generated the ChangesetTree,
273
326
        so build up an inventory, and make sure the hashes match.
274
327
        """
 
328
        from bzrlib.xml import pack_xml
 
329
        from cStringIO import StringIO
 
330
        from bzrlib.osutils import sha_file, pumpfile
 
331
 
 
332
        # Now we should have a complete inventory entry.
 
333
        sio = StringIO()
 
334
        pack_xml(inv, sio)
 
335
        sio.seek(0)
 
336
        sha1 = sha_file(sio)
 
337
        # Target revision is the last entry in the real_revisions list
 
338
        rev = self.info.real_revisions[-1]
 
339
        if sha1 != rev.inventory_sha1:
 
340
            raise BzrError('Inventory sha hash mismatch.')
 
341
 
275
342
        
276
 
    def get_info_and_tree(self, branch):
 
343
    def get_info_tree_inv(self, branch):
277
344
        """Return the meta information, and a Changeset tree which can
278
345
        be used to populate the local stores and working tree, respectively.
279
346
        """
281
348
        tree = ChangesetTree(branch.revision_tree(self.info.base))
282
349
        self._update_tree(tree)
283
350
 
284
 
        self._validate_inventory(branch, tree)
 
351
        inv = self._create_inventory(tree)
 
352
        self._validate_inventory(inv)
285
353
 
286
 
        return self.info, tree
 
354
        return self.info, tree, inv
287
355
 
288
356
    def _next(self):
289
357
        """yield the next line, but secretly
523
591
                text_id = get_text_id(None, file_id)
524
592
            tree.note_rename(old_path, new_path)
525
593
            if lines:
526
 
                tree.note_patch(new_path, lines)
 
594
                tree.note_patch(new_path, ''.join(lines))
527
595
 
528
596
        def removed(kind, extra, lines):
529
597
            info = extra.split('\t')
553
621
                text_id = get_text_id(info[2], file_id)
554
622
            else:
555
623
                text_id = get_text_id(None, file_id)
556
 
            tree.note_id(file_id, path)
557
 
            tree.note_patch(path, lines)
 
624
            tree.note_id(file_id, path, kind)
 
625
            tree.note_patch(path, ''.join(lines))
558
626
 
559
627
        def modified(kind, extra, lines):
560
628
            info = extra.split('\t')
568
636
                text_id = get_text_id(info[1], file_id)
569
637
            else:
570
638
                text_id = get_text_id(None, file_id)
571
 
            tree.note_patch(path, lines)
 
639
            tree.note_patch(path, ''.join(lines))
572
640
            
573
641
 
574
642
        valid_actions = {
607
675
                   won't know about until after the changeset is parsed.)
608
676
    """
609
677
    cr = ChangesetReader(from_file)
610
 
    return cr.get_info_and_tree(branch)
 
678
    return cr.get_info_tree_inv(branch)
611
679
 
612
680
class ChangesetTree:
613
681
    def __init__(self, base_tree=None):
616
684
        self._renamed_r = {} # new_path => old_path
617
685
        self._new_id = {} # new_path => new_id
618
686
        self._new_id_r = {} # new_id => new_path
 
687
        self._kinds = {} # new_id => kind
619
688
        self.patches = {}
620
689
        self.deleted = []
621
690
        self.contents_by_id = True
630
699
        self._renamed[new_path] = old_path
631
700
        self._renamed_r[old_path] = new_path
632
701
 
633
 
    def note_id(self, new_id, new_path):
 
702
    def note_id(self, new_id, new_path, kind='file'):
634
703
        """Files that don't exist in base need a new id."""
635
704
        self._new_id[new_path] = new_id
636
705
        self._new_id_r[new_id] = new_path
 
706
        self._kinds[new_id] = kind
637
707
 
638
708
    def note_patch(self, new_path, patch):
639
709
        """There is a patch for a given filename."""
667
737
            return None
668
738
        return old_path 
669
739
 
670
 
 
671
740
    def new_path(self, old_path):
672
741
        """Get the new_path (path in the target_tree) for the file at old_path
673
742
        in the base tree.
752
821
            return patch_original
753
822
        return patched_file(file_patch, patch_original)
754
823
 
 
824
    def get_kind(self, file_id):
 
825
        if file_id in self._kinds:
 
826
            return self._kinds[file_id]
 
827
        return self.base_tree.inventory[file_id].kind
 
828
 
 
829
    def get_size_and_sha1(self, file_id):
 
830
        """Return the size and sha1 hash of the given file id.
 
831
        If the file was not locally modified, this is extracted
 
832
        from the base_tree. Rather than re-reading the file.
 
833
        """
 
834
        from bzrlib.osutils import sha_string
 
835
 
 
836
        new_path = self.id2path(file_id)
 
837
        if new_path is None:
 
838
            return None, None
 
839
        if new_path not in self.patches:
 
840
            # If the entry does not have a patch, then the
 
841
            # contents must be the same as in the base_tree
 
842
            ie = self.base_tree.inventory[file_id]
 
843
            return int(ie.text_size), ie.text_sha1
 
844
        content = self.get_file(file_id).read()
 
845
        return len(content), sha_string(content)
 
846
 
 
847
        
 
848
 
755
849
    def __iter__(self):
756
850
        for file_id in self._new_id_r.iterkeys():
757
851
            yield file_id