~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/bundle_data.py

Merge bzr.dev and tree-file-ids-as-tuples.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Read in a bundle stream, and process it into a BundleReader object."""
18
18
 
 
19
from __future__ import absolute_import
 
20
 
19
21
import base64
20
22
from cStringIO import StringIO
21
23
import os
25
27
    osutils,
26
28
    timestamp,
27
29
    )
28
 
import bzrlib.errors
29
30
from bzrlib.bundle import apply_bundle
30
 
from bzrlib.errors import (TestamentMismatch, BzrError, 
31
 
                           MalformedHeader, MalformedPatches, NotABundle)
32
 
from bzrlib.inventory import (Inventory, InventoryEntry,
33
 
                              InventoryDirectory, InventoryFile,
34
 
                              InventoryLink)
35
 
from bzrlib.osutils import sha_file, sha_string, pathjoin
 
31
from bzrlib.errors import (
 
32
    TestamentMismatch,
 
33
    BzrError,
 
34
    )
 
35
from bzrlib.inventory import (
 
36
    Inventory,
 
37
    InventoryDirectory,
 
38
    InventoryFile,
 
39
    InventoryLink,
 
40
    )
 
41
from bzrlib.osutils import sha_string, pathjoin
36
42
from bzrlib.revision import Revision, NULL_REVISION
37
43
from bzrlib.testament import StrictTestament
38
44
from bzrlib.trace import mutter, warning
39
 
import bzrlib.transport
40
45
from bzrlib.tree import Tree
41
 
import bzrlib.urlutils
42
46
from bzrlib.xml5 import serializer_v5
43
47
 
44
48
 
200
204
            self._validate_references_from_repository(repository)
201
205
        revision_info = self.get_revision_info(revision_id)
202
206
        inventory_revision_id = revision_id
203
 
        bundle_tree = BundleTree(repository.revision_tree(base), 
 
207
        bundle_tree = BundleTree(repository.revision_tree(base),
204
208
                                  inventory_revision_id)
205
209
        self._update_tree(bundle_tree, revision_id)
206
210
 
207
211
        inv = bundle_tree.inventory
208
212
        self._validate_inventory(inv, revision_id)
209
 
        self._validate_revision(inv, revision_id)
 
213
        self._validate_revision(bundle_tree, revision_id)
210
214
 
211
215
        return bundle_tree
212
216
 
239
243
        for rev_info in self.revisions:
240
244
            checked[rev_info.revision_id] = True
241
245
            add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
242
 
                
 
246
 
243
247
        for (rev, rev_info) in zip(self.real_revisions, self.revisions):
244
248
            add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
245
249
 
247
251
        missing = {}
248
252
        for revision_id, sha1 in rev_to_sha.iteritems():
249
253
            if repository.has_revision(revision_id):
250
 
                testament = StrictTestament.from_revision(repository, 
 
254
                testament = StrictTestament.from_revision(repository,
251
255
                                                          revision_id)
252
256
                local_sha1 = self._testament_sha1_from_revision(repository,
253
257
                                                                revision_id)
254
258
                if sha1 != local_sha1:
255
 
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
 
259
                    raise BzrError('sha1 mismatch. For revision id {%s}'
256
260
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
257
261
                else:
258
262
                    count += 1
278
282
        if rev.revision_id != revision_id:
279
283
            raise AssertionError()
280
284
        if sha1 != rev.inventory_sha1:
281
 
            open(',,bogus-inv', 'wb').write(s)
 
285
            f = open(',,bogus-inv', 'wb')
 
286
            try:
 
287
                f.write(s)
 
288
            finally:
 
289
                f.close()
282
290
            warning('Inventory sha hash mismatch for revision %s. %s'
283
291
                    ' != %s' % (revision_id, sha1, rev.inventory_sha1))
284
292
 
285
 
    def _validate_revision(self, inventory, revision_id):
 
293
    def _validate_revision(self, tree, revision_id):
286
294
        """Make sure all revision entries match their checksum."""
287
295
 
288
 
        # This is a mapping from each revision id to it's sha hash
 
296
        # This is a mapping from each revision id to its sha hash
289
297
        rev_to_sha1 = {}
290
 
        
 
298
 
291
299
        rev = self.get_revision(revision_id)
292
300
        rev_info = self.get_revision_info(revision_id)
293
301
        if not (rev.revision_id == rev_info.revision_id):
294
302
            raise AssertionError()
295
303
        if not (rev.revision_id == revision_id):
296
304
            raise AssertionError()
297
 
        sha1 = self._testament_sha1(rev, inventory)
 
305
        sha1 = self._testament_sha1(rev, tree)
298
306
        if sha1 != rev_info.sha1:
299
307
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
300
308
        if rev.revision_id in rev_to_sha1:
327
335
                try:
328
336
                    name, value = info_item.split(':', 1)
329
337
                except ValueError:
330
 
                    raise 'Value %r has no colon' % info_item
 
338
                    raise ValueError('Value %r has no colon' % info_item)
331
339
                if name == 'last-changed':
332
340
                    last_changed = value
333
341
                elif name == 'executable':
411
419
            revision = get_rev_id(last_modified, path, kind)
412
420
            if lines:
413
421
                do_patch(path, lines, encoding)
414
 
            
 
422
 
415
423
        valid_actions = {
416
424
            'renamed':renamed,
417
425
            'removed':removed,
458
466
 
459
467
 
460
468
class BundleTree(Tree):
 
469
 
461
470
    def __init__(self, base_tree, revision_id):
462
471
        self.base_tree = base_tree
463
472
        self._renamed = {} # Mapping from old_path => new_path
539
548
        #renamed_r
540
549
        if old_path in self._renamed_r:
541
550
            return None
542
 
        return old_path 
 
551
        return old_path
543
552
 
544
553
    def new_path(self, old_path):
545
554
        """Get the new_path (path in the target_tree) for the file at old_path
