~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

Merge in knit repository use of knits - still not a stable format, but can be experimented with.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
import bzrlib.bzrdir as bzrdir
23
23
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
24
import bzrlib.errors as errors
24
25
from bzrlib.errors import InvalidRevisionId
 
26
import bzrlib.gpg as gpg
25
27
from bzrlib.graph import Graph
 
28
from bzrlib.inter import InterObject
 
29
from bzrlib.knit import KnitVersionedFile
26
30
from bzrlib.lockable_files import LockableFiles, TransportLock
27
31
from bzrlib.lockdir import LockDir
28
32
from bzrlib.osutils import safe_unicode
29
33
from bzrlib.revision import NULL_REVISION
30
 
import bzrlib.errors as errors
31
 
import bzrlib.gpg as gpg
32
 
from bzrlib.store import copy_all
33
 
from bzrlib.store.weave import WeaveStore
 
34
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
34
35
from bzrlib.store.text import TextStore
35
36
from bzrlib.symbol_versioning import *
36
37
from bzrlib.trace import mutter
37
38
from bzrlib.tree import RevisionTree
 
39
from bzrlib.tsort import topo_sort
38
40
from bzrlib.testament import Testament
39
41
from bzrlib.tree import EmptyTree
40
42
import bzrlib.ui
 
43
from bzrlib.weave import WeaveFile
41
44
import bzrlib.xml5
42
45
 
43
46
 
64
67
        """
65
68
        inv_text = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
66
69
        inv_sha1 = bzrlib.osutils.sha_string(inv_text)
67
 
        self.control_weaves.add_text('inventory', revid,
68
 
                   bzrlib.osutils.split_lines(inv_text), parents,
69
 
                   self.get_transaction())
 
70
        inv_vf = self.control_weaves.get_weave('inventory',
 
71
                                               self.get_transaction())
 
72
        inv_vf.add_lines(revid, parents, bzrlib.osutils.split_lines(inv_text))
70
73
        return inv_sha1
71
74
 
72
75
    @needs_write_lock
94
97
            else:
95
98
                # yes, this is not suitable for adding with ghosts.
96
99
                self.add_inventory(rev_id, inv, rev.parent_ids)
97
 
            
98
 
        rev_tmp = StringIO()
99
 
        bzrlib.xml5.serializer_v5.write_revision(rev, rev_tmp)
100
 
        rev_tmp.seek(0)
101
 
        self.revision_store.add(rev_tmp, rev_id)
102
 
        mutter('added revision_id {%s}', rev_id)
 
100
        self._revision_store.add_revision(rev, self.get_transaction())   
103
101
 
104
102
    @needs_read_lock
105
103
    def _all_possible_ids(self):
106
104
        """Return all the possible revisions that we could find."""
107
 
        return self.get_inventory_weave().names()
 
105
        return self.get_inventory_weave().versions()
108
106
 
109
107
    @needs_read_lock
110
108
    def all_revision_ids(self):
114
112
        present: for weaves ghosts may lead to a lack of correctness until
115
113
        the reweave updates the parents list.
116
114
        """
 
115
        if self._revision_store.text_store.listable():
 
116
            return self._revision_store.all_revision_ids(self.get_transaction())
117
117
        result = self._all_possible_ids()
118
118
        return self._eliminate_revisions_not_present(result)
119
119
 
134
134
        """Construct the current default format repository in a_bzrdir."""
135
135
        return RepositoryFormat.get_default_format().initialize(a_bzrdir)
136
136
 
137
 
    def __init__(self, _format, a_bzrdir, control_files, revision_store):
 
137
    def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
138
138
        """instantiate a Repository.
139
139
 
140
140
        :param _format: The format of the repository on disk.
149
149
        # the following are part of the public API for Repository:
150
150
        self.bzrdir = a_bzrdir
151
151
        self.control_files = control_files
152
 
        self.revision_store = revision_store
 
152
        self._revision_store = _revision_store
 
153
        self.text_store = text_store
 
154
        # backwards compatability
 
155
        self.weave_store = text_store
 
156
        # not right yet - should be more semantically clear ? 
 
157
        # 
 
158
        self.control_store = control_store
 
159
        self.control_weaves = control_store
153
160
 
154
161
    def lock_write(self):
155
162
        self.control_files.lock_write()
220
227
        self.copy_content_into(result, revision_id, basis)
221
228
        return result
222
229
 
 
230
    @needs_read_lock
223
231
    def has_revision(self, revision_id):
224
 
        """True if this branch has a copy of the revision.
225
 
 
226
 
        This does not necessarily imply the revision is merge
