/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/bundle/bundle_data.py

  • Committer: Jelmer Vernooij
  • Date: 2017-06-10 01:35:53 UTC
  • mto: (6670.4.8 move-bzr)
  • mto: This revision was merged to the branch mainline in revision 6681.
  • Revision ID: jelmer@jelmer.uk-20170610013553-560y7mn3su4pp763
Fix remaining tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
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
16
16
 
17
17
"""Read in a bundle stream, and process it into a BundleReader object."""
18
18
 
 
19
from __future__ import absolute_import
 
20
 
19
21
import base64
20
 
from cStringIO import StringIO
21
22
import os
22
23
import pprint
23
24
 
24
 
from bzrlib import (
 
25
from .. import (
 
26
    cache_utf8,
25
27
    osutils,
26
28
    timestamp,
27
29
    )
28
 
import bzrlib.errors
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,
34
 
                              InventoryLink)
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 (
 
32
    TestamentMismatch,
 
33
    BzrError,
 
34
    )
 
35
from ..inventory import (
 
36
    Inventory,
 
37
    InventoryDirectory,
 
38
    InventoryFile,
 
39
    InventoryLink,
 
40
    )
 
41
from ..osutils import sha_string, pathjoin
 
42
from ..revision import Revision, NULL_REVISION
 
43
from ..sixish import (
 
44
    BytesIO,
 
45
    viewitems,
 
46
    )
 
47
from ..testament import StrictTestament
 
48
from ..trace import mutter, warning
 
49
from ..tree import Tree
 
50
from ..xml5 import serializer_v5
43
51
 
44
52
 
45
53
class RevisionInfo(object):
99
107
        revision_info.timestamp = revision.timestamp
100
108
        revision_info.message = revision.message.split('\n')
101
109
        revision_info.properties = [': '.join(p) for p in
102
 
                                    revision.properties.iteritems()]
 
110
                                    viewitems(revision.properties)]
103
111
        return revision_info
104
112
 
105
113
 
136
144
        split up, based on the assumptions that can be made
137
145
        when information is missing.
