/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/repository.py

Merge from bzr.dev.  Breaks a few tests because there are new methods not yet implemented.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
from bzrlib.lazy_import import lazy_import
20
20
lazy_import(globals(), """
21
 
from binascii import hexlify
22
 
from copy import deepcopy
23
21
import re
24
22
import time
25
23
import unittest
27
25
from bzrlib import (
28
26
    bzrdir,
29
27
    check,
30
 
    delta,
31
28
    errors,
32
29
    generate_ids,
33
30
    gpg,
34
31
    graph,
35
 
    knit,
36
32
    lazy_regex,
37
33
    lockable_files,
38
34
    lockdir,
42
38
    symbol_versioning,
43
39
    transactions,
44
40
    ui,
45
 
    weave,
46
 
    weavefile,
47
 
    xml5,
48
 
    xml6,
49
 
    )
50
 
from bzrlib.osutils import (
51
 
    rand_bytes,
52
 
    compact_date, 
53
 
    local_time_offset,
54
41
    )
55
42
from bzrlib.revisiontree import RevisionTree
56
43
from bzrlib.store.versioned import VersionedFileStore
57
44
from bzrlib.store.text import TextStore
58
45
from bzrlib.testament import Testament
 
46
 
59
47
""")
60
48
 
61
49
from bzrlib.decorators import needs_read_lock, needs_write_lock
90
78
        )
91
79
 
92
80
    @needs_write_lock
93
 
    def add_inventory(self, revid, inv, parents):
94
 
        """Add the inventory inv to the repository as revid.
 
81
    def add_inventory(self, revision_id, inv, parents):
 
82
        """Add the inventory inv to the repository as revision_id.
95
83
        
96
 
        :param parents: The revision ids of the parents that revid
 
84
        :param parents: The revision ids of the parents that revision_id
97
85
                        is known to have and are in the repository already.
98
86
 
99
87
        returns the sha1 of the serialized inventory.
100
88
        """
101
 
        _mod_revision.check_not_reserved_id(revid)
102
 
        assert inv.revision_id is None or inv.revision_id == revid, \
 
89
        revision_id = osutils.safe_revision_id(revision_id)
 
90
        _mod_revision.check_not_reserved_id(revision_id)
 
91
        assert inv.revision_id is None or inv.revision_id == revision_id, \
103
92
            "Mismatch between inventory revision" \
104
 
            " id and insertion revid (%r, %r)" % (inv.revision_id, revid)
 
93
            " id and insertion revid (%r, %r)" % (inv.revision_id, revision_id)
105
94
        assert inv.root is not None
106
95
        inv_text = self.serialise_inventory(inv)
107
96
        inv_sha1 = osutils.sha_string(inv_text)
108
97
        inv_vf = self.control_weaves.get_weave('inventory',
109
98
                                               self.get_transaction())
110
 
        self._inventory_add_lines(inv_vf, revid, parents, osutils.split_lines(inv_text))
 
99
        self._inventory_add_lines(inv_vf, revision_id, parents,
 
100
                                  osutils.split_lines(inv_text))
111
101
        return inv_sha1
112
102
 
113
 
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
 
103
    def _inventory_add_lines(self, inv_vf, revision_id, parents, lines):
114
104
        final_parents = []
115
105
        for parent in parents:
116
106
            if parent in inv_vf:
117
107
                final_parents.append(parent)
118
108
 
119
 
        inv_vf.add_lines(revid, final_parents, lines)
 
109
        inv_vf.add_lines(revision_id, final_parents, lines)
120
110
 
121
111
    @needs_write_lock
122
 
    def add_revision(self, rev_id, rev, inv=None, config=None):
123
 
        """Add rev to the revision store as rev_id.
 
112
    def add_revision(self, revision_id, rev, inv=None, config=None):
 
113
        """Add rev to the revision store as revision_id.
124
114
 
125
 
        :param rev_id: the revision id to use.
 
115
        :param revision_id: the revision id to use.
126
116
        :param rev: The revision object.
127
117
        :param inv: The inventory for the revision. if None, it will be looked
128
118
                    up in the inventory storer
130
120
                       If supplied its signature_needed method will be used
131
121
                       to determine if a signature should be made.
132
122
        """
133
 
        _mod_revision.check_not_reserved_id(rev_id)
 
123
        revision_id = osutils.safe_revision_id(revision_id)
 
124
        # TODO: jam 20070210 Shouldn't we check rev.revision_id and
 
125
        #       rev.parent_ids?
 
126
        _mod_revision.check_not_reserved_id(revision_id)
134
127
        if config is not None and config.signature_needed():
135
128
            if inv is None:
136
 
                inv = self.get_inventory(rev_id)
 
129
                inv = self.get_inventory(revision_id)
137
130
            plaintext = Testament(rev, inv).as_short_text()
138
131
            self.store_revision_signature(
139
 
                gpg.GPGStrategy(config), plaintext, rev_id)
140
 
        if not rev_id in self.get_inventory_weave():
 
132
                gpg.GPGStrategy(config), plaintext, revision_id)
 
133
        if not revision_id in self.get_inventory_weave():
141
134
            if inv is None:
142
 
                raise errors.WeaveRevisionNotPresent(rev_id,
 
135
                raise errors.WeaveRevisionNotPresent(revision_id,
143
136
                                                     self.get_inventory_weave())
144
137
            else:
145
138
                # yes, this is not suitable for adding with ghosts.
146
 
                self.add_inventory(rev_id, inv, rev.parent_ids)
 
139
                self.add_inventory(revision_id, inv, rev.parent_ids)
147
140
        self._revision_store.add_revision(rev, self.get_transaction())
148
141
 
149
142
    @needs_read_lock
171
164
        if self._revision_store.text_store.listable():
172
165
            return self._revision_store.all_revision_ids(self.get_transaction())
173
166
        result = self._all_possible_ids()
 
167
        # TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
 
168
        #       ids. (It should, since _revision_store's API should change to
 
169
        #       return utf8 revision_ids)
174
170
        return self._eliminate_revisions_not_present(result)
175
171
 
176
172
    def break_lock(self):
224
220
        # TODO: make sure to construct the right store classes, etc, depending
225
221
        # on whether escaping is required.
226
222
        self._warn_if_deprecated()
227
 
        self._serializer = xml5.serializer_v5
228
223
 
229
224
    def __repr__(self):
230
225
        return '%s(%r)' % (self.__class__.__name__, 
328
323
 
329
324
        revision_id: only return revision ids included by revision_id.
330
325
        """
 
326
        revision_id = osutils.safe_revision_id(revision_id)
331
327
        return InterRepository.get(other, self).missing_revision_ids(revision_id)
332
328
 
333
329
    @staticmethod
346
342
        This is a destructive operation! Do not use it on existing 
347
343
        repositories.
348
344
        """
 
345
        revision_id = osutils.safe_revision_id(revision_id)
349
346
        return InterRepository.get(self, destination).copy_content(revision_id, basis)
350
347
 
351
348
    def fetch(self, source, revision_id=None, pb=None):
353
350
 
354
351
        If revision_id is None all content is copied.
355
352
        """
 
353
        revision_id = osutils.safe_revision_id(revision_id)
356
354
        return InterRepository.get(source, self).fetch(revision_id=revision_id,
357
355
                                                       pb=pb)
358
356
 
370
368
        :param revprops: Optional dictionary of revision properties.
371
369
        :param revision_id: Optional revision id.
372
370
        """
 
371
        revision_id = osutils.safe_revision_id(revision_id)
373
372
        return _CommitBuilder(self, parents, config, timestamp, timezone,
374
373
                              committer, revprops, revision_id)
375
374
 
382
381
 
383
382
        Currently no check is made that the format of this repository and
384
383
        the bzrdir format are compatible. FIXME RBC 20060201.
 
384
 
 
385
        :return: The newly created destination repository.
385
386
        """
386
387
        if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
387
388
            # use target default format.
388
 
            result = a_bzrdir.create_repository()
389
 
        # FIXME RBC 20060209 split out the repository type to avoid this check ?
390
 
        elif isinstance(a_bzrdir._format,
391
 
                      (bzrdir.BzrDirFormat4,
392
 
                       bzrdir.BzrDirFormat5,
393
 
                       bzrdir.BzrDirFormat6)):
394
 
            result = a_bzrdir.open_repository()
 
389
            dest_repo = a_bzrdir.create_repository()
395
390
        else:
396
 
            result = self._format.initialize(a_bzrdir, shared=self.is_shared())
397
 
        self.copy_content_into(result, revision_id, basis)
398
 
        return result
 
391
            # Most control formats need the repository to be specifically
 
392
            # created, but on some old all-in-one formats it's not needed
 
393
            try:
 
394
                dest_repo = self._format.initialize(a_bzrdir, shared=self.is_shared())
 
395
            except errors.UninitializableFormat:
 
396
                dest_repo = a_bzrdir.open_repository()
 
397
        self.copy_content_into(dest_repo, revision_id, basis)
 
398
        return dest_repo
399
399
 
400
400
    @needs_read_lock
401
401
    def has_revision(self, revision_id):
402
402
        """True if this repository has a copy of the revision."""
 
403
        revision_id = osutils.safe_revision_id(revision_id)
403
404
        return self._revision_store.has_revision_id(revision_id,
404
405
                                                    self.get_transaction())
405
406
 
415
416
        if not revision_id or not isinstance(revision_id, basestring):
416
417
            raise errors.InvalidRevisionId(revision_id=revision_id,
417
418
                                           branch=self)
418
 
        return self._revision_store.get_revisions([revision_id],
419
 
                                                  self.get_transaction())[0]
 
419
        return self.get_revisions([revision_id])[0]
 
420
 
420
421
    @needs_read_lock
421
422
    def get_revisions(self, revision_ids):
422
 
        return self._revision_store.get_revisions(revision_ids,
 
423
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
 
424
        revs = self._revision_store.get_revisions(revision_ids,
423
425
                                                  self.get_transaction())
 
426
        for rev in revs:
 
427
            assert not isinstance(rev.revision_id, unicode)
 
428
            for parent_id in rev.parent_ids:
 
429
                assert not isinstance(parent_id, unicode)
 
430
        return revs
424
431
 
425
432
    @needs_read_lock
426
433
    def get_revision_xml(self, revision_id):
427
 
        rev = self.get_revision(revision_id) 
 
434
        # TODO: jam 20070210 This shouldn't be necessary since get_revision
 
435
        #       would have already do it.
 
436
        # TODO: jam 20070210 Just use _serializer.write_revision_to_string()
 
437
        revision_id = osutils.safe_revision_id(revision_id)
 
438
        rev = self.get_revision(revision_id)
428
439
        rev_tmp = StringIO()
429
440
        # the current serializer..
430
441
        self._revision_store._serializer.write_revision(rev, rev_tmp)
434
445
    @needs_read_lock
435
446
    def get_revision(self, revision_id):
436
447
        """Return the Revision object for a named revision"""
 
448
        # TODO: jam 20070210 get_revision_reconcile should do this for us
 
449
        revision_id = osutils.safe_revision_id(revision_id)
437
450
        r = self.get_revision_reconcile(revision_id)
438
451
        # weave corruption can lead to absent revision markers that should be
439
452
        # present.
495
508
 
496
509
    @needs_write_lock
497
510
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
 
511
        revision_id = osutils.safe_revision_id(revision_id)
498
512
        signature = gpg_strategy.sign(plaintext)
499
513
        self._revision_store.add_revision_signature_text(revision_id,
500
514
                                                         signature,
511
525
        assert self._serializer.support_altered_by_hack, \
512
526
            ("fileids_altered_by_revision_ids only supported for branches " 
513
527
             "which store inventory as unnested xml, not on %r" % self)
514
 
        selected_revision_ids = set(revision_ids)
 
528
        selected_revision_ids = set(osutils.safe_revision_id(r)
 
529
                                    for r in revision_ids)
515
530
        w = self.get_inventory_weave()
516
531
        result = {}
517
532
 
581
596
    @needs_read_lock
582
597
    def get_inventory(self, revision_id):
583
598
        """Get Inventory object by hash."""
 
599
        # TODO: jam 20070210 Technically we don't need to sanitize, since all
 
600
        #       called functions must sanitize.
 
601
        revision_id = osutils.safe_revision_id(revision_id)
584
602
        return self.deserialise_inventory(
585
603
            revision_id, self.get_inventory_xml(revision_id))
586
604
 
590
608
        :param revision_id: The expected revision id of the inventory.
591
609
        :param xml: A serialised inventory.
592
610
        """
 
611
        revision_id = osutils.safe_revision_id(revision_id)
593
612
        result = self._serializer.read_inventory_from_string(xml)
594
613
        result.root.revision = revision_id
595
614
        return result
600
619
    @needs_read_lock
601
620
    def get_inventory_xml(self, revision_id):
602
621
        """Get inventory XML as a file object."""
 
622
        revision_id = osutils.safe_revision_id(revision_id)
603
623
        try:
604
 
            assert isinstance(revision_id, basestring), type(revision_id)
 
624
            assert isinstance(revision_id, str), type(revision_id)
605
625
            iw = self.get_inventory_weave()
606
626
            return iw.get_text(revision_id)
607
627
        except IndexError:
611
631
    def get_inventory_sha1(self, revision_id):
612
632
        """Return the sha1 hash of the inventory entry
613
633
        """
 
634
        # TODO: jam 20070210 Shouldn't this be deprecated / removed?
 
635
        revision_id = osutils.safe_revision_id(revision_id)
614
636
        return self.get_revision(revision_id).inventory_sha1
615
637
 
616
638
    @needs_read_lock
625
647
        # special case NULL_REVISION
626
648
        if revision_id == _mod_revision.NULL_REVISION:
627
649
            return {}
 
650
        revision_id = osutils.safe_revision_id(revision_id)
628
651
        a_weave = self.get_inventory_weave()
629
652
        all_revisions = self._eliminate_revisions_not_present(
630
653
                                a_weave.versions())
658
681
            pending = set(self.all_revision_ids())
659
682
            required = set([])
660
683
        else:
661
 
            pending = set(revision_ids)
 
684
            pending = set(osutils.safe_revision_id(r) for r in revision_ids)
662
685
            # special case NULL_REVISION
663
686
            if _mod_revision.NULL_REVISION in pending:
664
687
                pending.remove(_mod_revision.NULL_REVISION)
684
707
            done.add(revision_id)
685
708
        return result
686
709
 
 
710
    def _get_history_vf(self):
 
711
        """Get a versionedfile whose history graph reflects all revisions.
 
712
 
 
713
        For weave repositories, this is the inventory weave.
 
714
        """
 
715
        return self.get_inventory_weave()
 
716
 
 
717
    def iter_reverse_revision_history(self, revision_id):
 
718
        """Iterate backwards through revision ids in the lefthand history
 
719
 
 
720
        :param revision_id: The revision id to start with.  All its lefthand
 
721
            ancestors will be traversed.
 
722
        """
 
723
        revision_id = osutils.safe_revision_id(revision_id)
 
724
        if revision_id in (None, _mod_revision.NULL_REVISION):
 
725
            return
 
726
        next_id = revision_id
 
727
        versionedfile = self._get_history_vf()
 
728
        while True:
 
729
            yield next_id
 
730
            parents = versionedfile.get_parents(next_id)
 
731
            if len(parents) == 0:
 
732
                return
 
733
            else:
 
734
                next_id = parents[0]
 
735
 
687
736
    @needs_read_lock
688
737
    def get_revision_inventory(self, revision_id):
689
738
        """Return inventory of a past revision."""
725
774
            return RevisionTree(self, Inventory(root_id=None), 
726
775
                                _mod_revision.NULL_REVISION)
727
776
        else:
 
777
            revision_id = osutils.safe_revision_id(revision_id)
728
778
            inv = self.get_revision_inventory(revision_id)
729
779
            return RevisionTree(self, inv, revision_id)
730
780
 
752
802
        """
753
803
        if revision_id is None:
754
804
            return [None]
 
805
        revision_id = osutils.safe_revision_id(revision_id)
755
806
        if not self.has_revision(revision_id):
756
807
            raise errors.NoSuchRevision(self, revision_id)
757
808
        w = self.get_inventory_weave()
766
817
        - it writes to stdout, it assumes that that is valid etc. Fix
767
818
        by creating a new more flexible convenience function.
768
819
        """
 
820
        revision_id = osutils.safe_revision_id(revision_id)
769
821
        tree = self.revision_tree(revision_id)
770
822
        # use inventory as it was in that revision
771
823
        file_id = tree.inventory.path2id(file)
779
831
    def get_transaction(self):
780
832
        return self.control_files.get_transaction()
781
833
 
782
 
    def revision_parents(self, revid):
783
 
        return self.get_inventory_weave().parent_names(revid)
 
834
    def revision_parents(self, revision_id):
 
835
        revision_id = osutils.safe_revision_id(revision_id)
 
836
        return self.get_inventory_weave().parent_names(revision_id)
784
837
 
785
838
    @needs_write_lock
786
839
    def set_make_working_trees(self, new_value):
800
853
 
801
854
    @needs_write_lock
802
855
    def sign_revision(self, revision_id, gpg_strategy):
 
856
        revision_id = osutils.safe_revision_id(revision_id)
803
857
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
804
858
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
805
859
 
806
860
    @needs_read_lock
807
861
    def has_signature_for_revision_id(self, revision_id):
808
862
        """Query for a revision signature for revision_id in the repository."""
 
863
        revision_id = osutils.safe_revision_id(revision_id)
809
864
        return self._revision_store.has_signature(revision_id,
810
865
                                                  self.get_transaction())
811
866
 
812
867
    @needs_read_lock
813
868
    def get_signature_text(self, revision_id):
814
869
        """Return the text for a signature."""
 
870
        revision_id = osutils.safe_revision_id(revision_id)
815
871
        return self._revision_store.get_signature_text(revision_id,
816
872
                                                       self.get_transaction())
817
873
 
827
883
        if not revision_ids:
828
884
            raise ValueError("revision_ids must be non-empty in %s.check" 
829
885
                    % (self,))
 
886
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
830
887
        return self._check(revision_ids)
831
888
 
832
889
    def _check(self, revision_ids):
855
912
                    revision_id.encode('ascii')
856
913
                except UnicodeEncodeError:
857
914
                    raise errors.NonAsciiRevisionId(method, self)
858
 
 
859
 
 
860
 
class AllInOneRepository(Repository):
861
 
    """Legacy support - the repository behaviour for all-in-one branches."""
862
 
 
863
 
    def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
864
 
        # we reuse one control files instance.
865
 
        dir_mode = a_bzrdir._control_files._dir_mode
866
 
        file_mode = a_bzrdir._control_files._file_mode
867
 
 
868
 
        def get_store(name, compressed=True, prefixed=False):
869
 
            # FIXME: This approach of assuming stores are all entirely compressed
870
 
            # or entirely uncompressed is tidy, but breaks upgrade from 
871
 
            # some existing branches where there's a mixture; we probably 
872
 
            # still want the option to look for both.
873
 
            relpath = a_bzrdir._control_files._escape(name)
874
 
            store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
875
 
                              prefixed=prefixed, compressed=compressed,
876
 
                              dir_mode=dir_mode,
877
 
                              file_mode=file_mode)
878
 
            #if self._transport.should_cache():
879
 
            #    cache_path = os.path.join(self.cache_root, name)
880
 
            #    os.mkdir(cache_path)
881
 
            #    store = bzrlib.store.CachedStore(store, cache_path)
882
 
            return store
883
 
 
884
 
        # not broken out yet because the controlweaves|inventory_store
885
 
        # and text_store | weave_store bits are still different.
886
 
        if isinstance(_format, RepositoryFormat4):
887
 
            # cannot remove these - there is still no consistent api 
888
 
            # which allows access to this old info.
889
 
            self.inventory_store = get_store('inventory-store')
890
 
            text_store = get_store('text-store')
891
 
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
892
 
 
893
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
894
 
                           timezone=None, committer=None, revprops=None,
895
 
                           revision_id=None):
