~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-03-08 00:37:41 UTC
  • mfrom: (1594.2.4 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060308003741-08afccbf89005e87
Merge in :
 * Knit repositories use knits
 * Nested progress bar support.
 * Ghost aware graph api.

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
 
27
from bzrlib.graph import Graph
 
28
from bzrlib.inter import InterObject
 
29
from bzrlib.knit import KnitVersionedFile
25
30
from bzrlib.lockable_files import LockableFiles, TransportLock
26
31
from bzrlib.lockdir import LockDir
27
32
from bzrlib.osutils import safe_unicode
28
33
from bzrlib.revision import NULL_REVISION
29
 
import bzrlib.errors as errors
30
 
import bzrlib.gpg as gpg
31
 
from bzrlib.store import copy_all
32
 
from bzrlib.store.weave import WeaveStore
 
34
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
33
35
from bzrlib.store.text import TextStore
34
36
from bzrlib.symbol_versioning import *
35
37
from bzrlib.trace import mutter
36
38
from bzrlib.tree import RevisionTree
 
39
from bzrlib.tsort import topo_sort
37
40
from bzrlib.testament import Testament
38
41
from bzrlib.tree import EmptyTree
39
42
import bzrlib.ui
 
43
from bzrlib.weave import WeaveFile
40
44
import bzrlib.xml5
41
45
 
42
46
 
63
67
        """
64
68
        inv_text = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
65
69
        inv_sha1 = bzrlib.osutils.sha_string(inv_text)
66
 
        self.control_weaves.add_text('inventory', revid,
67
 
                   bzrlib.osutils.split_lines(inv_text), parents,
68
 
                   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))
69
73
        return inv_sha1
70
74
 
71
75
    @needs_write_lock
93
97
            else:
94
98
                # yes, this is not suitable for adding with ghosts.
95
99
                self.add_inventory(rev_id, inv, rev.parent_ids)
96
 
            
97
 
        rev_tmp = StringIO()
98
 
        bzrlib.xml5.serializer_v5.write_revision(rev, rev_tmp)
99
 
        rev_tmp.seek(0)
100
 
        self.revision_store.add(rev_tmp, rev_id)
101
 
        mutter('added revision_id {%s}', rev_id)
 
100
        self._revision_store.add_revision(rev, self.get_transaction())   
102
101
 
103
102
    @needs_read_lock
104
103
    def _all_possible_ids(self):
105
104
        """Return all the possible revisions that we could find."""
106
 
        return self.get_inventory_weave().names()
 
105
        return self.get_inventory_weave().versions()
107
106
 
108
107
    @needs_read_lock
109
108
    def all_revision_ids(self):
113
112
        present: for weaves ghosts may lead to a lack of correctness until
114
113
        the reweave updates the parents list.
115
114
        """
 
115
        if self._revision_store.text_store.listable():
 
116
            return self._revision_store.all_revision_ids(self.get_transaction())
116
117
        result = self._all_possible_ids()
117
118
        return self._eliminate_revisions_not_present(result)
118
119
 
133
134
        """Construct the current default format repository in a_bzrdir."""
134
135
        return RepositoryFormat.get_default_format().initialize(a_bzrdir)
135
136
 
136
 
    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):
137
138
        """instantiate a Repository.
138
139
 
139
140
        :param _format: The format of the repository on disk.
148
149
        # the following are part of the public API for Repository:
149
150
        self.bzrdir = a_bzrdir
150
151
        self.control_files = control_files
151
 
        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
152
160
 
153
161
    def lock_write(self):
154
162
        self.control_files.lock_write()
219
227
        self.copy_content_into(result, revision_id, basis)
220
228
        return result
221
229
 
 
230
    @needs_read_lock
222
231
    def has_revision(self, revision_id):
223
 
        """True if this branch has a copy of the revision.
224
 
 
225
 
        This does not necessarily imply the revision is merge
226
 
        or on the mainline."""
227
 
        return (revision_id is None
228
 
                or self.revision_store.has_id(revision_id))
229
 
 
230
 
    @needs_read_lock
231
 
    def get_revision_xml_file(self, revision_id):
232
 
        """Return XML file object for revision object."""
233
 
        if not revision_id or not isinstance(revision_id, basestring):
234
 
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
235
 
        try:
236
 
            return self.revision_store.get(revision_id)
237
 
        except (IndexError, KeyError):
238
 
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
239
 
 
240
 
    @needs_read_lock
241
 
    def get_revision_xml(self, revision_id):
242
 
        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())
243
235
 
244
236
    @needs_read_lock
245
237
    def get_revision_reconcile(self, revision_id):
250
242
        be used by reconcile, or reconcile-alike commands that are correcting
251
243
        or testing the revision graph.
252
244
        """
