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,
35
from bzrlib.osutils import sha_file, sha_string, pathjoin
31
from bzrlib.errors import (
35
from bzrlib.inventory import (
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
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)
211
215
return bundle_tree
286
290
warning('Inventory sha hash mismatch for revision %s. %s'
287
291
' != %s' % (revision_id, sha1, rev.inventory_sha1))
289
def _validate_revision(self, inventory, revision_id):
293
def _validate_revision(self, tree, revision_id):
290
294
"""Make sure all revision entries match their checksum."""
292
# 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
295
299
rev = self.get_revision(revision_id)
298
302
raise AssertionError()
299
303
if not (rev.revision_id == revision_id):
300
304
raise AssertionError()
301
sha1 = self._testament_sha1(rev, inventory)
305
sha1 = self._testament_sha1(rev, tree)
302
306
if sha1 != rev_info.sha1:
303
307
raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
304
308
if rev.revision_id in rev_to_sha1:
582
590
if old_path in self.deleted:
584
if getattr(self.base_tree, 'path2id', None) is not None:
585
return self.base_tree.path2id(old_path)
587
return self.base_tree.inventory.path2id(old_path)
592
return self.base_tree.path2id(old_path)
589
594
def id2path(self, file_id):
590
595
"""Return the new path in the target tree of the file with id file_id"""
621
626
base_id = self.old_contents_id(file_id)
622
627
if (base_id is not None and
623
base_id != self.base_tree.inventory.root.file_id):
628
base_id != self.base_tree.get_root_id()):
624
629
patch_original = self.base_tree.get_file(base_id)
626
631
patch_original = None
627
632
file_patch = self.patches.get(self.id2path(file_id))
628
633
if file_patch is None:
629
634
if (patch_original is None and
630
self.get_kind(file_id) == 'directory'):
635
self.kind(file_id) == 'directory'):
631
636
return StringIO()
632
637
if patch_original is None:
633
638
raise AssertionError("None: %s" % file_id)
638
643
'Malformed patch for %s, %r' % (file_id, file_patch))
639
644
return patched_file(file_patch, patch_original)
641
def get_symlink_target(self, file_id):
642
new_path = self.id2path(file_id)
646
def get_symlink_target(self, file_id, path=None):
648
path = self.id2path(file_id)
644
return self._targets[new_path]
650
return self._targets[path]
646
652
return self.base_tree.get_symlink_target(file_id)
648
def get_kind(self, file_id):
654
def kind(self, file_id):
649
655
if file_id in self._kinds:
650
656
return self._kinds[file_id]
651
return self.base_tree.inventory[file_id].kind
657
return self.base_tree.kind(file_id)
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]
664
return self.base_tree.get_file_revision(file_id)
653
666
def is_executable(self, file_id):
654
667
path = self.id2path(file_id)
655
668
if path in self._executable:
656
669
return self._executable[path]
658
return self.base_tree.inventory[file_id].executable
671
return self.base_tree.is_executable(file_id)
660
673
def get_last_changed(self, file_id):
661
674
path = self.id2path(file_id)
662
675
if path in self._last_changed:
663
676
return self._last_changed[path]
664
return self.base_tree.inventory[file_id].revision
677
return self.base_tree.get_file_revision(file_id)
666
679
def get_size_and_sha1(self, file_id):
667
680
"""Return the size and sha1 hash of the given file id.
674
687
if new_path not in self.patches:
675
688
# If the entry does not have a patch, then the
676
689
# contents must be the same as in the base_tree
677
ie = self.base_tree.inventory[file_id]
678
if ie.text_size is None:
679
return ie.text_size, ie.text_sha1
680
return int(ie.text_size), ie.text_sha1
690
text_size = self.base_tree.get_file_size(file_id)
691
text_sha1 = self.base_tree.get_file_sha1(file_id)
692
return text_size, text_sha1
681
693
fileobj = self.get_file(file_id)
682
694
content = fileobj.read()
683
695
return len(content), sha_string(content)
688
700
This need to be called before ever accessing self.inventory
690
702
from os.path import dirname, basename
691
base_inv = self.base_tree.inventory
692
703
inv = Inventory(None, self.revision_id)
694
705
def add_entry(file_id):
701
712
parent_path = dirname(path)
702
713
parent_id = self.path2id(parent_path)
704
kind = self.get_kind(file_id)
715
kind = self.kind(file_id)
705
716
revision_id = self.get_last_changed(file_id)
707
718
name = basename(path)
712
723
ie.executable = self.is_executable(file_id)
713
724
elif kind == 'symlink':
714
725
ie = InventoryLink(file_id, name, parent_id)
715
ie.symlink_target = self.get_symlink_target(file_id)
726
ie.symlink_target = self.get_symlink_target(file_id, path)
716
727
ie.revision = revision_id
718
729
if kind == 'file':
735
746
# at that instant
736
747
inventory = property(_get_inventory)
739
for path, entry in self.inventory.iter_entries():
749
root_inventory = property(_get_inventory)
751
def all_file_ids(self):
753
[entry.file_id for path, entry in self.inventory.iter_entries()])
755
def list_files(self, include_root=False, from_dir=None, recursive=True):
756
# The only files returned by this are those from the version
761
from_dir_id = inv.path2id(from_dir)
762
if from_dir_id is None:
763
# Directory not versioned
765
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
766
if inv.root is not None and not include_root and from_dir is None:
767
# skip the root for compatability with the current apis.
769
for path, entry in entries:
770
yield path, 'V', entry.kind, entry.file_id, entry
742
772
def sorted_path_id(self):
744
774
for result in self._new_id.iteritems():
745
775
paths.append(result)
746
for id in self.base_tree:
776
for id in self.base_tree.all_file_ids():
747
777
path = self.id2path(id)