~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Andrew Bennetts
  • Date: 2007-03-28 07:08:42 UTC
  • mfrom: (2380 +trunk)
  • mto: (2018.5.146 hpss)
  • mto: This revision was merged to the branch mainline in revision 2414.
  • Revision ID: andrew.bennetts@canonical.com-20070328070842-r843houy668oxb9o
Merge from bzr.dev.

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
72
60
_deprecation_warning_done = False
73
61
 
74
62
 
 
63
######################################################################
 
64
# Repositories
 
65
 
75
66
class Repository(object):
76
67
    """Repository holding history for one or more branches.
77
68
 
90
81
        )
91
82
 
92
83
    @needs_write_lock
93
 
    def add_inventory(self, revid, inv, parents):
94
 
        """Add the inventory inv to the repository as revid.
 
84
    def add_inventory(self, revision_id, inv, parents):
 
85
        """Add the inventory inv to the repository as revision_id.
95
86
        
96
 
        :param parents: The revision ids of the parents that revid
 
87
        :param parents: The revision ids of the parents that revision_id
97
88
                        is known to have and are in the repository already.
98
89
 
99
90
        returns the sha1 of the serialized inventory.
100
91
        """
101
 
        _mod_revision.check_not_reserved_id(revid)
102
 
        assert inv.revision_id is None or inv.revision_id == revid, \
 
92
        revision_id = osutils.safe_revision_id(revision_id)
 
93
        _mod_revision.check_not_reserved_id(revision_id)
 
94
        assert inv.revision_id is None or inv.revision_id == revision_id, \
103
95
            "Mismatch between inventory revision" \
104
 
            " id and insertion revid (%r, %r)" % (inv.revision_id, revid)
 
96
            " id and insertion revid (%r, %r)" % (inv.revision_id, revision_id)
105
97
        assert inv.root is not None
106
98
        inv_text = self.serialise_inventory(inv)
107
99
        inv_sha1 = osutils.sha_string(inv_text)
108
100
        inv_vf = self.control_weaves.get_weave('inventory',
109
101
                                               self.get_transaction())
110
 
        self._inventory_add_lines(inv_vf, revid, parents, osutils.split_lines(inv_text))
 
102
        self._inventory_add_lines(inv_vf, revision_id, parents,
 
103
                                  osutils.split_lines(inv_text))
111
104
        return inv_sha1
112
105
 
113
 
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
 
106
    def _inventory_add_lines(self, inv_vf, revision_id, parents, lines):
114
107
        final_parents = []
115
108
        for parent in parents:
116
109
            if parent in inv_vf:
117
110
                final_parents.append(parent)
118
111
 
119
 
        inv_vf.add_lines(revid, final_parents, lines)
 
112
        inv_vf.add_lines(revision_id, final_parents, lines)
120
113
 
121
114
    @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.
 
115
    def add_revision(self, revision_id, rev, inv=None, config=None):
 
116
        """Add rev to the revision store as revision_id.
124
117
 
125
 
        :param rev_id: the revision id to use.
 
118
        :param revision_id: the revision id to use.
126
119
        :param rev: The revision object.
127
120
        :param inv: The inventory for the revision. if None, it will be looked
128
121
                    up in the inventory storer
130
123
                       If supplied its signature_needed method will be used
131
124
                       to determine if a signature should be made.
132
125
        """
133
 
        _mod_revision.check_not_reserved_id(rev_id)
 
126
        revision_id = osutils.safe_revision_id(revision_id)
 
127
        # TODO: jam 20070210 Shouldn't we check rev.revision_id and
 
128
        #       rev.parent_ids?
 
129
        _mod_revision.check_not_reserved_id(revision_id)
134
130
        if config is not None and config.signature_needed():
135
131
            if inv is None:
136
 
                inv = self.get_inventory(rev_id)
 
132
                inv = self.get_inventory(revision_id)
137
133
            plaintext = Testament(rev, inv).as_short_text()
138
134
            self.store_revision_signature(
139
 
                gpg.GPGStrategy(config), plaintext, rev_id)
140
 
        if not rev_id in self.get_inventory_weave():
 
135
                gpg.GPGStrategy(config), plaintext, revision_id)
 
136
        if not revision_id in self.get_inventory_weave():
141
137
            if inv is None:
142
 
                raise errors.WeaveRevisionNotPresent(rev_id,
 
138
                raise errors.WeaveRevisionNotPresent(revision_id,
143
139
                                                     self.get_inventory_weave())
144
140
            else:
145
141
                # yes, this is not suitable for adding with ghosts.
146
 
                self.add_inventory(rev_id, inv, rev.parent_ids)
 
142
                self.add_inventory(revision_id, inv, rev.parent_ids)
147
143
        self._revision_store.add_revision(rev, self.get_transaction())
148
144
 
149
145
    @needs_read_lock
171
167
        if self._revision_store.text_store.listable():
172
168
            return self._revision_store.all_revision_ids(self.get_transaction())
173
169
        result = self._all_possible_ids()
 
170
        # TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
 
171
        #       ids. (It should, since _revision_store's API should change to
 
172
        #       return utf8 revision_ids)
174
173
        return self._eliminate_revisions_not_present(result)
175
174
 
176
175
    def break_lock(self):
224
223
        # TODO: make sure to construct the right store classes, etc, depending
225
224
        # on whether escaping is required.
226
225
        self._warn_if_deprecated()
227
 
        self._serializer = xml5.serializer_v5
228
226
 
229
227
    def __repr__(self):
230
228
        return '%s(%r)' % (self.__class__.__name__, 
300
298
 
301
299
        revision_id: only return revision ids included by revision_id.
302
300
        """
 
301
        revision_id = osutils.safe_revision_id(revision_id)
303
302
        return InterRepository.get(other, self).missing_revision_ids(revision_id)
304
303
 
305
304
    @staticmethod
318
317
        This is a destructive operation! Do not use it on existing 
319
318
        repositories.
320
319
        """
 
320
        revision_id = osutils.safe_revision_id(revision_id)
321
321
        return InterRepository.get(self, destination).copy_content(revision_id, basis)
322
322
 
323
323
    def fetch(self, source, revision_id=None, pb=None):
325
325
 
326
326
        If revision_id is None all content is copied.
327
327
        """
 
328
        revision_id = osutils.safe_revision_id(revision_id)
328
329
        return InterRepository.get(source, self).fetch(revision_id=revision_id,
329
330
                                                       pb=pb)
330
331
 
342
343
        :param revprops: Optional dictionary of revision properties.
343
344
        :param revision_id: Optional revision id.
344
345
        """
 
346
        revision_id = osutils.safe_revision_id(revision_id)
345
347
        return _CommitBuilder(self, parents, config, timestamp, timezone,
346
348
                              committer, revprops, revision_id)
347
349
 
354
356
 
355
357
        Currently no check is made that the format of this repository and
356
358
        the bzrdir format are compatible. FIXME RBC 20060201.
 
359
 
 
360
        :return: The newly created destination repository.
357
361
        """
358
362
        if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
359
363
            # use target default format.
360
 
            result = a_bzrdir.create_repository()
361
 
        # FIXME RBC 20060209 split out the repository type to avoid this check ?
362
 
        elif isinstance(a_bzrdir._format,
363
 
                      (bzrdir.BzrDirFormat4,
364
 
                       bzrdir.BzrDirFormat5,
365
 
                       bzrdir.BzrDirFormat6)):
366
 
            result = a_bzrdir.open_repository()
 
364
            dest_repo = a_bzrdir.create_repository()
367
365
        else:
368
 
            result = self._format.initialize(a_bzrdir, shared=self.is_shared())
369
 
        self.copy_content_into(result, revision_id, basis)
370
 
        return result
 
366
            # Most control formats need the repository to be specifically
 
367
            # created, but on some old all-in-one formats it's not needed
 
368
            try:
 
369
                dest_repo = self._format.initialize(a_bzrdir, shared=self.is_shared())
 
370
            except errors.UninitializableFormat:
 
371
                dest_repo = a_bzrdir.open_repository()
 
372
        self.copy_content_into(dest_repo, revision_id, basis)
 
373
        return dest_repo
371
374
 
372
375
    @needs_read_lock
373
376
    def has_revision(self, revision_id):
374
377
        """True if this repository has a copy of the revision."""
 
378
        revision_id = osutils.safe_revision_id(revision_id)
375
379
        return self._revision_store.has_revision_id(revision_id,
376
380
                                                    self.get_transaction())
377
381
 
387
391
        if not revision_id or not isinstance(revision_id, basestring):
388
392
            raise errors.InvalidRevisionId(revision_id=revision_id,
389
393
                                           branch=self)
390
 
        return self._revision_store.get_revisions([revision_id],
391
 
                                                  self.get_transaction())[0]
 
394
        return self.get_revisions([revision_id])[0]
 
395
 
392
396
    @needs_read_lock
393
397
    def get_revisions(self, revision_ids):
394
 
        return self._revision_store.get_revisions(revision_ids,
 
398
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
 
399
        revs = self._revision_store.get_revisions(revision_ids,
395
400
                                                  self.get_transaction())
 
401
        for rev in revs:
 
402
            assert not isinstance(rev.revision_id, unicode)
 
403
            for parent_id in rev.parent_ids:
 
404
                assert not isinstance(parent_id, unicode)
 
405
        return revs
396
406
 
397
407
    @needs_read_lock
398
408
    def get_revision_xml(self, revision_id):
399
 
        rev = self.get_revision(revision_id) 
 
409
        # TODO: jam 20070210 This shouldn't be necessary since get_revision
 
410
        #       would have already do it.
 
411
        # TODO: jam 20070210 Just use _serializer.write_revision_to_string()
 
412
        revision_id = osutils.safe_revision_id(revision_id)
 
413
        rev = self.get_revision(revision_id)
400
414
        rev_tmp = StringIO()
401
415
        # the current serializer..
402
416
        self._revision_store._serializer.write_revision(rev, rev_tmp)
406
420
    @needs_read_lock
407
421
    def get_revision(self, revision_id):
408
422
        """Return the Revision object for a named revision"""
 
423
        # TODO: jam 20070210 get_revision_reconcile should do this for us
 
424
        revision_id = osutils.safe_revision_id(revision_id)
409
425
        r = self.get_revision_reconcile(revision_id)
410
426
        # weave corruption can lead to absent revision markers that should be
411
427
        # present.
467
483
 
468
484
    @needs_write_lock
469
485
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
 
486
        revision_id = osutils.safe_revision_id(revision_id)
470
487
        signature = gpg_strategy.sign(plaintext)
471
488
        self._revision_store.add_revision_signature_text(revision_id,
472
489
                                                         signature,
483
500
        assert self._serializer.support_altered_by_hack, \
484
501
            ("fileids_altered_by_revision_ids only supported for branches " 
485
502
             "which store inventory as unnested xml, not on %r" % self)
486
 
        selected_revision_ids = set(revision_ids)
 
503
        selected_revision_ids = set(osutils.safe_revision_id(r)
 
504
                                    for r in revision_ids)
487
505
        w = self.get_inventory_weave()
488
506
        result = {}
489
507
 
553
571
    @needs_read_lock
554
572
    def get_inventory(self, revision_id):
555
573
        """Get Inventory object by hash."""
 
574
        # TODO: jam 20070210 Technically we don't need to sanitize, since all
 
575
        #       called functions must sanitize.
 
576
        revision_id = osutils.safe_revision_id(revision_id)
556
577
        return self.deserialise_inventory(
557
578
            revision_id, self.get_inventory_xml(revision_id))
558
579
 
562
583
        :param revision_id: The expected revision id of the inventory.
563
584
        :param xml: A serialised inventory.
564
585
        """
 
586
        revision_id = osutils.safe_revision_id(revision_id)
565
587
        result = self._serializer.read_inventory_from_string(xml)
566
588
        result.root.revision = revision_id
567
589
        return result
572
594
    @needs_read_lock
573
595
    def get_inventory_xml(self, revision_id):
574
596
        """Get inventory XML as a file object."""
 
597
        revision_id = osutils.safe_revision_id(revision_id)
575
598
        try:
576
 
            assert isinstance(revision_id, basestring), type(revision_id)
 
599
            assert isinstance(revision_id, str), type(revision_id)
577
600
            iw = self.get_inventory_weave()
578
601
            return iw.get_text(revision_id)
579
602
        except IndexError:
583
606
    def get_inventory_sha1(self, revision_id):
584
607
        """Return the sha1 hash of the inventory entry
585
608
        """
 
609
        # TODO: jam 20070210 Shouldn't this be deprecated / removed?
 
610
        revision_id = osutils.safe_revision_id(revision_id)
586
611
        return self.get_revision(revision_id).inventory_sha1
587
612
 
588
613
    @needs_read_lock
597
622
        # special case NULL_REVISION
598
623
        if revision_id == _mod_revision.NULL_REVISION:
599
624
            return {}
 
625
        revision_id = osutils.safe_revision_id(revision_id)
600
626
        a_weave = self.get_inventory_weave()
601
627
        all_revisions = self._eliminate_revisions_not_present(
602
628
                                a_weave.versions())
630
656
            pending = set(self.all_revision_ids())
631
657
            required = set([])
632
658
        else:
633
 
            pending = set(revision_ids)
 
659
            pending = set(osutils.safe_revision_id(r) for r in revision_ids)
634
660
            # special case NULL_REVISION
635
661
            if _mod_revision.NULL_REVISION in pending:
636
662
                pending.remove(_mod_revision.NULL_REVISION)
656
682
            done.add(revision_id)
657
683
        return result
658
684
 
 
685
    def _get_history_vf(self):
 
686
        """Get a versionedfile whose history graph reflects all revisions.
 
687
 
 
688
        For weave repositories, this is the inventory weave.
 
689
        """
 
690
        return self.get_inventory_weave()
 
691
 
 
692
    def iter_reverse_revision_history(self, revision_id):
 
693
        """Iterate backwards through revision ids in the lefthand history
 
694
 
 
695
        :param revision_id: The revision id to start with.  All its lefthand
 
696
            ancestors will be traversed.
 
697
        """
 
698
        revision_id = osutils.safe_revision_id(revision_id)
 
699
        if revision_id in (None, _mod_revision.NULL_REVISION):
 
700
            return
 
701
        next_id = revision_id
 
702
        versionedfile = self._get_history_vf()
 
703
        while True:
 
704
            yield next_id
 
705
            parents = versionedfile.get_parents(next_id)
 
706
            if len(parents) == 0:
 
707
                return
 
708
            else:
 
709
                next_id = parents[0]
 
710
 
659
711
    @needs_read_lock
660
712
    def get_revision_inventory(self, revision_id):
661
713
        """Return inventory of a past revision."""
697
749
            return RevisionTree(self, Inventory(root_id=None), 
698
750
                                _mod_revision.NULL_REVISION)
699
751
        else:
 
752
            revision_id = osutils.safe_revision_id(revision_id)
700
753
            inv = self.get_revision_inventory(revision_id)
701
754
            return RevisionTree(self, inv, revision_id)
702
755
 
724
777
        """
725
778
        if revision_id is None:
726
779
            return [None]
 
780
        revision_id = osutils.safe_revision_id(revision_id)
727
781
        if not self.has_revision(revision_id):
728
782
            raise errors.NoSuchRevision(self, revision_id)
729
783
        w = self.get_inventory_weave()
738
792
        - it writes to stdout, it assumes that that is valid etc. Fix
739
793
        by creating a new more flexible convenience function.
740
794
        """
 
795
        revision_id = osutils.safe_revision_id(revision_id)
741
796
        tree = self.revision_tree(revision_id)
742
797
        # use inventory as it was in that revision
743
798
        file_id = tree.inventory.path2id(file)
751
806
    def get_transaction(self):
752
807
        return self.control_files.get_transaction()
753
808
 
754
 
    def revision_parents(self, revid):
755
 
        return self.get_inventory_weave().parent_names(revid)
 
809
    def revision_parents(self, revision_id):
 
810
        revision_id = osutils.safe_revision_id(revision_id)
 
811
        return self.get_inventory_weave().parent_names(revision_id)
756
812
 
757
813
    @needs_write_lock
758
814
    def set_make_working_trees(self, new_value):
772
828
 
773
829
    @needs_write_lock
774
830
    def sign_revision(self, revision_id, gpg_strategy):
 
831
        revision_id = osutils.safe_revision_id(revision_id)
775
832
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
776
833
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
777
834
 
778
835
    @needs_read_lock
779
836
    def has_signature_for_revision_id(self, revision_id):
780
837
        """Query for a revision signature for revision_id in the repository."""
 
838
        revision_id = osutils.safe_revision_id(revision_id)
781
839
        return self._revision_store.has_signature(revision_id,
782
840
                                                  self.get_transaction())
783
841
 
784
842
    @needs_read_lock
785
843
    def get_signature_text(self, revision_id):
786
844
        """Return the text for a signature."""
 
845
        revision_id = osutils.safe_revision_id(revision_id)
787
846
        return self._revision_store.get_signature_text(revision_id,
788
847
                                                       self.get_transaction())
789
848
 
799
858
        if not revision_ids:
800
859
            raise ValueError("revision_ids must be non-empty in %s.check" 
801
860
                    % (self,))
 
861
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
802
862
        return self._check(revision_ids)
803
863
 
804
864
    def _check(self, revision_ids):
827
887
                    revision_id.encode('ascii')
828
888
                except UnicodeEncodeError:
829
889
                    raise errors.NonAsciiRevisionId(method, self)
830
 
 
831
 
 
832
 
class AllInOneRepository(Repository):
833
 
    """Legacy support - the repository behaviour for all-in-one branches."""
834
 
 
835
 
    def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
836
 
        # we reuse one control files instance.
837
 
        dir_mode = a_bzrdir._control_files._dir_mode
838
 
        file_mode = a_bzrdir._control_files._file_mode
839
 
 
840
 
        def get_store(name, compressed=True, prefixed=False):
841
 
            # FIXME: This approach of assuming stores are all entirely compressed
842
 
            # or entirely uncompressed is tidy, but breaks upgrade from 
843
 
            # some existing branches where there's a mixture; we probably 
844
 
            # still want the option to look for both.
845
 
            relpath = a_bzrdir._control_files._escape(name)
846
 
            store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
847
 
                              prefixed=prefixed, compressed=compressed,
848
 
                              dir_mode=dir_mode,
849
 
                              file_mode=file_mode)
850
 
            #if self._transport.should_cache():
851
 
            #    cache_path = os.path.join(self.cache_root, name)
852
 
            #    os.mkdir(cache_path)
853
 
            #    store = bzrlib.store.CachedStore(store, cache_path)
854
 
            return store
855
 
 
856
 
        # not broken out yet because the controlweaves|inventory_store
857
 
        # and text_store | weave_store bits are still different.
858
 
        if isinstance(_format, RepositoryFormat4):
859
 
            # cannot remove these - there is still no consistent api 
860
 
            # which allows access to this old info.
861
 
            self.inventory_store = get_store('inventory-store')
862
 
            text_store = get_store('text-store')
863
 
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
864
 
 
865
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
866
 
                           timezone=None, committer=None, revprops=None,
867
 
                           revision_id=None):