227
 
        or on the mainline."""
228
 
        return (revision_id is None
229
 
                or self.revision_store.has_id(revision_id))
230
 
 
231
 
    @needs_read_lock
232
 
    def get_revision_xml_file(self, revision_id):
233
 
        """Return XML file object for revision object."""
234
 
        if not revision_id or not isinstance(revision_id, basestring):
235
 
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
236
 
        try:
237
 
            return self.revision_store.get(revision_id)
238
 
        except (IndexError, KeyError):
239
 
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
240
 
 
241
 
    @needs_read_lock
242
 
    def get_revision_xml(self, revision_id):
243
 
        return self.get_revision_xml_file(revision_id).read()
 
232
        """True if this repository has a copy of the revision."""
 
233
        return self._revision_store.has_revision_id(revision_id,
 
234
                                                    self.get_transaction())
244
235
 
245
236
    @needs_read_lock
246
237
    def get_revision_reconcile(self, revision_id):
251
242
        be used by reconcile, or reconcile-alike commands that are correcting
252
243
        or testing the revision graph.
253
244
        """
254
 
        xml_file = self.get_revision_xml_file(revision_id)
 
245
        if not revision_id or not isinstance(revision_id, basestring):
 
246
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
 
247
        return self._revision_store.get_revision(revision_id,
 
248
                                                 self.get_transaction())
255
249
 
256
 
        try:
257
 
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
258
 
        except SyntaxError, e:
259
 
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
260
 
                                         [revision_id,
261
 
                                          str(e)])
262
 
            
263
 
        assert r.revision_id == revision_id
264
 
        return r
 
250
    @needs_read_lock
 
251
    def get_revision_xml(self, revision_id):
 
252
        rev = self.get_revision(revision_id) 
 
253
        rev_tmp = StringIO()
 
254
        # the current serializer..
 
255
        self._revision_store._serializer.write_revision(rev, rev_tmp)
 
256
        rev_tmp.seek(0)
 
257
        return rev_tmp.getvalue()
265
258
 
266
259
    @needs_read_lock
267
260
    def get_revision(self, revision_id):
285
278
        consistency and is only applicable to inventory-weave-for-ancestry
286
279
        using repository formats & fetchers.
287
280
        """
288
 
        weave_parents = inventory.parent_names(revision.revision_id)
289
 
        weave_names = inventory.names()
 
281
        weave_parents = inventory.get_parents(revision.revision_id)
 
282
        weave_names = inventory.versions()
290
283
        for parent_id in revision.parent_ids:
291
284
            if parent_id in weave_names:
292
285
                # this parent must not be a ghost.
294
287
                    # but it is a ghost
295
288
                    raise errors.CorruptRepository(self)
296
289
 
297
 
    @needs_read_lock
298
 
    def get_revision_sha1(self, revision_id):
299
 
        """Hash the stored value of a revision, and return it."""
300
 
        # In the future, revision entries will be signed. At that
301
 
        # point, it is probably best *not* to include the signature
302
 
        # in the revision hash. Because that lets you re-sign
303
 
        # the revision, (add signatures/remove signatures) and still
304
 
        # have all hash pointers stay consistent.
305
 
        # But for now, just hash the contents.
306
 
        return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
307
 
 
308
290
    @needs_write_lock
309
291
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
310
 
        self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)), 
311
 
                                revision_id, "sig")
 
292
        signature = gpg_strategy.sign(plaintext)
 
293
        self._revision_store.add_revision_signature_text(revision_id,
 
294
                                                         signature,
 
295
                                                         self.get_transaction())
312
296
 
313
297
    def fileid_involved_between_revs(self, from_revid, to_revid):
314
298
        """Find file_id(s) which are involved in the changes between revisions.
327
311
        #       won't be fixed, because AD never saw revision C
328
312
        #       to cause a conflict which would force a reweave.
329
313
        w = self.get_inventory_weave()
330
 
        from_set = set(w.inclusions([w.lookup(from_revid)]))
331
 
        to_set = set(w.inclusions([w.lookup(to_revid)]))
332
 
        included = to_set.difference(from_set)
333
 
        changed = map(w.idx_to_name, included)
 
314
        from_set = set(w.get_ancestry(from_revid))
 
315
        to_set = set(w.get_ancestry(to_revid))
 
316
        changed = to_set.difference(from_set)
334
317
        return self._fileid_involved_by_set(changed)
335
318
 
336
319
    def fileid_involved(self, last_revid=None):
340
323
        """
341
324
        w = self.get_inventory_weave()
342
325
        if not last_revid:
343
 
            changed = set(w._names)
 
326
            changed = set(w.versions())
344
327
        else:
345
 
            included = w.inclusions([w.lookup(last_revid)])
346
 
            changed = map(w.idx_to_name, included)
 
328
            changed = set(w.get_ancestry(last_revid))
347
329
        return self._fileid_involved_by_set(changed)
348
330
 
349
331
    def fileid_involved_by_set(self, changes):
378
360
 
379
361
        w = self.get_inventory_weave()
380
362
        file_ids = set()
381
 
        for line in w._weave:
382
 
 
383
 
            # it is ugly, but it is due to the weave structure
384
 
            if not isinstance(line, basestring): continue
385
 
 
 
363
 
 
364
        for lineno, insert, deletes, line in w.walk(changes):
386
365
            start = line.find('file_id="')+9
387
366
            if start < 9: continue
388
367
            end = line.find('"', start)
419
398
        try:
420
399
            assert isinstance(revision_id, basestring), type(revision_id)
421
400
            iw = self.get_inventory_weave()
422
 
            return iw.get_text(iw.lookup(revision_id))
 
401
            return iw.get_text(revision_id)
423
402
        except IndexError:
424
403
            raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
425
404
 
436
415
        :return: a dictionary of revision_id->revision_parents_list.
437
416
        """