896
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
897
 
        return Repository.get_commit_builder(self, branch, parents, config,
898
 
            timestamp, timezone, committer, revprops, revision_id)
899
 
 
900
 
    @needs_read_lock
901
 
    def is_shared(self):
902
 
        """AllInOne repositories cannot be shared."""
903
 
        return False
904
 
 
905
 
    @needs_write_lock
906
 
    def set_make_working_trees(self, new_value):
907
 
        """Set the policy flag for making working trees when creating branches.
908
 
 
909
 
        This only applies to branches that use this repository.
910
 
 
911
 
        The default is 'True'.
912
 
        :param new_value: True to restore the default, False to disable making
913
 
                          working trees.
914
 
        """
915
 
        raise NotImplementedError(self.set_make_working_trees)
916
 
    
917
 
    def make_working_trees(self):
918
 
        """Returns the policy for making working trees on new branches."""
919
 
        return True
 
915
            else:
 
916
                try:
 
917
                    revision_id.decode('ascii')
 
918
                except UnicodeDecodeError:
 
919
                    raise errors.NonAsciiRevisionId(method, self)
 
920
 
 
921
 
 
922
 
 
923
# remove these delegates a while after bzr 0.15
 
924
def __make_delegated(name, from_module):
 
925
    def _deprecated_repository_forwarder():
 
926
        symbol_versioning.warn('%s moved to %s in bzr 0.15'
 
927
            % (name, from_module),
 
928
            DeprecationWarning,
 
929
            stacklevel=2)
 
930
        m = __import__(from_module, globals(), locals(), [name])
 
931
        try:
 
932
            return getattr(m, name)
 
933
        except AttributeError:
 
934
            raise AttributeError('module %s has no name %s'
 
935
                    % (m, name))
 
936
    globals()[name] = _deprecated_repository_forwarder
 
937
 
 
938
for _name in [
 
939
        'AllInOneRepository',
 
940
        'WeaveMetaDirRepository',
 
941
        'PreSplitOutRepositoryFormat',
 
942
        'RepositoryFormat4',
 
943
        'RepositoryFormat5',
 
944
        'RepositoryFormat6',
 
945
        'RepositoryFormat7',
 
946
        ]:
 
947
    __make_delegated(_name, 'bzrlib.repofmt.weaverepo')
 
948
 
 
949
for _name in [
 
950
        'KnitRepository',
 
951
        'KnitRepository2',
 
952
        'RepositoryFormatKnit',
 
953
        'RepositoryFormatKnit1',
 
954
        'RepositoryFormatKnit2',
 
955
        ]:
 
956
    __make_delegated(_name, 'bzrlib.repofmt.knitrepo')
920
957
 
921
958
 
922
959
def install_revision(repository, rev, revision_tree):
1008
1045
        return not self.control_files._transport.has('no-working-trees')
1009
1046
 
1010
1047
 
1011
 
class WeaveMetaDirRepository(MetaDirRepository):
1012
 
    """A subclass of MetaDirRepository to set weave specific policy."""
1013
 
 
1014
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1015
 
                           timezone=None, committer=None, revprops=None,
1016
 
                           revision_id=None):
1017
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
1018
 
        return MetaDirRepository.get_commit_builder(self, branch, parents,
1019
 
            config, timestamp, timezone, committer, revprops, revision_id)
1020
 
 
1021
 
 
1022
 
class KnitRepository(MetaDirRepository):
1023
 
    """Knit format repository."""
1024
 
 
1025
 
    def _warn_if_deprecated(self):
1026
 
        # This class isn't deprecated
1027
 
        pass
1028
 
 
1029
 
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
1030
 
        inv_vf.add_lines_with_ghosts(revid, parents, lines)
1031
 
 
1032
 
    @needs_read_lock
1033
 
    def _all_revision_ids(self):
1034
 
        """See Repository.all_revision_ids()."""
1035
 
        # Knits get the revision graph from the index of the revision knit, so
1036
 
        # it's always possible even if they're on an unlistable transport.
1037
 
        return self._revision_store.all_revision_ids(self.get_transaction())
1038
 
 
1039
 
    def fileid_involved_between_revs(self, from_revid, to_revid):
1040
 
        """Find file_id(s) which are involved in the changes between revisions.
1041
 
 
1042
 
        This determines the set of revisions which are involved, and then
1043
 
        finds all file ids affected by those revisions.
1044
 
        """
1045
 
        vf = self._get_revision_vf()
1046
 
        from_set = set(vf.get_ancestry(from_revid))
1047
 
        to_set = set(vf.get_ancestry(to_revid))
1048
 
        changed = to_set.difference(from_set)
1049
 
        return self._fileid_involved_by_set(changed)
1050
 
 
1051
 
    def fileid_involved(self, last_revid=None):
1052
 
        """Find all file_ids modified in the ancestry of last_revid.
1053
 
 
1054
 
        :param last_revid: If None, last_revision() will be used.
1055
 
        """
1056
 
        if not last_revid:
1057
 
            changed = set(self.all_revision_ids())
1058
 
        else:
1059
 
            changed = set(self.get_ancestry(last_revid))
1060
 
        if None in changed:
1061
 
            changed.remove(None)
1062
 
        return self._fileid_involved_by_set(changed)
1063
 
 
1064
 
    @needs_read_lock
1065
 
    def get_ancestry(self, revision_id):
1066
 
        """Return a list of revision-ids integrated by a revision.
1067
 
        
1068
 
        This is topologically sorted.
1069
 
        """