868
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
869
 
        return Repository.get_commit_builder(self, branch, parents, config,
870
 
            timestamp, timezone, committer, revprops, revision_id)
871
 
 
872
 
    @needs_read_lock
873
 
    def is_shared(self):
874
 
        """AllInOne repositories cannot be shared."""
875
 
        return False
876
 
 
877
 
    @needs_write_lock
878
 
    def set_make_working_trees(self, new_value):
879
 
        """Set the policy flag for making working trees when creating branches.
880
 
 
881
 
        This only applies to branches that use this repository.
882
 
 
883
 
        The default is 'True'.
884
 
        :param new_value: True to restore the default, False to disable making
885
 
                          working trees.
886
 
        """
887
 
        raise NotImplementedError(self.set_make_working_trees)
888
 
    
889
 
    def make_working_trees(self):
890
 
        """Returns the policy for making working trees on new branches."""
891
 
        return True
 
890
            else:
 
891
                try:
 
892
                    revision_id.decode('ascii')
 
893
                except UnicodeDecodeError:
 
894
                    raise errors.NonAsciiRevisionId(method, self)
 
895
 
 
896
 
 
897
 
 
898
# remove these delegates a while after bzr 0.15
 
899
def __make_delegated(name, from_module):
 
900
    def _deprecated_repository_forwarder():
 
901
        symbol_versioning.warn('%s moved to %s in bzr 0.15'
 
902
            % (name, from_module),
 
903
            DeprecationWarning,
 
904
            stacklevel=2)
 
905
        m = __import__(from_module, globals(), locals(), [name])
 
906
        try:
 
907
            return getattr(m, name)
 
908
        except AttributeError:
 
909
            raise AttributeError('module %s has no name %s'
 
910
                    % (m, name))
 
911
    globals()[name] = _deprecated_repository_forwarder
 
912
 
 
913
for _name in [
 
914
        'AllInOneRepository',
 
915
        'WeaveMetaDirRepository',
 
916
        'PreSplitOutRepositoryFormat',
 
917
        'RepositoryFormat4',
 
918
        'RepositoryFormat5',
 
919
        'RepositoryFormat6',
 
920
        'RepositoryFormat7',
 
921
        ]:
 
922
    __make_delegated(_name, 'bzrlib.repofmt.weaverepo')
 
923
 
 
924
for _name in [
 
925
        'KnitRepository',
 
926
        'RepositoryFormatKnit',
 
927
        'RepositoryFormatKnit1',
 
928
        ]:
 
929
    __make_delegated(_name, 'bzrlib.repofmt.knitrepo')
892
930
 
893
931
 
894
932
def install_revision(repository, rev, revision_tree):
980
1018
        return not self.control_files._transport.has('no-working-trees')
981
1019
 
982
1020
 
983
 
class WeaveMetaDirRepository(MetaDirRepository):
984
 
    """A subclass of MetaDirRepository to set weave specific policy."""
985
 
 
986
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
987
 
                           timezone=None, committer=None, revprops=None,
988
 
                           revision_id=None):
989
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
990
 
        return MetaDirRepository.get_commit_builder(self, branch, parents,
991
 
            config, timestamp, timezone, committer, revprops, revision_id)
992
 
 
993
 
 
994
 
class KnitRepository(MetaDirRepository):
995
 
    """Knit format repository."""
996
 
 
997
 
    def _warn_if_deprecated(self):
998
 
        # This class isn't deprecated
999
 
        pass
1000
 
 
1001
 
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
1002
 
        inv_vf.add_lines_with_ghosts(revid, parents, lines)
1003
 
 
1004
 
    @needs_read_lock
1005
 
    def _all_revision_ids(self):
1006
 
        """See Repository.all_revision_ids()."""
1007
 
        # Knits get the revision graph from the index of the revision knit, so
1008
 
        # it's always possible even if they're on an unlistable transport.
1009
 
        return self._revision_store.all_revision_ids(self.get_transaction())
1010
 
 
1011
 
    def fileid_involved_between_revs(self, from_revid, to_revid):
1012
 
        """Find file_id(s) which are involved in the changes between revisions.
1013
 
 
1014
 
        This determines the set of revisions which are involved, and then
1015
 
        finds all file ids affected by those revisions.
1016
 
        """
1017
 
        vf = self._get_revision_vf()
1018
 
        from_set = set(vf.get_ancestry(from_revid))
1019
 
        to_set = set(vf.get_ancestry(to_revid))
1020
 
        changed = to_set.difference(from_set)
1021
 
        return self._fileid_involved_by_set(changed)
1022
 
 
1023
 
    def fileid_involved(self, last_revid=None):
1024
 
        """Find all file_ids modified in the ancestry of last_revid.
1025
 
 
1026
 
        :param last_revid: If None, last_revision() will be used.
1027
 
        """
1028
 
        if not last_revid:
1029
 
            changed = set(self.all_revision_ids())
1030
 
        else:
1031
 
            changed = set(self.get_ancestry(last_revid))
1032
 
        if None in changed:
1033
 
            changed.remove(None)
1034
 
        return self._fileid_involved_by_set(changed)
1035
 
 
1036
 
    @needs_read_lock
1037
 
    def get_ancestry(self, revision_id):
1038
 
        """Return a list of revision-ids integrated by a revision.
1039
 
        
1040
 
        This is topologically sorted.
1041
 
        """
1042
 
        if revision_id is None:
1043
 
            return [None]
1044
 
        vf = self._get_revision_vf()
1045
 
        try:
1046
 
            return [None] + vf.get_ancestry(revision_id)
1047
 
        except errors.RevisionNotPresent:
1048
 
            raise errors.NoSuchRevision(self, revision_id)
1049
 
 
1050
 
    @needs_read_lock
1051
 
    def get_revision(self, revision_id):
1052
 
        """Return the Revision object for a named revision"""
1053
 
        return self.get_revision_reconcile(revision_id)
1054
 
 
1055
 
    @needs_read_lock
1056
 
    def get_revision_graph(self, revision_id=None):
1057
 
        """Return a dictionary containing the revision graph.
1058
 
 
1059
 
        :param revision_id: The revision_id to get a graph from. If None, then
1060
 
        the entire revision graph is returned. This is a deprecated mode of
1061
 
        operation and will be removed in the future.
1062
 
        :return: a dictionary of revision_id->revision_parents_list.
1063
 
        """
1064
 
        # special case NULL_REVISION
1065
 
        if revision_id == _mod_revision.NULL_REVISION:
1066
 
            return {}
1067
 
        a_weave = self._get_revision_vf()
1068
 
        entire_graph = a_weave.get_graph()
1069
 
        if revision_id is None:
1070
 
            return a_weave.get_graph()
1071
 
        elif revision_id not in a_weave:
1072
 
            raise errors.NoSuchRevision(self, revision_id)
1073
 
        else:
1074
 
            # add what can be reached from revision_id
1075
 
            result = {}
1076
 
            pending = set([revision_id])
1077
 
            while len(pending) > 0:
1078
 
                node = pending.pop()
1079
 
                result[node] = a_weave.get_parents(node)
1080
 
                for revision_id in result[node]:
1081
 
                    if revision_id not in result:
1082
 
                        pending.add(revision_id)
1083
 
            return result
1084
 
 
1085
 
    @needs_read_lock
1086
 
    def get_revision_graph_with_ghosts(self, revision_ids=None):
1087
 
        """Return a graph of the revisions with ghosts marked as applicable.
1088
 
 
1089
 
        :param revision_ids: an iterable of revisions to graph or None for all.
1090
 
        :return: a Graph object with the graph reachable from revision_ids.
1091
 
        """
1092
 
        result = graph.Graph()
1093
 
        vf = self._get_revision_vf()
1094
 
        versions = set(vf.versions())
1095
 
        if not revision_ids:
1096
 
            pending = set(self.all_revision_ids())
1097
 
            required = set([])
1098
 
        else:
1099
 
            pending = set(revision_ids)
1100
 
            # special case NULL_REVISION
1101
 
            if _mod_revision.NULL_REVISION in pending:
1102
 
                pending.remove(_mod_revision.NULL_REVISION)
1103
 
            required = set(pending)
1104
 
        done = set([])
1105
 
        while len(pending):
1106
 
            revision_id = pending.pop()
1107
 
            if not revision_id in versions:
1108
 
                if revision_id in required:
1109
 
                    raise errors.NoSuchRevision(self, revision_id)
1110
 
                # a ghost
1111
 
                result.add_ghost(revision_id)
1112
 
                # mark it as done so we don't try for it again.
1113
 
                done.add(revision_id)
1114
 
                continue
1115
 
            parent_ids = vf.get_parents_with_ghosts(revision_id)
1116
 
            for parent_id in parent_ids:
1117
 
                # is this queued or done ?
1118
 
                if (parent_id not in pending and
1119
 
                    parent_id not in done):
1120
 
                    # no, queue it.
1121
 
                    pending.add(parent_id)
1122
 
            result.add_node(revision_id, parent_ids)
1123
 
            done.add(revision_id)
1124
 
        return result
1125
 
 
1126
 
    def _get_revision_vf(self):
1127
 
        """:return: a versioned file containing the revisions."""
1128
 
        vf = self._revision_store.get_revision_file(self.get_transaction())
1129
 
        return vf
1130
 
 
1131
 
    @needs_write_lock
1132
 
    def reconcile(self, other=None, thorough=False):
1133
 
        """Reconcile this repository."""
1134
 
        from bzrlib.reconcile import KnitReconciler
1135
 
        reconciler = KnitReconciler(self, thorough=thorough)
1136
 
        reconciler.reconcile()
1137
 
        return reconciler
1138
 
    
1139
 
    def revision_parents(self, revision_id):
1140
 
        return self._get_revision_vf().get_parents(revision_id)
1141
 
 
1142
 
 
1143
 
class KnitRepository2(KnitRepository):
1144
 
    """"""
1145
 
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1146
 
                 control_store, text_store):
1147
 
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1148
 
                              _revision_store, control_store, text_store)
1149
 
        self._serializer = xml6.serializer_v6
1150
 
 
1151
 
    def deserialise_inventory(self, revision_id, xml):
1152
 
        """Transform the xml into an inventory object. 
1153
 
 
1154
 
        :param revision_id: The expected revision id of the inventory.
1155
 
        :param xml: A serialised inventory.
1156
 
        """
1157
 
        result = self._serializer.read_inventory_from_string(xml)
1158
 
        assert result.root.revision is not None
1159
 
        return result
1160
 
 
1161
 
    def serialise_inventory(self, inv):
1162
 
        """Transform the inventory object into XML text.
1163
 
 
1164
 
        :param revision_id: The expected revision id of the inventory.
1165
 
        :param xml: A serialised inventory.
1166
 
        """
1167
 
        assert inv.revision_id is not None
1168
 
        assert inv.root.revision is not None
1169
 
        return KnitRepository.serialise_inventory(self, inv)
1170
 
 
1171
 
    def get_commit_builder(self, branch, parents, config, timestamp=None, 
1172
 
                           timezone=None, committer=None, revprops=None, 
1173
 
                           revision_id=None):
1174
 
        """Obtain a CommitBuilder for this repository.
1175
 
        
1176
 
        :param branch: Branch to commit to.
1177
 
        :param parents: Revision ids of the parents of the new revision.
1178
 
        :param config: Configuration to use.
1179
 
        :param timestamp: Optional timestamp recorded for commit.
1180
 
        :param timezone: Optional timezone for timestamp.
1181
 
        :param committer: Optional committer to set for commit.
1182
 
        :param revprops: Optional dictionary of revision properties.
1183
 
        :param revision_id: Optional revision id.
1184
 
        """
1185
 
        return RootCommitBuilder(self, parents, config, timestamp, timezone,
1186
 
                                 committer, revprops, revision_id)
1187
 
 
1188
 
 
1189
1021
class RepositoryFormatRegistry(registry.Registry):
1190
1022
    """Registry of RepositoryFormats.
1191
1023
    """
 
1024
 
 
1025
    def get(self, format_string):
 
1026
        r = registry.Registry.get(self, format_string)
 
1027
        if callable(r):
 
1028
            r = r()
 
1029
        return r
1192
1030
    
1193
1031
 
1194
1032
format_registry = RepositoryFormatRegistry()
1195
 
"""Registry of formats, indexed by their identifying format string."""
1196
 
 
 
1033
"""Registry of formats, indexed by their identifying format string.
 
1034
 
 
1035
This can contain either format instances themselves, or classes/factories that
 
1036
can be called to obtain one.
 
1037
"""
 
1038
 
 
1039
 
 
1040
#####################################################################
 
1041
# Repository Formats
1197
1042
 
1198
1043
class RepositoryFormat(object):
1199
1044
    """A repository format.
1222
1067
    def __str__(self):
1223
1068
        return "<%s>" % self.__class__.__name__
1224
1069
 
 
1070
    def __eq__(self, other):
 
1071
        # format objects are generally stateless
 
1072
        return isinstance(other, self.__class__)
 
1073
 
 
1074
    def __ne__(self, other):
 
1075
        return not self == other
 
1076
 
1225
1077
    @classmethod
1226
1078
    def find_format(klass, a_bzrdir):
1227
1079
        """Return the format for the repository object in a_bzrdir.
1288
1140
        from bzrlib.store.revision.text import TextRevisionStore
1289
1141
        dir_mode = control_files._dir_mode
1290
1142
        file_mode = control_files._file_mode