438
417
        weave = self.get_inventory_weave()
439
 
        all_revisions = self._eliminate_revisions_not_present(weave.names())
440
 
        entire_graph = dict([(node, weave.parent_names(node)) for 
 
418
        all_revisions = self._eliminate_revisions_not_present(weave.versions())
 
419
        entire_graph = dict([(node, weave.get_parents(node)) for 
441
420
                             node in all_revisions])
442
421
        if revision_id is None:
443
422
            return entire_graph
537
516
        if not self.has_revision(revision_id):
538
517
            raise errors.NoSuchRevision(self, revision_id)
539
518
        w = self.get_inventory_weave()
540
 
        return [None] + map(w.idx_to_name,
541
 
                            w.inclusions([w.lookup(revision_id)]))
 
519
        return [None] + w.get_ancestry(revision_id)
542
520
 
543
521
    @needs_read_lock
544
522
    def print_file(self, file, revision_id):
608
586
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
609
587
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
610
588
 
 
589
    @needs_read_lock
 
590
    def has_signature_for_revision_id(self, revision_id):
 
591
        """Query for a revision signature for revision_id in the repository."""
 
592
        return self._revision_store.has_signature(revision_id,
 
593
                                                  self.get_transaction())
 
594
 
 
595
    @needs_read_lock
 
596
    def get_signature_text(self, revision_id):
 
597
        """Return the text for a signature."""
 
598
        return self._revision_store.get_signature_text(revision_id,
 
599
                                                       self.get_transaction())
 
600
 
611
601
 
612
602
class AllInOneRepository(Repository):
613
603
    """Legacy support - the repository behaviour for all-in-one branches."""
614
604
 
615
 
    def __init__(self, _format, a_bzrdir, revision_store):
 
605
    def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
616
606
        # we reuse one control files instance.
617
607
        dir_mode = a_bzrdir._control_files._dir_mode
618
608
        file_mode = a_bzrdir._control_files._file_mode
650
640
        # not broken out yet because the controlweaves|inventory_store
651
641
        # and text_store | weave_store bits are still different.
652
642
        if isinstance(_format, RepositoryFormat4):
 
643
            # cannot remove these - there is still no consistent api 
 
644
            # which allows access to this old info.
653
645
            self.inventory_store = get_store('inventory-store')
654
 
            self.text_store = get_store('text-store')
655
 
        elif isinstance(_format, RepositoryFormat5):
656
 
            self.control_weaves = get_weave('')
657
 
            self.weave_store = get_weave('weaves')
658
 
        elif isinstance(_format, RepositoryFormat6):
659
 
            self.control_weaves = get_weave('')
660
 
            self.weave_store = get_weave('weaves', prefixed=True)
661
 
        else:
662
 
            raise errors.BzrError('unreachable code: unexpected repository'
663
 
                                  ' format.')
664
 
        revision_store.register_suffix('sig')
665
 
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, revision_store)
 
646
            text_store = get_store('text-store')
 
647
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
666
648
 
667
649
 
668
650
class MetaDirRepository(Repository):
669
651
    """Repositories in the new meta-dir layout."""
670
652
 
671
 
    def __init__(self, _format, a_bzrdir, control_files, revision_store):
 
653
    def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
672
654
        super(MetaDirRepository, self).__init__(_format,
673
655
                                                a_bzrdir,
674
656
                                                control_files,
675
 
                                                revision_store)
 
657
                                                _revision_store,
 
658
                                                control_store,
 
659
                                                text_store)
676
660
 
677
661
        dir_mode = self.control_files._dir_mode
678
662
        file_mode = self.control_files._file_mode
691
675
                ws.enable_cache = True
692
676
            return ws
693
677
 
694
 
        if isinstance(self._format, RepositoryFormat7):
695
 
            self.control_weaves = get_weave('')
696
 
            self.weave_store = get_weave('weaves', prefixed=True)
697
 
        elif isinstance(self._format, RepositoryFormatKnit1):
698
 
            self.control_weaves = get_weave('')
699
 
            self.weave_store = get_weave('knits', prefixed=True)
700
 
        else:
701
 
            raise errors.BzrError('unreachable code: unexpected repository'
702
 
                                  ' format.')
 
678
 
 
679
class KnitRepository(MetaDirRepository):
 
680
    """Knit format repository."""
 
681
 
 
682
    @needs_read_lock
 
683
    def all_revision_ids(self):
 
684
        """See Repository.all_revision_ids()."""
 
685
        return self._revision_store.all_revision_ids(self.get_transaction())
703
686
 
704
687
 
705
688
class RepositoryFormat(object):
744
727
        except KeyError:
745
728
            raise errors.UnknownFormatError(format_string)
