~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/bundle_data.py

  • Committer: John Arbash Meinel
  • Date: 2010-08-02 17:16:12 UTC
  • mto: This revision was merged to the branch mainline in revision 5369.
  • Revision ID: john@arbash-meinel.com-20100802171612-rdh5ods70w2bl3j7
We also have to re-implement it for _simple_set_pyx.pyx

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
 
23
23
 
24
24
from bzrlib import (
25
25
    osutils,
 
26
    timestamp,
26
27
    )
27
28
import bzrlib.errors
28
29
from bzrlib.bundle import apply_bundle
29
 
from bzrlib.errors import (TestamentMismatch, BzrError, 
 
30
from bzrlib.errors import (TestamentMismatch, BzrError,
30
31
                           MalformedHeader, MalformedPatches, NotABundle)
31
32
from bzrlib.inventory import (Inventory, InventoryEntry,
32
33
                              InventoryDirectory, InventoryFile,
77
78
            for property in self.properties:
78
79
                key_end = property.find(': ')
79
80
                if key_end == -1:
80
 
                    assert property.endswith(':')
 
81
                    if not property.endswith(':'):
 
82
                        raise ValueError(property)
81
83
                    key = str(property[:-1])
82
84
                    value = ''
83
85
                else:
87
89
 
88
90
        return rev
89
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
 
90
105
 
91
106
class BundleInfo(object):
92
107
    """This contains the meta information. Stuff that allows you to
93
108
    recreate the revision or inventory XML.
94
109
    """
95
 
    def __init__(self):
 
110
    def __init__(self, bundle_format=None):
 
111
        self.bundle_format = None
96
112
        self.committer = None
97
113
        self.date = None
98
114
        self.message = None
143
159
    def get_base(self, revision):
144
160
        revision_info = self.get_revision_info(revision.revision_id)
145
161
        if revision_info.base_id is not None:
146
 
            if revision_info.base_id == NULL_REVISION:
147
 
                return None
148
 
            else:
149
 
                return revision_info.base_id
 
162
            return revision_info.base_id
150
163
        if len(revision.parent_ids) == 0:
151
164
            # There is no base listed, and
152
165
            # the lowest revision doesn't have a parent
153
166
            # so this is probably against the empty tree
154
 
            # and thus base truly is None
155
 
            return None
 
167
            # and thus base truly is NULL_REVISION
 
168
            return NULL_REVISION
156
169
        else:
157
170
            return revision.parent_ids[-1]
158
171
 
179
192
        raise KeyError(revision_id)
180
193
 
181
194
    def revision_tree(self, repository, revision_id, base=None):
182
 
        revision_id = osutils.safe_revision_id(revision_id)
183
195
        revision = self.get_revision(revision_id)
184
196
        base = self.get_base(revision)
185
 
        assert base != revision_id
 
197
        if base == revision_id:
 
198
            raise AssertionError()
186
199
        if not self._validated_revisions_against_repo:
187
200
            self._validate_references_from_repository(repository)
188
201
        revision_info = self.get_revision_info(revision_id)
189
202
        inventory_revision_id = revision_id
190
 
        bundle_tree = BundleTree(repository.revision_tree(base), 
 
203
        bundle_tree = BundleTree(repository.revision_tree(base),
191
204
                                  inventory_revision_id)
192
205
        self._update_tree(bundle_tree, revision_id)
193
206
 
226
239
        for rev_info in self.revisions:
227
240
            checked[rev_info.revision_id] = True
228
241
            add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
229
 
                
 
242
 
230
243
        for (rev, rev_info) in zip(self.real_revisions, self.revisions):
231
244
            add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
232
245
 
234
247
        missing = {}
235
248
        for revision_id, sha1 in rev_to_sha.iteritems():
236
249
            if repository.has_revision(revision_id):
237
 
                testament = StrictTestament.from_revision(repository, 
 
250
                testament = StrictTestament.from_revision(repository,
238
251
                                                          revision_id)
239
252
                local_sha1 = self._testament_sha1_from_revision(repository,
240
253
                                                                revision_id)
241
254
                if sha1 != local_sha1:
242
 
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
 
255
                    raise BzrError('sha1 mismatch. For revision id {%s}'
243
256
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
244
257
                else:
245
258
                    count += 1
246
259
            elif revision_id not in checked:
247
260
                missing[revision_id] = sha1
248
261
 
249
 
        for inv_id, sha1 in inv_to_sha.iteritems():
250
 
            if repository.has_revision(inv_id):
251
 
                # Note: branch.get_inventory_sha1() just returns the value that
252
 
                # is stored in the revision text, and that value may be out
253
 
                # of date. This is bogus, because that means we aren't
254
 
                # validating the actual text, just that we wrote and read the
255
 
                # string. But for now, what the hell.
256
 
                local_sha1 = repository.get_inventory_sha1(inv_id)
257
 
                if sha1 != local_sha1:
258
 
                    raise BzrError('sha1 mismatch. For inventory id {%s}' 
259
 
                                   'local: %s, bundle: %s' % 
260
 
                                   (inv_id, local_sha1, sha1))
261
 
                else:
262
 
                    count += 1
263
 
 
264
262
        if len(missing) > 0:
265
263
            # I don't know if this is an error yet
266
264
            warning('Not all revision hashes could be validated.'
272
270
        """At this point we should have generated the BundleTree,
273
271
        so build up an inventory, and make sure the hashes match.
274
272
        """
275
 
 
276
 
        assert inv is not None
277
 
 
278
273
        # Now we should have a complete inventory entry.
279
274
        s = serializer_v5.write_inventory_to_string(inv)
280
275
        sha1 = sha_string(s)
281
276
        # Target revision is the last entry in the real_revisions list
282
277
        rev = self.get_revision(revision_id)
283
 
        assert rev.revision_id == revision_id
 
278
        if rev.revision_id != revision_id:
 
279
            raise AssertionError()
284
280
        if sha1 != rev.inventory_sha1:
285
 
            open(',,bogus-inv', 'wb').write(s)
 
281
            f = open(',,bogus-inv', 'wb')
 
282
            try:
 
283
                f.write(s)
 
284
            finally:
 
285
                f.close()
286
286
            warning('Inventory sha hash mismatch for revision %s. %s'
287
287
                    ' != %s' % (revision_id, sha1, rev.inventory_sha1))
288
288
 
291
291
 
292
292
        # This is a mapping from each revision id to it's sha hash
293
293
        rev_to_sha1 = {}
294
 
        
 
294
 
295
295
        rev = self.get_revision(revision_id)
296
296
        rev_info = self.get_revision_info(revision_id)
297
 
        assert rev.revision_id == rev_info.revision_id
298
 
        assert rev.revision_id == revision_id
 
297
        if not (rev.revision_id == rev_info.revision_id):
 
298
            raise AssertionError()
 
299
        if not (rev.revision_id == revision_id):
 
300
            raise AssertionError()
299
301
        sha1 = self._testament_sha1(rev, inventory)
300
302
        if sha1 != rev_info.sha1:
301
303
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
329
331
                try:
330
332
                    name, value = info_item.split(':', 1)
331
333
                except ValueError:
332
 
                    raise 'Value %r has no colon' % info_item
 
334
                    raise ValueError('Value %r has no colon' % info_item)
333
335
                if name == 'last-changed':
334
336
                    last_changed = value
335
337
                elif name == 'executable':
336
 
                    assert value in ('yes', 'no'), value
337
338
                    val = (value == 'yes')
338
339
                    bundle_tree.note_executable(new_path, val)
339
340
                elif name == 'target':
343
344
            return last_changed, encoding
344
345
 
345
346
        def do_patch(path, lines, encoding):
346
 
            if encoding is not None:
347
 
                assert encoding == 'base64'
 
347
            if encoding == 'base64':
348
348
                patch = base64.decodestring(''.join(lines))
349
 
            else:
 
349
            elif encoding is None:
350
350
                patch =  ''.join(lines)
 
351
            else:
 
352
                raise ValueError(encoding)
351
353
            bundle_tree.note_patch(path, patch)
352
354
 
353
355
        def renamed(kind, extra, lines):
413
415
            revision = get_rev_id(last_modified, path, kind)
414
416
            if lines:
415
417
                do_patch(path, lines, encoding)
416
 
            
 
418
 
417
419
        valid_actions = {
418
420
            'renamed':renamed,
419
421
            'removed':removed,
442
444
                        ' (unrecognized action): %r' % action_line)
443
445
            valid_actions[action](kind, extra, lines)
444
446
 
445
 
    def install_revisions(self, target_repo):
446
 
        """Install revisions and return the target revision"""
 
447
    def install_revisions(self, target_repo, stream_input=True):
 
448
        """Install revisions and return the target revision
 
449
 
 
450
        :param target_repo: The repository to install into
 
451
        :param stream_input: Ignored by this implementation.
 
452
        """
447
453
        apply_bundle.install_bundle(target_repo, self)
448
454
        return self.target
449
455
 
 
456
    def get_merge_request(self, target_repo):
 
457
        """Provide data for performing a merge
 
458
 
 
459
        Returns suggested base, suggested target, and patch verification status
 
460
        """
 
461
        return None, self.target, 'inapplicable'
 
462
 
450
463
 
451
464
class BundleTree(Tree):
452
465
    def __init__(self, base_tree, revision_id):
470
483
 
471
484
    def note_rename(self, old_path, new_path):
472
485
        """A file/directory has been renamed from old_path => new_path"""
473
 
        assert new_path not in self._renamed
474
 
        assert old_path not in self._renamed_r
 
486
        if new_path in self._renamed:
 
487
            raise AssertionError(new_path)
 
488
        if old_path in self._renamed_r:
 
489
            raise AssertionError(old_path)
475
490
        self._renamed[new_path] = old_path
476
491
        self._renamed_r[old_path] = new_path
477
492
 
507
522
 
508
523
    def old_path(self, new_path):
509
524
        """Get the old_path (path in the base_tree) for the file at new_path"""
510
 
        assert new_path[:1] not in ('\\', '/')
 
525
        if new_path[:1] in ('\\', '/'):
 
526
            raise ValueError(new_path)
511
527
        old_path = self._renamed.get(new_path)
512
528
        if old_path is not None:
513
529
            return old_path
527
543
        #renamed_r
528
544
        if old_path in self._renamed_r:
529
545
            return None
530
 
        return old_path 
 
546
        return old_path
531
547
 
532
548
    def new_path(self, old_path):
533
549
        """Get the new_path (path in the target_tree) for the file at old_path
534
550
        in the base tree.
535
551
        """
536
 
        assert old_path[:1] not in ('\\', '/')
 
552
        if old_path[:1] in ('\\', '/'):
 
553
            raise ValueError(old_path)
537
554
        new_path = self._renamed_r.get(old_path)
538
555
        if new_path is not None:
539
556
            return new_path
552
569
        #renamed_r
553
570
        if new_path in self._renamed:
554
571
            return None
555
 
        return new_path 
 
572
        return new_path
556
573
 
557
574
    def path2id(self, path):
558
575
        """Return the id of the file present at path in the target tree."""
592
609
                return None
593
610
        new_path = self.id2path(file_id)
594
611
        return self.base_tree.path2id(new_path)
595
 
        
 
612
 
596
613
    def get_file(self, file_id):
597
614
        """Return a file-like object containing the new contents of the
598
615
        file given by file_id.
609
626
            patch_original = None
610
627
        file_patch = self.patches.get(self.id2path(file_id))
611
628
        if file_patch is None:
612
 
            if (patch_original is None and 
 
629
            if (patch_original is None and
613
630
                self.get_kind(file_id) == 'directory'):
614
631
                return StringIO()
615
 
            assert patch_original is not None, "None: %s" % file_id
 
632
            if patch_original is None:
 
633
                raise AssertionError("None: %s" % file_id)
616
634
            return patch_original
617
635
 
618
 
        assert not file_patch.startswith('\\'), \
619
 
            'Malformed patch for %s, %r' % (file_id, file_patch)
 
636
        if file_patch.startswith('\\'):
 
637
            raise ValueError(
 
638
                'Malformed patch for %s, %r' % (file_id, file_patch))
620
639
        return patched_file(file_patch, patch_original)
621
640
 
622
641
    def get_symlink_target(self, file_id):
669
688
        This need to be called before ever accessing self.inventory
670
689
        """
671
690
        from os.path import dirname, basename
672
 
 
673
 
        assert self.base_tree is not None
674
691
        base_inv = self.base_tree.inventory
675
692
        inv = Inventory(None, self.revision_id)
676
693