~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/bundle_data.py

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 04:10:52 UTC
  • mfrom: (6616.1.1 fix-en-user-guide)
  • Revision ID: pqm@pqm.ubuntu.com-20160421041052-clcye7ns1qcl2n7w
(richard-wilbur) Ensure build of English use guide always uses English text
 even when user's locale specifies a different language. (Jelmer Vernooij)

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
12
12
#
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
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
22
from cStringIO import StringIO
21
23
import os
25
27
    osutils,
26
28
    timestamp,
27
29
    )
28
 
import bzrlib.errors
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,
34
 
                              InventoryLink)
35
 
from bzrlib.osutils import sha_file, sha_string, pathjoin
 
31
from bzrlib.errors import (
 
32
    TestamentMismatch,
 
33
    BzrError,
 
34
    )
 
35
from bzrlib.inventory import (
 
36
    Inventory,
 
37
    InventoryDirectory,
 
38
    InventoryFile,
 
39
    InventoryLink,
 
40
    )
 
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
43
47
 
44
48
 
159
163
    def get_base(self, revision):
160
164
        revision_info = self.get_revision_info(revision.revision_id)
161
165
        if revision_info.base_id is not None:
162
 
            if revision_info.base_id == NULL_REVISION:
163
 
                return None
164
 
            else:
165
 
                return revision_info.base_id
 
166
            return revision_info.base_id
166
167
        if len(revision.parent_ids) == 0:
167
168
            # There is no base listed, and
168
169
            # the lowest revision doesn't have a parent
169
170
            # so this is probably against the empty tree
170
 
            # and thus base truly is None
171
 
            return None
 
171
            # and thus base truly is NULL_REVISION
 
172
            return NULL_REVISION
172
173
        else:
173
174
            return revision.parent_ids[-1]
174
175
 
203
204
            self._validate_references_from_repository(repository)
204
205
        revision_info = self.get_revision_info(revision_id)
205
206
        inventory_revision_id = revision_id