565
574
        #renamed_r
566
575
        if new_path in self._renamed:
567
576
            return None
568
 
        return new_path 
 
577
        return new_path
 
578
 
 
579
    def get_root_id(self):
 
580
        return self.path2id('')
569
581
 
570
582
    def path2id(self, path):
571
583
        """Return the id of the file present at path in the target tree."""
577
589
            return None
578
590
        if old_path in self.deleted:
579
591
            return None
580
 
        if getattr(self.base_tree, 'path2id', None) is not None:
581
 
            return self.base_tree.path2id(old_path)
582
 
        else:
583
 
            return self.base_tree.inventory.path2id(old_path)
 
592
        return self.base_tree.path2id(old_path)
584
593
 
585
594
    def id2path(self, file_id):
586
595
        """Return the new path in the target tree of the file with id file_id"""
605
614
                return None
606
615
        new_path = self.id2path(file_id)
607
616
        return self.base_tree.path2id(new_path)
608
 
        
 
617
 
609
618
    def get_file(self, file_id):
610
619
        """Return a file-like object containing the new contents of the
611
620
        file given by file_id.
616
625
        """
617
626
        base_id = self.old_contents_id(file_id)
618
627
        if (base_id is not None and
619
 
            base_id != self.base_tree.inventory.root.file_id):
 
628
            base_id != self.base_tree.get_root_id()):
620
629
            patch_original = self.base_tree.get_file(base_id)
621
630
        else:
622
631
            patch_original = None
623
632
        file_patch = self.patches.get(self.id2path(file_id))
624
633
        if file_patch is None:
625
 
            if (patch_original is None and 
626
 
                self.get_kind(file_id) == 'directory'):
 
634
            if (patch_original is None and
 
635
                self.kind(file_id) == 'directory'):
627
636
                return StringIO()
628
637
            if patch_original is None:
629
638
                raise AssertionError("None: %s" % file_id)
634
643
                'Malformed patch for %s, %r' % (file_id, file_patch))
635
644
        return patched_file(file_patch, patch_original)
636
645
 
637
 
    def get_symlink_target(self, file_id):
638
 
        new_path = self.id2path(file_id)
 
646
    def get_symlink_target(self, file_id, path=None):
 
647
        if path is None:
 
648
            path = self.id2path(file_id)
639
649
        try:
640
 
            return self._targets[new_path]
 
650
            return self._targets[path]
641
651
        except KeyError:
642
652
            return self.base_tree.get_symlink_target(file_id)
643
653
 
644
 
    def get_kind(self, file_id):
 
654
    def kind(self, file_id):
645
655
        if file_id in self._kinds:
646
656
            return self._kinds[file_id]
647
 
        return self.base_tree.inventory[file_id].kind
 
657
        return self.base_tree.kind(file_id)
 
658
 
 
659
    def get_file_revision(self, file_id):
 
