1
# Copyright (C) 2006 Canonical Ltd
1
# Copyright (C) 2005-2010 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
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
17
17
"""Read in a bundle stream, and process it into a BundleReader object."""
19
from __future__ import absolute_import
20
22
from cStringIO import StringIO
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
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)
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
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)
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)
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,
252
256
local_sha1 = self._testament_sha1_from_revision(repository,
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))
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')
282
290
warning('Inventory sha hash mismatch for revision %s. %s'
283
291
' != %s' % (revision_id, sha1, rev.inventory_sha1))
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."""
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
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:
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':
578
590
if old_path in self.deleted:
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)
592
return self.base_tree.path2id(old_path)
585
594
def id2path(self, file_id):
586
595
"""Return the new path in the target tree of the file with id file_id"""
606
615
new_path = self.id2path(file_id)
607
616
return self.base_tree.path2id(new_path)
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.
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)
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)
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):
648
path = self.id2path(file_id)
640
return self._targets[new_path]
650
return self._targets[path]
642
652
return self.base_tree.get_symlink_target(file_id)
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)
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)
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]
654
return self.base_tree.inventory[file_id].executable
671
return self.base_tree.is_executable(file_id)
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)
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
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)
690
707
def add_entry(file_id):
697
714
parent_path = dirname(path)
698
715
parent_id = self.path2id(parent_path)
700
kind = self.get_kind(file_id)
717
kind = self.kind(file_id)
701
718
revision_id = self.get_last_changed(file_id)
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
714
if kind in ('directory', 'symlink'):
715
ie.text_size, ie.text_sha1 = None, None
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:
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)
751
root_inventory = property(_get_inventory)
735
753
def __iter__(self):
736
754
for path, entry in self.inventory.iter_entries():
737
755
yield entry.file_id
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
763
from_dir_id = inv.path2id(from_dir)
764
if from_dir_id is None:
765
# Directory not versioned
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.
771
for path, entry in entries:
772
yield path, 'V', entry.kind, entry.file_id, entry
739
774
def sorted_path_id(self):
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)