~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/read_bundle.py

  • Committer: Aaron Bentley
  • Date: 2006-05-30 15:18:12 UTC
  • mto: This revision was merged to the branch mainline in revision 1738.
  • Revision ID: abentley@panoramicfeedback.com-20060530151812-0e3e9b78cc15a804
Rename changesets to revision bundles

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/env python
2
2
"""\
3
 
Read in a changeset output, and process it into a Changeset object.
 
3
Read in a bundle stream, and process it into a BundleReader object.
4
4
"""
5
5
 
6
6
import base64
9
9
import pprint
10
10
 
11
11
from bzrlib.errors import TestamentMismatch, BzrError
12
 
from bzrlib.changeset.common import get_header, header_str
 
12
from bzrlib.bundle.common import get_header, header_str
13
13
from bzrlib.inventory import (Inventory, InventoryEntry,
14
14
                              InventoryDirectory, InventoryFile,
15
15
                              InventoryLink)
21
21
from bzrlib.xml5 import serializer_v5
22
22
 
23
23
 
24
 
class BadChangeset(Exception): pass
25
 
class MalformedHeader(BadChangeset): pass
26
 
class MalformedPatches(BadChangeset): pass
27
 
class MalformedFooter(BadChangeset): pass
 
24
class BadBundle(Exception): pass
 
25
class MalformedHeader(BadBundle): pass
 
26
class MalformedPatches(BadBundle): pass
 
27
class MalformedFooter(BadBundle): pass
28
28
 
29
29
 
30
30
class RevisionInfo(object):
70
70
        return rev
71
71
 
72
72
 
73
 
class ChangesetInfo(object):
 
73
class BundleInfo(object):
74
74
    """This contains the meta information. Stuff that allows you to
75
75
    recreate the revision or inventory XML.
76
76
    """
99
99
        split up, based on the assumptions that can be made
100
100
        when information is missing.
101
101
        """
102
 
        from bzrlib.changeset.common import unpack_highres_date
 
102
        from bzrlib.bundle.common import unpack_highres_date
103
103
        # Put in all of the guessable information.
104
104
        if not self.timestamp and self.date:
105
105
            self.timestamp, self.timezone = unpack_highres_date(self.date)
158
158
        raise KeyError(revision_id)
159
159
 
160
160
 
161
 
class ChangesetReader(object):
162
 
    """This class reads in a changeset from a file, and returns
163
 
    a Changeset object, which can then be applied against a tree.
 
161
class BundleReader(object):
 
162
    """This class reads in a bundle from a file, and returns
 
163
    a Bundle object, which can then be applied against a tree.
164
164
    """
165
165
    def __init__(self, from_file):
166
 
        """Read in the changeset from the file.
 
166
        """Read in the bundle from the file.
167
167
 
168
168
        :param from_file: A file-like object (must have iterator support).
169
169
        """
171
171
        self.from_file = iter(from_file)
172
172
        self._next_line = None
173
173
        
174
 
        self.info = ChangesetInfo()
 
174
        self.info = BundleInfo()
175
175
        # We put the actual inventory ids in the footer, so that the patch
176
176
        # is easier to read for humans.
177
177
        # Unfortunately, that means we need to read everything before we
178
 
        # can create a proper changeset.
 
178
        # can create a proper bundle.
179
179
        self._read()
180
180
        self._validate()
181
181
 
256
256
                local_sha1 = testament.as_sha1()
257
257
                if sha1 != local_sha1:
258
258
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
259
 
                            'local: %s, cset: %s' % (revision_id, local_sha1, sha1))
 
259
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
260
260
                else:
261
261
                    count += 1
262
262
            elif revision_id not in checked:
272
272
                local_sha1 = repository.get_inventory_sha1(inv_id)
273
273
                if sha1 != local_sha1:
274
274
                    raise BzrError('sha1 mismatch. For inventory id {%s}' 
275
 
                            'local: %s, cset: %s' % (inv_id, local_sha1, sha1))
 
275
                                   'local: %s, bundle: %s' % 
 
276
                                   (inv_id, local_sha1, sha1))
276
277
                else:
277
278
                    count += 1
278
279
 
280
281
            # I don't know if this is an error yet
281
282
            warning('Not all revision hashes could be validated.'
282
283
                    ' Unable validate %d hashes' % len(missing))
283
 
        mutter('Verified %d sha hashes for the changeset.' % count)
 
284
        mutter('Verified %d sha hashes for the bundle.' % count)
284
285
 
285
286
    def _validate_inventory(self, inv, revision_id):
286
 
        """At this point we should have generated the ChangesetTree,
 
287
        """At this point we should have generated the BundleTree,
287
288
        so build up an inventory, and make sure the hashes match.
288
289
        """
289
290
 
300
301
            warning('Inventory sha hash mismatch for revision %s. %s'
301
302
                    ' != %s' % (revision_id, sha1, rev.inventory_sha1))
302
303
 
303
 
    def get_changeset(self, repository):
304
 
        """Return the meta information, and a Changeset tree which can
 
304
    def get_bundle(self, repository):
 
305
        """Return the meta information, and a Bundle tree which can
305
306
        be used to populate the local stores and working tree, respectively.
