/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 bzrlib/bundle/bundle_data.py

First attempt to merge .dev and resolve the conflicts (but tests are 
failing)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by Canonical Ltd
 
1
# Copyright (C) 2006 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
21
21
import os
22
22
import pprint
23
23
 
 
24
from bzrlib import (
 
25
    osutils,
 
26
    timestamp,
 
27
    )
24
28
import bzrlib.errors
 
29
from bzrlib.bundle import apply_bundle
25
30
from bzrlib.errors import (TestamentMismatch, BzrError, 
26
31
                           MalformedHeader, MalformedPatches, NotABundle)
27
32
from bzrlib.inventory import (Inventory, InventoryEntry,
72
77
        if self.properties:
73
78
            for property in self.properties:
74
79
                key_end = property.find(': ')
75
 
                assert key_end is not None
76
 
                key = property[:key_end].encode('utf-8')
77
 
                value = property[key_end+2:].encode('utf-8')
 
80
                if key_end == -1:
 
81
                    if not property.endswith(':'):
 
82
                        raise ValueError(property)
 
83
                    key = str(property[:-1])
 
84
                    value = ''
 
85
                else:
 
86
                    key = str(property[:key_end])
 
87
                    value = property[key_end+2:]
78
88
                rev.properties[key] = value
79
89
 
80
90
        return rev
81
91
 
 
92
    @staticmethod
 
93
    def from_revision(revision):
 
94
        revision_info = RevisionInfo(revision.revision_id)
 
95
        date = timestamp.format_highres_date(revision.timestamp,
 
96
                                             revision.timezone)
 
97
        revision_info.date = date
 
98
        revision_info.timezone = revision.timezone
 
99
        revision_info.timestamp = revision.timestamp
 
100
        revision_info.message = revision.message.split('\n')
 
101
        revision_info.properties = [': '.join(p) for p in
 
102
                                    revision.properties.iteritems()]
 
103
        return revision_info
 
104
 
82
105
 
83
106
class BundleInfo(object):
84
107
    """This contains the meta information. Stuff that allows you to
85
108
    recreate the revision or inventory XML.
86
109
    """
87
 
    def __init__(self):
 
110
    def __init__(self, bundle_format=None):
 
111
        self.bundle_format = None
88
112
        self.committer = None
89
113
        self.date = None
90
114
        self.message = None
101
125
        self.timestamp = None
102
126
        self.timezone = None
103
127
 
 
128
        # Have we checked the repository yet?
 
129
        self._validated_revisions_against_repo = False
 
130
 
104
131
    def __str__(self):
105
132
        return pprint.pformat(self.__dict__)
106
133
 
109
136
        split up, based on the assumptions that can be made
110
137
        when information is missing.
111
138
        """
112
 
        from bzrlib.bundle.serializer import unpack_highres_date
 
139
        from bzrlib.timestamp import unpack_highres_date
113
140
        # Put in all of the guessable information.
114
141
        if not self.timestamp and self.date:
115
142
            self.timestamp, self.timezone = unpack_highres_date(self.date)
170
197
    def revision_tree(self, repository, revision_id, base=None):
171
198
        revision = self.get_revision(revision_id)
172
199
        base = self.get_base(revision)
173
 
        assert base != revision_id
174
 
        self._validate_references_from_repository(repository)
 
200
        if base == revision_id:
 
201
            raise AssertionError()
 
202
        if not self._validated_revisions_against_repo:
 
203
            self._validate_references_from_repository(repository)
175
204
        revision_info = self.get_revision_info(revision_id)
176
205
        inventory_revision_id = revision_id
177
206
        bundle_tree = BundleTree(repository.revision_tree(base), 
223
252
            if repository.has_revision(revision_id):
224
253
                testament = StrictTestament.from_revision(repository, 
225
254
                                                          revision_id)
226
 
                local_sha1 = testament.as_sha1()
 
255
                local_sha1 = self._testament_sha1_from_revision(repository,
 
256
                                                                revision_id)
227
257
                if sha1 != local_sha1:
228
258
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
229
259
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
232
262
            elif revision_id not in checked:
233
263
                missing[revision_id] = sha1
234
264
 
235
 
        for inv_id, sha1 in inv_to_sha.iteritems():
236
 
            if repository.has_revision(inv_id):
237
 
                # Note: branch.get_inventory_sha1() just returns the value that
238
 
                # is stored in the revision text, and that value may be out
239
 
                # of date. This is bogus, because that means we aren't
240
 
                # validating the actual text, just that we wrote and read the
241
 
                # string. But for now, what the hell.
242
 
                local_sha1 = repository.get_inventory_sha1(inv_id)
243
 
                if sha1 != local_sha1:
244
 
                    raise BzrError('sha1 mismatch. For inventory id {%s}' 
245
 
                                   'local: %s, bundle: %s' % 
246
 
                                   (inv_id, local_sha1, sha1))
247
 
                else:
248
 
                    count += 1
249
 
 
250
265
        if len(missing) > 0:
251
266
            # I don't know if this is an error yet
252
267
            warning('Not all revision hashes could be validated.'
253
268
                    ' Unable validate %d hashes' % len(missing))
254
269
        mutter('Verified %d sha hashes for the bundle.' % count)
 
270
        self._validated_revisions_against_repo = True
255
271
 
256
272
    def _validate_inventory(self, inv, revision_id):
257
273
        """At this point we should have generated the BundleTree,
258
274
        so build up an inventory, and make sure the hashes match.
259
275
        """
260
 
 
261
 
        assert inv is not None
262
 
 
263
276
        # Now we should have a complete inventory entry.
264
277
        s = serializer_v5.write_inventory_to_string(inv)
265
278
        sha1 = sha_string(s)
266
279
        # Target revision is the last entry in the real_revisions list
267
280
        rev = self.get_revision(revision_id)
268
 
        assert rev.revision_id == revision_id
 
281
        if rev.revision_id != revision_id:
 
282
            raise AssertionError()
269
283
        if sha1 != rev.inventory_sha1:
270
284
            open(',,bogus-inv', 'wb').write(s)
271
285
            warning('Inventory sha hash mismatch for revision %s. %s'
279
293
        
280
294
        rev = self.get_revision(revision_id)
281
295
        rev_info = self.get_revision_info(revision_id)
282
 
        assert rev.revision_id == rev_info.revision_id
283
 
        assert rev.revision_id == revision_id
284
 
        sha1 = StrictTestament(rev, inventory).as_sha1()
 
296
        if not (rev.revision_id == rev_info.revision_id):
 
297
            raise AssertionError()
 
298
        if not (rev.revision_id == revision_id):
 
299
            raise AssertionError()
 
300
        sha1 = self._testament_sha1(rev, inventory)
285
301
        if sha1 != rev_info.sha1:
286
302
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
287
303
        if rev.revision_id in rev_to_sha1:
298
314
 
299
315
        def get_rev_id(last_changed, path, kind):
300
316
            if last_changed is not None:
301
 
                changed_revision_id = last_changed.decode('utf-8')
 
317
                # last_changed will be a Unicode string because of how it was
 
318
                # read. Convert it back to utf8.
 
319
                changed_revision_id = osutils.safe_revision_id(last_changed,
 
320
                                                               warn=False)
302
321
            else:
303
322
                changed_revision_id = revision_id
304
323
            bundle_tree.note_last_changed(path, changed_revision_id)
315
334
                if name == 'last-changed':
316
335
                    last_changed = value
317
336
                elif name == 'executable':
318
 
                    assert value in ('yes', 'no'), value
319
337
                    val = (value == 'yes')
320
338
                    bundle_tree.note_executable(new_path, val)
321
339
                elif name == 'target':
325
343
            return last_changed, encoding
326
344
 
327
345
        def do_patch(path, lines, encoding):
328
 
            if encoding is not None:
329
 
                assert encoding == 'base64'
 
346
            if encoding == 'base64':
330
347
                patch = base64.decodestring(''.join(lines))
331
 
            else:
 
348
            elif encoding is None:
332
349
                patch =  ''.join(lines)
 
350
            else:
 
351
                raise ValueError(encoding)
333
352
            bundle_tree.note_patch(path, patch)
334
353
 
335
354
        def renamed(kind, extra, lines):
371
390
            if not info[1].startswith('file-id:'):
372
391
                raise BzrError('The file-id should follow the path for an add'
373
392
                        ': %r' % extra)
374
 
            file_id = info[1][8:]
 
393
            # This will be Unicode because of how the stream is read. Turn it
 
394
            # back into a utf8 file_id
 
395
            file_id = osutils.safe_file_id(info[1][8:], warn=False)
375
396
 
376
397
            bundle_tree.note_id(file_id, path, kind)
377
398
            # this will be overridden in extra_info if executable is specified.
422
443
                        ' (unrecognized action): %r' % action_line)
423
444
            valid_actions[action](kind, extra, lines)
424
445
 
 
446
    def install_revisions(self, target_repo, stream_input=True):
 
447
        """Install revisions and return the target revision
 
448
 
 
449
        :param target_repo: The repository to install into
 
450
        :param stream_input: Ignored by this implementation.
 
451
        """
 
452
        apply_bundle.install_bundle(target_repo, self)
 
453
        return self.target
 
454
 
 
455
    def get_merge_request(self, target_repo):
 
456
        """Provide data for performing a merge
 
457
 
 
458
        Returns suggested base, suggested target, and patch verification status
 
459
        """
 
460
        return None, self.target, 'inapplicable'
 
461
 
425
462
 
426
463
class BundleTree(Tree):
427
464
    def __init__(self, base_tree, revision_id):
445
482
 
446
483
    def note_rename(self, old_path, new_path):
447
484
        """A file/directory has been renamed from old_path => new_path"""
448
 
        assert new_path not in self._renamed
449
 
        assert old_path not in self._renamed_r
 
485
        if new_path in self._renamed:
 
486
            raise AssertionError(new_path)
 
487
        if old_path in self._renamed_r:
 
488
            raise AssertionError(old_path)
450
489
        self._renamed[new_path] = old_path
451
490
        self._renamed_r[old_path] = new_path
452
491
 
482
521
 
483
522
    def old_path(self, new_path):
484
523
        """Get the old_path (path in the base_tree) for the file at new_path"""
485
 
        assert new_path[:1] not in ('\\', '/')
 
524
        if new_path[:1] in ('\\', '/'):
 
525
            raise ValueError(new_path)
486
526
        old_path = self._renamed.get(new_path)
487
527
        if old_path is not None:
488
528
            return old_path
508
548
        """Get the new_path (path in the target_tree) for the file at old_path
509
549
        in the base tree.
510
550
        """
511
 
        assert old_path[:1] not in ('\\', '/')
 
551
        if old_path[:1] in ('\\', '/'):
 
552
            raise ValueError(old_path)
512
553
        new_path = self._renamed_r.get(old_path)
513
554
        if new_path is not None:
514
555
            return new_path
577
618
                then be cached.
578
619
        """
579
620
        base_id = self.old_contents_id(file_id)
580
 
        if base_id is not None:
 
621
        if (base_id is not None and
 
622
            base_id != self.base_tree.inventory.root.file_id):
581
623
            patch_original = self.base_tree.get_file(base_id)
582
624
        else:
583
625
            patch_original = None
586
628
            if (patch_original is None and 
587
629
                self.get_kind(file_id) == 'directory'):
588
630
                return StringIO()
589
 
            assert patch_original is not None, "None: %s" % file_id
 
631
            if patch_original is None:
 
632
                raise AssertionError("None: %s" % file_id)
590
633
            return patch_original
591
634
 
592
 
        assert not file_patch.startswith('\\'), \
593
 
            'Malformed patch for %s, %r' % (file_id, file_patch)
 
635
        if file_patch.startswith('\\'):
 
636
            raise ValueError(
 
637
                'Malformed patch for %s, %r' % (file_id, file_patch))
594
638
        return patched_file(file_patch, patch_original)
595
639
 
596
640
    def get_symlink_target(self, file_id):
643
687
        This need to be called before ever accessing self.inventory
644
688
        """
645
689
        from os.path import dirname, basename
646
 
 
647
 
        assert self.base_tree is not None
648
690
        base_inv = self.base_tree.inventory
649
 
        root_id = base_inv.root.file_id
650
 
        try:
651
 
            # New inventories have a unique root_id
652
 
            inv = Inventory(root_id, self.revision_id)
653
 
        except TypeError:
654
 
            inv = Inventory(revision_id=self.revision_id)
655
 
        inv.root.revision = self.get_last_changed(root_id)
 
691
        inv = Inventory(None, self.revision_id)
656
692
 
657
693
        def add_entry(file_id):
658
694
            path = self.id2path(file_id)
659
695
            if path is None:
660
696
                return
661
 
            parent_path = dirname(path)
662
 
            if parent_path == u'':
663
 
                parent_id = root_id
 
697
            if path == '':
 
698
                parent_id = None
664
699
            else:
 
700
                parent_path = dirname(path)
665
701
                parent_id = self.path2id(parent_path)
666
702
 
667
703
            kind = self.get_kind(file_id)
688
724
 
689
725
        sorted_entries = self.sorted_path_id()
690
726
        for path, file_id in sorted_entries:
691
 
            if file_id == inv.root.file_id:
692
 
                continue
693
727
            add_entry(file_id)
694
728
 
695
729
        return inv