138
146
        """
139
 
        from bzrlib.timestamp import unpack_highres_date
 
147
        from breezy.timestamp import unpack_highres_date
140
148
        # Put in all of the guessable information.
141
149
        if not self.timestamp and self.date:
142
150
            self.timestamp, self.timezone = unpack_highres_date(self.date)
206
214
 
207
215
        inv = bundle_tree.inventory
208
216
        self._validate_inventory(inv, revision_id)
209
 
        self._validate_revision(inv, revision_id)
 
217
        self._validate_revision(bundle_tree, revision_id)
210
218
 
211
219
        return bundle_tree
212
220
 
245
253
 
246
254
        count = 0
247
255
        missing = {}
248
 
        for revision_id, sha1 in rev_to_sha.iteritems():
 
256
        for revision_id, sha1 in viewitems(rev_to_sha):
249
257
            if repository.has_revision(revision_id):
250
258
                testament = StrictTestament.from_revision(repository,
251
259
                                                          revision_id)
278
286
        if rev.revision_id != revision_id:
279
287
            raise AssertionError()
280
288
        if sha1 != rev.inventory_sha1:
281
 
            open(',,bogus-inv', 'wb').write(s)
 
289
            f = open(',,bogus-inv', 'wb')
 
290
            try:
 
291
                f.write(s)
 
292
            finally:
 
293
                f.close()
282
294
            warning('Inventory sha hash mismatch for revision %s. %s'
283
295
                    ' != %s' % (revision_id, sha1, rev.inventory_sha1))
284
296
 
285
 
    def _validate_revision(self, inventory, revision_id):
 
297
    def _validate_revision(self, tree, revision_id):
286
298
        """Make sure all revision entries match their checksum."""
287
299
 
288
 
        # This is a mapping from each revision id to it's sha hash
 
300
        # This is a mapping from each revision id to its sha hash
289
301
        rev_to_sha1 = {}
290
302
 
291
303
        rev = self.get_revision(revision_id)
294
306
            raise AssertionError()
295
307
        if not (rev.revision_id == revision_id):
296
308
            raise AssertionError()
297
 
        sha1 = self._testament_sha1(rev, inventory)
 
309
        sha1 = self._testament_sha1(rev, tree)
298
310
        if sha1 != rev_info.sha1:
299
311
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
300
312
        if rev.revision_id in rev_to_sha1:
313
325
            if last_changed is not None:
314
326
                # last_changed will be a Unicode string because of how it was
315
327
                # read. Convert it back to utf8.
316
 
                changed_revision_id = osutils.safe_revision_id(last_changed,
317
 
                                                               warn=False)
 
328
                changed_revision_id = cache_utf8.encode(last_changed)
318
329
            else:
319
330
                changed_revision_id = revision_id
320
331
            bundle_tree.note_last_changed(path, changed_revision_id)
327
338
                try:
328
339
                    name, value = info_item.split(':', 1)
329
340
                except ValueError:
330
 
                    raise 'Value %r has no colon' % info_item
 
341
                    raise ValueError('Value %r has no colon' % info_item)
331
342
                if name == 'last-changed':
332
343
                    last_changed = value
333
344
                elif name == 'executable':
389
400
                        ': %r' % extra)
390
401
            # This will be Unicode because of how the stream is read. Turn it
391
402
            # back into a utf8 file_id
392
 
            file_id = osutils.safe_file_id(info[1][8:], warn=False)
 
403
            file_id = cache_utf8.encode(info[1][8:])
393
404
 
394
405
            bundle_tree.note_id(file_id, path, kind)
395
406
            # this will be overridden in extra_info if executable is specified.
458
469
 
459
470
 
460
471
class BundleTree(Tree):
 
472
 
461
473
    def __init__(self, base_tree, revision_id):
462
474
        self.base_tree = base_tree
463
475
        self._renamed = {} # Mapping from old_path => new_path
567
579
            return None
568
580
        return new_path
569
581
 
 
582
    def get_root_id(self):
 
583
        return self.path2id('')
 
584
 
570
585
    def path2id(self, path):
571
586
        """Return the id of the file present at path in the target tree."""
572
587
        file_id = self._new_id.get(path)
577
592
            return None
578
593
        if old_path in self.deleted:
579
594
            return None
580
 
        if getattr(self.base_tree, 'path2id', None) is not None:
581
 
            return self.base_tree.path2id(old_path)
582
 
        else:
583
 
            return self.base_tree.inventory.path2id(old_path)
 
595
        return self.base_tree.path2id(old_path)
584
596
 
585
597
    def id2path(self, file_id):
586
598
        """Return the new path in the target tree of the file with id file_id"""
616
628
        """
617
629
        base_id = self.old_contents_id(file_id)
618
630
        if (base_id is not None and
619
 
            base_id != self.base_tree.inventory.root.file_id):
 
631
            base_id != self.base_tree.get_root_id()):
620
632
            patch_original = self.base_tree.get_file(base_id)
621
633
        else:
622
634
            patch_original = None
623
635
        file_patch = self.patches.get(self.id2path(file_id))
624
636
        if file_patch is None:
625
637
            if (patch_original is None and
626
 
                self.get_kind(file_id) == 'directory'):
627
 
                return StringIO()
 
638
                self.kind(file_id) == 'directory'):
 
639
                return BytesIO()
628
640
            if patch_original is None:
629
641
                raise AssertionError("None: %s" % file_id)
630
642
            return patch_original
634
646
                'Malformed patch for %s, %r' % (file_id, file_patch))
635
647
        return patched_file(file_patch, patch_original)
636
648
 
637
 
    def get_symlink_target(self, file_id):
638
 
        new_path = self.id2path(file_id)
 
649
    def get_symlink_target(self, file_id, path=None):
 
650
        if path is None:
 
651
            path = self.id2path(file_id)
639
652
        try:
640
 
            return self._targets[new_path]
 
653
            return self._targets[path]
641
654
        except KeyError:
642
655
            return self.base_tree.get_symlink_target(file_id)
643
656
 
644
 
    def get_kind(self, file_id):
 
657
    def kind(self, file_id):
645
658
        if file_id in self._kinds:
646
659
            return self._kinds[file_id]
647
 
        return self.base_tree.inventory[file_id].kind
 
660
        return self.base_tree.kind(file_id)
 
661
 
 
662
    def get_file_revision(self, file_id):
 
663
        path = self.id2path(file_id)
 
664
        if path in self._last_changed:
 
665
            return self._last_changed[path]
 
666
        else:
 
667
            return self.base_tree.get_file_revision(file_id)
648
668
 
649
669
    def is_executable(self, file_id):