253
 
        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())
254
249
 
255
 
        try:
256
 
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
257
 
        except SyntaxError, e:
258
 
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
259
 
                                         [revision_id,
260
 
                                          str(e)])
261
 
            
262
 
        assert r.revision_id == revision_id
263
 
        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()
264
258
 
265
259
    @needs_read_lock
266
260
    def get_revision(self, revision_id):
284
278
        consistency and is only applicable to inventory-weave-for-ancestry
285
279
        using repository formats & fetchers.
286
280
        """
287
 
        weave_parents = inventory.parent_names(revision.revision_id)
288
 
        weave_names = inventory.names()
 
281
        weave_parents = inventory.get_parents(revision.revision_id)
 
282
        weave_names = inventory.versions()
289
283
        for parent_id in revision.parent_ids:
290
284
            if parent_id in weave_names:
291
285
                # this parent must not be a ghost.
293
287
                    # but it is a ghost
294
288
                    raise errors.CorruptRepository(self)
295
289
 
296
 
    @needs_read_lock
297
 
    def get_revision_sha1(self, revision_id):
298
 
        """Hash the stored value of a revision, and return it."""
299
 
        # In the future, revision entries will be signed. At that
300
 
        # point, it is probably best *not* to include the signature
301
 
        # in the revision hash. Because that lets you re-sign
302
 
        # the revision, (add signatures/remove signatures) and still
303
 
        # have all hash pointers stay consistent.
304
 
        # But for now, just hash the contents.
305
 
        return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
306
 
 
307
290
    @needs_write_lock
308
291
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
309
 
        self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)), 
310
 
                                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())
311
296
 
312
297
    def fileid_involved_between_revs(self, from_revid, to_revid):
313
298
        """Find file_id(s) which are involved in the changes between revisions.
326
311
        #       won't be fixed, because AD never saw revision C
327
312
        #       to cause a conflict which would force a reweave.
328
313
        w = self.get_inventory_weave()
329
 
        from_set = set(w.inclusions([w.lookup(from_revid)]))
330
 
        to_set = set(w.inclusions([w.lookup(to_revid)]))
331
 
        included = to_set.difference(from_set)
332
 
        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)
333
317
        return self._fileid_involved_by_set(changed)
334
318
 
335
319
    def fileid_involved(self, last_revid=None):
339
323
        """
340
324
        w = self.get_inventory_weave()
341
325
        if not last_revid:
342
 
            changed = set(w._names)
 
326
            changed = set(w.versions())
343
327
        else:
344
 
            included = w.inclusions([w.lookup(last_revid)])
345
 
            changed = map(w.idx_to_name, included)
 
328
            changed = set(w.get_ancestry(last_revid))
346
329
        return self._fileid_involved_by_set(changed)
347
330
 
348
331
    def fileid_involved_by_set(self, changes):
377
360
 
378
361
        w = self.get_inventory_weave()
379
362
        file_ids = set()
380
 
        for line in w._weave:
381
 
 
382
 
            # it is ugly, but it is due to the weave structure
383
 
            if not isinstance(line, basestring): continue
384
 
 
 
363
 
 
364
        for lineno, insert, deletes, line in w.walk(changes):
385
365
            start = line.find('file_id="')+9
386
366
            if start < 9: continue
387
367
            end = line.find('"', start)
418
398
        try:
419
399
            assert isinstance(revision_id, basestring), type(revision_id)
420
400
            iw = self.get_inventory_weave()
421
 
            return iw.get_text(iw.lookup(revision_id))
 
401
            return iw.get_text(revision_id)
422
402
        except IndexError:
423
403
            raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
424
404
 
435
415
        :return: a dictionary of revision_id->revision_parents_list.
436
416
        """
437
417
        weave = self.get_inventory_weave()
438
 
        all_revisions = self._eliminate_revisions_not_present(weave.names())
439
 
        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 
440
420
                             node in all_revisions])
441
421
        if revision_id is None:
442
422
            return entire_graph
455
435
            return result
456
436
 
457
437
    @needs_read_lock
 
438
    def get_revision_graph_with_ghosts(self, revision_ids=None):
 
439
        """Return a graph of the revisions with ghosts marked as applicable.
 
440
 
 
441
        :param revision_ids: an iterable of revisions to graph or None for all.
 
442
        :return: a Graph object with the graph reachable from revision_ids.
 
443
        """
 