206
 
        bundle_tree = BundleTree(repository.revision_tree(base), 
 
207
        bundle_tree = BundleTree(repository.revision_tree(base),
207
208
                                  inventory_revision_id)
208
209
        self._update_tree(bundle_tree, revision_id)
209
210
 
210
211
        inv = bundle_tree.inventory
211
212
        self._validate_inventory(inv, revision_id)
212
 
        self._validate_revision(inv, revision_id)
 
213
        self._validate_revision(bundle_tree, revision_id)
213
214
 
214
215
        return bundle_tree
215
216
 
242
243
        for rev_info in self.revisions:
243
244
            checked[rev_info.revision_id] = True
244
245
            add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
245
 
                
 
246
 
246
247
        for (rev, rev_info) in zip(self.real_revisions, self.revisions):
247
248
            add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
248
249
 
250
251
        missing = {}
251
252
        for revision_id, sha1 in rev_to_sha.iteritems():
252
253
            if repository.has_revision(revision_id):
253
 
                testament = StrictTestament.from_revision(repository, 
 
254
                testament = StrictTestament.from_revision(repository,
254
255
                                                          revision_id)
255
256
                local_sha1 = self._testament_sha1_from_revision(repository,
256
257
                                                                revision_id)
257
258
                if sha1 != local_sha1:
258
 
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
 
259
                    raise BzrError('sha1 mismatch. For revision id {%s}'
259
260
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
260
261
                else:
261
262
                    count += 1
281
282
        if rev.revision_id != revision_id:
282
283
            raise AssertionError()
283
284
        if sha1 != rev.inventory_sha1:
284
 
            open(',,bogus-inv', 'wb').write(s)
 
285
            f = open(',,bogus-inv', 'wb')
 
286
            try:
 
287
                f.write(s)
 
288
            finally:
 
289
                f.close()
285
290
            warning('Inventory sha hash mismatch for revision %s. %s'
286
291
                    ' != %s' % (revision_id, sha1, rev.inventory_sha1))
287
292
 
288
 
    def _validate_revision(self, inventory, revision_id):
 
293
    def _validate_revision(self, tree, revision_id):
289
294
        """Make sure all revision entries match their checksum."""
290
295
 
291
 
        # 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
292
297
        rev_to_sha1 = {}
293
 
        
 
298
 
294
299
        rev = self.get_revision(revision_id)
295
300
        rev_info = self.get_revision_info(revision_id)
296
301
        if not (rev.revision_id == rev_info.revision_id):
297
302
            raise AssertionError()
298
303
        if not (rev.revision_id == revision_id):
299
304
            raise AssertionError()
300
 
        sha1 = self._testament_sha1(rev, inventory)
 
305
        sha1 = self._testament_sha1(rev, tree)
301
306
        if sha1 != rev_info.sha1:
302
307
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
303
308
        if rev.revision_id in rev_to_sha1:
330
335
                try:
331
336
                    name, value = info_item.split(':', 1)
332
337
                except ValueError:
333
 
                    raise 'Value %r has no colon' % info_item
 
338
                    raise ValueError('Value %r has no colon' % info_item)
334
339
                if name == 'last-changed':
335
340
                    last_changed = value
336
341
                elif name == 'executable':
414
419
            revision = get_rev_id(last_modified, path, kind)
415
420
            if lines:
416
421
                do_patch(path, lines, encoding)
417
 
            
 
422
 
418
423
        valid_actions = {
419
424
            'renamed':renamed,
420
425
            'removed':removed,
461
466
 
462
467
 
463
468
class BundleTree(Tree):
 
469
 
464
470
    def __init__(self, base_tree, revision_id):
465
471
        self.base_tree = base_tree
466
472
        self._renamed = {} # Mapping from old_path => new_path
542
548
        #renamed_r
543
549
        if old_path in self._renamed_r:
544
550
            return None
545
 
        return old_path 
 
551
        return old_path
546
552
 
547
553
    def new_path(self, old_path):
548
554
        """Get the new_path (path in the target_tree) for the file at old_path
568
574
        #renamed_r
569
575
        if new_path in self._renamed:
570
576
            return None
571
 
        return new_path 
 
577
        return new_path
 
578
 
 
579
    def get_root_id(self):
 
580
        return self.path2id('')
572
581
 
573
582
    def path2id(self, path):
574
583
        """Return the id of the file present at path in the target tree."""
580
589
            return None
581
590
        if old_path in self.deleted:
582
591
            return None
583
 
        if getattr(self.base_tree, 'path2id', None) is not None:
584
 
            return self.base_tree.path2id(old_path)
585
 
        else:
586
 
            return self.base_tree.inventory.path2id(old_path)
 
592
        return self.base_tree.path2id(old_path)
587
593
 
588
594
    def id2path(self, file_id):
589
595
        """Return the new path in the target tree of the file with id file_id"""
608
614
                return None
609
615
        new_path = self.id2path(file_id)
610
616
        return self.base_tree.path2id(new_path)
611
 
        
 
617
 
612
618
    def get_file(self, file_id):
613
619
        """Return a file-like object containing the new contents of the
614
620
        file given by file_id.
619
625
        """
620
626
        base_id = self.old_contents_id(file_id)
621
627
        if (base_id is not None and
622
 
            base_id != self.base_tree.inventory.root.file_id):
 
628
            base_id != self.base_tree.get_root_id()):
623
629
            patch_original = self.base_tree.get_file(base_id)
624
630
        else:
625
631
            patch_original = None
626
632
        file_patch = self.patches.get(self.id2path(file_id))
627
633
        if file_patch is None:
628
 
            if (patch_original is None and 
629
 
                self.get_kind(file_id) == 'directory'):
 
634
            if (patch_original is None and
 
635
                self.kind(file_id) == 'directory'):
630
636
                return StringIO()
631
637
            if patch_original is None:
632
638
                raise AssertionError("None: %s" % file_id)
637
643
                'Malformed patch for %s, %r' % (file_id, file_patch))
638
644
        return patched_file(file_patch, patch_original)
639
645
 
640
 
    def get_symlink_target(self, file_id):
641
 
        new_path = self.id2path(file_id)
 
646
    def get_symlink_target(self, file_id, path=None):
 
647
        if path is None:
 
648
            path = self.id2path(file_id)
642
649
        try:
643
 
            return self._targets[new_path]
 
650
            return self._targets[path]
644
651
        except KeyError:
645
652
            return self.base_tree.get_symlink_target(file_id)
646
653
 
647
 
    def get_kind(self, file_id):
 
654
    def kind(self, file_id):
648
655
        if file_id in self._kinds:
649
656
            return self._kinds[file_id]
650
 
        return self.base_tree.inventory[file_id].kind
 
657
        return self.base_tree.kind(file_id)
 
658
 
 
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]
 
663
        else:
 
664
            return self.base_tree.get_file_revision(file_id)
651
665
 
652
666
    def is_executable(self, file_id):
653
667
        path = self.id2path(file_id)
654
668
        if path in self._executable:
655
669
            return self._executable[path]
656
670
        else:
657
 
            return self.base_tree.inventory[file_id].executable
 
671
            return self.base_tree.is_executable(file_id)
658
672
 
659
673
    def get_last_changed(self, file_id):
660
674
        path = self.id2path(file_id)
661
675
        if path in self._last_changed:
662
676
            return self._last_changed[path]
663
 
        return self.base_tree.inventory[file_id].revision
 
677
        return self.base_tree.get_file_revision(file_id)
664
678
 
665
679
    def get_size_and_sha1(self, file_id):
666
680
        """Return the size and sha1 hash of the given file id.
673
687
        if new_path not in self.patches:
674
688
            # If the entry does not have a patch, then the
675
689
            # contents must be the same as in the base_tree
676
 
            ie = self.base_tree.inventory[file_id]
677
 
            if ie.text_size is None:
678
 
                return ie.text_size, ie.text_sha1
679
 
            return int(ie.text_size), ie.text_sha1
 
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
680
693
        fileobj = self.get_file(file_id)
681
694
        content = fileobj.read()
682
695
        return len(content), sha_string(content)
687
700
        This need to be called before ever accessing self.inventory
688
701
        """
689
702
        from os.path import dirname, basename
690
 
        base_inv = self.base_tree.inventory
691
703
        inv = Inventory(None, self.revision_id)
692
704
 
693
705
        def add_entry(file_id):
700
712
                parent_path = dirname(path)
701
713
                parent_id = self.path2id(parent_path)
702
714
 
703
 
            kind = self.get_kind(file_id)
 
715
            kind = self.kind(file_id)
704
716
            revision_id = self.get_last_changed(file_id)
705
717
 
706
718
            name = basename(path)
711
723
                ie.executable = self.is_executable(file_id)
712
724
            elif kind == 'symlink':
713
725
                ie = InventoryLink(file_id, name, parent_id)
714
 
                ie.symlink_target = self.get_symlink_target(file_id)
 
726
                ie.symlink_target = self.get_symlink_target(file_id, path)
715
727
            ie.revision = revision_id
716
728
 
717
 
            if kind in ('directory', 'symlink'):
718
 
                ie.text_size, ie.text_sha1 = None, None
719
 
            else:
 
729
            if kind == 'file':
720
730
                ie.text_size, ie.text_sha1 = self.get_size_and_sha1(file_id)
721
 
            if (ie.text_size is None) and (kind == 'file'):
722
 
                raise BzrError('Got a text_size of None for file_id %r' % file_id)
 
731
                if ie.text_size is None:
 
732
                    raise BzrError(
 
733
                        'Got a text_size of None for file_id %r' % file_id)
723
734
            inv.add(ie)
724
735
 
725
736
        sorted_entries = self.sorted_path_id()
735
746
    # at that instant
736
747
    inventory = property(_get_inventory)
737
748
 
738
 
    def __iter__(self):
739
 
        for path, entry in self.inventory.iter_entries():
740
 
            yield entry.file_id
 
749
    root_inventory = property(_get_inventory)
 
750
 
 
751
    def all_file_ids(self):
 
752
        return set(
 
753
            [entry.file_id for path, entry in self.inventory.iter_entries()])
 
754
 
 
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
 
757
        inv = self.inventory
 
758
        if from_dir is None:
 
759
            from_dir_id = None
 
760
        else:
 
761
            from_dir_id = inv.path2id(from_dir)
 
762
            if from_dir_id is None:
 
763
                # Directory not versioned
 
764
                return
 
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.
 
768
            entries.next()
 
769
        for path, entry in entries:
 
770
            yield path, 'V', entry.kind, entry.file_id, entry
741
771
 
742
772
    def sorted_path_id(self):
743
773
        paths = []
744
774
        for result in self._new_id.iteritems():
745
775
            paths.append(result)
746
 
        for id in self.base_tree:
 
776
        for id in self.base_tree.all_file_ids():
747
777
            path = self.id2path(id)
748
778
            if path is None:
749
779
                continue