746
729
 
 
730
    def _get_control_store(self, repo_transport, control_files):
 
731
        """Return the control store for this repository."""
 
732
        raise NotImplementedError(self._get_control_store)
 
733
    
747
734
    @classmethod
748
735
    def get_default_format(klass):
749
736
        """Return the current default format."""
761
748
        """Return the revision store object for this a_bzrdir."""
762
749
        raise NotImplementedError(self._get_revision_store)
763
750
 
764
 
    def _get_rev_store(self,
765
 
                   transport,
766
 
                   control_files,
767
 
                   name,
768
 
                   compressed=True,
769
 
                   prefixed=False):
 
751
    def _get_text_rev_store(self,
 
752
                            transport,
 
753
                            control_files,
 
754
                            name,
 
755
                            compressed=True,
 
756
                            prefixed=False,
 
757
                            serializer=None):
770
758
        """Common logic for getting a revision store for a repository.
771
759
        
772
 
        see self._get_revision_store for the method to 
 
760
        see self._get_revision_store for the subclass-overridable method to 
773
761
        get the store for a repository.
774
762
        """
775
 
        if name:
776
 
            name = safe_unicode(name)
777
 
        else:
778
 
            name = ''
779
 
        dir_mode = control_files._dir_mode
780
 
        file_mode = control_files._file_mode
781
 
        revision_store =TextStore(transport.clone(name),
782
 
                                  prefixed=prefixed,
783
 
                                  compressed=compressed,
784
 
                                  dir_mode=dir_mode,
785
 
                                  file_mode=file_mode)
786
 
        revision_store.register_suffix('sig')
787
 
        return revision_store
 
763
        from bzrlib.store.revision.text import TextRevisionStore
 
764
        dir_mode = control_files._dir_mode
 
765
        file_mode = control_files._file_mode
 
766
        text_store =TextStore(transport.clone(name),
 
767
                              prefixed=prefixed,
 
768
                              compressed=compressed,
 
769
                              dir_mode=dir_mode,
 
770
                              file_mode=file_mode)
 
771
        _revision_store = TextRevisionStore(text_store, serializer)
 
772
        return _revision_store
 
773
 
 
774
    def _get_versioned_file_store(self,
 
775
                                  name,
 
776
                                  transport,
 
777
                                  control_files,
 
778
                                  prefixed=True,
 
779
                                  versionedfile_class=WeaveFile):
 
780
        weave_transport = control_files._transport.clone(name)
 
781
        dir_mode = control_files._dir_mode
 
782
        file_mode = control_files._file_mode
 
783
        return VersionedFileStore(weave_transport, prefixed=prefixed,
 
784
                                dir_mode=dir_mode,
 
785
                                file_mode=file_mode,
 
786
                                versionedfile_class=versionedfile_class)
788
787
 
789
788
    def initialize(self, a_bzrdir, shared=False):
790
789
        """Initialize a repository of this format in a_bzrdir.
870
869
            control_files.unlock()
871
870
        return self.open(a_bzrdir, _found=True)
872
871
 
 
872
    def _get_control_store(self, repo_transport, control_files):
 
873
        """Return the control store for this repository."""
 
874
        return self._get_versioned_file_store('',
 
875
                                              repo_transport,
 
876
                                              control_files,
 
877
                                              prefixed=False)
 
878
 
 
879
    def _get_text_store(self, transport, control_files):
 
880
        """Get a store for file texts for this format."""
 
881
        raise NotImplementedError(self._get_text_store)
 
882
 
873
883
    def open(self, a_bzrdir, _found=False):
874
884
        """See RepositoryFormat.open()."""
875
885
        if not _found:
878
888
 
879
889
        repo_transport = a_bzrdir.get_repository_transport(None)
880
890
        control_files = a_bzrdir._control_files
881
 
        revision_store = self._get_revision_store(repo_transport, control_files)
 
891
        text_store = self._get_text_store(repo_transport, control_files)
 
892
        control_store = self._get_control_store(repo_transport, control_files)
 
893
        _revision_store = self._get_revision_store(repo_transport, control_files)
882
894
        return AllInOneRepository(_format=self,
883
895
                                  a_bzrdir=a_bzrdir,
884
 
                                  revision_store=revision_store)
 
896
                                  _revision_store=_revision_store,
 
897
                                  control_store=control_store,
 
898
                                  text_store=text_store)
885
899
 
886
900
 
887
901
class RepositoryFormat4(PreSplitOutRepositoryFormat):
913
927
        """
914
928
        return False
915
929
 
 
930
    def _get_control_store(self, repo_transport, control_files):
 
931
        """Format 4 repositories have no formal control store at this point.
 
932
        
 
933
        This will cause any control-file-needing apis to fail - this is desired.
 
934
        """
 
935
        return None
 
936
    
916
937
    def _get_revision_store(self, repo_transport, control_files):
917
938
        """See RepositoryFormat._get_revision_store()."""
918
 
        return self._get_rev_store(repo_transport,
919
 
                                   control_files,
920
 
                                   'revision-store')
 