1291
 
        text_store =TextStore(transport.clone(name),
 
1143
        text_store = TextStore(transport.clone(name),
1292
1144
                              prefixed=prefixed,
1293
1145
                              compressed=compressed,
1294
1146
                              dir_mode=dir_mode,
1296
1148
        _revision_store = TextRevisionStore(text_store, serializer)
1297
1149
        return _revision_store
1298
1150
 
 
1151
    # TODO: this shouldn't be in the base class, it's specific to things that
 
1152
    # use weaves or knits -- mbp 20070207
1299
1153
    def _get_versioned_file_store(self,
1300
1154
                                  name,
1301
1155
                                  transport,
1302
1156
                                  control_files,
1303
1157
                                  prefixed=True,
1304
 
                                  versionedfile_class=weave.WeaveFile,
 
1158
                                  versionedfile_class=None,
1305
1159
                                  versionedfile_kwargs={},
1306
1160
                                  escaped=False):
 
1161
        if versionedfile_class is None:
 
1162
            versionedfile_class = self._versionedfile_class
1307
1163
        weave_transport = control_files._transport.clone(name)
1308
1164
        dir_mode = control_files._dir_mode
1309
1165
        file_mode = control_files._file_mode
1344
1200
        raise NotImplementedError(self.open)
1345
1201
 
1346
1202
 
1347
 
class PreSplitOutRepositoryFormat(RepositoryFormat):
1348
 
    """Base class for the pre split out repository formats."""
1349
 
 
1350
 
    rich_root_data = False
1351
 
 
1352
 
    def initialize(self, a_bzrdir, shared=False, _internal=False):
1353
 
        """Create a weave repository.
1354
 
        
1355
 
        TODO: when creating split out bzr branch formats, move this to a common
1356
 
        base for Format5, Format6. or something like that.
1357
 
        """
1358
 
        if shared:
1359
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
1360
 
 
1361
 
        if not _internal:
1362
 
            # always initialized when the bzrdir is.
1363
 
            return self.open(a_bzrdir, _found=True)
1364
 
        
1365
 
        # Create an empty weave
1366
 
        sio = StringIO()
1367
 
        weavefile.write_weave_v5(weave.Weave(), sio)
1368
 
        empty_weave = sio.getvalue()
1369
 
 
1370
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1371
 
        dirs = ['revision-store', 'weaves']
1372
 
        files = [('inventory.weave', StringIO(empty_weave)),
1373
 
                 ]
1374
 
        
1375
 
        # FIXME: RBC 20060125 don't peek under the covers
1376
 
        # NB: no need to escape relative paths that are url safe.
1377
 
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
1378
 
                                'branch-lock', lockable_files.TransportLock)
1379
 
        control_files.create_lock()
1380
 
        control_files.lock_write()
1381
 
        control_files._transport.mkdir_multi(dirs,
1382
 
                mode=control_files._dir_mode)
1383
 
        try:
1384
 
            for file, content in files:
1385
 
                control_files.put(file, content)
1386
 
        finally:
1387
 
            control_files.unlock()
1388
 
        return self.open(a_bzrdir, _found=True)
1389
 
 
1390
 
    def _get_control_store(self, repo_transport, control_files):
1391
 
        """Return the control store for this repository."""
1392
 
        return self._get_versioned_file_store('',
1393
 
                                              repo_transport,
1394
 
                                              control_files,
1395
 
                                              prefixed=False)
1396
 
 
1397
 
    def _get_text_store(self, transport, control_files):
1398
 
        """Get a store for file texts for this format."""
1399
 
        raise NotImplementedError(self._get_text_store)
1400
 
 
1401
 
    def open(self, a_bzrdir, _found=False):
1402
 
        """See RepositoryFormat.open()."""
1403
 
        if not _found:
1404
 
            # we are being called directly and must probe.
1405
 
            raise NotImplementedError
1406
 
 
1407
 
        repo_transport = a_bzrdir.get_repository_transport(None)
1408
 
        control_files = a_bzrdir._control_files
1409
 
        text_store = self._get_text_store(repo_transport, control_files)
1410
 
        control_store = self._get_control_store(repo_transport, control_files)
1411
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1412
 
        return AllInOneRepository(_format=self,
1413
 
                                  a_bzrdir=a_bzrdir,
1414
 
                                  _revision_store=_revision_store,
1415
 
                                  control_store=control_store,
1416
 
                                  text_store=text_store)
1417
 
 
1418
 
    def check_conversion_target(self, target_format):
1419
 
        pass
1420
 
 
1421
 
 
1422
 
class RepositoryFormat4(PreSplitOutRepositoryFormat):
1423
 
    """Bzr repository format 4.
1424
 
 
1425
 
    This repository format has:
1426
 
     - flat stores
1427
 
     - TextStores for texts, inventories,revisions.
1428
 
 
1429
 
    This format is deprecated: it indexes texts using a text id which is
1430
 
    removed in format 5; initialization and write support for this format
1431
 
    has been removed.
1432
 
    """
1433
 
 
1434
 
    def __init__(self):
1435
 
        super(RepositoryFormat4, self).__init__()
1436
 
        self._matchingbzrdir = bzrdir.BzrDirFormat4()
1437
 
 
1438
 
    def get_format_description(self):
1439
 
        """See RepositoryFormat.get_format_description()."""
1440
 
        return "Repository format 4"
1441
 
 
1442
 
    def initialize(self, url, shared=False, _internal=False):
1443
 
        """Format 4 branches cannot be created."""
1444
 
        raise errors.UninitializableFormat(self)
1445
 
 
1446
 
    def is_supported(self):
1447
 
        """Format 4 is not supported.
1448
 
 
1449
 
        It is not supported because the model changed from 4 to 5 and the
1450
 
        conversion logic is expensive - so doing it on the fly was not 
1451
 
        feasible.
1452
 
        """
1453
 
        return False
1454
 
 
1455
 
    def _get_control_store(self, repo_transport, control_files):
1456
 
        """Format 4 repositories have no formal control store at this point.
1457
 
        
1458
 
        This will cause any control-file-needing apis to fail - this is desired.
1459
 
        """
1460
 
        return None
1461
 
    
1462
 
    def _get_revision_store(self, repo_transport, control_files):
1463
 
        """See RepositoryFormat._get_revision_store()."""
1464
 
        from bzrlib.xml4 import serializer_v4
1465
 
        return self._get_text_rev_store(repo_transport,
1466
 
                                        control_files,
1467
 
                                        'revision-store',
1468
 
                                        serializer=serializer_v4)
1469
 
 
1470
 
    def _get_text_store(self, transport, control_files):
1471
 
        """See RepositoryFormat._get_text_store()."""
1472
 
 
1473
 
 
1474
 
class RepositoryFormat5(PreSplitOutRepositoryFormat):
1475
 
    """Bzr control format 5.
1476
 
 
1477
 
    This repository format has:
1478
 
     - weaves for file texts and inventory
1479
 
     - flat stores
1480
 
     - TextStores for revisions and signatures.
1481
 
    """
1482
 
 
1483
 
    def __init__(self):
1484
 
        super(RepositoryFormat5, self).__init__()
1485
 
        self._matchingbzrdir = bzrdir.BzrDirFormat5()
1486
 
 
1487
 
    def get_format_description(self):
1488
 
        """See RepositoryFormat.get_format_description()."""
1489
 
        return "Weave repository format 5"
1490
 
 
1491
 
    def _get_revision_store(self, repo_transport, control_files):
1492
 
        """See RepositoryFormat._get_revision_store()."""
1493
 
        """Return the revision store object for this a_bzrdir."""
1494
 
        return self._get_text_rev_store(repo_transport,
1495
 
                                        control_files,
1496
 
                                        'revision-store',
1497
 
                                        compressed=False)
1498
 
 
1499
 
    def _get_text_store(self, transport, control_files):
1500
 
        """See RepositoryFormat._get_text_store()."""
1501
 
        return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
1502
 
 
1503
 
 
1504
 
class RepositoryFormat6(PreSplitOutRepositoryFormat):
1505
 
    """Bzr control format 6.
1506
 
 
1507
 
    This repository format has:
1508
 
     - weaves for file texts and inventory
1509
 
     - hash subdirectory based stores.
1510
 
     - TextStores for revisions and signatures.
1511
 
    """
1512
 
 
1513
 
    def __init__(self):
1514
 
        super(RepositoryFormat6, self).__init__()
1515
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
1516
 
 
1517
 
    def get_format_description(self):
1518
 
        """See RepositoryFormat.get_format_description()."""
1519
 
        return "Weave repository format 6"
1520
 
 
1521
 
    def _get_revision_store(self, repo_transport, control_files):
1522
 
        """See RepositoryFormat._get_revision_store()."""
1523
 
        return self._get_text_rev_store(repo_transport,
1524
 
                                        control_files,
1525
 
                                        'revision-store',
1526
 
                                        compressed=False,
1527
 
                                        prefixed=True)
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)
1532
 
 
1533
 
 
1534
1203
class MetaDirRepositoryFormat(RepositoryFormat):
1535
1204
    """Common base class for the new repositories using the metadir layout."""
1536
1205
 
1537
1206
    rich_root_data = False
 
1207
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1538
1208
 
1539
1209
    def __init__(self):
1540
1210
        super(MetaDirRepositoryFormat, self).__init__()
1541
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1542
1211
 
1543
1212
    def _create_control_files(self, a_bzrdir):
1544
1213
        """Create the required files and the initial control_files object."""
1567
1236
            control_files.unlock()
1568
1237
 
1569
1238
 
1570
 
class RepositoryFormat7(MetaDirRepositoryFormat):
1571
 
    """Bzr repository 7.
1572
 
 
1573
 
    This repository format has:
1574
 
     - weaves for file texts and inventory
1575
 
     - hash subdirectory based stores.
1576
 
     - TextStores for revisions and signatures.
1577
 
     - a format marker of its own
1578
 
     - an optional 'shared-storage' flag
1579
 
     - an optional 'no-working-trees' flag
1580
 
    """
1581
 
 
1582
 
    def _get_control_store(self, repo_transport, control_files):
1583
 
        """Return the control store for this repository."""
1584
 
        return self._get_versioned_file_store('',
1585
 
                                              repo_transport,
1586
 
                                              control_files,
1587
 
                                              prefixed=False)
1588
 
 
1589
 
    def get_format_string(self):