1070
 
        if revision_id is None:
1071
 
            return [None]
1072
 
        vf = self._get_revision_vf()
1073
 
        try:
1074
 
            return [None] + vf.get_ancestry(revision_id)
1075
 
        except errors.RevisionNotPresent:
1076
 
            raise errors.NoSuchRevision(self, revision_id)
1077
 
 
1078
 
    @needs_read_lock
1079
 
    def get_revision(self, revision_id):
1080
 
        """Return the Revision object for a named revision"""
1081
 
        return self.get_revision_reconcile(revision_id)
1082
 
 
1083
 
    @needs_read_lock
1084
 
    def get_revision_graph(self, revision_id=None):
1085
 
        """Return a dictionary containing the revision graph.
1086
 
 
1087
 
        :param revision_id: The revision_id to get a graph from. If None, then
1088
 
        the entire revision graph is returned. This is a deprecated mode of
1089
 
        operation and will be removed in the future.
1090
 
        :return: a dictionary of revision_id->revision_parents_list.
1091
 
        """
1092
 
        # special case NULL_REVISION
1093
 
        if revision_id == _mod_revision.NULL_REVISION:
1094
 
            return {}
1095
 
        a_weave = self._get_revision_vf()
1096
 
        entire_graph = a_weave.get_graph()
1097
 
        if revision_id is None:
1098
 
            return a_weave.get_graph()
1099
 
        elif revision_id not in a_weave:
1100
 
            raise errors.NoSuchRevision(self, revision_id)
1101
 
        else:
1102
 
            # add what can be reached from revision_id
1103
 
            result = {}
1104
 
            pending = set([revision_id])
1105
 
            while len(pending) > 0:
1106
 
                node = pending.pop()
1107
 
                result[node] = a_weave.get_parents(node)
1108
 
                for revision_id in result[node]:
1109
 
                    if revision_id not in result:
1110
 
                        pending.add(revision_id)
1111
 
            return result
1112
 
 
1113
 
    @needs_read_lock
1114
 
    def get_revision_graph_with_ghosts(self, revision_ids=None):
1115
 
        """Return a graph of the revisions with ghosts marked as applicable.
1116
 
 
1117
 
        :param revision_ids: an iterable of revisions to graph or None for all.
1118
 
        :return: a Graph object with the graph reachable from revision_ids.
1119
 
        """
1120
 
        result = graph.Graph()
1121
 
        vf = self._get_revision_vf()
1122
 
        versions = set(vf.versions())
1123
 
        if not revision_ids:
1124
 
            pending = set(self.all_revision_ids())
1125
 
            required = set([])
1126
 
        else:
1127
 
            pending = set(revision_ids)
1128
 
            # special case NULL_REVISION
1129
 
            if _mod_revision.NULL_REVISION in pending:
1130
 
                pending.remove(_mod_revision.NULL_REVISION)
1131
 
            required = set(pending)
1132
 
        done = set([])
1133
 
        while len(pending):
1134
 
            revision_id = pending.pop()
1135
 
            if not revision_id in versions:
1136
 
                if revision_id in required:
1137
 
                    raise errors.NoSuchRevision(self, revision_id)
1138
 
                # a ghost
1139
 
                result.add_ghost(revision_id)
1140
 
                # mark it as done so we don't try for it again.
1141
 
                done.add(revision_id)
1142
 
                continue
1143
 
            parent_ids = vf.get_parents_with_ghosts(revision_id)
1144
 
            for parent_id in parent_ids:
1145
 
                # is this queued or done ?
1146
 
                if (parent_id not in pending and
1147
 
                    parent_id not in done):
1148
 
                    # no, queue it.
1149
 
                    pending.add(parent_id)
1150
 
            result.add_node(revision_id, parent_ids)
1151
 
            done.add(revision_id)
1152
 
        return result
1153
 
 
1154
 
    def _get_revision_vf(self):
1155
 
        """:return: a versioned file containing the revisions."""
1156
 
        vf = self._revision_store.get_revision_file(self.get_transaction())
1157
 
        return vf
1158
 
 
1159
 
    @needs_write_lock
1160
 
    def reconcile(self, other=None, thorough=False):
1161
 
        """Reconcile this repository."""
1162
 
        from bzrlib.reconcile import KnitReconciler
1163
 
        reconciler = KnitReconciler(self, thorough=thorough)
1164
 
        reconciler.reconcile()
1165
 
        return reconciler
1166
 
    
1167
 
    def revision_parents(self, revision_id):
1168
 
        return self._get_revision_vf().get_parents(revision_id)
1169
 
 
1170
 
 
1171
 
class KnitRepository2(KnitRepository):
1172
 
    """"""
1173
 
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1174
 
                 control_store, text_store):
1175
 
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1176
 
                              _revision_store, control_store, text_store)
1177
 
        self._serializer = xml6.serializer_v6
1178
 
 
1179
 
    def deserialise_inventory(self, revision_id, xml):
1180
 
        """Transform the xml into an inventory object. 
1181
 
 
1182
 
        :param revision_id: The expected revision id of the inventory.
1183
 
        :param xml: A serialised inventory.
1184
 
        """
1185
 
        result = self._serializer.read_inventory_from_string(xml)
1186
 
        assert result.root.revision is not None
1187
 
        return result
1188
 
 
1189
 
    def serialise_inventory(self, inv):
1190
 
        """Transform the inventory object into XML text.
1191
 
 
1192
 
        :param revision_id: The expected revision id of the inventory.
1193
 
        :param xml: A serialised inventory.
1194
 
        """
1195
 
        assert inv.revision_id is not None
1196
 
        assert inv.root.revision is not None
1197
 
        return KnitRepository.serialise_inventory(self, inv)
1198
 
 
1199
 
    def get_commit_builder(self, branch, parents, config, timestamp=None, 
1200
 
                           timezone=None, committer=None, revprops=None, 
1201
 
                           revision_id=None):
1202
 
        """Obtain a CommitBuilder for this repository.
1203
 
        
1204
 
        :param branch: Branch to commit to.
1205
 
        :param parents: Revision ids of the parents of the new revision.
1206
 
        :param config: Configuration to use.
1207
 
        :param timestamp: Optional timestamp recorded for commit.
1208
 
        :param timezone: Optional timezone for timestamp.
1209
 
        :param committer: Optional committer to set for commit.
1210
 
        :param revprops: Optional dictionary of revision properties.
1211
 
        :param revision_id: Optional revision id.
1212
 
        """
1213
 
        return RootCommitBuilder(self, parents, config, timestamp, timezone,
1214
 
                                 committer, revprops, revision_id)
1215
 
 
1216
 
 
1217
1048
class RepositoryFormatRegistry(registry.Registry):
1218
1049
    """Registry of RepositoryFormats.
1219
1050
    """
 
1051
 
 
1052
    def get(self, format_string):
 
1053
        r = registry.Registry.get(self, format_string)
 
1054
        if callable(r):
 
1055
            r = r()
 
1056
        return r
1220
1057
    
1221
1058
 
1222
1059
format_registry = RepositoryFormatRegistry()
1223
 
"""Registry of formats, indexed by their identifying format string."""
 
1060
"""Registry of formats, indexed by their identifying format string.
 
1061
 
 
1062
This can contain either format instances themselves, or classes/factories that
 
1063
can be called to obtain one.
 
1064
"""
1224
1065
 
1225
1066
 
1226
1067
class RepositoryFormat(object):
1250
1091
    def __str__(self):
1251
1092
        return "<%s>" % self.__class__.__name__
1252
1093
 
 
1094
    def __eq__(self, other):
 
1095
        # format objects are generally stateless
 
1096
        return isinstance(other, self.__class__)
 
1097
 
1253
1098
    @classmethod
1254
1099
    def find_format(klass, a_bzrdir):
1255
1100
        """Return the format for the repository object in a_bzrdir.
1324
1169
        _revision_store = TextRevisionStore(text_store, serializer)
1325
1170
        return _revision_store
1326
1171
 
 
1172
    # TODO: this shouldn't be in the base class, it's specific to things that
 
1173
    # use weaves or knits -- mbp 20070207
1327
1174
    def _get_versioned_file_store(self,
1328
1175
                                  name,
1329
1176
                                  transport,
1330
1177
                                  control_files,
1331
1178
                                  prefixed=True,
1332
 
                                  versionedfile_class=weave.WeaveFile,
 
1179
                                  versionedfile_class=None,
1333
1180
                                  versionedfile_kwargs={},
1334
1181
                                  escaped=False):
 
1182
        if versionedfile_class is None:
 
1183
            versionedfile_class = self._versionedfile_class
1335
1184
        weave_transport = control_files._transport.clone(name)
1336
1185
        dir_mode = control_files._dir_mode
1337
1186
        file_mode = control_files._file_mode
1374
1223
        raise NotImplementedError(self.open)
1375
1224
 
1376
1225
 
1377
 
class PreSplitOutRepositoryFormat(RepositoryFormat):
1378
 
    """Base class for the pre split out repository formats."""
1379
 
 
1380
 
    rich_root_data = False
1381
 
 
1382
 
    def initialize(self, a_bzrdir, shared=False, _internal=False):
1383
 
        """Create a weave repository.