939
        from bzrlib.xml4 import serializer_v4
 
940
        return self._get_text_rev_store(repo_transport,
 
941
                                        control_files,
 
942
                                        'revision-store',
 
943
                                        serializer=serializer_v4)
 
944
 
 
945
    def _get_text_store(self, transport, control_files):
 
946
        """See RepositoryFormat._get_text_store()."""
921
947
 
922
948
 
923
949
class RepositoryFormat5(PreSplitOutRepositoryFormat):
936
962
    def _get_revision_store(self, repo_transport, control_files):
937
963
        """See RepositoryFormat._get_revision_store()."""
938
964
        """Return the revision store object for this a_bzrdir."""
939
 
        return self._get_rev_store(repo_transport,
940
 
                                   control_files,
941
 
                                   'revision-store',
942
 
                                   compressed=False)
 
965
        return self._get_text_rev_store(repo_transport,
 
966
                                        control_files,
 
967
                                        'revision-store',
 
968
                                        compressed=False)
 
969
 
 
970
    def _get_text_store(self, transport, control_files):
 
971
        """See RepositoryFormat._get_text_store()."""
 
972
        return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
943
973
 
944
974
 
945
975
class RepositoryFormat6(PreSplitOutRepositoryFormat):
957
987
 
958
988
    def _get_revision_store(self, repo_transport, control_files):
959
989
        """See RepositoryFormat._get_revision_store()."""
960
 
        return self._get_rev_store(repo_transport,
961
 
                                   control_files,
962
 
                                   'revision-store',
963
 
                                   compressed=False,
964
 
                                   prefixed=True)
 
990
        return self._get_text_rev_store(repo_transport,
 
991
                                        control_files,
 
992
                                        'revision-store',
 
993
                                        compressed=False,
 
994
                                        prefixed=True)
 
995
 
 
996
    def _get_text_store(self, transport, control_files):
 
997
        """See RepositoryFormat._get_text_store()."""
 
998
        return self._get_versioned_file_store('weaves', transport, control_files)
965
999
 
966
1000
 
967
1001
class MetaDirRepositoryFormat(RepositoryFormat):
980
1014
        control_files.create_lock()
981
1015
        return control_files
982
1016
 
983
 
    def _get_revision_store(self, repo_transport, control_files):
984
 
        """See RepositoryFormat._get_revision_store()."""
985
 
        return self._get_rev_store(repo_transport,
986
 
                                   control_files,
987
 
                                   'revision-store',
988
 
                                   compressed=False,
989
 
                                   prefixed=True,
990
 
                                   )
991
 
 
992
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
993
 
        """See RepositoryFormat.open().
994
 
        
995
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
996
 
                                    repository at a slightly different url
997
 
                                    than normal. I.e. during 'upgrade'.
998
 
        """
999
 
        if not _found:
1000
 
            format = RepositoryFormat.find_format(a_bzrdir)
1001
 
            assert format.__class__ ==  self.__class__
1002
 
        if _override_transport is not None:
1003
 
            repo_transport = _override_transport
1004
 
        else:
1005
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1006
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
1007
 
        revision_store = self._get_revision_store(repo_transport, control_files)
1008
 
        return MetaDirRepository(_format=self,
1009
 
                                 a_bzrdir=a_bzrdir,
1010
 
                                 control_files=control_files,
1011
 
                                 revision_store=revision_store)
1012
 
 
1013
1017
    def _upload_blank_content(self, a_bzrdir, dirs, files, utf8_files, shared):
1014
1018
        """Upload the initial blank content."""
1015
1019
        control_files = self._create_control_files(a_bzrdir)
1039
1043
     - an optional 'no-working-trees' flag
1040
1044
    """
1041
1045
 
 
1046
    def _get_control_store(self, repo_transport, control_files):
 
1047
        """Return the control store for this repository."""
 
1048
        return self._get_versioned_file_store('',
 
1049
                                              repo_transport,
 
1050
                                              control_files,
 
1051
                                              prefixed=False)
 
1052
 
1042
1053
    def get_format_string(self):
1043
1054
        """See RepositoryFormat.get_format_string()."""
1044
1055
        return "Bazaar-NG Repository format 7"
1045
1056
 
 
1057
    def _get_revision_store(self, repo_transport, control_files):
 
1058
        """See RepositoryFormat._get_revision_store()."""
 
1059
        return self._get_text_rev_store(repo_transport,
 
1060
                                        control_files,
 
1061
                                        'revision-store',
 
1062
                                        compressed=False,
 
1063
                                        prefixed=True,
 
1064
                                        )
 
1065
 
 
1066
    def _get_text_store(self, transport, control_files):
 
1067
        """See RepositoryFormat._get_text_store()."""
 
1068
        return self._get_versioned_file_store('weaves',
 
1069
                                              transport,
 
1070
                                              control_files)
 
1071
 
1046
1072
    def initialize(self, a_bzrdir, shared=False):
1047
1073
        """Create a weave repository.
1048
1074
 