1590
 
        """See RepositoryFormat.get_format_string()."""
1591
 
        return "Bazaar-NG Repository format 7"
1592
 
 
1593
 
    def get_format_description(self):
1594
 
        """See RepositoryFormat.get_format_description()."""
1595
 
        return "Weave repository format 7"
1596
 
 
1597
 
    def check_conversion_target(self, target_format):
1598
 
        pass
1599
 
 
1600
 
    def _get_revision_store(self, repo_transport, control_files):
1601
 
        """See RepositoryFormat._get_revision_store()."""
1602
 
        return self._get_text_rev_store(repo_transport,
1603
 
                                        control_files,
1604
 
                                        'revision-store',
1605
 
                                        compressed=False,
1606
 
                                        prefixed=True,
1607
 
                                        )
1608
 
 
1609
 
    def _get_text_store(self, transport, control_files):
1610
 
        """See RepositoryFormat._get_text_store()."""
1611
 
        return self._get_versioned_file_store('weaves',
1612
 
                                              transport,
1613
 
                                              control_files)
1614
 
 
1615
 
    def initialize(self, a_bzrdir, shared=False):
1616
 
        """Create a weave repository.
1617
 
 
1618
 
        :param shared: If true the repository will be initialized as a shared
1619
 
                       repository.
1620
 
        """
1621
 
        # Create an empty weave
1622
 
        sio = StringIO()
1623
 
        weavefile.write_weave_v5(weave.Weave(), sio)
1624
 
        empty_weave = sio.getvalue()
1625
 
 
1626
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1627
 
        dirs = ['revision-store', 'weaves']
1628
 
        files = [('inventory.weave', StringIO(empty_weave)), 
1629
 
                 ]
1630
 
        utf8_files = [('format', self.get_format_string())]
1631
 
 
1632
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1633
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1634
 
 
1635
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1636
 
        """See RepositoryFormat.open().
1637
 
        
1638
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1639
 
                                    repository at a slightly different url
1640
 
                                    than normal. I.e. during 'upgrade'.
1641
 
        """
1642
 
        if not _found:
1643
 
            format = RepositoryFormat.find_format(a_bzrdir)
1644
 
            assert format.__class__ ==  self.__class__
1645
 
        if _override_transport is not None:
1646
 
            repo_transport = _override_transport
1647
 
        else:
1648
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1649
 
        control_files = lockable_files.LockableFiles(repo_transport,
1650
 
                                'lock', lockdir.LockDir)
1651
 
        text_store = self._get_text_store(repo_transport, control_files)
1652
 
        control_store = self._get_control_store(repo_transport, control_files)
1653
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1654
 
        return WeaveMetaDirRepository(_format=self,
1655
 
            a_bzrdir=a_bzrdir,
1656
 
            control_files=control_files,
1657
 
            _revision_store=_revision_store,
1658
 
            control_store=control_store,
1659
 
            text_store=text_store)
1660
 
 
1661
 
 
1662
 
class RepositoryFormatKnit(MetaDirRepositoryFormat):
1663
 
    """Bzr repository knit format (generalized). 
1664
 
 
1665
 
    This repository format has:
1666
 
     - knits for file texts and inventory
1667
 
     - hash subdirectory based stores.
1668
 
     - knits for revisions and signatures
1669
 
     - TextStores for revisions and signatures.
1670
 
     - a format marker of its own
1671
 
     - an optional 'shared-storage' flag
1672
 
     - an optional 'no-working-trees' flag
1673
 
     - a LockDir lock
1674
 
    """
1675
 
 
1676
 
    def _get_control_store(self, repo_transport, control_files):
1677
 
        """Return the control store for this repository."""
1678
 
        return VersionedFileStore(
1679
 
            repo_transport,
1680
 
            prefixed=False,
1681
 
            file_mode=control_files._file_mode,
1682
 
            versionedfile_class=knit.KnitVersionedFile,
1683
 
            versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
1684
 
            )
1685
 
 
1686
 
    def _get_revision_store(self, repo_transport, control_files):
1687
 
        """See RepositoryFormat._get_revision_store()."""
1688
 
        from bzrlib.store.revision.knit import KnitRevisionStore
1689
 
        versioned_file_store = VersionedFileStore(
1690
 
            repo_transport,
1691
 
            file_mode=control_files._file_mode,
1692
 
            prefixed=False,
1693
 
            precious=True,
1694
 
            versionedfile_class=knit.KnitVersionedFile,
1695
 
            versionedfile_kwargs={'delta':False,
1696
 
                                  'factory':knit.KnitPlainFactory(),
1697
 
                                 },
1698
 
            escaped=True,
1699
 
            )
1700
 
        return KnitRevisionStore(versioned_file_store)
1701
 
 
1702
 
    def _get_text_store(self, transport, control_files):
1703
 
        """See RepositoryFormat._get_text_store()."""
1704
 
        return self._get_versioned_file_store('knits',
1705
 
                                  transport,
1706
 
                                  control_files,
1707
 
                                  versionedfile_class=knit.KnitVersionedFile,
1708
 
                                  versionedfile_kwargs={
1709
 
                                      'create_parent_dir':True,
1710
 
                                      'delay_create':True,
1711
 
                                      'dir_mode':control_files._dir_mode,
1712
 
                                  },
1713
 
                                  escaped=True)
1714
 
 
1715
 
    def initialize(self, a_bzrdir, shared=False):
1716
 
        """Create a knit format 1 repository.
1717
 
 
1718
 
        :param a_bzrdir: bzrdir to contain the new repository; must already
1719
 
            be initialized.
1720
 
        :param shared: If true the repository will be initialized as a shared
1721
 
                       repository.
1722
 
        """
1723
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1724
 
        dirs = ['revision-store', 'knits']
1725
 
        files = []
1726
 
        utf8_files = [('format', self.get_format_string())]
1727
 
        
1728
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1729
 
        repo_transport = a_bzrdir.get_repository_transport(None)
1730
 
        control_files = lockable_files.LockableFiles(repo_transport,
1731
 
                                'lock', lockdir.LockDir)
1732
 
        control_store = self._get_control_store(repo_transport, control_files)
1733
 
        transaction = transactions.WriteTransaction()
1734
 
        # trigger a write of the inventory store.
1735
 
        control_store.get_weave_or_empty('inventory', transaction)
1736
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1737
 
        # the revision id here is irrelevant: it will not be stored, and cannot
1738
 
        # already exist.
1739
 
        _revision_store.has_revision_id('A', transaction)
1740
 
        _revision_store.get_signature_file(transaction)
1741
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1742
 
 
1743
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1744
 
        """See RepositoryFormat.open().
1745
 
        
1746
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1747
 
                                    repository at a slightly different url
1748
 
                                    than normal. I.e. during 'upgrade'.
1749
 
        """
1750
 
        if not _found:
1751
 
            format = RepositoryFormat.find_format(a_bzrdir)
1752
 
            assert format.__class__ ==  self.__class__
1753
 
        if _override_transport is not None:
1754
 
            repo_transport = _override_transport
1755
 
        else:
1756
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1757
 
        control_files = lockable_files.LockableFiles(repo_transport,
1758
 
                                'lock', lockdir.LockDir)
1759
 
        text_store = self._get_text_store(repo_transport, control_files)
1760
 
        control_store = self._get_control_store(repo_transport, control_files)
1761
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1762
 
        return KnitRepository(_format=self,
1763
 
                              a_bzrdir=a_bzrdir,
1764
 
                              control_files=control_files,
1765
 
                              _revision_store=_revision_store,
1766
 
                              control_store=control_store,
1767
 
                              text_store=text_store)
1768
 
 
1769
 
 
1770
 
class RepositoryFormatKnit1(RepositoryFormatKnit):
1771
 
    """Bzr repository knit format 1.
1772
 
 
1773
 
    This repository format has:
1774
 
     - knits for file texts and inventory
1775
 
     - hash subdirectory based stores.
1776
 
     - knits for revisions and signatures
1777
 
     - TextStores for revisions and signatures.
1778
 
     - a format marker of its own
1779
 
     - an optional 'shared-storage' flag
1780
 
     - an optional 'no-working-trees' flag
1781
 
     - a LockDir lock
1782
 
 
1783
 
    This format was introduced in bzr 0.8.
1784
 
    """
1785
 
    def get_format_string(self):
1786
 
        """See RepositoryFormat.get_format_string()."""
1787
 
        return "Bazaar-NG Knit Repository Format 1"
1788
 
 
1789
 
    def get_format_description(self):
1790
 
        """See RepositoryFormat.get_format_description()."""
1791
 
        return "Knit repository format 1"
1792
 
 
1793
 
    def check_conversion_target(self, target_format):
1794
 
        pass