1384
 
        
1385
 
        TODO: when creating split out bzr branch formats, move this to a common
1386
 
        base for Format5, Format6. or something like that.
1387
 
        """
1388
 
        if shared:
1389
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
1390
 
 
1391
 
        if not _internal:
1392
 
            # always initialized when the bzrdir is.
1393
 
            return self.open(a_bzrdir, _found=True)
1394
 
        
1395
 
        # Create an empty weave
1396
 
        sio = StringIO()
1397
 
        weavefile.write_weave_v5(weave.Weave(), sio)
1398
 
        empty_weave = sio.getvalue()
1399
 
 
1400
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1401
 
        dirs = ['revision-store', 'weaves']
1402
 
        files = [('inventory.weave', StringIO(empty_weave)),
1403
 
                 ]
1404
 
        
1405
 
        # FIXME: RBC 20060125 don't peek under the covers
1406
 
        # NB: no need to escape relative paths that are url safe.
1407
 
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
1408
 
                                'branch-lock', lockable_files.TransportLock)
1409
 
        control_files.create_lock()
1410
 
        control_files.lock_write()
1411
 
        control_files._transport.mkdir_multi(dirs,
1412
 
                mode=control_files._dir_mode)
1413
 
        try:
1414
 
            for file, content in files:
1415
 
                control_files.put(file, content)
1416
 
        finally:
1417
 
            control_files.unlock()
1418
 
        return self.open(a_bzrdir, _found=True)
1419
 
 
1420
 
    def _get_control_store(self, repo_transport, control_files):
1421
 
        """Return the control store for this repository."""
1422
 
        return self._get_versioned_file_store('',
1423
 
                                              repo_transport,
1424
 
                                              control_files,
1425
 
                                              prefixed=False)
1426
 
 
1427
 
    def _get_text_store(self, transport, control_files):
1428
 
        """Get a store for file texts for this format."""
1429
 
        raise NotImplementedError(self._get_text_store)
1430
 
 
1431
 
    def open(self, a_bzrdir, _found=False):
1432
 
        """See RepositoryFormat.open()."""
1433
 
        if not _found:
1434
 
            # we are being called directly and must probe.
1435
 
            raise NotImplementedError
1436
 
 
1437
 
        repo_transport = a_bzrdir.get_repository_transport(None)
1438
 
        control_files = a_bzrdir._control_files
1439
 
        text_store = self._get_text_store(repo_transport, control_files)
1440
 
        control_store = self._get_control_store(repo_transport, control_files)
1441
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1442
 
        return AllInOneRepository(_format=self,
1443
 
                                  a_bzrdir=a_bzrdir,
1444
 
                                  _revision_store=_revision_store,
1445
 
                                  control_store=control_store,
1446
 
                                  text_store=text_store)
1447
 
 
1448
 
    def check_conversion_target(self, target_format):
1449
 
        pass
1450
 
 
1451
 
 
1452
 
class RepositoryFormat4(PreSplitOutRepositoryFormat):
1453
 
    """Bzr repository format 4.
1454
 
 
1455
 
    This repository format has:
1456
 
     - flat stores
1457
 
     - TextStores for texts, inventories,revisions.
1458
 
 
1459
 
    This format is deprecated: it indexes texts using a text id which is
1460
 
    removed in format 5; initialization and write support for this format
1461
 
    has been removed.
1462
 
    """
1463
 
 
1464
 
    def __init__(self):
1465
 
        super(RepositoryFormat4, self).__init__()
1466
 
        self._matchingbzrdir = bzrdir.BzrDirFormat4()
1467
 
 
1468
 
    def get_format_description(self):
1469
 
        """See RepositoryFormat.get_format_description()."""
1470
 
        return "Repository format 4"
1471
 
 
1472
 
    def initialize(self, url, shared=False, _internal=False):
1473
 
        """Format 4 branches cannot be created."""
1474
 
        raise errors.UninitializableFormat(self)
1475
 
 
1476
 
    def is_supported(self):
1477
 
        """Format 4 is not supported.
1478
 
 
1479
 
        It is not supported because the model changed from 4 to 5 and the
1480
 
        conversion logic is expensive - so doing it on the fly was not 
1481
 
        feasible.
1482
 
        """
1483
 
        return False
1484
 
 
1485
 
    def _get_control_store(self, repo_transport, control_files):
1486
 
        """Format 4 repositories have no formal control store at this point.
1487
 
        
1488
 
        This will cause any control-file-needing apis to fail - this is desired.
1489
 
        """
1490
 
        return None
1491
 
    
1492
 
    def _get_revision_store(self, repo_transport, control_files):
1493
 
        """See RepositoryFormat._get_revision_store()."""
1494
 
        from bzrlib.xml4 import serializer_v4
1495
 
        return self._get_text_rev_store(repo_transport,
1496
 
                                        control_files,
1497
 
                                        'revision-store',
1498
 
                                        serializer=serializer_v4)
1499
 
 
1500
 
    def _get_text_store(self, transport, control_files):
1501
 
        """See RepositoryFormat._get_text_store()."""
1502
 
 
1503
 
 
1504
 
class RepositoryFormat5(PreSplitOutRepositoryFormat):
1505
 
    """Bzr control format 5.
1506
 
 
1507
 
    This repository format has:
1508
 
     - weaves for file texts and inventory
1509
 
     - flat stores
1510
 
     - TextStores for revisions and signatures.
1511
 
    """
1512
 
 
1513
 
    def __init__(self):
1514
 
        super(RepositoryFormat5, self).__init__()
1515
 
        self._matchingbzrdir = bzrdir.BzrDirFormat5()
1516
 
 
1517
 
    def get_format_description(self):
1518
 
        """See RepositoryFormat.get_format_description()."""
1519
 
        return "Weave repository format 5"
1520
 
 
1521
 
    def _get_revision_store(self, repo_transport, control_files):
1522
 
        """See RepositoryFormat._get_revision_store()."""
1523
 
        """Return the revision store object for this a_bzrdir."""
1524
 
        return self._get_text_rev_store(repo_transport,
1525
 
                                        control_files,
1526
 
                                        'revision-store',
1527
 
                                        compressed=False)
1528
 
 
1529
 
    def _get_text_store(self, transport, control_files):
1530
 
        """See RepositoryFormat._get_text_store()."""
1531
 
        return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
1532
 
 
1533
 
 
1534
 
class RepositoryFormat6(PreSplitOutRepositoryFormat):
1535
 
    """Bzr control format 6.
1536
 
 
1537
 
    This repository format has:
1538
 
     - weaves for file texts and inventory
1539
 
     - hash subdirectory based stores.
1540
 
     - TextStores for revisions and signatures.
1541
 
    """
1542
 
 
1543
 
    def __init__(self):
1544
 
        super(RepositoryFormat6, self).__init__()
1545
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
1546
 
 
1547
 
    def get_format_description(self):
1548
 
        """See RepositoryFormat.get_format_description()."""
1549
 
        return "Weave repository format 6"
1550
 
 
1551
 
    def _get_revision_store(self, repo_transport, control_files):
1552
 
        """See RepositoryFormat._get_revision_store()."""
1553
 
        return self._get_text_rev_store(repo_transport,
1554
 
                                        control_files,
1555
 
                                        'revision-store',
1556
 
                                        compressed=False,
1557
 
                                        prefixed=True)
1558
 
 
1559
 
    def _get_text_store(self, transport, control_files):
1560
 
        """See RepositoryFormat._get_text_store()."""
1561
 
        return self._get_versioned_file_store('weaves', transport, control_files)
1562
 
 
1563
 
 
1564
1226
class MetaDirRepositoryFormat(RepositoryFormat):
1565
1227
    """Common base class for the new repositories using the metadir layout."""
1566
1228
 
1567
1229
    rich_root_data = False
 
1230
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1568
1231
 
1569
1232
    def __init__(self):
1570
1233
        super(MetaDirRepositoryFormat, self).__init__()
1571
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1572
1234
 
1573
1235
    def _create_control_files(self, a_bzrdir):
1574
1236
        """Create the required files and the initial control_files object."""
1597
1259
            control_files.unlock()
1598
1260
 
1599
1261
 
1600
 
class RepositoryFormat7(MetaDirRepositoryFormat):
1601
 
    """Bzr repository 7.