1066
1092
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1067
1093
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1068
1094
 
 
1095
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
1096
        """See RepositoryFormat.open().
 
1097
        
 
1098
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
1099
                                    repository at a slightly different url
 
1100
                                    than normal. I.e. during 'upgrade'.
 
1101
        """
 
1102
        if not _found:
 
1103
            format = RepositoryFormat.find_format(a_bzrdir)
 
1104
            assert format.__class__ ==  self.__class__
 
1105
        if _override_transport is not None:
 
1106
            repo_transport = _override_transport
 
1107
        else:
 
1108
            repo_transport = a_bzrdir.get_repository_transport(None)
 
1109
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1110
        text_store = self._get_text_store(repo_transport, control_files)
 
1111
        control_store = self._get_control_store(repo_transport, control_files)
 
1112
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
1113
        return MetaDirRepository(_format=self,
 
1114
                                 a_bzrdir=a_bzrdir,
 
1115
                                 control_files=control_files,
 
1116
                                 _revision_store=_revision_store,
 
1117
                                 control_store=control_store,
 
1118
                                 text_store=text_store)
 
1119
 
1069
1120
 
1070
1121
class RepositoryFormatKnit1(MetaDirRepositoryFormat):
1071
1122
    """Bzr repository knit format 1.
1081
1132
     - a LockDir lock
1082
1133
    """
1083
1134
 
 
1135
    def _get_control_store(self, repo_transport, control_files):
 
1136
        """Return the control store for this repository."""
 
1137
        return self._get_versioned_file_store('',
 
1138
                                              repo_transport,
 
1139
                                              control_files,
 
1140
                                              prefixed=False,
 
1141
                                              versionedfile_class=KnitVersionedFile)
 
1142
 
1084
1143
    def get_format_string(self):
1085
1144
        """See RepositoryFormat.get_format_string()."""
1086
1145
        return "Bazaar-NG Knit Repository Format 1"
1087
1146
 
 
1147
    def _get_revision_store(self, repo_transport, control_files):
 
1148
        """See RepositoryFormat._get_revision_store()."""
 
1149
        from bzrlib.store.revision.knit import KnitRevisionStore
 
1150
        versioned_file_store = VersionedFileStore(
 
1151
            repo_transport,
 
1152
            file_mode = control_files._file_mode,
 
1153
            prefixed=False,
 
1154
            precious=True,
 
1155
            versionedfile_class=KnitVersionedFile)
 
1156
        return KnitRevisionStore(versioned_file_store)
 
1157
 
 
1158
    def _get_text_store(self, transport, control_files):
 
1159
        """See RepositoryFormat._get_text_store()."""
 
1160
        return self._get_versioned_file_store('knits',
 
1161
                                              transport,
 
1162
                                              control_files,
 
1163
                                              versionedfile_class=KnitVersionedFile)
 
1164
 
1088
1165
    def initialize(self, a_bzrdir, shared=False):
1089
1166
        """Create a knit format 1 repository.
1090
1167
 
1102
1179
        empty_weave = sio.getvalue()
1103
1180
 
1104
1181
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1105
 
        dirs = ['revision-store', 'knits']
1106
 
        files = [('inventory.weave', StringIO(empty_weave)), 
 
1182
        dirs = ['revision-store', 'knits', 'control']
 
1183
        files = [('control/inventory.weave', StringIO(empty_weave)), 
1107
1184
                 ]
1108
1185
        utf8_files = [('format', self.get_format_string())]
1109
1186
        
1110
1187
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
 
1188
        repo_transport = a_bzrdir.get_repository_transport(None)
 
1189
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1190
        control_store = self._get_control_store(repo_transport, control_files)
 
1191
        transaction = bzrlib.transactions.PassThroughTransaction()
 
1192
        # trigger a write of the inventory store.
 
1193
        control_store.get_weave_or_empty('inventory', transaction)
 
1194
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
1195
        _revision_store.has_revision_id('A', transaction)
 
1196
        _revision_store.get_signature_file(transaction)
1111
1197
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1112
1198
 
 
1199
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
1200
        """See RepositoryFormat.open().
 
1201
        
 
1202
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
1203
                                    repository at a slightly different url
 
1204
                                    than normal. I.e. during 'upgrade'.
 
1205
        """
 
1206
        if not _found:
 
1207
            format = RepositoryFormat.find_format(a_bzrdir)
 
1208
            assert format.__class__ ==  self.__class__
 
1209
        if _override_transport is not None:
 
1210
            repo_transport = _override_transport
 
1211
        else:
 
1212
            repo_transport = a_bzrdir.get_repository_transport(None)
 
1213
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
 
1214
        text_store = self._get_text_store(repo_transport, control_files)
 
1215
        control_store = self._get_control_store(repo_transport, control_files)
 
1216
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
1217
        return KnitRepository(_format=self,
 
1218
                              a_bzrdir=a_bzrdir,
 
1219
                              control_files=control_files,
 
1220
                              _revision_store=_revision_store,
 
1221
                              control_store=control_store,
 
1222
                              text_store=text_store)
 
1223
 
1113
1224
 
1114
1225
# formats which have no format string are not discoverable
1115
1226
# and not independently creatable, so are not registered.
1122
1233
                   RepositoryFormat6()]
1123
1234
 
1124
1235
 
1125
 
class InterRepository(object):
 
1236
class InterRepository(InterObject):
1126
1237
    """This class represents operations taking place between two repositories.