650
670
        path = self.id2path(file_id)
651
671
        if path in self._executable:
652
672
            return self._executable[path]
653
673
        else:
654
 
            return self.base_tree.inventory[file_id].executable
 
674
            return self.base_tree.is_executable(file_id)
655
675
 
656
676
    def get_last_changed(self, file_id):
657
677
        path = self.id2path(file_id)
658
678
        if path in self._last_changed:
659
679
            return self._last_changed[path]
660
 
        return self.base_tree.inventory[file_id].revision
 
680
        return self.base_tree.get_file_revision(file_id)
661
681
 
662
682
    def get_size_and_sha1(self, file_id):
663
683
        """Return the size and sha1 hash of the given file id.
670
690
        if new_path not in self.patches:
671
691
            # If the entry does not have a patch, then the
672
692
            # 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
 
693
            text_size = self.base_tree.get_file_size(file_id)
 
694
            text_sha1 = self.base_tree.get_file_sha1(file_id)
 
695
            return text_size, text_sha1
677
696
        fileobj = self.get_file(file_id)
678
697
        content = fileobj.read()
679
698
        return len(content), sha_string(content)
684
703
        This need to be called before ever accessing self.inventory
685
704
        """
686
705
        from os.path import dirname, basename
687
 
        base_inv = self.base_tree.inventory
688
706
        inv = Inventory(None, self.revision_id)
689
707
 
690
708
        def add_entry(file_id):
697
715
                parent_path = dirname(path)
698
716
                parent_id = self.path2id(parent_path)
699
717
 
700
 
            kind = self.get_kind(file_id)
 
718
            kind = self.kind(file_id)
701
719
            revision_id = self.get_last_changed(file_id)
702
720
 
703
721
            name = basename(path)
708
726
                ie.executable = self.is_executable(file_id)
709
727
            elif kind == 'symlink':
710
728
                ie = InventoryLink(file_id, name, parent_id)
711
 
                ie.symlink_target = self.get_symlink_target(file_id)
 
729
                ie.symlink_target = self.get_symlink_target(file_id, path)
712
730
            ie.revision = revision_id
713
731
 
714
 
            if kind in ('directory', 'symlink'):
715
 
                ie.text_size, ie.text_sha1 = None, None
716
 
            else:
 
732
            if kind == 'file':
717
733
                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)
 
734
                if ie.text_size is None:
 
735
                    raise BzrError(
 
736
                        'Got a text_size of None for file_id %r' % file_id)
720
737
            inv.add(ie)
721
738
 
722
739
        sorted_entries = self.sorted_path_id()
732
749
    # at that instant
733
750
    inventory = property(_get_inventory)
734
751
 
735
 
    def __iter__(self):
736
 
        for path, entry in self.inventory.iter_entries():
737
 
            yield entry.file_id
 
752
    root_inventory = property(_get_inventory)
 
753
 
 
754
    def all_file_ids(self):
 
755
        return {entry.file_id for path, entry in self.inventory.iter_entries()}
 
756
 
 
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
 
759
        inv = self.inventory
 
760
        if from_dir is None:
 
761
            from_dir_id = None
 
762
        else:
 
763
            from_dir_id = inv.path2id(from_dir)
 
764
            if from_dir_id is None:
 
765
                # Directory not versioned
 
766
                return
 
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.
 
770
            next(entries)
 
771
        for path, entry in entries:
 
772
            yield path, 'V', entry.kind, entry.file_id, entry
738
773
 
739
774
    def sorted_path_id(self):
740
775
        paths = []
741
 
        for result in self._new_id.iteritems():
 
776
        for result in viewitems(self._new_id):
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)
745
780
            if path is None:
746
781
                continue
751
786
 
752
787
def patched_file(file_patch, original):
753
788
    """Produce a file-like object with the patched version of a text"""
754
 
    from bzrlib.patches import iter_patched
755
 
    from bzrlib.iterablefile import IterableFile
 
789
    from breezy.patches import iter_patched
 
790
    from breezy.iterablefile import IterableFile
756
791
    if file_patch == "":
757
792
        return IterableFile(())
758
793
    # string.splitlines(True) also splits on '\r', but the iter_patched code
759
794
    # only expects to iterate over '\n' style lines
760
795
    return IterableFile(iter_patched(original,
761
 
                StringIO(file_patch).readlines()))
 
796
                BytesIO(file_patch).readlines()))