1602
 
 
1603
 
    This repository format has:
1604
 
     - weaves for file texts and inventory
1605
 
     - hash subdirectory based stores.
1606
 
     - TextStores for revisions and signatures.
1607
 
     - a format marker of its own
1608
 
     - an optional 'shared-storage' flag
1609
 
     - an optional 'no-working-trees' flag
1610
 
    """
1611
 
 
1612
 
    def _get_control_store(self, repo_transport, control_files):
1613
 
        """Return the control store for this repository."""
1614
 
        return self._get_versioned_file_store('',
1615
 
                                              repo_transport,
1616
 
                                              control_files,
1617
 
                                              prefixed=False)
1618
 
 
1619
 
    def get_format_string(self):
1620
 
        """See RepositoryFormat.get_format_string()."""
1621
 
        return "Bazaar-NG Repository format 7"
1622
 
 
1623
 
    def get_format_description(self):
1624
 
        """See RepositoryFormat.get_format_description()."""
1625
 
        return "Weave repository format 7"
1626
 
 
1627
 
    def check_conversion_target(self, target_format):
1628
 
        pass
1629
 
 
1630
 
    def _get_revision_store(self, repo_transport, control_files):
1631
 
        """See RepositoryFormat._get_revision_store()."""
1632
 
        return self._get_text_rev_store(repo_transport,
1633
 
                                        control_files,
1634
 
                                        'revision-store',
1635
 
                                        compressed=False,
1636
 
                                        prefixed=True,
1637
 
                                        )
1638
 
 
1639
 
    def _get_text_store(self, transport, control_files):
1640
 
        """See RepositoryFormat._get_text_store()."""
1641
 
        return self._get_versioned_file_store('weaves',
1642
 
                                              transport,
1643
 
                                              control_files)
1644
 
 
1645
 
    def initialize(self, a_bzrdir, shared=False):
1646
 
        """Create a weave repository.
1647
 
 
1648
 
        :param shared: If true the repository will be initialized as a shared
1649
 
                       repository.
1650
 
        """
1651
 
        # Create an empty weave
1652
 
        sio = StringIO()
1653
 
        weavefile.write_weave_v5(weave.Weave(), sio)
1654
 
        empty_weave = sio.getvalue()
1655
 
 
1656
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1657
 
        dirs = ['revision-store', 'weaves']
1658
 
        files = [('inventory.weave', StringIO(empty_weave)), 
1659
 
                 ]
1660
 
        utf8_files = [('format', self.get_format_string())]
1661
 
 
1662
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1663
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1664
 
 
1665
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1666
 
        """See RepositoryFormat.open().
1667
 
        
1668
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1669
 
                                    repository at a slightly different url
1670
 
                                    than normal. I.e. during 'upgrade'.
1671
 
        """
1672
 
        if not _found:
1673
 
            format = RepositoryFormat.find_format(a_bzrdir)
1674
 
            assert format.__class__ ==  self.__class__
1675
 
        if _override_transport is not None:
1676
 
            repo_transport = _override_transport
1677
 
        else:
1678
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1679
 
        control_files = lockable_files.LockableFiles(repo_transport,
1680
 
                                'lock', lockdir.LockDir)
1681
 
        text_store = self._get_text_store(repo_transport, control_files)
1682
 
        control_store = self._get_control_store(repo_transport, control_files)
1683
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1684
 
        return WeaveMetaDirRepository(_format=self,
1685
 
            a_bzrdir=a_bzrdir,
1686
 
            control_files=control_files,
1687
 
            _revision_store=_revision_store,
1688
 
            control_store=control_store,
1689
 
            text_store=text_store)
1690
 
 
1691
 
 
1692
 
class RepositoryFormatKnit(MetaDirRepositoryFormat):
1693
 
    """Bzr repository knit format (generalized). 
1694
 
 
1695
 
    This repository format has:
1696
 
     - knits for file texts and inventory
1697
 
     - hash subdirectory based stores.
1698
 
     - knits for revisions and signatures
1699
 
     - TextStores for revisions and signatures.
1700
 
     - a format marker of its own
1701
 
     - an optional 'shared-storage' flag
1702
 
     - an optional 'no-working-trees' flag
1703
 
     - a LockDir lock
1704
 
    """
1705
 
 
1706
 
    def _get_control_store(self, repo_transport, control_files):
1707
 
        """Return the control store for this repository."""
1708
 
        return VersionedFileStore(
1709
 
            repo_transport,
1710
 
            prefixed=False,
1711
 
            file_mode=control_files._file_mode,
1712
 
            versionedfile_class=knit.KnitVersionedFile,
1713
 
            versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
1714
 
            )
1715
 
 
1716
 
    def _get_revision_store(self, repo_transport, control_files):
1717
 
        """See RepositoryFormat._get_revision_store()."""
1718
 
        from bzrlib.store.revision.knit import KnitRevisionStore
1719
 
        versioned_file_store = VersionedFileStore(
1720
 
            repo_transport,
1721
 
            file_mode=control_files._file_mode,
1722
 
            prefixed=False,
1723
 
            precious=True,
1724
 
            versionedfile_class=knit.KnitVersionedFile,
1725
 
            versionedfile_kwargs={'delta':False,
1726
 
                                  'factory':knit.KnitPlainFactory(),
1727
 
                                 },
1728
 
            escaped=True,
1729
 
            )
1730
 
        return KnitRevisionStore(versioned_file_store)
1731
 
 
1732
 
    def _get_text_store(self, transport, control_files):
1733
 
        """See RepositoryFormat._get_text_store()."""
1734
 
        return self._get_versioned_file_store('knits',
1735
 
                                  transport,
1736
 
                                  control_files,
1737
 
                                  versionedfile_class=knit.KnitVersionedFile,
1738
 
                                  versionedfile_kwargs={
1739
 
                                      'create_parent_dir':True,
1740
 
                                      'delay_create':True,
1741
 
                                      'dir_mode':control_files._dir_mode,
1742
 
                                  },
1743
 
                                  escaped=True)
1744
 
 
1745
 
    def initialize(self, a_bzrdir, shared=False):
1746
 
        """Create a knit format 1 repository.
1747
 
 
1748
 
        :param a_bzrdir: bzrdir to contain the new repository; must already
1749
 
            be initialized.
1750
 
        :param shared: If true the repository will be initialized as a shared
1751
 
                       repository.
1752
 
        """
1753
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1754
 
        dirs = ['revision-store', 'knits']
1755
 
        files = []
1756
 
        utf8_files = [('format', self.get_format_string())]
1757
 
        
1758
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1759
 
        repo_transport = a_bzrdir.get_repository_transport(None)
1760
 
        control_files = lockable_files.LockableFiles(repo_transport,
1761
 
                                'lock', lockdir.LockDir)
1762
 
        control_store = self._get_control_store(repo_transport, control_files)
1763
 
        transaction = transactions.WriteTransaction()
1764
 
        # trigger a write of the inventory store.
1765
 
        control_store.get_weave_or_empty('inventory', transaction)
1766
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1767
 
        # the revision id here is irrelevant: it will not be stored, and cannot
1768
 
        # already exist.
1769
 
        _revision_store.has_revision_id('A', transaction)
1770
 
        _revision_store.get_signature_file(transaction)
1771
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1772
 
 
1773
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1774
 
        """See RepositoryFormat.open().
1775
 
        
1776
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1777
 
                                    repository at a slightly different url
1778
 
                                    than normal. I.e. during 'upgrade'.
1779
 
        """
1780
 
        if not _found:
1781
 
            format = RepositoryFormat.find_format(a_bzrdir)
1782
 
            assert format.__class__ ==  self.__class__
1783
 
        if _override_transport is not None:
1784
 
            repo_transport = _override_transport
1785
 
        else:
1786
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1787
 
        control_files = lockable_files.LockableFiles(repo_transport,
1788
 
                                'lock', lockdir.LockDir)
1789
 
        text_store = self._get_text_store(repo_transport, control_files)
1790
 
        control_store = self._get_control_store(repo_transport, control_files)
1791
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1792
 
        return KnitRepository(_format=self,
1793
 
                              a_bzrdir=a_bzrdir,
1794
 
                              control_files=control_files,
1795
 
                              _revision_store=_revision_store,
1796
 
                              control_store=control_store,
1797
 
                              text_store=text_store)
1798
 
 
1799
 
 
1800
 
class RepositoryFormatKnit1(RepositoryFormatKnit):
1801
 
    """Bzr repository knit format 1.