1127
1238
 
1128
1239
    Its instances have methods like copy_content and fetch, and contain
1133
1244
    operations with another repository - they will always forward to
1134
1245
    InterRepository.get(other).method_name(parameters).
1135
1246
    """
1136
 
    # XXX: FIXME: FUTURE: robertc
1137
 
    # testing of these probably requires a factory in optimiser type, and 
1138
 
    # then a test adapter to test each type thoroughly.
1139
 
    #
1140
1247
 
1141
1248
    _optimisers = set()
1142
1249
    """The available optimised InterRepository types."""
1143
1250
 
1144
 
    def __init__(self, source, target):
1145
 
        """Construct a default InterRepository instance. Please use 'get'.
1146
 
        
1147
 
        Only subclasses of InterRepository should call 
1148
 
        InterRepository.__init__ - clients should call InterRepository.get
1149
 
        instead which will create an optimised InterRepository if possible.
1150
 
        """
1151
 
        self.source = source
1152
 
        self.target = target
1153
 
 
1154
1251
    @needs_write_lock
1155
1252
    def copy_content(self, revision_id=None, basis=None):
1156
1253
        """Make a complete copy of the content in self into destination.
1169
1266
        # grab the basis available data
1170
1267
        if basis is not None:
1171
1268
            self.target.fetch(basis, revision_id=revision_id)
1172
 
        # but dont both fetching if we have the needed data now.
 
1269
        # but dont bother fetching if we have the needed data now.
1173
1270
        if (revision_id not in (None, NULL_REVISION) and 
1174
1271
            self.target.has_revision(revision_id)):
1175
1272
            return
1200
1297
        Returns the copied revision count and the failed revisions in a tuple:
1201
1298
        (copied, failures).
1202
1299
        """
1203
 
        from bzrlib.fetch import RepoFetcher
 
1300
        from bzrlib.fetch import GenericRepoFetcher
1204
1301
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1205
1302
               self.source, self.source._format, self.target, self.target._format)
1206
 
        f = RepoFetcher(to_repository=self.target,
1207
 
                        from_repository=self.source,
1208
 
                        last_revision=revision_id,
1209
 
                        pb=pb)
 
1303
        f = GenericRepoFetcher(to_repository=self.target,
 
1304
                               from_repository=self.source,
 
1305
                               last_revision=revision_id,
 
1306
                               pb=pb)
1210
1307
        return f.count_copied, f.failed_revisions
1211
1308
 
1212
 
    @classmethod
1213
 
    def get(klass, repository_source, repository_target):
1214
 
        """Retrieve a InterRepository worker object for these repositories.
1215
 
 
1216
 
        :param repository_source: the repository to be the 'source' member of
1217
 
                                  the InterRepository instance.
1218
 
        :param repository_target: the repository to be the 'target' member of
1219
 
                                the InterRepository instance.
1220
 
        If an optimised InterRepository worker exists it will be used otherwise
1221
 
        a default InterRepository instance will be created.
1222
 
        """
1223
 
        for provider in klass._optimisers:
1224
 
            if provider.is_compatible(repository_source, repository_target):
1225
 
                return provider(repository_source, repository_target)
1226
 
        return InterRepository(repository_source, repository_target)
1227
 
 
1228
1309
    def lock_read(self):
1229
1310
        """Take out a logical read lock.
1230
1311
 
1263
1344
        # that we've decided we need.
1264
1345
        return [rev_id for rev_id in source_ids if rev_id in result_set]
1265
1346
 
1266
 
    @classmethod
1267
 
    def register_optimiser(klass, optimiser):
1268
 
        """Register an InterRepository optimiser."""
1269
 
        klass._optimisers.add(optimiser)
1270
 
 
1271
1347
    def unlock(self):
1272
1348
        """Release the locks on source and target."""
1273
1349
        try:
1275
1351
        finally:
1276
1352
            self.source.unlock()
1277
1353
 
1278
 
    @classmethod
1279
 
    def unregister_optimiser(klass, optimiser):
1280
 
        """Unregister an InterRepository optimiser."""
1281
 
        klass._optimisers.remove(optimiser)
1282
 
 
1283
1354
 
1284
1355
class InterWeaveRepo(InterRepository):
1285
1356
    """Optimised code paths between Weave based repositories."""
1327
1398
            if self.source.control_files._transport.listable():
1328
1399
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
1329
1400
                try:
1330
 
                    copy_all(self.source.weave_store,
1331
 
                        self.target.weave_store, pb=pb)
 
1401
                    self.target.weave_store.copy_all_ids(
 
1402
                        self.source.weave_store,
 
1403
                        pb=pb,
 
1404
                        from_transaction=self.source.get_transaction(),
 
1405
                        to_transaction=self.target.get_transaction())