660
        path = self.id2path(file_id)
 
661
        if path in self._last_changed:
 
662
            return self._last_changed[path]
 
663
        else:
 
664
            return self.base_tree.get_file_revision(file_id)
648
665
 
649
666
    def is_executable(self, file_id):
650
667
        path = self.id2path(file_id)
651
668
        if path in self._executable:
652
669
            return self._executable[path]
653
670
        else:
654
 
            return self.base_tree.inventory[file_id].executable
 
671
            return self.base_tree.is_executable(file_id)
655
672
 
656
673
    def get_last_changed(self, file_id):
657
674
        path = self.id2path(file_id)
658
675
        if path in self._last_changed:
659
676
            return self._last_changed[path]
660
 
        return self.base_tree.inventory[file_id].revision
 
677
        return self.base_tree.get_file_revision(file_id)
661
678
 
662
679
    def get_size_and_sha1(self, file_id):
663
680
        """Return the size and sha1 hash of the given file id.
670
687
        if new_path not in self.patches:
671
688
            # If the entry does not have a patch, then the
672
689
            # contents must be the same as in the base_tree
673
 
            ie = self.base_tree.inventory[file_id]
 
690
            ie = self.base_tree.root_inventory[file_id]
674
691
            if ie.text_size is None:
675
692
                return ie.text_size, ie.text_sha1
676
693
            return int(ie.text_size), ie.text_sha1
684
701
        This need to be called before ever accessing self.inventory
685
702
        """
686
703
        from os.path import dirname, basename
687
 
        base_inv = self.base_tree.inventory
 
704
        base_inv = self.base_tree.root_inventory
688
705
        inv = Inventory(None, self.revision_id)
689
706
 
690
707
        def add_entry(file_id):
697
714
                parent_path = dirname(path)
698
715
                parent_id = self.path2id(parent_path)
699
716
 
700
 
            kind = self.get_kind(file_id)
 
717
            kind = self.kind(file_id)
701
718
            revision_id = self.get_last_changed(file_id)
702
719
 
703
720
            name = basename(path)
708
725
                ie.executable = self.is_executable(file_id)
709
726
            elif kind == 'symlink':
710
727
                ie = InventoryLink(file_id, name, parent_id)
711
 
                ie.symlink_target = self.get_symlink_target(file_id)
 
728
                ie.symlink_target = self.get_symlink_target(file_id, path)
712
729
            ie.revision = revision_id
713
730
 
714
 
            if kind in ('directory', 'symlink'):
715
 
                ie.text_size, ie.text_sha1 = None, None
716
 
            else:
 
731
            if kind == 'file':
717
732
                ie.text_size, ie.text_sha1 = self.get_size_and_sha1(file_id)
718
 
            if (ie.text_size is None) and (kind == 'file'):
719
 
                raise BzrError('Got a text_size of None for file_id %r' % file_id)
 
733
                if ie.text_size is None:
 
734
                    raise BzrError(
 
735
                        'Got a text_size of None for file_id %r' % file_id)
720
736
            inv.add(ie)
721
737
 
722
738
        sorted_entries = self.sorted_path_id()
732
748
    # at that instant
733
749
    inventory = property(_get_inventory)
734
750
 
 
751
    root_inventory = property(_get_inventory)
 
752
 
735
753
    def __iter__(self):
736
754
        for path, entry in self.inventory.iter_entries():
737
755
            yield entry.file_id
738
756
 
 
757
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
758
        # The only files returned by this are those from the version
 
759
        inv = self.inventory
 
760
        if from_dir is None:
 
761
            from_dir_id = None
 
762
        else:
 
763
            from_dir_id = inv.path2id(from_dir)
 
764
            if from_dir_id is None:
 
765
                # Directory not versioned
 
766
                return
 
767
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
 
768
        if inv.root is not None and not include_root and from_dir is None:
 
769
            # skip the root for compatability with the current apis.
 
770
            entries.next()
 
771
        for path, entry in entries:
 
772
            yield path, 'V', entry.kind, entry.file_id, entry
 
773
 
739
774
    def sorted_path_id(self):
740
775
        paths = []
741
776
        for result in self._new_id.iteritems():
742
777
            paths.append(result)
743
 
        for id in self.base_tree:
 
778
        for id in self.base_tree.all_file_ids():
744
779
            path = self.id2path(id)
745
780
            if path is None:
746
781
                continue