306
307
        """
307
308
        return self.info, self.revision_tree(repository, self.info.target)
313
314
        self._validate_references_from_repository(repository)
314
315
        revision_info = self.info.get_revision_info(revision_id)
315
316
        inventory_revision_id = revision_id
316
 
        cset_tree = ChangesetTree(repository.revision_tree(base), 
 
317
        bundle_tree = BundleTree(repository.revision_tree(base), 
317
318
                                  inventory_revision_id)
318
 
        self._update_tree(cset_tree, revision_id)
 
319
        self._update_tree(bundle_tree, revision_id)
319
320
 
320
 
        inv = cset_tree.inventory
 
321
        inv = bundle_tree.inventory
321
322
        self._validate_inventory(inv, revision_id)
322
323
        self._validate_revision(inv, revision_id)
323
324
 
324
 
        return cset_tree
 
325
        return bundle_tree
325
326
 
326
327
    def _next(self):
327
328
        """yield the next line, but secretly
500
501
            if self._next_line is None:
501
502
                break
502
503
 
503
 
    def _update_tree(self, cset_tree, revision_id):
504
 
        """This fills out a ChangesetTree based on the information
 
504
    def _update_tree(self, bundle_tree, revision_id):
 
505
        """This fills out a BundleTree based on the information
505
506
        that was read in.
506
507
 
507
 
        :param cset_tree: A ChangesetTree to update with the new information.
 
508
        :param bundle_tree: A BundleTree to update with the new information.
508
509
        """
509
510
 
510
511
        def get_rev_id(last_changed, path, kind):
512
513
                changed_revision_id = last_changed.decode('utf-8')
513
514
            else:
514
515
                changed_revision_id = revision_id
515
 
            cset_tree.note_last_changed(path, changed_revision_id)
 
516
            bundle_tree.note_last_changed(path, changed_revision_id)
516
517
            return changed_revision_id
517
518
 
518
519
        def extra_info(info, new_path):
528
529
                elif name == 'executable':
529
530
                    assert value in ('yes', 'no'), value
530
531
                    val = (value == 'yes')
531
 
                    cset_tree.note_executable(new_path, val)
 
532
                    bundle_tree.note_executable(new_path, val)
532
533
                elif name == 'target':
533
 
                    cset_tree.note_target(new_path, value)
 
534
                    bundle_tree.note_target(new_path, value)
534
535
                elif name == 'encoding':
535
536
                    encoding = value
536
537
            return last_changed, encoding
541
542
                patch = base64.decodestring(''.join(lines))
542
543
            else:
543
544
                patch =  ''.join(lines)
544
 
            cset_tree.note_patch(path, patch)
 
545
            bundle_tree.note_patch(path, patch)
545
546
 
546
547
        def renamed(kind, extra, lines):
547
548
            info = extra.split(' // ')
554
555
            else:
555
556
                new_path = info[1]
556
557
 
557
 
            cset_tree.note_rename(old_path, new_path)
 
558
            bundle_tree.note_rename(old_path, new_path)
558
559
            last_modified, encoding = extra_info(info[2:], new_path)
559
560
            revision = get_rev_id(last_modified, new_path, kind)
560
561
            if lines:
568
569
                raise BzrError('removed action lines should only have the path'
569
570
                        ': %r' % extra)
570
571
            path = info[0]
571
 
            cset_tree.note_deletion(path)
 
572
            bundle_tree.note_deletion(path)
572
573
 
573
574
        def added(kind, extra, lines):
574
575
            info = extra.split(' // ')
584
585
                        ': %r' % extra)
585
586
            file_id = info[1][8:]
586
587
 
587
 
            cset_tree.note_id(file_id, path, kind)
 
588
            bundle_tree.note_id(file_id, path, kind)
588
589
            # this will be overridden in extra_info if executable is specified.
589
 
            cset_tree.note_executable(path, False)
 
590
            bundle_tree.note_executable(path, False)
590
591
            last_changed, encoding = extra_info(info[2:], path)
591
592
            revision = get_rev_id(last_changed, path, kind)
592
593
            if kind == 'directory':
634
635
            valid_actions[action](kind, extra, lines)
635
636
 
636
637
 
637
 
def read_changeset(from_file, repository):
638
 
    """Read in a changeset from a iterable object (such as a file object)
639
 
 
640
 
    :param from_file: A file-like object to read the changeset information.
641
 
    :param repository: This will be used to build the changeset tree, it needs
642
 
                       to contain the base of the changeset. (Which you
643
 
                       probably won't know about until after the changeset is
644
 
                       parsed.)
645
 
    """
646
 
    cr = ChangesetReader(from_file)
647
 
    return cr.get_changeset(repository)
648
 
 
649
 
 
650
 
class ChangesetTree(Tree):
 
638
class BundleTree(Tree):
651
639
    def __init__(self, base_tree, revision_id):
652
640
        self.base_tree = base_tree
653
641
        self._renamed = {} # Mapping from old_path => new_path
862
850
        return len(content), sha_string(content)
863
851
 
864
852
    def _get_inventory(self):
865
 
        """Build up the inventory entry for the ChangesetTree.
 
853
        """Build up the inventory entry for the BundleTree.
866
854
 
867
855
        This need to be called before ever accessing self.inventory
868
856
        """