1332
1406
                    pb.update('copying inventory', 0, 1)
1333
1407
                    self.target.control_weaves.copy_multi(
1334
 
                        self.source.control_weaves, ['inventory'])
1335
 
                    copy_all(self.source.revision_store,
1336
 
                        self.target.revision_store, pb=pb)
 
1408
                        self.source.control_weaves, ['inventory'],
 
1409
                        from_transaction=self.source.get_transaction(),
 
1410
                        to_transaction=self.target.get_transaction())
 
1411
                    self.target._revision_store.text_store.copy_all_ids(
 
1412
                        self.source._revision_store.text_store,
 
1413
                        pb=pb)
1337
1414
                finally:
1338
1415
                    pb.finished()
1339
1416
            else:
1342
1419
    @needs_write_lock
1343
1420
    def fetch(self, revision_id=None, pb=None):
1344
1421
        """See InterRepository.fetch()."""
1345
 
        from bzrlib.fetch import RepoFetcher
 
1422
        from bzrlib.fetch import GenericRepoFetcher
1346
1423
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1347
1424
               self.source, self.source._format, self.target, self.target._format)
1348
 
        f = RepoFetcher(to_repository=self.target,
1349
 
                        from_repository=self.source,
1350
 
                        last_revision=revision_id,
1351
 
                        pb=pb)
 
1425
        f = GenericRepoFetcher(to_repository=self.target,
 
1426
                               from_repository=self.source,
 
1427
                               last_revision=revision_id,
 
1428
                               pb=pb)
1352
1429
        return f.count_copied, f.failed_revisions
1353
1430
 
1354
1431
    @needs_read_lock
1392
1469
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
1393
1470
 
1394
1471
 
 
1472
class InterKnitRepo(InterRepository):
 
1473
    """Optimised code paths between Knit based repositories."""
 
1474
 
 
1475
    _matching_repo_format = RepositoryFormatKnit1()
 
1476
    """Repository format for testing with."""
 
1477
 
 
1478
    @staticmethod
 
1479
    def is_compatible(source, target):
 
1480
        """Be compatible with known Knit formats.
 
1481
        
 
1482
        We dont test for the stores being of specific types becase that
 
1483
        could lead to confusing results, and there is no need to be 
 
1484
        overly general.
 
1485
        """
 
1486
        try:
 
1487
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
 
1488
                    isinstance(target._format, (RepositoryFormatKnit1)))
 
1489
        except AttributeError:
 
1490
            return False
 
1491
 
 
1492
    @needs_write_lock
 
1493
    def fetch(self, revision_id=None, pb=None):
 
1494
        """See InterRepository.fetch()."""
 
1495
        from bzrlib.fetch import KnitRepoFetcher
 
1496
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
 
1497
               self.source, self.source._format, self.target, self.target._format)
 
1498
        f = KnitRepoFetcher(to_repository=self.target,
 
1499
                            from_repository=self.source,
 
1500
                            last_revision=revision_id,
 
1501
                            pb=pb)
 
1502
        return f.count_copied, f.failed_revisions
 
1503
 
 
1504
    @needs_read_lock
 
1505
    def missing_revision_ids(self, revision_id=None):
 
1506
        """See InterRepository.missing_revision_ids()."""
 
1507
        if revision_id is not None:
 
1508
            source_ids = self.source.get_ancestry(revision_id)
 
1509
            assert source_ids.pop(0) == None
 
1510
        else:
 
1511
            source_ids = self.source._all_possible_ids()
 
1512
        source_ids_set = set(source_ids)
 
1513
        # source_ids is the worst possible case we may need to pull.
 
1514
        # now we want to filter source_ids against what we actually
 
1515
        # have in target, but dont try to check for existence where we know
 
1516
        # we do not have a revision as that would be pointless.
 
1517
        target_ids = set(self.target._all_possible_ids())
 
1518
        possibly_present_revisions = target_ids.intersection(source_ids_set)
 
1519
        actually_present_revisions = set(self.target._eliminate_revisions_not_present(possibly_present_revisions))
 
1520
        required_revisions = source_ids_set.difference(actually_present_revisions)
 
1521
        required_topo_revisions = [rev_id for rev_id in source_ids if rev_id in required_revisions]
 
1522
        if revision_id is not None:
 
1523
            # we used get_ancestry to determine source_ids then we are assured all
 
1524
            # revisions referenced are present as they are installed in topological order.
 
1525
            # and the tip revision was validated by get_ancestry.
 
1526
            return required_topo_revisions
 
1527
        else:
 
1528
            # if we just grabbed the possibly available ids, then 
 
1529
            # we only have an estimate of whats available and need to validate
 
1530
            # that against the revision records.
 
1531
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
 
1532
 
1395
1533
InterRepository.register_optimiser(InterWeaveRepo)
 
1534
InterRepository.register_optimiser(InterKnitRepo)
1396
1535
 
1397
1536
 
1398
1537
class RepositoryTestProviderAdapter(object):