~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/bundle_data.py

  • Committer: Aaron Bentley
  • Date: 2007-02-06 14:52:16 UTC
  • mfrom: (2266 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2268.
  • Revision ID: abentley@panoramicfeedback.com-20070206145216-fcpi8o3ufvuzwbp9
Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
import os
22
22
import pprint
23
23
 
24
 
from bzrlib import (
25
 
    osutils,
26
 
    timestamp,
27
 
    )
28
24
import bzrlib.errors
29
 
from bzrlib.bundle import apply_bundle
30
25
from bzrlib.errors import (TestamentMismatch, BzrError, 
31
26
                           MalformedHeader, MalformedPatches, NotABundle)
32
27
from bzrlib.inventory import (Inventory, InventoryEntry,
77
72
        if self.properties:
78
73
            for property in self.properties:
79
74
                key_end = property.find(': ')
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:]
 
75
                assert key_end is not None
 
76
                key = property[:key_end].encode('utf-8')
 
77
                value = property[key_end+2:].encode('utf-8')
88
78
                rev.properties[key] = value
89
79
 
90
80
        return rev
91
81
 
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
 
 
105
82
 
106
83
class BundleInfo(object):
107
84
    """This contains the meta information. Stuff that allows you to
108
85
    recreate the revision or inventory XML.
109
86
    """
110
 
    def __init__(self, bundle_format=None):
111
 
        self.bundle_format = None
 
87
    def __init__(self):
112
88
        self.committer = None
113
89
        self.date = None
114
90
        self.message = None
125
101
        self.timestamp = None
126
102
        self.timezone = None
127
103
 
128
 
        # Have we checked the repository yet?
129
 
        self._validated_revisions_against_repo = False
130
 
 
131
104
    def __str__(self):
132
105
        return pprint.pformat(self.__dict__)
133
106
 
136
109
        split up, based on the assumptions that can be made
137
110
        when information is missing.
138
111
        """
139
 
        from bzrlib.timestamp import unpack_highres_date
 
112
        from bzrlib.bundle.serializer import unpack_highres_date
140
113
        # Put in all of the guessable information.
141
114
        if not self.timestamp and self.date:
142
115
            self.timestamp, self.timezone = unpack_highres_date(self.date)
197
170
    def revision_tree(self, repository, revision_id, base=None):
198
171
        revision = self.get_revision(revision_id)
199
172
        base = self.get_base(revision)
200
 
        if base == revision_id:
201
 
            raise AssertionError()
202
 
        if not self._validated_revisions_against_repo:
203
 
            self._validate_references_from_repository(repository)
 
173
        assert base != revision_id
 
174
        self._validate_references_from_repository(repository)
204
175
        revision_info = self.get_revision_info(revision_id)
205
176
        inventory_revision_id = revision_id
206
177
        bundle_tree = BundleTree(repository.revision_tree(base), 
262
233
            elif revision_id not in checked:
263
234
                missing[revision_id] = sha1
264
235
 
 
236
        for inv_id, sha1 in inv_to_sha.iteritems():
 
237
            if repository.has_revision(inv_id):
 
238
                # Note: branch.get_inventory_sha1() just returns the value that
 
239
                # is stored in the revision text, and that value may be out
 
240
                # of date. This is bogus, because that means we aren't
 
241
                # validating the actual text, just that we wrote and read the
 
242
                # string. But for now, what the hell.
 
243
                local_sha1 = repository.get_inventory_sha1(inv_id)
 
244
                if sha1 != local_sha1:
 
245
                    raise BzrError('sha1 mismatch. For inventory id {%s}' 
 
246
                                   'local: %s, bundle: %s' % 
 
247
                                   (inv_id, local_sha1, sha1))
 
248
                else:
 
249
                    count += 1
 
250
 
265
251
        if len(missing) > 0:
266
252
            # I don't know if this is an error yet
267
253
            warning('Not all revision hashes could be validated.'
268
254
                    ' Unable validate %d hashes' % len(missing))
269
255
        mutter('Verified %d sha hashes for the bundle.' % count)
270
 
        self._validated_revisions_against_repo = True
271
256
 
272
257
    def _validate_inventory(self, inv, revision_id):
273
258
        """At this point we should have generated the BundleTree,
274
259
        so build up an inventory, and make sure the hashes match.
275
260
        """
 
261
 
 
262
        assert inv is not None
 
263
 
276
264
        # Now we should have a complete inventory entry.
277
265
        s = serializer_v5.write_inventory_to_string(inv)
278
266
        sha1 = sha_string(s)
279
267
        # Target revision is the last entry in the real_revisions list
280
268
        rev = self.get_revision(revision_id)
281
 
        if rev.revision_id != revision_id:
282
 
            raise AssertionError()
 
269
        assert rev.revision_id == revision_id
283
270
        if sha1 != rev.inventory_sha1:
284
271
            open(',,bogus-inv', 'wb').write(s)
285
272
            warning('Inventory sha hash mismatch for revision %s. %s'
293
280
        
294
281
        rev = self.get_revision(revision_id)
295
282
        rev_info = self.get_revision_info(revision_id)
296
 
        if not (rev.revision_id == rev_info.revision_id):
297
 
            raise AssertionError()
298
 
        if not (rev.revision_id == revision_id):
299
 
            raise AssertionError()
 
283
        assert rev.revision_id == rev_info.revision_id
 
284
        assert rev.revision_id == revision_id
300
285
        sha1 = self._testament_sha1(rev, inventory)
301
286
        if sha1 != rev_info.sha1:
302
287
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
314
299
 
315
300
        def get_rev_id(last_changed, path, kind):
316
301
            if last_changed is not None:
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
                changed_revision_id = last_changed.decode('utf-8')
321
303
            else:
322
304
                changed_revision_id = revision_id
323
305
            bundle_tree.note_last_changed(path, changed_revision_id)
334
316
                if name == 'last-changed':
335
317
                    last_changed = value
336
318
                elif name == 'executable':
 
319
                    assert value in ('yes', 'no'), value
337
320
                    val = (value == 'yes')
338
321
                    bundle_tree.note_executable(new_path, val)
339
322
                elif name == 'target':
343
326
            return last_changed, encoding
344
327
 
345
328
        def do_patch(path, lines, encoding):
346
 
            if encoding == 'base64':
 
329
            if encoding is not None:
 
330
                assert encoding == 'base64'
347
331
                patch = base64.decodestring(''.join(lines))
348
 
            elif encoding is None:
 
332
            else:
349
333
                patch =  ''.join(lines)
350
 
            else:
351
 
                raise ValueError(encoding)
352
334
            bundle_tree.note_patch(path, patch)
353
335
 
354
336
        def renamed(kind, extra, lines):
390
372
            if not info[1].startswith('file-id:'):
391
373
                raise BzrError('The file-id should follow the path for an add'
392
374
                        ': %r' % extra)
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
            file_id = info[1][8:]
396
376
 
397
377
            bundle_tree.note_id(file_id, path, kind)
398
378
            # this will be overridden in extra_info if executable is specified.
443
423
                        ' (unrecognized action): %r' % action_line)
444
424
            valid_actions[action](kind, extra, lines)
445
425
 
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
 
 
462
426
 
463
427
class BundleTree(Tree):
464
428
    def __init__(self, base_tree, revision_id):
482
446
 
483
447
    def note_rename(self, old_path, new_path):
484
448
        """A file/directory has been renamed from old_path => new_path"""
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)
 
449
        assert new_path not in self._renamed
 
450
        assert old_path not in self._renamed_r
489
451
        self._renamed[new_path] = old_path
490
452
        self._renamed_r[old_path] = new_path
491
453
 
521
483
 
522
484
    def old_path(self, new_path):
523
485
        """Get the old_path (path in the base_tree) for the file at new_path"""
524
 
        if new_path[:1] in ('\\', '/'):
525
 
            raise ValueError(new_path)
 
486
        assert new_path[:1] not in ('\\', '/')
526
487
        old_path = self._renamed.get(new_path)
527
488
        if old_path is not None:
528
489
            return old_path
548
509
        """Get the new_path (path in the target_tree) for the file at old_path
549
510
        in the base tree.
550
511
        """
551
 
        if old_path[:1] in ('\\', '/'):
552
 
            raise ValueError(old_path)
 
512
        assert old_path[:1] not in ('\\', '/')
553
513
        new_path = self._renamed_r.get(old_path)
554
514
        if new_path is not None:
555
515
            return new_path
628
588
            if (patch_original is None and 
629
589
                self.get_kind(file_id) == 'directory'):
630
590
                return StringIO()
631
 
            if patch_original is None:
632
 
                raise AssertionError("None: %s" % file_id)
 
591
            assert patch_original is not None, "None: %s" % file_id
633
592
            return patch_original
634
593
 
635
 
        if file_patch.startswith('\\'):
636
 
            raise ValueError(
637
 
                'Malformed patch for %s, %r' % (file_id, file_patch))
 
594
        assert not file_patch.startswith('\\'), \
 
595
            'Malformed patch for %s, %r' % (file_id, file_patch)
638
596
        return patched_file(file_patch, patch_original)
639
597
 
640
598
    def get_symlink_target(self, file_id):
687
645
        This need to be called before ever accessing self.inventory
688
646
        """
689
647
        from os.path import dirname, basename
 
648
 
 
649
        assert self.base_tree is not None
690
650
        base_inv = self.base_tree.inventory
691
651
        inv = Inventory(None, self.revision_id)
692
652