75
75
def as_revision(self):
76
76
rev = Revision(revision_id=self.revision_id,
77
committer=self.committer,
78
timestamp=float(self.timestamp),
79
timezone=int(self.timezone),
80
inventory_sha1=self.inventory_sha1,
81
message='\n'.join(self.message))
77
committer=self.committer,
78
timestamp=float(self.timestamp),
79
timezone=int(self.timezone),
80
inventory_sha1=self.inventory_sha1,
81
message='\n'.join(self.message))
83
83
if self.parent_ids:
84
84
rev.parent_ids.extend(self.parent_ids)
229
231
def add_sha(d, revision_id, sha1):
230
232
if revision_id is None:
231
233
if sha1 is not None:
232
234
raise BzrError('A Null revision should always'
233
'have a null sha1 hash')
235
'have a null sha1 hash')
235
237
if revision_id in d:
236
238
# This really should have been validated as part
237
239
# of _validate_revisions but lets do it again
238
240
if sha1 != d[revision_id]:
239
241
raise BzrError('** Revision %r referenced with 2 different'
240
' sha hashes %s != %s' % (revision_id,
241
sha1, d[revision_id]))
242
' sha hashes %s != %s' % (revision_id,
243
sha1, d[revision_id]))
243
245
d[revision_id] = sha1
263
265
if sha1 != local_sha1:
264
266
raise BzrError('sha1 mismatch. For revision id {%s}'
265
'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
267
'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
268
270
elif revision_id not in checked:
309
311
raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
310
312
if rev.revision_id in rev_to_sha1:
311
313
raise BzrError('Revision {%s} given twice in the list'
313
315
rev_to_sha1[rev.revision_id] = sha1
315
317
def _update_tree(self, bundle_tree, revision_id):
388
390
info = extra.split(' // ')
389
391
if len(info) <= 1:
390
392
raise BzrError('add action lines require the path and file id'
392
394
elif len(info) > 5:
393
395
raise BzrError('add action lines have fewer than 5 entries.'
396
398
if not info[1].startswith('file-id:'):
397
399
raise BzrError('The file-id should follow the path for an add'
399
401
# This will be Unicode because of how the stream is read. Turn it
400
402
# back into a utf8 file_id
401
403
file_id = cache_utf8.encode(info[1][8:])
422
424
do_patch(path, lines, encoding)
424
426
valid_actions = {
430
432
for action_line, lines in \
431
self.get_revision_info(revision_id).tree_actions:
433
self.get_revision_info(revision_id).tree_actions:
432
434
first = action_line.find(' ')
434
436
raise BzrError('Bogus action line'
435
' (no opening space): %r' % action_line)
436
second = action_line.find(' ', first+1)
437
' (no opening space): %r' % action_line)
438
second = action_line.find(' ', first + 1)
438
440
raise BzrError('Bogus action line'
439
' (missing second space): %r' % action_line)
441
' (missing second space): %r' % action_line)
440
442
action = action_line[:first]
441
kind = action_line[first+1:second]
443
kind = action_line[first + 1:second]
442
444
if kind not in ('file', 'directory', 'symlink'):
443
445
raise BzrError('Bogus action line'
444
' (invalid object kind %r): %r' % (kind, action_line))
445
extra = action_line[second+1:]
446
' (invalid object kind %r): %r' % (kind, action_line))
447
extra = action_line[second + 1:]
447
449
if action not in valid_actions:
448
450
raise BzrError('Bogus action line'
449
' (unrecognized action): %r' % action_line)
451
' (unrecognized action): %r' % action_line)
450
452
valid_actions[action](kind, extra, lines)
452
454
def install_revisions(self, target_repo, stream_input=True):
471
473
def __init__(self, base_tree, revision_id):
472
474
self.base_tree = base_tree
473
self._renamed = {} # Mapping from old_path => new_path
474
self._renamed_r = {} # new_path => old_path
475
self._new_id = {} # new_path => new_id
476
self._new_id_r = {} # new_id => new_path
477
self._kinds = {} # new_path => kind
478
self._last_changed = {} # new_id => revision_id
479
self._executable = {} # new_id => executable value
475
self._renamed = {} # Mapping from old_path => new_path
476
self._renamed_r = {} # new_path => old_path
477
self._new_id = {} # new_path => new_id
478
self._new_id_r = {} # new_id => new_path
479
self._kinds = {} # new_path => kind
480
self._last_changed = {} # new_id => revision_id
481
self._executable = {} # new_id => executable value
480
482
self.patches = {}
481
self._targets = {} # new path => new symlink target
483
self._targets = {} # new path => new symlink target
482
484
self.deleted = []
483
485
self.contents_by_id = True
484
486
self.revision_id = revision_id
506
508
if (file_id in self._last_changed
507
509
and self._last_changed[file_id] != revision_id):
508
510
raise BzrError('Mismatched last-changed revision for file_id {%s}'
509
': %s != %s' % (file_id,
510
self._last_changed[file_id],
511
': %s != %s' % (file_id,
512
self._last_changed[file_id],
512
514
self._last_changed[file_id] = revision_id
514
516
def note_patch(self, new_path, patch):
624
626
in the text-store, so that the file contents would
628
file_id = self.path2id(path)
629
file_id = self.path2id(path)
629
630
base_id = self.old_contents_id(file_id)
630
631
if (base_id is not None and
631
base_id != self.base_tree.get_root_id()):
632
old_path = self.old_path(path)
633
patch_original = self.base_tree.get_file(
632
base_id != self.base_tree.get_root_id()):
633
old_path = self.base_tree.id2path(base_id)
634
patch_original = self.base_tree.get_file(old_path)
636
636
patch_original = None
637
637
file_patch = self.patches.get(path)
638
638
if file_patch is None:
639
639
if (patch_original is None and
640
self.kind(path, file_id) == 'directory'):
640
self.kind(path) == 'directory'):
642
642
if patch_original is None:
643
643
raise AssertionError("None: %s" % file_id)
648
648
'Malformed patch for %s, %r' % (file_id, file_patch))
649
649
return patched_file(file_patch, patch_original)
651
def get_symlink_target(self, path, file_id=None):
651
def get_symlink_target(self, path):
653
653
return self._targets[path]
655
655
old_path = self.old_path(path)
656
return self.base_tree.get_symlink_target(old_path, file_id)
656
return self.base_tree.get_symlink_target(old_path)
658
def kind(self, path, file_id=None):
658
def kind(self, path):
660
660
return self._kinds[path]
662
662
old_path = self.old_path(path)
663
return self.base_tree.kind(old_path, file_id)
663
return self.base_tree.kind(old_path)
665
def get_file_revision(self, path, file_id=None):
665
def get_file_revision(self, path):
666
666
if path in self._last_changed:
667
667
return self._last_changed[path]
669
669
old_path = self.old_path(path)
670
return self.base_tree.get_file_revision(old_path, file_id)
670
return self.base_tree.get_file_revision(old_path)
672
def is_executable(self, path, file_id=None):
672
def is_executable(self, path):
673
673
if path in self._executable:
674
674
return self._executable[path]
676
676
old_path = self.old_path(path)
677
return self.base_tree.is_executable(old_path, file_id)
677
return self.base_tree.is_executable(old_path)
679
def get_last_changed(self, path, file_id=None):
679
def get_last_changed(self, path):
680
680
if path in self._last_changed:
681
681
return self._last_changed[path]
682
682
old_path = self.old_path(path)
683
return self.base_tree.get_file_revision(old_path, file_id)
683
return self.base_tree.get_file_revision(old_path)
685
def get_size_and_sha1(self, new_path, file_id=None):
685
def get_size_and_sha1(self, new_path):
686
686
"""Return the size and sha1 hash of the given file id.
687
687
If the file was not locally modified, this is extracted
688
688
from the base_tree. Rather than re-reading the file.
693
693
# If the entry does not have a patch, then the
694
694
# contents must be the same as in the base_tree
695
695
base_path = self.old_path(new_path)
696
text_size = self.base_tree.get_file_size(base_path, file_id)
697
text_sha1 = self.base_tree.get_file_sha1(base_path, file_id)
696
text_size = self.base_tree.get_file_size(base_path)
697
text_sha1 = self.base_tree.get_file_sha1(base_path)
698
698
return text_size, text_sha1
699
fileobj = self.get_file(new_path, file_id)
699
fileobj = self.get_file(new_path)
700
700
content = fileobj.read()
701
701
return len(content), sha_string(content)
715
715
parent_path = dirname(path)
716
716
parent_id = self.path2id(parent_path)
718
kind = self.kind(path, file_id)
719
revision_id = self.get_last_changed(path, file_id)
718
kind = self.kind(path)
719
revision_id = self.get_last_changed(path)
721
721
name = basename(path)
722
722
if kind == 'directory':
723
723
ie = InventoryDirectory(file_id, name, parent_id)
724
724
elif kind == 'file':
725
725
ie = InventoryFile(file_id, name, parent_id)
726
ie.executable = self.is_executable(path, file_id)
726
ie.executable = self.is_executable(path)
727
727
elif kind == 'symlink':
728
728
ie = InventoryLink(file_id, name, parent_id)
729
ie.symlink_target = self.get_symlink_target(path, file_id)
729
ie.symlink_target = self.get_symlink_target(path)
730
730
ie.revision = revision_id
732
732
if kind == 'file':
733
ie.text_size, ie.text_sha1 = self.get_size_and_sha1(
733
ie.text_size, ie.text_sha1 = self.get_size_and_sha1(path)
735
734
if ie.text_size is None:
737
736
'Got a text_size of None for file_id %r' % file_id)
771
770
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
772
771
if inv.root is not None and not include_root and from_dir is None:
773
# skip the root for compatability with the current apis.
772
# skip the root for compatibility with the current apis.
775
774
for path, entry in entries:
776
yield path, 'V', entry.kind, entry.file_id, entry
775
yield path, 'V', entry.kind, entry
778
777
def sorted_path_id(self):