1802
 
 
1803
 
    This repository format has:
1804
 
     - knits for file texts and inventory
1805
 
     - hash subdirectory based stores.
1806
 
     - knits for revisions and signatures
1807
 
     - TextStores for revisions and signatures.
1808
 
     - a format marker of its own
1809
 
     - an optional 'shared-storage' flag
1810
 
     - an optional 'no-working-trees' flag
1811
 
     - a LockDir lock
1812
 
 
1813
 
    This format was introduced in bzr 0.8.
1814
 
    """
1815
 
    def get_format_string(self):
1816
 
        """See RepositoryFormat.get_format_string()."""
1817
 
        return "Bazaar-NG Knit Repository Format 1"
1818
 
 
1819
 
    def get_format_description(self):
1820
 
        """See RepositoryFormat.get_format_description()."""
1821
 
        return "Knit repository format 1"
1822
 
 
1823
 
    def check_conversion_target(self, target_format):
1824
 
        pass
1825
 
 
1826
 
 
1827
 
class RepositoryFormatKnit2(RepositoryFormatKnit):
1828
 
    """Bzr repository knit format 2.
1829
 
 
1830
 
    THIS FORMAT IS EXPERIMENTAL
1831
 
    This repository format has:
1832
 
     - knits for file texts and inventory
1833
 
     - hash subdirectory based stores.
1834
 
     - knits for revisions and signatures
1835
 
     - TextStores for revisions and signatures.
1836
 
     - a format marker of its own
1837
 
     - an optional 'shared-storage' flag
1838
 
     - an optional 'no-working-trees' flag
1839
 
     - a LockDir lock
1840
 
     - Support for recording full info about the tree root
1841
 
 
1842
 
    """
1843
 
    
1844
 
    rich_root_data = True
1845
 
 
1846
 
    def get_format_string(self):
1847
 
        """See RepositoryFormat.get_format_string()."""
1848
 
        return "Bazaar Knit Repository Format 2\n"
1849
 
 
1850
 
    def get_format_description(self):
1851
 
        """See RepositoryFormat.get_format_description()."""
1852
 
        return "Knit repository format 2"
1853
 
 
1854
 
    def check_conversion_target(self, target_format):
1855
 
        if not target_format.rich_root_data:
1856
 
            raise errors.BadConversionTarget(
1857
 
                'Does not support rich root data.', target_format)
1858
 
 
1859
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1860
 
        """See RepositoryFormat.open().
1861
 
        
1862
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1863
 
                                    repository at a slightly different url
1864
 
                                    than normal. I.e. during 'upgrade'.
1865
 
        """
1866
 
        if not _found:
1867
 
            format = RepositoryFormat.find_format(a_bzrdir)
1868
 
            assert format.__class__ ==  self.__class__
1869
 
        if _override_transport is not None:
1870
 
            repo_transport = _override_transport
1871
 
        else:
1872
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1873
 
        control_files = lockable_files.LockableFiles(repo_transport, 'lock',
1874
 
                                                     lockdir.LockDir)
1875
 
        text_store = self._get_text_store(repo_transport, control_files)
1876
 
        control_store = self._get_control_store(repo_transport, control_files)
1877
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1878
 
        return KnitRepository2(_format=self,
1879
 
                               a_bzrdir=a_bzrdir,
1880
 
                               control_files=control_files,
1881
 
                               _revision_store=_revision_store,
1882
 
                               control_store=control_store,
1883
 
                               text_store=text_store)
1884
 
 
1885
 
 
1886
 
 
1887
1262
# formats which have no format string are not discoverable
1888
 
# and not independently creatable, so are not registered.
1889
 
RepositoryFormat.register_format(RepositoryFormat7())
1890
 
# KEEP in sync with bzrdir.format_registry default
1891
 
RepositoryFormat.register_format(RepositoryFormatKnit1())
1892
 
RepositoryFormat.register_format(RepositoryFormatKnit2())
1893
 
_legacy_formats = [RepositoryFormat4(),
1894
 
                   RepositoryFormat5(),
1895
 
                   RepositoryFormat6()]
 
1263
# and not independently creatable, so are not registered.  They're 
 
1264
# all in bzrlib.repofmt.weaverepo now.  When an instance of one of these is
 
1265
# needed, it's constructed directly by the BzrDir.  Non-native formats where
 
1266
# the repository is not separately opened are similar.
 
1267
 
 
1268
format_registry.register_lazy(
 
1269
    'Bazaar-NG Repository format 7',
 
1270
    'bzrlib.repofmt.weaverepo',
 
1271
    'RepositoryFormat7'
 
1272
    )
 
1273
# KEEP in sync with bzrdir.format_registry default, which controls the overall
 
1274
# default control directory format
 
1275
 
 
1276
format_registry.register_lazy(
 
1277
    'Bazaar-NG Knit Repository Format 1',
 
1278
    'bzrlib.repofmt.knitrepo',
 
1279
    'RepositoryFormatKnit1',
 
1280
    )
 
1281
format_registry.default_key = 'Bazaar-NG Knit Repository Format 1'
 
1282
 
 
1283
format_registry.register_lazy(
 
1284
    'Bazaar Knit Repository Format 2\n',
 
1285
    'bzrlib.repofmt.knitrepo',
 
1286
    'RepositoryFormatKnit2',
 
1287
    )
1896
1288
 
1897
1289
 
1898
1290
class InterRepository(InterObject):
1940
1332
        # generic, possibly worst case, slow code path.
1941
1333
        target_ids = set(self.target.all_revision_ids())
1942
1334
        if revision_id is not None:
 
1335
            # TODO: jam 20070210 InterRepository is internal enough that it
 
1336
            #       should assume revision_ids are already utf-8
 
1337
            revision_id = osutils.safe_revision_id(revision_id)
1943
1338
            source_ids = self.source.get_ancestry(revision_id)
1944
1339
            assert source_ids[0] is None
1945
1340
            source_ids.pop(0)
1958
1353
    Data format and model must match for this to work.
1959
1354
    """
1960
1355
 
1961
 
    _matching_repo_format = RepositoryFormat4()
1962
 
    """Repository format for testing with."""
 
1356
    @classmethod
 
1357
    def _get_repo_format_to_test(self):
 
1358
        """Repository format for testing with."""
 
1359
        return RepositoryFormat.get_default_format()
1963
1360
 
1964
1361
    @staticmethod
1965
1362
    def is_compatible(source, target):
1983
1380
            self.target.set_make_working_trees(self.source.make_working_trees())
1984
1381
        except NotImplementedError:
1985
1382
            pass
 
1383
        # TODO: jam 20070210 This is fairly internal, so we should probably
 
1384
        #       just assert that revision_id is not unicode.
 
1385
        revision_id = osutils.safe_revision_id(revision_id)
1986
1386
        # grab the basis available data
1987
1387
        if basis is not None:
1988
1388
            self.target.fetch(basis, revision_id=revision_id)
1999
1399
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2000
1400
               self.source, self.source._format, self.target, 
2001
1401
               self.target._format)
 
1402
        # TODO: jam 20070210 This should be an assert, not a translate
 
1403
        revision_id = osutils.safe_revision_id(revision_id)
