17
17
"""Read in a bundle stream, and process it into a BundleReader object."""
19
from __future__ import absolute_import
20
from cStringIO import StringIO
29
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
36
from bzrlib.revision import Revision, NULL_REVISION
37
from bzrlib.testament import StrictTestament
38
from bzrlib.trace import mutter, warning
39
import bzrlib.transport
40
from bzrlib.tree import Tree
41
import bzrlib.urlutils
42
from bzrlib.xml5 import serializer_v5
30
from . import apply_bundle
31
from ..errors import (
37
from ..bzr.inventory import (
43
from ..osutils import sha_string, pathjoin
44
from ..revision import Revision, NULL_REVISION
45
from ..sixish import (
49
from ..testament import StrictTestament
50
from ..trace import mutter, warning
51
from ..tree import Tree
52
from ..bzr.xml5 import serializer_v5
45
55
class RevisionInfo(object):
278
288
if rev.revision_id != revision_id:
279
289
raise AssertionError()
280
290
if sha1 != rev.inventory_sha1:
281
open(',,bogus-inv', 'wb').write(s)
291
f = open(',,bogus-inv', 'wb')
282
296
warning('Inventory sha hash mismatch for revision %s. %s'
283
297
' != %s' % (revision_id, sha1, rev.inventory_sha1))
285
def _validate_revision(self, inventory, revision_id):
299
def _validate_revision(self, tree, revision_id):
286
300
"""Make sure all revision entries match their checksum."""
288
# This is a mapping from each revision id to it's sha hash
302
# This is a mapping from each revision id to its sha hash
291
305
rev = self.get_revision(revision_id)
614
628
in the text-store, so that the file contents would
617
base_id = self.old_contents_id(file_id)
618
if (base_id is not None and
619
base_id != self.base_tree.inventory.root.file_id):
620
patch_original = self.base_tree.get_file(base_id)
632
patch_original = self.base_tree.get_file(path, file_id)
633
except (NoSuchId, NoSuchFile):
622
634
patch_original = None
623
file_patch = self.patches.get(self.id2path(file_id))
635
file_patch = self.patches.get(path)
624
636
if file_patch is None:
625
637
if (patch_original is None and
626
self.get_kind(file_id) == 'directory'):
638
self.kind(path, file_id) == 'directory'):
628
640
if patch_original is None:
629
raise AssertionError("None: %s" % file_id)
641
raise AssertionError("None: %s" % path)
630
642
return patch_original
632
644
if file_patch.startswith('\\'):
633
645
raise ValueError(
634
'Malformed patch for %s, %r' % (file_id, file_patch))
646
'Malformed patch for %s, %r' % (path, file_patch))
635
647
return patched_file(file_patch, patch_original)
637
def get_symlink_target(self, file_id):
638
new_path = self.id2path(file_id)
649
def get_symlink_target(self, path, file_id=None):
640
return self._targets[new_path]
651
return self._targets[path]
642
return self.base_tree.get_symlink_target(file_id)
653
return self.base_tree.get_symlink_target(path, file_id)
644
def get_kind(self, file_id):
655
def kind(self, path, file_id=None):
657
file_id = self.path2id(path)
645
658
if file_id in self._kinds:
646
659
return self._kinds[file_id]
647
return self.base_tree.inventory[file_id].kind
649
def is_executable(self, file_id):
650
path = self.id2path(file_id)
660
return self.base_tree.kind(path, file_id)
662
def get_file_revision(self, path, file_id=None):
663
if path in self._last_changed:
664
return self._last_changed[path]
666
return self.base_tree.get_file_revision(path, file_id)
668
def is_executable(self, path, file_id=None):
651
669
if path in self._executable:
652
670
return self._executable[path]
654
return self.base_tree.inventory[file_id].executable
672
return self.base_tree.is_executable(path, file_id)
656
674
def get_last_changed(self, file_id):
657
675
path = self.id2path(file_id)
658
676
if path in self._last_changed:
659
677
return self._last_changed[path]
660
return self.base_tree.inventory[file_id].revision
678
base_path = self.base_tree.id2path(file_id)
679
return self.base_tree.get_file_revision(base_path, file_id)
662
def get_size_and_sha1(self, file_id):
681
def get_size_and_sha1(self, new_path, file_id):
663
682
"""Return the size and sha1 hash of the given file id.
664
683
If the file was not locally modified, this is extracted
665
684
from the base_tree. Rather than re-reading the file.
667
new_path = self.id2path(file_id)
668
686
if new_path is None:
669
687
return None, None
670
688
if new_path not in self.patches:
689
base_path = self.base_tree.id2path(file_id)
671
690
# If the entry does not have a patch, then the
672
691
# contents must be the same as in the base_tree
673
ie = self.base_tree.inventory[file_id]
674
if ie.text_size is None:
675
return ie.text_size, ie.text_sha1
676
return int(ie.text_size), ie.text_sha1
677
fileobj = self.get_file(file_id)
692
text_size = self.base_tree.get_file_size(base_path, file_id)
693
text_sha1 = self.base_tree.get_file_sha1(base_path, file_id)
694
return text_size, text_sha1
695
fileobj = self.get_file(new_path, file_id)
678
696
content = fileobj.read()
679
697
return len(content), sha_string(content)
705
722
ie = InventoryDirectory(file_id, name, parent_id)
706
723
elif kind == 'file':
707
724
ie = InventoryFile(file_id, name, parent_id)
708
ie.executable = self.is_executable(file_id)
725
ie.executable = self.is_executable(path, 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(path, file_id)
712
729
ie.revision = revision_id
714
if kind in ('directory', 'symlink'):
715
ie.text_size, ie.text_sha1 = None, None
717
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)
732
ie.text_size, ie.text_sha1 = self.get_size_and_sha1(path, file_id)
733
if ie.text_size is None:
735
'Got a text_size of None for file_id %r' % file_id)
722
738
sorted_entries = self.sorted_path_id()
732
748
# at that instant
733
749
inventory = property(_get_inventory)
736
for path, entry in self.inventory.iter_entries():
751
root_inventory = property(_get_inventory)
753
def all_file_ids(self):
754
return {entry.file_id for path, entry in self.inventory.iter_entries()}
756
def list_files(self, include_root=False, from_dir=None, recursive=True):
757
# The only files returned by this are those from the version
762
from_dir_id = inv.path2id(from_dir)
763
if from_dir_id is None:
764
# Directory not versioned
766
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
767
if inv.root is not None and not include_root and from_dir is None:
768
# skip the root for compatability with the current apis.
770
for path, entry in entries:
771
yield path, 'V', entry.kind, entry.file_id, entry
739
773
def sorted_path_id(self):
741
for result in self._new_id.iteritems():
775
for result in viewitems(self._new_id):
742
776
paths.append(result)
743
for id in self.base_tree:
777
for id in self.base_tree.all_file_ids():
744
778
path = self.id2path(id)