444
        result = Graph()
 
445
        if not revision_ids:
 
446
            pending = set(self.all_revision_ids())
 
447
            required = set([])
 
448
        else:
 
449
            pending = set(revision_ids)
 
450
            required = set(revision_ids)
 
451
        done = set([])
 
452
        while len(pending):
 
453
            revision_id = pending.pop()
 
454
            try:
 
455
                rev = self.get_revision(revision_id)
 
456
            except errors.NoSuchRevision:
 
457
                if revision_id in required:
 
458
                    raise
 
459
                # a ghost
 
460
                result.add_ghost(revision_id)
 
461
                continue
 
462
            for parent_id in rev.parent_ids:
 
463
                # is this queued or done ?
 
464
                if (parent_id not in pending and
 
465
                    parent_id not in done):
 
466
                    # no, queue it.
 
467
                    pending.add(parent_id)
 
468
            result.add_node(revision_id, rev.parent_ids)
 
469
            done.add(result)
 
470
        return result
 
471
 
 
472
    @needs_read_lock
458
473
    def get_revision_inventory(self, revision_id):
459
474
        """Return inventory of a past revision."""
460
475
        # TODO: Unify this with get_inventory()
501
516
        if not self.has_revision(revision_id):
502
517
            raise errors.NoSuchRevision(self, revision_id)
503
518
        w = self.get_inventory_weave()
504
 
        return [None] + map(w.idx_to_name,
505
 
                            w.inclusions([w.lookup(revision_id)]))
 
519
        return [None] + w.get_ancestry(revision_id)
506
520
 
507
521
    @needs_read_lock
508
522
    def print_file(self, file, revision_id):
572
586
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
573
587
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
574
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
 
575
601
 
576
602
class AllInOneRepository(Repository):
577
603
    """Legacy support - the repository behaviour for all-in-one branches."""
578
604
 
579
 
    def __init__(self, _format, a_bzrdir, revision_store):
 
605
    def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
580
606
        # we reuse one control files instance.
581
607
        dir_mode = a_bzrdir._control_files._dir_mode
582
608
        file_mode = a_bzrdir._control_files._file_mode
614
640
        # not broken out yet because the controlweaves|inventory_store
615
641
        # and text_store | weave_store bits are still different.
616
642
        if isinstance(_format, RepositoryFormat4):
 
643
            # cannot remove these - there is still no consistent api 
 
644
            # which allows access to this old info.
617
645
            self.inventory_store = get_store('inventory-store')
618
 
            self.text_store = get_store('text-store')
619
 
        elif isinstance(_format, RepositoryFormat5):
620
 
            self.control_weaves = get_weave('')
621
 
            self.weave_store = get_weave('weaves')
622
 
        elif isinstance(_format, RepositoryFormat6):
623
 
            self.control_weaves = get_weave('')
624
 
            self.weave_store = get_weave('weaves', prefixed=True)
625
 
        else:
626
 
            raise errors.BzrError('unreachable code: unexpected repository'
627
 
                                  ' format.')
628
 
        revision_store.register_suffix('sig')
629
 
        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)
630
648
 
631
649
 
632
650
class MetaDirRepository(Repository):
633
651
    """Repositories in the new meta-dir layout."""
634
652
 
635
 
    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):
636
654
        super(MetaDirRepository, self).__init__(_format,
637
655
                                                a_bzrdir,
638
656
                                                control_files,
639
 
                                                revision_store)
 
657
                                                _revision_store,
 
658
                                                control_store,
 
659
                                                text_store)
640
660
 
641
661
        dir_mode = self.control_files._dir_mode
642
662
        file_mode = self.control_files._file_mode
655
675
                ws.enable_cache = True
656
676
            return ws
657
677
 
658
 
        if isinstance(self._format, RepositoryFormat7):
659
 
            self.control_weaves = get_weave('')
660
 
            self.weave_store = get_weave('weaves', prefixed=True)
661
 
        elif isinstance(self._format, RepositoryFormatKnit1):
662
 
            self.control_weaves = get_weave('')
663
 
            self.weave_store = get_weave('knits', prefixed=True)
664
 
        else:
665
 
            raise errors.BzrError('unreachable code: unexpected repository'
666
 
                                  ' 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())
667
686
 
668
687
 
669
688
class RepositoryFormat(object):
708
727
        except KeyError:
709
728
            raise errors.UnknownFormatError(format_string)
710
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
    
711
734
    @classmethod
712
735
    def get_default_format(klass):
713
736
        """Return the current default format."""
725
748
        """Return the revision store object for this a_bzrdir."""
726
749
        raise NotImplementedError(self._get_revision_store)
727
750
 
728
 
    def _get_rev_store(self,
729
 
                   transport,
730
 
                   control_files,
731
 
                   name,
732
 
                   compressed=True,
733
 
                   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):
734
758
        """Common logic for getting a revision store for a repository.
735
759
        
736
 
        see self._get_revision_store for the method to 
 
760
        see self._get_revision_store for the subclass-overridable method to 
737
761
        get the store for a repository.
738
762
        """
739
 
        if name:
740
 
            name = safe_unicode(name)
741
 
        else:
742
 
            name = ''
743
 
        dir_mode = control_files._dir_mode
744
 
        file_mode = control_files._file_mode
745
 
        revision_store =TextStore(transport.clone(name),
746
 
                                  prefixed=prefixed,
747
 
                                  compressed=compressed,
748
 
                                  dir_mode=dir_mode,
749
 
                                  file_mode=file_mode)
750
 
        revision_store.register_suffix('sig')
751
 
        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)
752
787
 
753
788
    def initialize(self, a_bzrdir, shared=False):
754
789
        """Initialize a repository of this format in a_bzrdir.
834
869
            control_files.unlock()
835
870
        return self.open(a_bzrdir, _found=True)
836
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
 
837
883
    def open(self, a_bzrdir, _found=False):
838
884
        """See RepositoryFormat.open()."""
839
885
        if not _found:
842
888
 
843
889
        repo_transport = a_bzrdir.get_repository_transport(None)
844
890
        control_files = a_bzrdir._control_files
845
 
        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)
846
894
        return AllInOneRepository(_format=self,
847
895
                                  a_bzrdir=a_bzrdir,
848
 
                                  revision_store=revision_store)
 
896
                                  _revision_store=_revision_store,
 
897
                                  control_store=control_store,
 
898
                                  text_store=text_store)
849
899
 
850
900
 
851
901
class RepositoryFormat4(PreSplitOutRepositoryFormat):
877
927
        """
878
928
        return False
879
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
    
880
937
    def _get_revision_store(self, repo_transport, control_files):
881
938
        """See RepositoryFormat._get_revision_store()."""
