1
# Copyright (C) 2005-2010 Canonical Ltd
1
# Copyright (C) 2006 Canonical Ltd
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
30
29
from bzrlib.bundle import apply_bundle
31
from bzrlib.errors import (
35
from bzrlib.inventory import (
41
from bzrlib.osutils import sha_string, pathjoin
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
42
36
from bzrlib.revision import Revision, NULL_REVISION
43
37
from bzrlib.testament import StrictTestament
44
38
from bzrlib.trace import mutter, warning
39
import bzrlib.transport
45
40
from bzrlib.tree import Tree
41
import bzrlib.urlutils
46
42
from bzrlib.xml5 import serializer_v5
211
207
inv = bundle_tree.inventory
212
208
self._validate_inventory(inv, revision_id)
213
self._validate_revision(bundle_tree, revision_id)
209
self._validate_revision(inv, revision_id)
215
211
return bundle_tree
282
278
if rev.revision_id != revision_id:
283
279
raise AssertionError()
284
280
if sha1 != rev.inventory_sha1:
285
f = open(',,bogus-inv', 'wb')
281
open(',,bogus-inv', 'wb').write(s)
290
282
warning('Inventory sha hash mismatch for revision %s. %s'
291
283
' != %s' % (revision_id, sha1, rev.inventory_sha1))
293
def _validate_revision(self, tree, revision_id):
285
def _validate_revision(self, inventory, revision_id):
294
286
"""Make sure all revision entries match their checksum."""
296
# This is a mapping from each revision id to its sha hash
288
# This is a mapping from each revision id to it's sha hash
299
291
rev = self.get_revision(revision_id)
302
294
raise AssertionError()
303
295
if not (rev.revision_id == revision_id):
304
296
raise AssertionError()
305
sha1 = self._testament_sha1(rev, tree)
297
sha1 = self._testament_sha1(rev, inventory)
306
298
if sha1 != rev_info.sha1:
307
299
raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
308
300
if rev.revision_id in rev_to_sha1:
336
328
name, value = info_item.split(':', 1)
337
329
except ValueError:
338
raise ValueError('Value %r has no colon' % info_item)
330
raise 'Value %r has no colon' % info_item
339
331
if name == 'last-changed':
340
332
last_changed = value
341
333
elif name == 'executable':
590
578
if old_path in self.deleted:
592
return self.base_tree.path2id(old_path)
580
if getattr(self.base_tree, 'path2id', None) is not None:
581
return self.base_tree.path2id(old_path)
583
return self.base_tree.inventory.path2id(old_path)
594
585
def id2path(self, file_id):
595
586
"""Return the new path in the target tree of the file with id file_id"""
626
617
base_id = self.old_contents_id(file_id)
627
618
if (base_id is not None and
628
base_id != self.base_tree.get_root_id()):
619
base_id != self.base_tree.inventory.root.file_id):
629
620
patch_original = self.base_tree.get_file(base_id)
631
622
patch_original = None
632
623
file_patch = self.patches.get(self.id2path(file_id))
633
624
if file_patch is None:
634
625
if (patch_original is None and
635
self.kind(file_id) == 'directory'):
626
self.get_kind(file_id) == 'directory'):
636
627
return StringIO()
637
628
if patch_original is None:
638
629
raise AssertionError("None: %s" % file_id)
643
634
'Malformed patch for %s, %r' % (file_id, file_patch))
644
635
return patched_file(file_patch, patch_original)
646
def get_symlink_target(self, file_id, path=None):
648
path = self.id2path(file_id)
637
def get_symlink_target(self, file_id):
638
new_path = self.id2path(file_id)
650
return self._targets[path]
640
return self._targets[new_path]
652
642
return self.base_tree.get_symlink_target(file_id)
654
def kind(self, file_id):
644
def get_kind(self, file_id):
655
645
if file_id in self._kinds:
656
646
return self._kinds[file_id]
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)
647
return self.base_tree.inventory[file_id].kind
666
649
def is_executable(self, file_id):
667
650
path = self.id2path(file_id)
668
651
if path in self._executable:
669
652
return self._executable[path]
671
return self.base_tree.is_executable(file_id)
654
return self.base_tree.inventory[file_id].executable
673
656
def get_last_changed(self, file_id):
674
657
path = self.id2path(file_id)
675
658
if path in self._last_changed:
676
659
return self._last_changed[path]
677
return self.base_tree.get_file_revision(file_id)
660
return self.base_tree.inventory[file_id].revision
679
662
def get_size_and_sha1(self, file_id):
680
663
"""Return the size and sha1 hash of the given file id.
687
670
if new_path not in self.patches:
688
671
# If the entry does not have a patch, then the
689
672
# contents must be the same as in the base_tree
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
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
693
677
fileobj = self.get_file(file_id)
694
678
content = fileobj.read()
695
679
return len(content), sha_string(content)
700
684
This need to be called before ever accessing self.inventory
702
686
from os.path import dirname, basename
687
base_inv = self.base_tree.inventory
703
688
inv = Inventory(None, self.revision_id)
705
690
def add_entry(file_id):
712
697
parent_path = dirname(path)
713
698
parent_id = self.path2id(parent_path)
715
kind = self.kind(file_id)
700
kind = self.get_kind(file_id)
716
701
revision_id = self.get_last_changed(file_id)
718
703
name = basename(path)
723
708
ie.executable = self.is_executable(file_id)
724
709
elif kind == 'symlink':
725
710
ie = InventoryLink(file_id, name, parent_id)
726
ie.symlink_target = self.get_symlink_target(file_id, path)
711
ie.symlink_target = self.get_symlink_target(file_id)
727
712
ie.revision = revision_id
714
if kind in ('directory', 'symlink'):
715
ie.text_size, ie.text_sha1 = None, None
730
717
ie.text_size, ie.text_sha1 = self.get_size_and_sha1(file_id)
731
if ie.text_size is None:
733
'Got a text_size of None for file_id %r' % 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)
736
722
sorted_entries = self.sorted_path_id()
746
732
# at that instant
747
733
inventory = property(_get_inventory)
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
736
for path, entry in self.inventory.iter_entries():
772
739
def sorted_path_id(self):
774
741
for result in self._new_id.iteritems():
775
742
paths.append(result)
776
for id in self.base_tree.all_file_ids():
743
for id in self.base_tree:
777
744
path = self.id2path(id)