2002
1404
        f = GenericRepoFetcher(to_repository=self.target,
2003
1405
                               from_repository=self.source,
2004
1406
                               last_revision=revision_id,
2009
1411
class InterWeaveRepo(InterSameDataRepository):
2010
1412
    """Optimised code paths between Weave based repositories."""
2011
1413
 
2012
 
    _matching_repo_format = RepositoryFormat7()
2013
 
    """Repository format for testing with."""
 
1414
    @classmethod
 
1415
    def _get_repo_format_to_test(self):
 
1416
        from bzrlib.repofmt import weaverepo
 
1417
        return weaverepo.RepositoryFormat7()
2014
1418
 
2015
1419
    @staticmethod
2016
1420
    def is_compatible(source, target):
2020
1424
        could lead to confusing results, and there is no need to be 
2021
1425
        overly general.
2022
1426
        """
 
1427
        from bzrlib.repofmt.weaverepo import (
 
1428
                RepositoryFormat5,
 
1429
                RepositoryFormat6,
 
1430
                RepositoryFormat7,
 
1431
                )
2023
1432
        try:
2024
1433
            return (isinstance(source._format, (RepositoryFormat5,
2025
1434
                                                RepositoryFormat6,
2034
1443
    def copy_content(self, revision_id=None, basis=None):
2035
1444
        """See InterRepository.copy_content()."""
2036
1445
        # weave specific optimised path:
 
1446
        # TODO: jam 20070210 Internal, should be an assert, not translate
 
1447
        revision_id = osutils.safe_revision_id(revision_id)
2037
1448
        if basis is not None:
2038
1449
            # copy the basis in, then fetch remaining data.
2039
1450
            basis.copy_content_into(self.target, revision_id)
2076
1487
        from bzrlib.fetch import GenericRepoFetcher
2077
1488
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2078
1489
               self.source, self.source._format, self.target, self.target._format)
 
1490
        # TODO: jam 20070210 This should be an assert, not a translate
 
1491
        revision_id = osutils.safe_revision_id(revision_id)
2079
1492
        f = GenericRepoFetcher(to_repository=self.target,
2080
1493
                               from_repository=self.source,
2081
1494
                               last_revision=revision_id,
2127
1540
class InterKnitRepo(InterSameDataRepository):
2128
1541
    """Optimised code paths between Knit based repositories."""
2129
1542
 
2130
 
    _matching_repo_format = RepositoryFormatKnit1()
2131
 
    """Repository format for testing with."""
 
1543
    @classmethod
 
1544
    def _get_repo_format_to_test(self):
 
1545
        from bzrlib.repofmt import knitrepo
 
1546
        return knitrepo.RepositoryFormatKnit1()
2132
1547
 
2133
1548
    @staticmethod
2134
1549
    def is_compatible(source, target):
2138
1553
        could lead to confusing results, and there is no need to be 
2139
1554
        overly general.
2140
1555
        """
 
1556
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
2141
1557
        try:
2142
1558
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
2143
1559
                    isinstance(target._format, (RepositoryFormatKnit1)))
2150
1566
        from bzrlib.fetch import KnitRepoFetcher
2151
1567
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2152
1568
               self.source, self.source._format, self.target, self.target._format)
 
1569
        # TODO: jam 20070210 This should be an assert, not a translate
 
1570
        revision_id = osutils.safe_revision_id(revision_id)
2153
1571
        f = KnitRepoFetcher(to_repository=self.target,
2154
1572
                            from_repository=self.source,
2155
1573
                            last_revision=revision_id,
2189
1607
 
2190
1608
class InterModel1and2(InterRepository):
2191
1609
 
2192
 
    _matching_repo_format = None
 
1610
    @classmethod
 
1611
    def _get_repo_format_to_test(self):
 
1612
        return None
2193
1613
 
2194
1614
    @staticmethod
2195
1615
    def is_compatible(source, target):
2206
1626
    def fetch(self, revision_id=None, pb=None):
2207
1627
        """See InterRepository.fetch()."""
2208
1628
        from bzrlib.fetch import Model1toKnit2Fetcher
 
1629
        # TODO: jam 20070210 This should be an assert, not a translate
 
1630
        revision_id = osutils.safe_revision_id(revision_id)
2209
1631
        f = Model1toKnit2Fetcher(to_repository=self.target,
2210
1632
                                 from_repository=self.source,
2211
1633
                                 last_revision=revision_id,
2227
1649
            self.target.set_make_working_trees(self.source.make_working_trees())
2228
1650
        except NotImplementedError:
2229
1651
            pass
 
1652
        # TODO: jam 20070210 Internal, assert, don't translate
 
1653
        revision_id = osutils.safe_revision_id(revision_id)
2230
1654
        # grab the basis available data
2231
1655
        if basis is not None:
2232
1656
            self.target.fetch(basis, revision_id=revision_id)
2239
1663
 
2240
1664
class InterKnit1and2(InterKnitRepo):
2241
1665
 
2242
 
    _matching_repo_format = None
 
1666
    @classmethod
 
1667
    def _get_repo_format_to_test(self):
 
1668
        return None
2243
1669
 
2244
1670
    @staticmethod
2245
1671
    def is_compatible(source, target):
2246
1672
        """Be compatible with Knit1 source and Knit2 target"""
 
1673
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit2
2247
1674
        try:
 
1675
            from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1, \
 
1676
                    RepositoryFormatKnit2
2248
1677
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
2249
1678
                    isinstance(target._format, (RepositoryFormatKnit2)))
2250
1679
        except AttributeError:
2257
1686
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2258
1687
               self.source, self.source._format, self.target, 
2259
1688
               self.target._format)
 
1689
        # TODO: jam 20070210 This should be an assert, not a translate
 
1690
        revision_id = osutils.safe_revision_id(revision_id)
2260
1691
        f = Knit1to2Fetcher(to_repository=self.target,
2261
1692
                            from_repository=self.source,
2262
1693
                            last_revision=revision_id,
2290
1721
    def adapt(self, test):
2291
1722
        result = unittest.TestSuite()
2292
1723
        for repository_format, bzrdir_format in self._formats:
 
1724
            from copy import deepcopy
2293
1725
            new_test = deepcopy(test)
2294
1726
            new_test.transport_server = self._transport_server
2295
1727
            new_test.transport_readonly_server = self._transport_readonly_server
2322
1754
    def adapt(self, test):
2323
1755
        result = unittest.TestSuite()
2324
1756
        for interrepo_class, repository_format, repository_format_to in self._formats:
 
1757
            from copy import deepcopy
2325
1758
            new_test = deepcopy(test)
2326
1759
            new_test.transport_server = self._transport_server
2327
1760
            new_test.transport_readonly_server = self._transport_readonly_server
2338
1771
    @staticmethod
2339
1772
    def default_test_list():
2340
1773
        """Generate the default list of interrepo permutations to test."""
 
1774
        from bzrlib.repofmt import knitrepo, weaverepo
2341
1775
        result = []
2342
1776
        # test the default InterRepository between format 6 and the current 
2343
1777
        # default format.
2346
1780
        #result.append((InterRepository,
2347
1781
        #               RepositoryFormat6(),
2348
1782
        #               RepositoryFormatKnit1()))
2349
 
        for optimiser in InterRepository._optimisers:
2350
 
            if optimiser._matching_repo_format is not None:
2351
 
                result.append((optimiser,
2352
 
                               optimiser._matching_repo_format,
2353
 
                               optimiser._matching_repo_format
2354
 
                               ))
 
1783
        for optimiser_class in InterRepository._optimisers:
 
1784
            format_to_test = optimiser_class._get_repo_format_to_test()
 
1785
            if format_to_test is not None:
 
1786
                result.append((optimiser_class,
 
1787
                               format_to_test, format_to_test))
2355
1788
        # if there are specific combinations we want to use, we can add them 
2356
1789
        # here.
2357
 
        result.append((InterModel1and2, RepositoryFormat5(),
2358
 
                       RepositoryFormatKnit2()))
2359
 
        result.append((InterKnit1and2, RepositoryFormatKnit1(),
2360
 
                       RepositoryFormatKnit2()))
 
1790
        result.append((InterModel1and2,
 
1791
                       weaverepo.RepositoryFormat5(),
 
1792
                       knitrepo.RepositoryFormatKnit2()))
 
1793
        result.append((InterKnit1and2,
 
1794
                       knitrepo.RepositoryFormatKnit1(),
 
1795
                       knitrepo.RepositoryFormatKnit2()))
2361
1796
        return result
2362
1797
 
2363
1798
 
2444
1879
            self._committer = committer
2445
1880
 
2446
1881
        self.new_inventory = Inventory(None)
2447
 
        self._new_revision_id = revision_id
 
1882
        self._new_revision_id = osutils.safe_revision_id(revision_id)
2448
1883
        self.parents = parents
2449
1884
        self.repository = repository
2450
1885
 
2458
1893
        self._timestamp = round(timestamp, 3)
2459
1894
 
2460
1895
        if timezone is None:
2461
 
            self._timezone = local_time_offset()
 
1896
            self._timezone = osutils.local_time_offset()
2462
1897
        else:
2463
1898
            self._timezone = int(timezone)
2464
1899
 
2676
2111
 
2677
2112
 
2678
2113
def _unescaper(match, _map=_unescape_map):
2679
 
    return _map[match.group(1)]
 
2114
    code = match.group(1)
 
2115
    try:
 
2116
        return _map[code]
 
2117
    except KeyError:
 
2118
        if not code.startswith('#'):
 
2119
            raise
 
2120
        return unichr(int(code[1:])).encode('utf8')
2680
2121
 
2681
2122
 
2682
2123
_unescape_re = None