882
 
        return self._get_rev_store(repo_transport,
883
 
                                   control_files,
884
 
                                   '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()."""
885
947
 
886
948
 
887
949
class RepositoryFormat5(PreSplitOutRepositoryFormat):
900
962
    def _get_revision_store(self, repo_transport, control_files):
901
963
        """See RepositoryFormat._get_revision_store()."""
902
964
        """Return the revision store object for this a_bzrdir."""
903
 
        return self._get_rev_store(repo_transport,
904
 
                                   control_files,
905
 
                                   'revision-store',
906
 
                                   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)
907
973
 
908
974
 
909
975
class RepositoryFormat6(PreSplitOutRepositoryFormat):
921
987
 
922
988
    def _get_revision_store(self, repo_transport, control_files):
923
989
        """See RepositoryFormat._get_revision_store()."""
924
 
        return self._get_rev_store(repo_transport,
925
 
                                   control_files,
926
 
                                   'revision-store',
927
 
                                   compressed=False,
928
 
                                   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)
929
999
 
930
1000
 
931
1001
class MetaDirRepositoryFormat(RepositoryFormat):
944
1014
        control_files.create_lock()
945
1015
        return control_files
946
1016
 
947
 
    def _get_revision_store(self, repo_transport, control_files):
948
 
        """See RepositoryFormat._get_revision_store()."""
949
 
        return self._get_rev_store(repo_transport,
950
 
                                   control_files,
951
 
                                   'revision-store',
952
 
                                   compressed=False,
953
 
                                   prefixed=True,
954
 
                                   )
955
 
 
956
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
957
 
        """See RepositoryFormat.open().
958
 
        
959
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
960
 
                                    repository at a slightly different url
961
 
                                    than normal. I.e. during 'upgrade'.
962
 
        """
963
 
        if not _found:
964
 
            format = RepositoryFormat.find_format(a_bzrdir)
965
 
            assert format.__class__ ==  self.__class__
966
 
        if _override_transport is not None:
967
 
            repo_transport = _override_transport
968
 
        else:
969
 
            repo_transport = a_bzrdir.get_repository_transport(None)
970
 
        control_files = LockableFiles(repo_transport, 'lock', LockDir)
971
 
        revision_store = self._get_revision_store(repo_transport, control_files)
972
 
        return MetaDirRepository(_format=self,
973
 
                                 a_bzrdir=a_bzrdir,
974
 
                                 control_files=control_files,
975
 
                                 revision_store=revision_store)
976
 
 
977
1017
    def _upload_blank_content(self, a_bzrdir, dirs, files, utf8_files, shared):
978
1018
        """Upload the initial blank content."""
979
1019
        control_files = self._create_control_files(a_bzrdir)
1003
1043
     - an optional 'no-working-trees' flag
1004
1044
    """
1005
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
 
1006
1053
    def get_format_string(self):
1007
1054
        """See RepositoryFormat.get_format_string()."""
1008
1055
        return "Bazaar-NG Repository format 7"
1009
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
 
1010
1072
    def initialize(self, a_bzrdir, shared=False):
1011
1073
        """Create a weave repository.
1012
1074
 
1030
1092
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1031
1093
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1032
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
 
1033
1120
 
1034
1121
class RepositoryFormatKnit1(MetaDirRepositoryFormat):
1035
1122
    """Bzr repository knit format 1.
1045
1132
     - a LockDir lock
1046
1133
    """
1047
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
 
1048
1143
    def get_format_string(self):
1049
1144
        """See RepositoryFormat.get_format_string()."""
1050
1145
        return "Bazaar-NG Knit Repository Format 1"
1051
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
 
1052
1165
    def initialize(self, a_bzrdir, shared=False):
1053
1166
        """Create a knit format 1 repository.
1054
1167
 
1066
1179
        empty_weave = sio.getvalue()
1067
1180
 
1068
1181
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1069
 
        dirs = ['revision-store', 'knits']
1070
 
        files = [('inventory.weave', StringIO(empty_weave)), 
 
1182
        dirs = ['revision-store', 'knits', 'control']
 
1183
        files = [('control/inventory.weave', StringIO(empty_weave)), 
1071
1184
                 ]
1072
1185
        utf8_files = [('format', self.get_format_string())]
1073
1186
        
1074
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)
1075
1197
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1076
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
 
1077
1224
 
1078
1225
# formats which have no format string are not discoverable
1079
1226
# and not independently creatable, so are not registered.
1086
1233
                   RepositoryFormat6()]
1087
1234
 
1088
1235
 
1089
 
class InterRepository(object):
 
1236
class InterRepository(InterObject):
1090
1237
    """This class represents operations taking place between two repositories.
1091
1238
 
1092
1239
    Its instances have methods like copy_content and fetch, and contain
1097
1244
    operations with another repository - they will always forward to
1098
1245
    InterRepository.get(other).method_name(parameters).
1099
1246
    """
1100
 
    # XXX: FIXME: FUTURE: robertc
1101
 
    # testing of these probably requires a factory in optimiser type, and 
1102
 
    # then a test adapter to test each type thoroughly.
1103
 
    #
1104
1247
 
1105
1248
    _optimisers = set()
1106
1249
    """The available optimised InterRepository types."""
1107
1250
 
1108
 
    def __init__(self, source, target):
1109
 
        """Construct a default InterRepository instance. Please use 'get'.
1110
 
        
1111
 
        Only subclasses of InterRepository should call 
1112
 
        InterRepository.__init__ - clients should call InterRepository.get
1113
 
        instead which will create an optimised InterRepository if possible.
1114
 
        """
1115
 
        self.source = source
1116
 
        self.target = target
1117
 
 
1118
1251
    @needs_write_lock
1119
1252
    def copy_content(self, revision_id=None, basis=None):
1120
1253
        """Make a complete copy of the content in self into destination.
1133
1266
        # grab the basis available data
1134
1267
        if basis is not None:
1135
1268
            self.target.fetch(basis, revision_id=revision_id)
1136
 
        # but dont both fetching if we have the needed data now.
 
1269
        # but dont bother fetching if we have the needed data now.
1137
1270
        if (revision_id not in (None, NULL_REVISION) and 
1138
1271
            self.target.has_revision(revision_id)):
1139
1272
            return
1164
1297
        Returns the copied revision count and the failed revisions in a tuple:
1165
1298
        (copied, failures).
1166
1299
        """
1167
 
        from bzrlib.fetch import RepoFetcher
 
1300
        from bzrlib.fetch import GenericRepoFetcher
1168
1301
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1169
1302
               self.source, self.source._format, self.target, self.target._format)
1170
 
        f = RepoFetcher(to_repository=self.target,
1171
 
                        from_repository=self.source,
1172
 
                        last_revision=revision_id,
1173
 
                        pb=pb)
 
1303
        f = GenericRepoFetcher(to_repository=self.target,
 
1304
                               from_repository=self.source,
 
1305
                               last_revision=revision_id,
 
1306
                               pb=pb)
1174
1307
        return f.count_copied, f.failed_revisions
1175
1308
 
1176
 
    @classmethod
1177
 
    def get(klass, repository_source, repository_target):
1178
 
        """Retrieve a InterRepository worker object for these repositories.
1179
 
 
1180
 
        :param repository_source: the repository to be the 'source' member of
1181
 
                                  the InterRepository instance.
1182
 
        :param repository_target: the repository to be the 'target' member of
1183
 
                                the InterRepository instance.
1184
 
        If an optimised InterRepository worker exists it will be used otherwise
1185
 
        a default InterRepository instance will be created.
1186
 
        """
1187
 
        for provider in klass._optimisers:
1188
 
            if provider.is_compatible(repository_source, repository_target):
1189
 
                return provider(repository_source, repository_target)
1190
 
        return InterRepository(repository_source, repository_target)
1191
 
 
1192
1309
    def lock_read(self):
1193
1310
        """Take out a logical read lock.
1194
1311
 
1227
1344
        # that we've decided we need.
1228
1345
        return [rev_id for rev_id in source_ids if rev_id in result_set]
1229
1346
 
1230
 
    @classmethod
1231
 
    def register_optimiser(klass, optimiser):
1232
 
        """Register an InterRepository optimiser."""
1233
 
        klass._optimisers.add(optimiser)
1234
 
 
1235
1347
    def unlock(self):
1236
1348
        """Release the locks on source and target."""
1237
1349
        try:
1239
1351
        finally:
1240
1352
            self.source.unlock()
1241
1353
 
1242
 
    @classmethod
1243
 
    def unregister_optimiser(klass, optimiser):
1244
 
        """Unregister an InterRepository optimiser."""
1245
 
        klass._optimisers.remove(optimiser)
1246
 
 
1247
1354
 
1248
1355
class InterWeaveRepo(InterRepository):
1249
1356
    """Optimised code paths between Weave based repositories."""
1289
1396
                pass
1290
1397
            # FIXME do not peek!
1291
1398
            if self.source.control_files._transport.listable():
1292
 
                pb = bzrlib.ui.ui_factory.progress_bar()
1293
 
                copy_all(self.source.weave_store,
1294
 
                    self.target.weave_store, pb=pb)
1295
 
                pb.update('copying inventory', 0, 1)
1296
 
                self.target.control_weaves.copy_multi(
1297
 
                    self.source.control_weaves, ['inventory'])
1298
 
                copy_all(self.source.revision_store,
1299
 
                    self.target.revision_store, pb=pb)
 
1399
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1400
                try:
 
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())
 