1795
 
 
1796
 
 
1797
 
class RepositoryFormatKnit2(RepositoryFormatKnit):
1798
 
    """Bzr repository knit format 2.
1799
 
 
1800
 
    THIS FORMAT IS EXPERIMENTAL
1801
 
    This repository format has:
1802
 
     - knits for file texts and inventory
1803
 
     - hash subdirectory based stores.
1804
 
     - knits for revisions and signatures
1805
 
     - TextStores for revisions and signatures.
1806
 
     - a format marker of its own
1807
 
     - an optional 'shared-storage' flag
1808
 
     - an optional 'no-working-trees' flag
1809
 
     - a LockDir lock
1810
 
     - Support for recording full info about the tree root
1811
 
 
1812
 
    """
1813
 
    
1814
 
    rich_root_data = True
1815
 
 
1816
 
    def get_format_string(self):
1817
 
        """See RepositoryFormat.get_format_string()."""
1818
 
        return "Bazaar Knit Repository Format 2\n"
1819
 
 
1820
 
    def get_format_description(self):
1821
 
        """See RepositoryFormat.get_format_description()."""
1822
 
        return "Knit repository format 2"
1823
 
 
1824
 
    def check_conversion_target(self, target_format):
1825
 
        if not target_format.rich_root_data:
1826
 
            raise errors.BadConversionTarget(
1827
 
                'Does not support rich root data.', target_format)
1828
 
 
1829
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1830
 
        """See RepositoryFormat.open().
1831
 
        
1832
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1833
 
                                    repository at a slightly different url
1834
 
                                    than normal. I.e. during 'upgrade'.
1835
 
        """
1836
 
        if not _found:
1837
 
            format = RepositoryFormat.find_format(a_bzrdir)
1838
 
            assert format.__class__ ==  self.__class__
1839
 
        if _override_transport is not None:
1840
 
            repo_transport = _override_transport
1841
 
        else:
1842
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1843
 
        control_files = lockable_files.LockableFiles(repo_transport, 'lock',
1844
 
                                                     lockdir.LockDir)
1845
 
        text_store = self._get_text_store(repo_transport, control_files)
1846
 
        control_store = self._get_control_store(repo_transport, control_files)
1847
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1848
 
        return KnitRepository2(_format=self,
1849
 
                               a_bzrdir=a_bzrdir,
1850
 
                               control_files=control_files,
1851
 
                               _revision_store=_revision_store,
1852
 
                               control_store=control_store,
1853
 
                               text_store=text_store)
1854
 
 
1855
 
 
1856
 
 
1857
1239
# formats which have no format string are not discoverable
1858
 
# and not independently creatable, so are not registered.
1859
 
RepositoryFormat.register_format(RepositoryFormat7())
1860
 
# KEEP in sync with bzrdir.format_registry default
1861
 
RepositoryFormat.register_format(RepositoryFormatKnit1())
1862
 
RepositoryFormat.register_format(RepositoryFormatKnit2())
1863
 
_legacy_formats = [RepositoryFormat4(),
1864
 
                   RepositoryFormat5(),
1865
 
                   RepositoryFormat6()]
 
1240
# and not independently creatable, so are not registered.  They're 
 
1241
# all in bzrlib.repofmt.weaverepo now.  When an instance of one of these is
 
1242
# needed, it's constructed directly by the BzrDir.  Non-native formats where
 
1243
# the repository is not separately opened are similar.
 
1244
 
 
1245
format_registry.register_lazy(
 
1246
    'Bazaar-NG Repository format 7',
 
1247
    'bzrlib.repofmt.weaverepo',
 
1248
    'RepositoryFormat7'
 
1249
    )
 
1250
# KEEP in sync with bzrdir.format_registry default, which controls the overall
 
1251
# default control directory format
 
1252
 
 
1253
format_registry.register_lazy(
 
1254
    'Bazaar-NG Knit Repository Format 1',
 
1255
    'bzrlib.repofmt.knitrepo',
 
1256
    'RepositoryFormatKnit1',
 
1257
    )
 
1258
format_registry.default_key = 'Bazaar-NG Knit Repository Format 1'
 
1259
 
 
1260
format_registry.register_lazy(
 
1261
    'Bazaar Knit Repository Format 3 (bzr 0.15)\n',
 
1262
    'bzrlib.repofmt.knitrepo',
 
1263
    'RepositoryFormatKnit3',
 
1264
    )
1866
1265
 
1867
1266
 
1868
1267
class InterRepository(InterObject):
1910
1309
        # generic, possibly worst case, slow code path.
1911
1310
        target_ids = set(self.target.all_revision_ids())
1912
1311
        if revision_id is not None:
 
1312
            # TODO: jam 20070210 InterRepository is internal enough that it
 
1313
            #       should assume revision_ids are already utf-8
 
1314
            revision_id = osutils.safe_revision_id(revision_id)
1913
1315
            source_ids = self.source.get_ancestry(revision_id)
1914
1316
            assert source_ids[0] is None
1915
1317
            source_ids.pop(0)
1928
1330
    Data format and model must match for this to work.
1929
1331
    """
1930
1332
 
1931
 
    _matching_repo_format = RepositoryFormat4()
1932
 
    """Repository format for testing with."""
 
1333
    @classmethod
 
1334
    def _get_repo_format_to_test(self):
 
1335
        """Repository format for testing with."""
 
1336
        return RepositoryFormat.get_default_format()
1933
1337
 
1934
1338
    @staticmethod
1935
1339
    def is_compatible(source, target):
1937
1341
            return False
1938
1342
        if not isinstance(target, Repository):
1939
1343
            return False
1940
 
        if source._format.rich_root_data == target._format.rich_root_data:
1941
 
            return True
 
1344
        if source._format.rich_root_data != target._format.rich_root_data:
 
1345
            return False
 
1346
        if source._serializer != target._serializer:
 
1347
            return False
1942
1348
        else:
1943
 
            return False
 
1349
            return True 
1944
1350
 
1945
1351
    @needs_write_lock
1946
1352
    def copy_content(self, revision_id=None, basis=None):
1957
1363
            self.target.set_make_working_trees(self.source.make_working_trees())
1958
1364
        except NotImplementedError:
1959
1365
            pass
 
1366
        # TODO: jam 20070210 This is fairly internal, so we should probably
 
1367
        #       just assert that revision_id is not unicode.
 
1368
        revision_id = osutils.safe_revision_id(revision_id)
1960
1369
        # grab the basis available data
1961
1370
        if basis is not None:
1962
1371
            self.target.fetch(basis, revision_id=revision_id)
1973
1382
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1974
1383
               self.source, self.source._format, self.target, 
1975
1384
               self.target._format)
 
1385
        # TODO: jam 20070210 This should be an assert, not a translate
 
1386
        revision_id = osutils.safe_revision_id(revision_id)
1976
1387
        f = GenericRepoFetcher(to_repository=self.target,
1977
1388
                               from_repository=self.source,
1978
1389
                               last_revision=revision_id,
1983
1394
class InterWeaveRepo(InterSameDataRepository):
1984
1395
    """Optimised code paths between Weave based repositories."""
1985
1396
 
1986
 
    _matching_repo_format = RepositoryFormat7()
1987
 
    """Repository format for testing with."""
 
1397
    @classmethod
 
1398
    def _get_repo_format_to_test(self):
 
1399
        from bzrlib.repofmt import weaverepo
 
1400
        return weaverepo.RepositoryFormat7()
1988
1401
 
1989
1402
    @staticmethod
1990
1403
    def is_compatible(source, target):
1994
1407
        could lead to confusing results, and there is no need to be 
1995
1408
        overly general.
1996
1409
        """
 
1410
        from bzrlib.repofmt.weaverepo import (
 
1411
                RepositoryFormat5,
 
1412
                RepositoryFormat6,
 
1413
                RepositoryFormat7,
 
1414
                )
1997
1415
        try:
1998
1416
            return (isinstance(source._format, (RepositoryFormat5,
1999
1417
                                                RepositoryFormat6,
2008
1426
    def copy_content(self, revision_id=None, basis=None):
2009
1427
        """See InterRepository.copy_content()."""
2010
1428
        # weave specific optimised path:
 
1429
        # TODO: jam 20070210 Internal, should be an assert, not translate
 
1430
        revision_id = osutils.safe_revision_id(revision_id)
2011
1431
        if basis is not None:
2012
1432
            # copy the basis in, then fetch remaining data.
2013
1433
            basis.copy_content_into(self.target, revision_id)
2050
1470
        from bzrlib.fetch import GenericRepoFetcher
2051
1471
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2052
1472
               self.source, self.source._format, self.target, self.target._format)
 
1473
        # TODO: jam 20070210 This should be an assert, not a translate
 
1474
        revision_id = osutils.safe_revision_id(revision_id)
2053
1475
        f = GenericRepoFetcher(to_repository=self.target,
2054
1476
                               from_repository=self.source,
2055
1477
                               last_revision=revision_id,
2101
1523
class InterKnitRepo(InterSameDataRepository):
2102
1524
    """Optimised code paths between Knit based repositories."""
2103
1525
 
2104
 
    _matching_repo_format = RepositoryFormatKnit1()
2105
 
    """Repository format for testing with."""
 
1526
    @classmethod
 
1527
    def _get_repo_format_to_test(self):
 
1528
        from bzrlib.repofmt import knitrepo
 
1529
        return knitrepo.RepositoryFormatKnit1()
2106
1530
 
2107
1531
    @staticmethod
2108
1532
    def is_compatible(source, target):
2112
1536
        could lead to confusing results, and there is no need to be 
2113
1537
        overly general.
2114
1538
        """
 
1539
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
2115
1540
        try:
2116
1541
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
2117
1542
                    isinstance(target._format, (RepositoryFormatKnit1)))
2124
1549
        from bzrlib.fetch import KnitRepoFetcher
2125
1550
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2126
1551
               self.source, self.source._format, self.target, self.target._format)
 
1552
        # TODO: jam 20070210 This should be an assert, not a translate
 
1553
        revision_id = osutils.safe_revision_id(revision_id)
2127
1554
        f = KnitRepoFetcher(to_repository=self.target,
2128
1555
                            from_repository=self.source,
2129
1556
                            last_revision=revision_id,
2163
1590
 
2164
1591
class InterModel1and2(InterRepository):
2165
1592
 
2166
 
    _matching_repo_format = None
 
1593
    @classmethod
 
1594
    def _get_repo_format_to_test(self):
 
1595
        return None
2167
1596
 
2168
1597
    @staticmethod
2169
1598
    def is_compatible(source, target):
2180
1609
    def fetch(self, revision_id=None, pb=None):
2181
1610
        """See InterRepository.fetch()."""
2182
1611
        from bzrlib.fetch import Model1toKnit2Fetcher
 
1612
        # TODO: jam 20070210 This should be an assert, not a translate
 
1613
        revision_id = osutils.safe_revision_id(revision_id)
2183
1614
        f = Model1toKnit2Fetcher(to_repository=self.target,
2184
1615
                                 from_repository=self.source,
2185
1616
                                 last_revision=revision_id,
2201
1632
            self.target.set_make_working_trees(self.source.make_working_trees())
2202
1633
        except NotImplementedError:
2203
1634
            pass
 
1635
        # TODO: jam 20070210 Internal, assert, don't translate
 
1636
        revision_id = osutils.safe_revision_id(revision_id)
2204
1637
        # grab the basis available data
2205
1638
        if basis is not None:
2206
1639
            self.target.fetch(basis, revision_id=revision_id)
2213
1646
 
2214
1647
class InterKnit1and2(InterKnitRepo):
2215
1648
 
2216
 
    _matching_repo_format = None
 
1649
    @classmethod
 
1650
    def _get_repo_format_to_test(self):
 
1651
        return None
2217
1652
 
2218
1653
    @staticmethod
2219
1654
    def is_compatible(source, target):
2220
 
        """Be compatible with Knit1 source and Knit2 target"""
 
1655
        """Be compatible with Knit1 source and Knit3 target"""
 
1656
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit3
2221
1657
        try:
 
1658
            from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1, \
 
1659
                    RepositoryFormatKnit3
2222
1660
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
2223
 
                    isinstance(target._format, (RepositoryFormatKnit2)))
 
1661
                    isinstance(target._format, (RepositoryFormatKnit3)))
2224
1662
        except AttributeError:
2225
1663
            return False
2226
1664
 
2231
1669
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2232
1670
               self.source, self.source._format, self.target, 
2233
1671
               self.target._format)
 
1672
        # TODO: jam 20070210 This should be an assert, not a translate
 
1673
        revision_id = osutils.safe_revision_id(revision_id)
2234
1674
        f = Knit1to2Fetcher(to_repository=self.target,
2235
1675
                            from_repository=self.source,
2236
1676
                            last_revision=revision_id,
2262
1702
    def adapt(self, test):
2263
1703
        result = unittest.TestSuite()
2264
1704
        for repository_format, bzrdir_format in self._formats:
 
1705
            from copy import deepcopy
2265
1706
            new_test = deepcopy(test)
2266
1707
            new_test.transport_server = self._transport_server
2267
1708
            new_test.transport_readonly_server = self._transport_readonly_server
2292
1733
    def adapt(self, test):
2293
1734
        result = unittest.TestSuite()
2294
1735
        for interrepo_class, repository_format, repository_format_to in self._formats:
 
1736
            from copy import deepcopy
2295
1737
            new_test = deepcopy(test)
2296
1738
            new_test.transport_server = self._transport_server
2297
1739
            new_test.transport_readonly_server = self._transport_readonly_server
2308
1750
    @staticmethod
2309
1751
    def default_test_list():
2310
1752
        """Generate the default list of interrepo permutations to test."""
 
1753
        from bzrlib.repofmt import knitrepo, weaverepo
2311
1754
        result = []
2312
1755
        # test the default InterRepository between format 6 and the current 
2313
1756
        # default format.
2316
1759
        #result.append((InterRepository,
2317
1760
        #               RepositoryFormat6(),
2318
1761
        #               RepositoryFormatKnit1()))
2319
 
        for optimiser in InterRepository._optimisers:
2320
 
            if optimiser._matching_repo_format is not None:
2321
 
                result.append((optimiser,
2322
 
                               optimiser._matching_repo_format,
2323
 
                               optimiser._matching_repo_format
2324
 
                               ))
 
1762
        for optimiser_class in InterRepository._optimisers:
 
1763
            format_to_test = optimiser_class._get_repo_format_to_test()
 
1764
            if format_to_test is not None:
 
1765
                result.append((optimiser_class,
 
1766
                               format_to_test, format_to_test))
2325
1767
        # if there are specific combinations we want to use, we can add them 
2326
1768
        # here.
2327
 
        result.append((InterModel1and2, RepositoryFormat5(),
2328
 
                       RepositoryFormatKnit2()))
2329
 
        result.append((InterKnit1and2, RepositoryFormatKnit1(),
2330
 
                       RepositoryFormatKnit2()))
 
1769
        result.append((InterModel1and2,
 
1770
                       weaverepo.RepositoryFormat5(),
 
1771
                       knitrepo.RepositoryFormatKnit3()))
 
1772
        result.append((InterKnit1and2,
 
1773
                       knitrepo.RepositoryFormatKnit1(),
 
1774
                       knitrepo.RepositoryFormatKnit3()))
2331
1775
        return result
2332
1776
 
2333
1777
 
2414
1858
            self._committer = committer
2415
1859
 
2416
1860
        self.new_inventory = Inventory(None)
2417
 
        self._new_revision_id = revision_id
 
1861
        self._new_revision_id = osutils.safe_revision_id(revision_id)
2418
1862
        self.parents = parents
2419
1863
        self.repository = repository
2420
1864
 
2428
1872
        self._timestamp = round(timestamp, 3)
2429
1873
 
2430
1874
        if timezone is None:
2431
 
            self._timezone = local_time_offset()
 
1875
            self._timezone = osutils.local_time_offset()
2432
1876
        else:
2433
1877
            self._timezone = int(timezone)
2434
1878
 
2542
1986
        :param file_parents: The per-file parent revision ids.
2543
1987
        """
2544
1988
        self._add_text_to_weave(file_id, [], file_parents.keys())
 
1989
 
 
1990
    def modified_reference(self, file_id, file_parents):
 
1991
        """Record the modification of a reference.
 
1992
 
 
1993
        :param file_id: The file_id of the link to record.
 
1994
        :param file_parents: The per-file parent revision ids.
 
1995
        """
 
1996
        self._add_text_to_weave(file_id, [], file_parents.keys())
2545
1997
    
2546
1998
    def modified_file_text(self, file_id, file_parents,
2547
1999
                           get_content_byte_lines, text_sha1=None,
2646
2098
 
2647
2099
 
2648
2100
def _unescaper(match, _map=_unescape_map):
2649
 
    return _map[match.group(1)]
 
2101
    code = match.group(1)
 
2102
    try:
 
2103
        return _map[code]
 
2104
    except KeyError:
 
2105
        if not code.startswith('#'):
 
2106
            raise
 
2107
        return unichr(int(code[1:])).encode('utf8')
2650
2108
 
2651
2109
 
2652
2110
_unescape_re = None