1406
                    pb.update('copying inventory', 0, 1)
 
1407
                    self.target.control_weaves.copy_multi(
 
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)
 
1414
                finally:
 
1415
                    pb.finished()
1300
1416
            else:
1301
1417
                self.target.fetch(self.source, revision_id=revision_id)
1302
1418
 
1303
1419
    @needs_write_lock
1304
1420
    def fetch(self, revision_id=None, pb=None):
1305
1421
        """See InterRepository.fetch()."""
1306
 
        from bzrlib.fetch import RepoFetcher
 
1422
        from bzrlib.fetch import GenericRepoFetcher
1307
1423
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1308
1424
               self.source, self.source._format, self.target, self.target._format)
1309
 
        f = RepoFetcher(to_repository=self.target,
1310
 
                        from_repository=self.source,
1311
 
                        last_revision=revision_id,
1312
 
                        pb=pb)
 
1425
        f = GenericRepoFetcher(to_repository=self.target,
 
1426
                               from_repository=self.source,
 
1427
                               last_revision=revision_id,
 
1428
                               pb=pb)
1313
1429
        return f.count_copied, f.failed_revisions
1314
1430
 
1315
1431
    @needs_read_lock
1353
1469
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
1354
1470
 
1355
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
 
1356
1533
InterRepository.register_optimiser(InterWeaveRepo)
 
1534
InterRepository.register_optimiser(InterKnitRepo)
1357
1535
 
1358
1536
 
1359
1537
class RepositoryTestProviderAdapter(object):