~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

[merge] bzr.dev 2294

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
from bzrlib.lazy_import import lazy_import
20
20
lazy_import(globals(), """
21
 
from binascii import hexlify
22
 
from copy import deepcopy
23
21
import re
24
22
import time
25
23
import unittest
27
25
from bzrlib import (
28
26
    bzrdir,
29
27
    check,
30
 
    delta,
31
28
    errors,
32
29
    generate_ids,
33
30
    gpg,
34
31
    graph,
35
 
    knit,
36
32
    lazy_regex,
37
33
    lockable_files,
38
34
    lockdir,
42
38
    symbol_versioning,
43
39
    transactions,
44
40
    ui,
45
 
    weave,
46
 
    weavefile,
47
 
    xml5,
48
 
    xml6,
49
 
    )
50
 
from bzrlib.osutils import (
51
 
    rand_bytes,
52
 
    compact_date, 
53
 
    local_time_offset,
54
41
    )
55
42
from bzrlib.revisiontree import RevisionTree
56
43
from bzrlib.store.versioned import VersionedFileStore
57
44
from bzrlib.store.text import TextStore
58
45
from bzrlib.testament import Testament
 
46
 
59
47
""")
60
48
 
61
49
from bzrlib.decorators import needs_read_lock, needs_write_lock
90
78
        )
91
79
 
92
80
    @needs_write_lock
93
 
    def add_inventory(self, revid, inv, parents):
94
 
        """Add the inventory inv to the repository as revid.
 
81
    def add_inventory(self, revision_id, inv, parents):
 
82
        """Add the inventory inv to the repository as revision_id.
95
83
        
96
 
        :param parents: The revision ids of the parents that revid
 
84
        :param parents: The revision ids of the parents that revision_id
97
85
                        is known to have and are in the repository already.
98
86
 
99
87
        returns the sha1 of the serialized inventory.
100
88
        """
101
 
        _mod_revision.check_not_reserved_id(revid)
102
 
        assert inv.revision_id is None or inv.revision_id == revid, \
 
89
        revision_id = osutils.safe_revision_id(revision_id)
 
90
        _mod_revision.check_not_reserved_id(revision_id)
 
91
        assert inv.revision_id is None or inv.revision_id == revision_id, \
103
92
            "Mismatch between inventory revision" \
104
 
            " id and insertion revid (%r, %r)" % (inv.revision_id, revid)
 
93
            " id and insertion revid (%r, %r)" % (inv.revision_id, revision_id)
105
94
        assert inv.root is not None
106
95
        inv_text = self.serialise_inventory(inv)
107
96
        inv_sha1 = osutils.sha_string(inv_text)
108
97
        inv_vf = self.control_weaves.get_weave('inventory',
109
98
                                               self.get_transaction())
110
 
        self._inventory_add_lines(inv_vf, revid, parents, osutils.split_lines(inv_text))
 
99
        self._inventory_add_lines(inv_vf, revision_id, parents,
 
100
                                  osutils.split_lines(inv_text))
111
101
        return inv_sha1
112
102
 
113
 
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
 
103
    def _inventory_add_lines(self, inv_vf, revision_id, parents, lines):
114
104
        final_parents = []
115
105
        for parent in parents:
116
106
            if parent in inv_vf:
117
107
                final_parents.append(parent)
118
108
 
119
 
        inv_vf.add_lines(revid, final_parents, lines)
 
109
        inv_vf.add_lines(revision_id, final_parents, lines)
120
110
 
121
111
    @needs_write_lock
122
 
    def add_revision(self, rev_id, rev, inv=None, config=None):
123
 
        """Add rev to the revision store as rev_id.
 
112
    def add_revision(self, revision_id, rev, inv=None, config=None):
 
113
        """Add rev to the revision store as revision_id.
124
114
 
125
 
        :param rev_id: the revision id to use.
 
115
        :param revision_id: the revision id to use.
126
116
        :param rev: The revision object.
127
117
        :param inv: The inventory for the revision. if None, it will be looked
128
118
                    up in the inventory storer
130
120
                       If supplied its signature_needed method will be used
131
121
                       to determine if a signature should be made.
132
122
        """
133
 
        _mod_revision.check_not_reserved_id(rev_id)
 
123
        revision_id = osutils.safe_revision_id(revision_id)
 
124
        # TODO: jam 20070210 Shouldn't we check rev.revision_id and
 
125
        #       rev.parent_ids?
 
126
        _mod_revision.check_not_reserved_id(revision_id)
134
127
        if config is not None and config.signature_needed():
135
128
            if inv is None:
136
 
                inv = self.get_inventory(rev_id)
 
129
                inv = self.get_inventory(revision_id)
137
130
            plaintext = Testament(rev, inv).as_short_text()
138
131
            self.store_revision_signature(
139
 
                gpg.GPGStrategy(config), plaintext, rev_id)
140
 
        if not rev_id in self.get_inventory_weave():
 
132
                gpg.GPGStrategy(config), plaintext, revision_id)
 
133
        if not revision_id in self.get_inventory_weave():
141
134
            if inv is None:
142
 
                raise errors.WeaveRevisionNotPresent(rev_id,
 
135
                raise errors.WeaveRevisionNotPresent(revision_id,
143
136
                                                     self.get_inventory_weave())
144
137
            else:
145
138
                # yes, this is not suitable for adding with ghosts.
146
 
                self.add_inventory(rev_id, inv, rev.parent_ids)
 
139
                self.add_inventory(revision_id, inv, rev.parent_ids)
147
140
        self._revision_store.add_revision(rev, self.get_transaction())
148
141
 
149
142
    @needs_read_lock
171
164
        if self._revision_store.text_store.listable():
172
165
            return self._revision_store.all_revision_ids(self.get_transaction())
173
166
        result = self._all_possible_ids()
 
167
        # TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
 
168
        #       ids. (It should, since _revision_store's API should change to
 
169
        #       return utf8 revision_ids)
174
170
        return self._eliminate_revisions_not_present(result)
175
171
 
176
172
    def break_lock(self):
224
220
        # TODO: make sure to construct the right store classes, etc, depending
225
221
        # on whether escaping is required.
226
222
        self._warn_if_deprecated()
227
 
        self._serializer = xml5.serializer_v5
228
223
 
229
224
    def __repr__(self):
230
225
        return '%s(%r)' % (self.__class__.__name__, 
243
238
        return self.control_files.get_physical_lock_status()
244
239
 
245
240
    @needs_read_lock
 
241
    def gather_stats(self, revid=None, committers=None):
 
242
        """Gather statistics from a revision id.
 
243
 
 
244
        :param revid: The revision id to gather statistics from, if None, then
 
245
            no revision specific statistics are gathered.
 
246
        :param committers: Optional parameter controlling whether to grab
 
247
            a count of committers from the revision specific statistics.
 
248
        :return: A dictionary of statistics. Currently this contains:
 
249
            committers: The number of committers if requested.
 
250
            firstrev: A tuple with timestamp, timezone for the penultimate left
 
251
                most ancestor of revid, if revid is not the NULL_REVISION.
 
252
            latestrev: A tuple with timestamp, timezone for revid, if revid is
 
253
                not the NULL_REVISION.
 
254
            revisions: The total revision count in the repository.
 
255
            size: An estimate disk size of the repository in bytes.
 
256
        """
 
257
        result = {}
 
258
        if revid and committers:
 
259
            result['committers'] = 0
 
260
        if revid and revid != _mod_revision.NULL_REVISION:
 
261
            if committers:
 
262
                all_committers = set()
 
263
            revisions = self.get_ancestry(revid)
 
264
            # pop the leading None
 
265
            revisions.pop(0)
 
266
            first_revision = None
 
267
            if not committers:
 
268
                # ignore the revisions in the middle - just grab first and last
 
269
                revisions = revisions[0], revisions[-1]
 
270
            for revision in self.get_revisions(revisions):
 
271
                if not first_revision:
 
272
                    first_revision = revision
 
273
                if committers:
 
274
                    all_committers.add(revision.committer)
 
275
            last_revision = revision
 
276
            if committers:
 
277
                result['committers'] = len(all_committers)
 
278
            result['firstrev'] = (first_revision.timestamp,
 
279
                first_revision.timezone)
 
280
            result['latestrev'] = (last_revision.timestamp,
 
281
                last_revision.timezone)
 
282
 
 
283
        # now gather global repository information
 
284
        if self.bzrdir.root_transport.listable():
 
285
            c, t = self._revision_store.total_size(self.get_transaction())
 
286
            result['revisions'] = c
 
287
            result['size'] = t
 
288
        return result
 
289
 
 
290
    @needs_read_lock
246
291
    def missing_revision_ids(self, other, revision_id=None):
247
292
        """Return the revision ids that other has that this does not.
248
293
        
250
295
 
251
296
        revision_id: only return revision ids included by revision_id.
252
297
        """
 
298
        revision_id = osutils.safe_revision_id(revision_id)
253
299
        return InterRepository.get(other, self).missing_revision_ids(revision_id)
254
300
 
255
301
    @staticmethod
268
314
        This is a destructive operation! Do not use it on existing 
269
315
        repositories.
270
316
        """
 
317
        revision_id = osutils.safe_revision_id(revision_id)
271
318
        return InterRepository.get(self, destination).copy_content(revision_id, basis)
272
319
 
273
320
    def fetch(self, source, revision_id=None, pb=None):
275
322
 
276
323
        If revision_id is None all content is copied.
277
324
        """
 
325
        revision_id = osutils.safe_revision_id(revision_id)
278
326
        return InterRepository.get(source, self).fetch(revision_id=revision_id,
279
327
                                                       pb=pb)
280
328
 
292
340
        :param revprops: Optional dictionary of revision properties.
293
341
        :param revision_id: Optional revision id.
294
342
        """
 
343
        revision_id = osutils.safe_revision_id(revision_id)
295
344
        return _CommitBuilder(self, parents, config, timestamp, timezone,
296
345
                              committer, revprops, revision_id)
297
346
 
304
353
 
305
354
        Currently no check is made that the format of this repository and
306
355
        the bzrdir format are compatible. FIXME RBC 20060201.
 
356
 
 
357
        :return: The newly created destination repository.
307
358
        """
308
359
        if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
309
360
            # use target default format.
310
 
            result = a_bzrdir.create_repository()
311
 
        # FIXME RBC 20060209 split out the repository type to avoid this check ?
312
 
        elif isinstance(a_bzrdir._format,
313
 
                      (bzrdir.BzrDirFormat4,
314
 
                       bzrdir.BzrDirFormat5,
315
 
                       bzrdir.BzrDirFormat6)):
316
 
            result = a_bzrdir.open_repository()
 
361
            dest_repo = a_bzrdir.create_repository()
317
362
        else:
318
 
            result = self._format.initialize(a_bzrdir, shared=self.is_shared())
319
 
        self.copy_content_into(result, revision_id, basis)
320
 
        return result
 
363
            # Most control formats need the repository to be specifically
 
364
            # created, but on some old all-in-one formats it's not needed
 
365
            try:
 
366
                dest_repo = self._format.initialize(a_bzrdir, shared=self.is_shared())
 
367
            except errors.UninitializableFormat:
 
368
                dest_repo = a_bzrdir.open_repository()
 
369
        self.copy_content_into(dest_repo, revision_id, basis)
 
370
        return dest_repo
321
371
 
322
372
    @needs_read_lock
323
373
    def has_revision(self, revision_id):
324
374
        """True if this repository has a copy of the revision."""
 
375
        revision_id = osutils.safe_revision_id(revision_id)
325
376
        return self._revision_store.has_revision_id(revision_id,
326
377
                                                    self.get_transaction())
327
378
 
337
388
        if not revision_id or not isinstance(revision_id, basestring):
338
389
            raise errors.InvalidRevisionId(revision_id=revision_id,
339
390
                                           branch=self)
340
 
        return self._revision_store.get_revisions([revision_id],
341
 
                                                  self.get_transaction())[0]
 
391
        return self.get_revisions([revision_id])[0]
 
392
 
342
393
    @needs_read_lock
343
394
    def get_revisions(self, revision_ids):
344
 
        return self._revision_store.get_revisions(revision_ids,
 
395
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
 
396
        revs = self._revision_store.get_revisions(revision_ids,
345
397
                                                  self.get_transaction())
 
398
        for rev in revs:
 
399
            assert not isinstance(rev.revision_id, unicode)
 
400
            for parent_id in rev.parent_ids:
 
401
                assert not isinstance(parent_id, unicode)
 
402
        return revs
346
403
 
347
404
    @needs_read_lock
348
405
    def get_revision_xml(self, revision_id):
349
 
        rev = self.get_revision(revision_id) 
 
406
        # TODO: jam 20070210 This shouldn't be necessary since get_revision
 
407
        #       would have already do it.
 
408
        # TODO: jam 20070210 Just use _serializer.write_revision_to_string()
 
409
        revision_id = osutils.safe_revision_id(revision_id)
 
410
        rev = self.get_revision(revision_id)
350
411
        rev_tmp = StringIO()
351
412
        # the current serializer..
352
413
        self._revision_store._serializer.write_revision(rev, rev_tmp)
356
417
    @needs_read_lock
357
418
    def get_revision(self, revision_id):
358
419
        """Return the Revision object for a named revision"""
 
420
        # TODO: jam 20070210 get_revision_reconcile should do this for us
 
421
        revision_id = osutils.safe_revision_id(revision_id)
359
422
        r = self.get_revision_reconcile(revision_id)
360
423
        # weave corruption can lead to absent revision markers that should be
361
424
        # present.
417
480
 
418
481
    @needs_write_lock
419
482
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
 
483
        revision_id = osutils.safe_revision_id(revision_id)
420
484
        signature = gpg_strategy.sign(plaintext)
421
485
        self._revision_store.add_revision_signature_text(revision_id,
422
486
                                                         signature,
433
497
        assert self._serializer.support_altered_by_hack, \
434
498
            ("fileids_altered_by_revision_ids only supported for branches " 
435
499
             "which store inventory as unnested xml, not on %r" % self)
436
 
        selected_revision_ids = set(revision_ids)
 
500
        selected_revision_ids = set(osutils.safe_revision_id(r)
 
501
                                    for r in revision_ids)
437
502
        w = self.get_inventory_weave()
438
503
        result = {}
439
504
 
503
568
    @needs_read_lock
504
569
    def get_inventory(self, revision_id):
505
570
        """Get Inventory object by hash."""
 
571
        # TODO: jam 20070210 Technically we don't need to sanitize, since all
 
572
        #       called functions must sanitize.
 
573
        revision_id = osutils.safe_revision_id(revision_id)
506
574
        return self.deserialise_inventory(
507
575
            revision_id, self.get_inventory_xml(revision_id))
508
576
 
512
580
        :param revision_id: The expected revision id of the inventory.
513
581
        :param xml: A serialised inventory.
514
582
        """
 
583
        revision_id = osutils.safe_revision_id(revision_id)
515
584
        result = self._serializer.read_inventory_from_string(xml)
516
585
        result.root.revision = revision_id
517
586
        return result
522
591
    @needs_read_lock
523
592
    def get_inventory_xml(self, revision_id):
524
593
        """Get inventory XML as a file object."""
 
594
        revision_id = osutils.safe_revision_id(revision_id)
525
595
        try:
526
 
            assert isinstance(revision_id, basestring), type(revision_id)
 
596
            assert isinstance(revision_id, str), type(revision_id)
527
597
            iw = self.get_inventory_weave()
528
598
            return iw.get_text(revision_id)
529
599
        except IndexError:
533
603
    def get_inventory_sha1(self, revision_id):
534
604
        """Return the sha1 hash of the inventory entry
535
605
        """
 
606
        # TODO: jam 20070210 Shouldn't this be deprecated / removed?
 
607
        revision_id = osutils.safe_revision_id(revision_id)
536
608
        return self.get_revision(revision_id).inventory_sha1
537
609
 
538
610
    @needs_read_lock
547
619
        # special case NULL_REVISION
548
620
        if revision_id == _mod_revision.NULL_REVISION:
549
621
            return {}
 
622
        revision_id = osutils.safe_revision_id(revision_id)
550
623
        a_weave = self.get_inventory_weave()
551
624
        all_revisions = self._eliminate_revisions_not_present(
552
625
                                a_weave.versions())
580
653
            pending = set(self.all_revision_ids())
581
654
            required = set([])
582
655
        else:
583
 
            pending = set(revision_ids)
 
656
            pending = set(osutils.safe_revision_id(r) for r in revision_ids)
584
657
            # special case NULL_REVISION
585
658
            if _mod_revision.NULL_REVISION in pending:
586
659
                pending.remove(_mod_revision.NULL_REVISION)
606
679
            done.add(revision_id)
607
680
        return result
608
681
 
 
682
    def _get_history_vf(self):
 
683
        """Get a versionedfile whose history graph reflects all revisions.
 
684
 
 
685
        For weave repositories, this is the inventory weave.
 
686
        """
 
687
        return self.get_inventory_weave()
 
688
 
 
689
    def iter_reverse_revision_history(self, revision_id):
 
690
        """Iterate backwards through revision ids in the lefthand history
 
691
 
 
692
        :param revision_id: The revision id to start with.  All its lefthand
 
693
            ancestors will be traversed.
 
694
        """
 
695
        revision_id = osutils.safe_revision_id(revision_id)
 
696
        if revision_id in (None, _mod_revision.NULL_REVISION):
 
697
            return
 
698
        next_id = revision_id
 
699
        versionedfile = self._get_history_vf()
 
700
        while True:
 
701
            yield next_id
 
702
            parents = versionedfile.get_parents(next_id)
 
703
            if len(parents) == 0:
 
704
                return
 
705
            else:
 
706
                next_id = parents[0]
 
707
 
609
708
    @needs_read_lock
610
709
    def get_revision_inventory(self, revision_id):
611
710
        """Return inventory of a past revision."""
647
746
            return RevisionTree(self, Inventory(root_id=None), 
648
747
                                _mod_revision.NULL_REVISION)
649
748
        else:
 
749
            revision_id = osutils.safe_revision_id(revision_id)
650
750
            inv = self.get_revision_inventory(revision_id)
651
751
            return RevisionTree(self, inv, revision_id)
652
752
 
674
774
        """
675
775
        if revision_id is None:
676
776
            return [None]
 
777
        revision_id = osutils.safe_revision_id(revision_id)
677
778
        if not self.has_revision(revision_id):
678
779
            raise errors.NoSuchRevision(self, revision_id)
679
780
        w = self.get_inventory_weave()
688
789
        - it writes to stdout, it assumes that that is valid etc. Fix
689
790
        by creating a new more flexible convenience function.
690
791
        """
 
792
        revision_id = osutils.safe_revision_id(revision_id)
691
793
        tree = self.revision_tree(revision_id)
692
794
        # use inventory as it was in that revision
693
795
        file_id = tree.inventory.path2id(file)
701
803
    def get_transaction(self):
702
804
        return self.control_files.get_transaction()
703
805
 
704
 
    def revision_parents(self, revid):
705
 
        return self.get_inventory_weave().parent_names(revid)
 
806
    def revision_parents(self, revision_id):
 
807
        revision_id = osutils.safe_revision_id(revision_id)
 
808
        return self.get_inventory_weave().parent_names(revision_id)
706
809
 
707
810
    @needs_write_lock
708
811
    def set_make_working_trees(self, new_value):
722
825
 
723
826
    @needs_write_lock
724
827
    def sign_revision(self, revision_id, gpg_strategy):
 
828
        revision_id = osutils.safe_revision_id(revision_id)
725
829
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
726
830
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
727
831
 
728
832
    @needs_read_lock
729
833
    def has_signature_for_revision_id(self, revision_id):
730
834
        """Query for a revision signature for revision_id in the repository."""
 
835
        revision_id = osutils.safe_revision_id(revision_id)
731
836
        return self._revision_store.has_signature(revision_id,
732
837
                                                  self.get_transaction())
733
838
 
734
839
    @needs_read_lock
735
840
    def get_signature_text(self, revision_id):
736
841
        """Return the text for a signature."""
 
842
        revision_id = osutils.safe_revision_id(revision_id)
737
843
        return self._revision_store.get_signature_text(revision_id,
738
844
                                                       self.get_transaction())
739
845
 
749
855
        if not revision_ids:
750
856
            raise ValueError("revision_ids must be non-empty in %s.check" 
751
857
                    % (self,))
 
858
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
752
859
        return self._check(revision_ids)
753
860
 
754
861
    def _check(self, revision_ids):
777
884
                    revision_id.encode('ascii')
778
885
                except UnicodeEncodeError:
779
886
                    raise errors.NonAsciiRevisionId(method, self)
780
 
 
781
 
 
782
 
class AllInOneRepository(Repository):
783
 
    """Legacy support - the repository behaviour for all-in-one branches."""
784
 
 
785
 
    def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
786
 
        # we reuse one control files instance.
787
 
        dir_mode = a_bzrdir._control_files._dir_mode
788
 
        file_mode = a_bzrdir._control_files._file_mode
789
 
 
790
 
        def get_store(name, compressed=True, prefixed=False):
791
 
            # FIXME: This approach of assuming stores are all entirely compressed
792
 
            # or entirely uncompressed is tidy, but breaks upgrade from 
793
 
            # some existing branches where there's a mixture; we probably 
794
 
            # still want the option to look for both.
795
 
            relpath = a_bzrdir._control_files._escape(name)
796
 
            store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
797
 
                              prefixed=prefixed, compressed=compressed,
798
 
                              dir_mode=dir_mode,
799
 
                              file_mode=file_mode)
800
 
            #if self._transport.should_cache():
801
 
            #    cache_path = os.path.join(self.cache_root, name)
802
 
            #    os.mkdir(cache_path)
803
 
            #    store = bzrlib.store.CachedStore(store, cache_path)
804
 
            return store
805
 
 
806
 
        # not broken out yet because the controlweaves|inventory_store
807
 
        # and text_store | weave_store bits are still different.
808
 
        if isinstance(_format, RepositoryFormat4):
809
 
            # cannot remove these - there is still no consistent api 
810
 
            # which allows access to this old info.
811
 
            self.inventory_store = get_store('inventory-store')
812
 
            text_store = get_store('text-store')
813
 
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
814
 
 
815
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
816
 
                           timezone=None, committer=None, revprops=None,
817
 
                           revision_id=None):
818
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
819
 
        return Repository.get_commit_builder(self, branch, parents, config,
820
 
            timestamp, timezone, committer, revprops, revision_id)
821
 
 
822
 
    @needs_read_lock
823
 
    def is_shared(self):
824
 
        """AllInOne repositories cannot be shared."""
825
 
        return False
826
 
 
827
 
    @needs_write_lock
828
 
    def set_make_working_trees(self, new_value):
829
 
        """Set the policy flag for making working trees when creating branches.
830
 
 
831
 
        This only applies to branches that use this repository.
832
 
 
833
 
        The default is 'True'.
834
 
        :param new_value: True to restore the default, False to disable making
835
 
                          working trees.
836
 
        """
837
 
        raise NotImplementedError(self.set_make_working_trees)
838
 
    
839
 
    def make_working_trees(self):
840
 
        """Returns the policy for making working trees on new branches."""
841
 
        return True
 
887
            else:
 
888
                try:
 
889
                    revision_id.decode('ascii')
 
890
                except UnicodeDecodeError:
 
891
                    raise errors.NonAsciiRevisionId(method, self)
 
892
 
 
893
 
 
894
 
 
895
# remove these delegates a while after bzr 0.15
 
896
def __make_delegated(name, from_module):
 
897
    def _deprecated_repository_forwarder():
 
898
        symbol_versioning.warn('%s moved to %s in bzr 0.15'
 
899
            % (name, from_module),
 
900
            DeprecationWarning,
 
901
            stacklevel=2)
 
902
        m = __import__(from_module, globals(), locals(), [name])
 
903
        try:
 
904
            return getattr(m, name)
 
905
        except AttributeError:
 
906
            raise AttributeError('module %s has no name %s'
 
907
                    % (m, name))
 
908
    globals()[name] = _deprecated_repository_forwarder
 
909
 
 
910
for _name in [
 
911
        'AllInOneRepository',
 
912
        'WeaveMetaDirRepository',
 
913
        'PreSplitOutRepositoryFormat',
 
914
        'RepositoryFormat4',
 
915
        'RepositoryFormat5',
 
916
        'RepositoryFormat6',
 
917
        'RepositoryFormat7',
 
918
        ]:
 
919
    __make_delegated(_name, 'bzrlib.repofmt.weaverepo')
 
920
 
 
921
for _name in [
 
922
        'KnitRepository',
 
923
        'KnitRepository2',
 
924
        'RepositoryFormatKnit',
 
925
        'RepositoryFormatKnit1',
 
926
        'RepositoryFormatKnit2',
 
927
        ]:
 
928
    __make_delegated(_name, 'bzrlib.repofmt.knitrepo')
842
929
 
843
930
 
844
931
def install_revision(repository, rev, revision_tree):
930
1017
        return not self.control_files._transport.has('no-working-trees')
931
1018
 
932
1019
 
933
 
class WeaveMetaDirRepository(MetaDirRepository):
934
 
    """A subclass of MetaDirRepository to set weave specific policy."""
935
 
 
936
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
937
 
                           timezone=None, committer=None, revprops=None,
938
 
                           revision_id=None):
939
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
940
 
        return MetaDirRepository.get_commit_builder(self, branch, parents,
941
 
            config, timestamp, timezone, committer, revprops, revision_id)
942
 
 
943
 
 
944
 
class KnitRepository(MetaDirRepository):
945
 
    """Knit format repository."""
946
 
 
947
 
    def _warn_if_deprecated(self):
948
 
        # This class isn't deprecated
949
 
        pass
950
 
 
951
 
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
952
 
        inv_vf.add_lines_with_ghosts(revid, parents, lines)
953
 
 
954
 
    @needs_read_lock
955
 
    def _all_revision_ids(self):
956
 
        """See Repository.all_revision_ids()."""
957
 
        # Knits get the revision graph from the index of the revision knit, so
958
 
        # it's always possible even if they're on an unlistable transport.
959
 
        return self._revision_store.all_revision_ids(self.get_transaction())
960
 
 
961
 
    def fileid_involved_between_revs(self, from_revid, to_revid):
962
 
        """Find file_id(s) which are involved in the changes between revisions.
963
 
 
964
 
        This determines the set of revisions which are involved, and then
965
 
        finds all file ids affected by those revisions.
966
 
        """
967
 
        vf = self._get_revision_vf()
968
 
        from_set = set(vf.get_ancestry(from_revid))
969
 
        to_set = set(vf.get_ancestry(to_revid))
970
 
        changed = to_set.difference(from_set)
971
 
        return self._fileid_involved_by_set(changed)
972
 
 
973
 
    def fileid_involved(self, last_revid=None):
974
 
        """Find all file_ids modified in the ancestry of last_revid.
975
 
 
976
 
        :param last_revid: If None, last_revision() will be used.
977
 
        """
978
 
        if not last_revid:
979
 
            changed = set(self.all_revision_ids())
980
 
        else:
981
 
            changed = set(self.get_ancestry(last_revid))
982
 
        if None in changed:
983
 
            changed.remove(None)
984
 
        return self._fileid_involved_by_set(changed)
985
 
 
986
 
    @needs_read_lock
987
 
    def get_ancestry(self, revision_id):
988
 
        """Return a list of revision-ids integrated by a revision.
989
 
        
990
 
        This is topologically sorted.
991
 
        """
992
 
        if revision_id is None:
993
 
            return [None]
994
 
        vf = self._get_revision_vf()
995
 
        try:
996
 
            return [None] + vf.get_ancestry(revision_id)
997
 
        except errors.RevisionNotPresent:
998
 
            raise errors.NoSuchRevision(self, revision_id)
999
 
 
1000
 
    @needs_read_lock
1001
 
    def get_revision(self, revision_id):
1002
 
        """Return the Revision object for a named revision"""
1003
 
        return self.get_revision_reconcile(revision_id)
1004
 
 
1005
 
    @needs_read_lock
1006
 
    def get_revision_graph(self, revision_id=None):
1007
 
        """Return a dictionary containing the revision graph.
1008
 
 
1009
 
        :param revision_id: The revision_id to get a graph from. If None, then
1010
 
        the entire revision graph is returned. This is a deprecated mode of
1011
 
        operation and will be removed in the future.
1012
 
        :return: a dictionary of revision_id->revision_parents_list.
1013
 
        """
1014
 
        # special case NULL_REVISION
1015
 
        if revision_id == _mod_revision.NULL_REVISION:
1016
 
            return {}
1017
 
        a_weave = self._get_revision_vf()
1018
 
        entire_graph = a_weave.get_graph()
1019
 
        if revision_id is None:
1020
 
            return a_weave.get_graph()
1021
 
        elif revision_id not in a_weave:
1022
 
            raise errors.NoSuchRevision(self, revision_id)
1023
 
        else:
1024
 
            # add what can be reached from revision_id
1025
 
            result = {}
1026
 
            pending = set([revision_id])
1027
 
            while len(pending) > 0:
1028
 
                node = pending.pop()
1029
 
                result[node] = a_weave.get_parents(node)
1030
 
                for revision_id in result[node]:
1031
 
                    if revision_id not in result:
1032
 
                        pending.add(revision_id)
1033
 
            return result
1034
 
 
1035
 
    @needs_read_lock
1036
 
    def get_revision_graph_with_ghosts(self, revision_ids=None):
1037
 
        """Return a graph of the revisions with ghosts marked as applicable.
1038
 
 
1039
 
        :param revision_ids: an iterable of revisions to graph or None for all.
1040
 
        :return: a Graph object with the graph reachable from revision_ids.
1041
 
        """
1042
 
        result = graph.Graph()
1043
 
        vf = self._get_revision_vf()
1044
 
        versions = set(vf.versions())
1045
 
        if not revision_ids:
1046
 
            pending = set(self.all_revision_ids())
1047
 
            required = set([])
1048
 
        else:
1049
 
            pending = set(revision_ids)
1050
 
            # special case NULL_REVISION
1051
 
            if _mod_revision.NULL_REVISION in pending:
1052
 
                pending.remove(_mod_revision.NULL_REVISION)
1053
 
            required = set(pending)
1054
 
        done = set([])
1055
 
        while len(pending):
1056
 
            revision_id = pending.pop()
1057
 
            if not revision_id in versions:
1058
 
                if revision_id in required:
1059
 
                    raise errors.NoSuchRevision(self, revision_id)
1060
 
                # a ghost
1061
 
                result.add_ghost(revision_id)
1062
 
                # mark it as done so we don't try for it again.
1063
 
                done.add(revision_id)
1064
 
                continue
1065
 
            parent_ids = vf.get_parents_with_ghosts(revision_id)
1066
 
            for parent_id in parent_ids:
1067
 
                # is this queued or done ?
1068
 
                if (parent_id not in pending and
1069
 
                    parent_id not in done):
1070
 
                    # no, queue it.
1071
 
                    pending.add(parent_id)
1072
 
            result.add_node(revision_id, parent_ids)
1073
 
            done.add(revision_id)
1074
 
        return result
1075
 
 
1076
 
    def _get_revision_vf(self):
1077
 
        """:return: a versioned file containing the revisions."""
1078
 
        vf = self._revision_store.get_revision_file(self.get_transaction())
1079
 
        return vf
1080
 
 
1081
 
    @needs_write_lock
1082
 
    def reconcile(self, other=None, thorough=False):
1083
 
        """Reconcile this repository."""
1084
 
        from bzrlib.reconcile import KnitReconciler
1085
 
        reconciler = KnitReconciler(self, thorough=thorough)
1086
 
        reconciler.reconcile()
1087
 
        return reconciler
1088
 
    
1089
 
    def revision_parents(self, revision_id):
1090
 
        return self._get_revision_vf().get_parents(revision_id)
1091
 
 
1092
 
 
1093
 
class KnitRepository2(KnitRepository):
1094
 
    """"""
1095
 
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1096
 
                 control_store, text_store):
1097
 
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1098
 
                              _revision_store, control_store, text_store)
1099
 
        self._serializer = xml6.serializer_v6
1100
 
 
1101
 
    def deserialise_inventory(self, revision_id, xml):
1102
 
        """Transform the xml into an inventory object. 
1103
 
 
1104
 
        :param revision_id: The expected revision id of the inventory.
1105
 
        :param xml: A serialised inventory.
1106
 
        """
1107
 
        result = self._serializer.read_inventory_from_string(xml)
1108
 
        assert result.root.revision is not None
1109
 
        return result
1110
 
 
1111
 
    def serialise_inventory(self, inv):
1112
 
        """Transform the inventory object into XML text.
1113
 
 
1114
 
        :param revision_id: The expected revision id of the inventory.
1115
 
        :param xml: A serialised inventory.
1116
 
        """
1117
 
        assert inv.revision_id is not None
1118
 
        assert inv.root.revision is not None
1119
 
        return KnitRepository.serialise_inventory(self, inv)
1120
 
 
1121
 
    def get_commit_builder(self, branch, parents, config, timestamp=None, 
1122
 
                           timezone=None, committer=None, revprops=None, 
1123
 
                           revision_id=None):
1124
 
        """Obtain a CommitBuilder for this repository.
1125
 
        
1126
 
        :param branch: Branch to commit to.
1127
 
        :param parents: Revision ids of the parents of the new revision.
1128
 
        :param config: Configuration to use.
1129
 
        :param timestamp: Optional timestamp recorded for commit.
1130
 
        :param timezone: Optional timezone for timestamp.
1131
 
        :param committer: Optional committer to set for commit.
1132
 
        :param revprops: Optional dictionary of revision properties.
1133
 
        :param revision_id: Optional revision id.
1134
 
        """
1135
 
        return RootCommitBuilder(self, parents, config, timestamp, timezone,
1136
 
                                 committer, revprops, revision_id)
1137
 
 
1138
 
 
1139
1020
class RepositoryFormatRegistry(registry.Registry):
1140
1021
    """Registry of RepositoryFormats.
1141
1022
    """
 
1023
 
 
1024
    def get(self, format_string):
 
1025
        r = registry.Registry.get(self, format_string)
 
1026
        if callable(r):
 
1027
            r = r()
 
1028
        return r
1142
1029
    
1143
1030
 
1144
1031
format_registry = RepositoryFormatRegistry()
1145
 
"""Registry of formats, indexed by their identifying format string."""
 
1032
"""Registry of formats, indexed by their identifying format string.
 
1033
 
 
1034
This can contain either format instances themselves, or classes/factories that
 
1035
can be called to obtain one.
 
1036
"""
1146
1037
 
1147
1038
 
1148
1039
class RepositoryFormat(object):
1172
1063
    def __str__(self):
1173
1064
        return "<%s>" % self.__class__.__name__
1174
1065
 
 
1066
    def __eq__(self, other):
 
1067
        # format objects are generally stateless
 
1068
        return isinstance(other, self.__class__)
 
1069
 
1175
1070
    @classmethod
1176
1071
    def find_format(klass, a_bzrdir):
1177
1072
        """Return the format for the repository object in a_bzrdir.
1190
1085
            raise errors.UnknownFormatError(format=format_string)
1191
1086
 
1192
1087
    @classmethod
1193
 
    @deprecated_method(symbol_versioning.zero_fourteen)
1194
 
    def set_default_format(klass, format):
1195
 
        klass._set_default_format(format)
1196
 
 
1197
 
    @classmethod
1198
 
    def _set_default_format(klass, format):
1199
 
        """Set the default format for new Repository creation.
1200
 
 
1201
 
        The format must already be registered.
1202
 
        """
1203
 
        format_registry.default_key = format.get_format_string()
1204
 
 
1205
 
    @classmethod
1206
1088
    def register_format(klass, format):
1207
1089
        format_registry.register(format.get_format_string(), format)
1208
1090
 
1213
1095
    @classmethod
1214
1096
    def get_default_format(klass):
1215
1097
        """Return the current default format."""
1216
 
        return format_registry.get(format_registry.default_key)
 
1098
        from bzrlib import bzrdir
 
1099
        return bzrdir.format_registry.make_bzrdir('default').repository_format
1217
1100
 
1218
1101
    def _get_control_store(self, repo_transport, control_files):
1219
1102
        """Return the control store for this repository."""
1258
1141
        _revision_store = TextRevisionStore(text_store, serializer)
1259
1142
        return _revision_store
1260
1143
 
 
1144
    # TODO: this shouldn't be in the base class, it's specific to things that
 
1145
    # use weaves or knits -- mbp 20070207
1261
1146
    def _get_versioned_file_store(self,
1262
1147
                                  name,
1263
1148
                                  transport,
1264
1149
                                  control_files,
1265
1150
                                  prefixed=True,
1266
 
                                  versionedfile_class=weave.WeaveFile,
 
1151
                                  versionedfile_class=None,
1267
1152
                                  versionedfile_kwargs={},
1268
1153
                                  escaped=False):
 
1154
        if versionedfile_class is None:
 
1155
            versionedfile_class = self._versionedfile_class
1269
1156
        weave_transport = control_files._transport.clone(name)
1270
1157
        dir_mode = control_files._dir_mode
1271
1158
        file_mode = control_files._file_mode
1306
1193
        raise NotImplementedError(self.open)
1307
1194
 
1308
1195
 
1309
 
class PreSplitOutRepositoryFormat(RepositoryFormat):
1310
 
    """Base class for the pre split out repository formats."""
1311
 
 
1312
 
    rich_root_data = False
1313
 
 
1314
 
    def initialize(self, a_bzrdir, shared=False, _internal=False):
1315
 
        """Create a weave repository.
1316
 
        
1317
 
        TODO: when creating split out bzr branch formats, move this to a common
1318
 
        base for Format5, Format6. or something like that.
1319
 
        """
1320
 
        if shared:
1321
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
1322
 
 
1323
 
        if not _internal:
1324
 
            # always initialized when the bzrdir is.
1325
 
            return self.open(a_bzrdir, _found=True)
1326
 
        
1327
 
        # Create an empty weave
1328
 
        sio = StringIO()
1329
 
        weavefile.write_weave_v5(weave.Weave(), sio)
1330
 
        empty_weave = sio.getvalue()
1331
 
 
1332
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1333
 
        dirs = ['revision-store', 'weaves']
1334
 
        files = [('inventory.weave', StringIO(empty_weave)),
1335
 
                 ]
1336
 
        
1337
 
        # FIXME: RBC 20060125 don't peek under the covers
1338
 
        # NB: no need to escape relative paths that are url safe.
1339
 
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
1340
 
                                'branch-lock', lockable_files.TransportLock)
1341
 
        control_files.create_lock()
1342
 
        control_files.lock_write()
1343
 
        control_files._transport.mkdir_multi(dirs,
1344
 
                mode=control_files._dir_mode)
1345
 
        try:
1346
 
            for file, content in files:
1347
 
                control_files.put(file, content)
1348
 
        finally:
1349
 
            control_files.unlock()
1350
 
        return self.open(a_bzrdir, _found=True)
1351
 
 
1352
 
    def _get_control_store(self, repo_transport, control_files):
1353
 
        """Return the control store for this repository."""
1354
 
        return self._get_versioned_file_store('',
1355
 
                                              repo_transport,
1356
 
                                              control_files,
1357
 
                                              prefixed=False)
1358
 
 
1359
 
    def _get_text_store(self, transport, control_files):
1360
 
        """Get a store for file texts for this format."""
1361
 
        raise NotImplementedError(self._get_text_store)
1362
 
 
1363
 
    def open(self, a_bzrdir, _found=False):
1364
 
        """See RepositoryFormat.open()."""
1365
 
        if not _found:
1366
 
            # we are being called directly and must probe.
1367
 
            raise NotImplementedError
1368
 
 
1369
 
        repo_transport = a_bzrdir.get_repository_transport(None)
1370
 
        control_files = a_bzrdir._control_files
1371
 
        text_store = self._get_text_store(repo_transport, control_files)
1372
 
        control_store = self._get_control_store(repo_transport, control_files)
1373
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1374
 
        return AllInOneRepository(_format=self,
1375
 
                                  a_bzrdir=a_bzrdir,
1376
 
                                  _revision_store=_revision_store,
1377
 
                                  control_store=control_store,
1378
 
                                  text_store=text_store)
1379
 
 
1380
 
    def check_conversion_target(self, target_format):
1381
 
        pass
1382
 
 
1383
 
 
1384
 
class RepositoryFormat4(PreSplitOutRepositoryFormat):
1385
 
    """Bzr repository format 4.
1386
 
 
1387
 
    This repository format has:
1388
 
     - flat stores
1389
 
     - TextStores for texts, inventories,revisions.
1390
 
 
1391
 
    This format is deprecated: it indexes texts using a text id which is
1392
 
    removed in format 5; initialization and write support for this format
1393
 
    has been removed.
1394
 
    """
1395
 
 
1396
 
    def __init__(self):
1397
 
        super(RepositoryFormat4, self).__init__()
1398
 
        self._matchingbzrdir = bzrdir.BzrDirFormat4()
1399
 
 
1400
 
    def get_format_description(self):
1401
 
        """See RepositoryFormat.get_format_description()."""
1402
 
        return "Repository format 4"
1403
 
 
1404
 
    def initialize(self, url, shared=False, _internal=False):
1405
 
        """Format 4 branches cannot be created."""
1406
 
        raise errors.UninitializableFormat(self)
1407
 
 
1408
 
    def is_supported(self):
1409
 
        """Format 4 is not supported.
1410
 
 
1411
 
        It is not supported because the model changed from 4 to 5 and the
1412
 
        conversion logic is expensive - so doing it on the fly was not 
1413
 
        feasible.
1414
 
        """
1415
 
        return False
1416
 
 
1417
 
    def _get_control_store(self, repo_transport, control_files):
1418
 
        """Format 4 repositories have no formal control store at this point.
1419
 
        
1420
 
        This will cause any control-file-needing apis to fail - this is desired.
1421
 
        """
1422
 
        return None
1423
 
    
1424
 
    def _get_revision_store(self, repo_transport, control_files):
1425
 
        """See RepositoryFormat._get_revision_store()."""
1426
 
        from bzrlib.xml4 import serializer_v4
1427
 
        return self._get_text_rev_store(repo_transport,
1428
 
                                        control_files,
1429
 
                                        'revision-store',
1430
 
                                        serializer=serializer_v4)
1431
 
 
1432
 
    def _get_text_store(self, transport, control_files):
1433
 
        """See RepositoryFormat._get_text_store()."""
1434
 
 
1435
 
 
1436
 
class RepositoryFormat5(PreSplitOutRepositoryFormat):
1437
 
    """Bzr control format 5.
1438
 
 
1439
 
    This repository format has:
1440
 
     - weaves for file texts and inventory
1441
 
     - flat stores
1442
 
     - TextStores for revisions and signatures.
1443
 
    """
1444
 
 
1445
 
    def __init__(self):
1446
 
        super(RepositoryFormat5, self).__init__()
1447
 
        self._matchingbzrdir = bzrdir.BzrDirFormat5()
1448
 
 
1449
 
    def get_format_description(self):
1450
 
        """See RepositoryFormat.get_format_description()."""
1451
 
        return "Weave repository format 5"
1452
 
 
1453
 
    def _get_revision_store(self, repo_transport, control_files):
1454
 
        """See RepositoryFormat._get_revision_store()."""
1455
 
        """Return the revision store object for this a_bzrdir."""
1456
 
        return self._get_text_rev_store(repo_transport,
1457
 
                                        control_files,
1458
 
                                        'revision-store',
1459
 
                                        compressed=False)
1460
 
 
1461
 
    def _get_text_store(self, transport, control_files):
1462
 
        """See RepositoryFormat._get_text_store()."""
1463
 
        return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
1464
 
 
1465
 
 
1466
 
class RepositoryFormat6(PreSplitOutRepositoryFormat):
1467
 
    """Bzr control format 6.
1468
 
 
1469
 
    This repository format has:
1470
 
     - weaves for file texts and inventory
1471
 
     - hash subdirectory based stores.
1472
 
     - TextStores for revisions and signatures.
1473
 
    """
1474
 
 
1475
 
    def __init__(self):
1476
 
        super(RepositoryFormat6, self).__init__()
1477
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
1478
 
 
1479
 
    def get_format_description(self):
1480
 
        """See RepositoryFormat.get_format_description()."""
1481
 
        return "Weave repository format 6"
1482
 
 
1483
 
    def _get_revision_store(self, repo_transport, control_files):
1484
 
        """See RepositoryFormat._get_revision_store()."""
1485
 
        return self._get_text_rev_store(repo_transport,
1486
 
                                        control_files,
1487
 
                                        'revision-store',
1488
 
                                        compressed=False,
1489
 
                                        prefixed=True)
1490
 
 
1491
 
    def _get_text_store(self, transport, control_files):
1492
 
        """See RepositoryFormat._get_text_store()."""
1493
 
        return self._get_versioned_file_store('weaves', transport, control_files)
1494
 
 
1495
 
 
1496
1196
class MetaDirRepositoryFormat(RepositoryFormat):
1497
1197
    """Common base class for the new repositories using the metadir layout."""
1498
1198
 
1499
1199
    rich_root_data = False
 
1200
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1500
1201
 
1501
1202
    def __init__(self):
1502
1203
        super(MetaDirRepositoryFormat, self).__init__()
1503
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1504
1204
 
1505
1205
    def _create_control_files(self, a_bzrdir):
1506
1206
        """Create the required files and the initial control_files object."""
1529
1229
            control_files.unlock()
1530
1230
 
1531
1231
 
1532
 
class RepositoryFormat7(MetaDirRepositoryFormat):
1533
 
    """Bzr repository 7.
1534
 
 
1535
 
    This repository format has:
1536
 
     - weaves for file texts and inventory
1537
 
     - hash subdirectory based stores.
1538
 
     - TextStores for revisions and signatures.
1539
 
     - a format marker of its own
1540
 
     - an optional 'shared-storage' flag
1541
 
     - an optional 'no-working-trees' flag
1542
 
    """
1543
 
 
1544
 
    def _get_control_store(self, repo_transport, control_files):
1545
 
        """Return the control store for this repository."""
1546
 
        return self._get_versioned_file_store('',
1547
 
                                              repo_transport,
1548
 
                                              control_files,
1549
 
                                              prefixed=False)
1550
 
 
1551
 
    def get_format_string(self):
1552
 
        """See RepositoryFormat.get_format_string()."""
1553
 
        return "Bazaar-NG Repository format 7"
1554
 
 
1555
 
    def get_format_description(self):
1556
 
        """See RepositoryFormat.get_format_description()."""
1557
 
        return "Weave repository format 7"
1558
 
 
1559
 
    def check_conversion_target(self, target_format):
1560
 
        pass
1561
 
 
1562
 
    def _get_revision_store(self, repo_transport, control_files):
1563
 
        """See RepositoryFormat._get_revision_store()."""
1564
 
        return self._get_text_rev_store(repo_transport,
1565
 
                                        control_files,
1566
 
                                        'revision-store',
1567
 
                                        compressed=False,
1568
 
                                        prefixed=True,
1569
 
                                        )
1570
 
 
1571
 
    def _get_text_store(self, transport, control_files):
1572
 
        """See RepositoryFormat._get_text_store()."""
1573
 
        return self._get_versioned_file_store('weaves',
1574
 
                                              transport,
1575
 
                                              control_files)
1576
 
 
1577
 
    def initialize(self, a_bzrdir, shared=False):
1578
 
        """Create a weave repository.
1579
 
 
1580
 
        :param shared: If true the repository will be initialized as a shared
1581
 
                       repository.
1582
 
        """
1583
 
        # Create an empty weave
1584
 
        sio = StringIO()
1585
 
        weavefile.write_weave_v5(weave.Weave(), sio)
1586
 
        empty_weave = sio.getvalue()
1587
 
 
1588
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1589
 
        dirs = ['revision-store', 'weaves']
1590
 
        files = [('inventory.weave', StringIO(empty_weave)), 
1591
 
                 ]
1592
 
        utf8_files = [('format', self.get_format_string())]
1593
 
 
1594
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1595
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1596
 
 
1597
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1598
 
        """See RepositoryFormat.open().
1599
 
        
1600
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1601
 
                                    repository at a slightly different url
1602
 
                                    than normal. I.e. during 'upgrade'.
1603
 
        """
1604
 
        if not _found:
1605
 
            format = RepositoryFormat.find_format(a_bzrdir)
1606
 
            assert format.__class__ ==  self.__class__
1607
 
        if _override_transport is not None:
1608
 
            repo_transport = _override_transport
1609
 
        else:
1610
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1611
 
        control_files = lockable_files.LockableFiles(repo_transport,
1612
 
                                'lock', lockdir.LockDir)
1613
 
        text_store = self._get_text_store(repo_transport, control_files)
1614
 
        control_store = self._get_control_store(repo_transport, control_files)
1615
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1616
 
        return WeaveMetaDirRepository(_format=self,
1617
 
            a_bzrdir=a_bzrdir,
1618
 
            control_files=control_files,
1619
 
            _revision_store=_revision_store,
1620
 
            control_store=control_store,
1621
 
            text_store=text_store)
1622
 
 
1623
 
 
1624
 
class RepositoryFormatKnit(MetaDirRepositoryFormat):
1625
 
    """Bzr repository knit format (generalized). 
1626
 
 
1627
 
    This repository format has:
1628
 
     - knits for file texts and inventory
1629
 
     - hash subdirectory based stores.
1630
 
     - knits for revisions and signatures
1631
 
     - TextStores for revisions and signatures.
1632
 
     - a format marker of its own
1633
 
     - an optional 'shared-storage' flag
1634
 
     - an optional 'no-working-trees' flag
1635
 
     - a LockDir lock
1636
 
    """
1637
 
 
1638
 
    def _get_control_store(self, repo_transport, control_files):
1639
 
        """Return the control store for this repository."""
1640
 
        return VersionedFileStore(
1641
 
            repo_transport,
1642
 
            prefixed=False,
1643
 
            file_mode=control_files._file_mode,
1644
 
            versionedfile_class=knit.KnitVersionedFile,
1645
 
            versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
1646
 
            )
1647
 
 
1648
 
    def _get_revision_store(self, repo_transport, control_files):
1649
 
        """See RepositoryFormat._get_revision_store()."""
1650
 
        from bzrlib.store.revision.knit import KnitRevisionStore
1651
 
        versioned_file_store = VersionedFileStore(
1652
 
            repo_transport,
1653
 
            file_mode=control_files._file_mode,
1654
 
            prefixed=False,
1655
 
            precious=True,
1656
 
            versionedfile_class=knit.KnitVersionedFile,
1657
 
            versionedfile_kwargs={'delta':False,
1658
 
                                  'factory':knit.KnitPlainFactory(),
1659
 
                                 },
1660
 
            escaped=True,
1661
 
            )
1662
 
        return KnitRevisionStore(versioned_file_store)
1663
 
 
1664
 
    def _get_text_store(self, transport, control_files):
1665
 
        """See RepositoryFormat._get_text_store()."""
1666
 
        return self._get_versioned_file_store('knits',
1667
 
                                  transport,
1668
 
                                  control_files,
1669
 
                                  versionedfile_class=knit.KnitVersionedFile,
1670
 
                                  versionedfile_kwargs={
1671
 
                                      'create_parent_dir':True,
1672
 
                                      'delay_create':True,
1673
 
                                      'dir_mode':control_files._dir_mode,
1674
 
                                  },
1675
 
                                  escaped=True)
1676
 
 
1677
 
    def initialize(self, a_bzrdir, shared=False):
1678
 
        """Create a knit format 1 repository.
1679
 
 
1680
 
        :param a_bzrdir: bzrdir to contain the new repository; must already
1681
 
            be initialized.
1682
 
        :param shared: If true the repository will be initialized as a shared
1683
 
                       repository.
1684
 
        """
1685
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1686
 
        dirs = ['revision-store', 'knits']
1687
 
        files = []
1688
 
        utf8_files = [('format', self.get_format_string())]
1689
 
        
1690
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1691
 
        repo_transport = a_bzrdir.get_repository_transport(None)
1692
 
        control_files = lockable_files.LockableFiles(repo_transport,
1693
 
                                'lock', lockdir.LockDir)
1694
 
        control_store = self._get_control_store(repo_transport, control_files)
1695
 
        transaction = transactions.WriteTransaction()
1696
 
        # trigger a write of the inventory store.
1697
 
        control_store.get_weave_or_empty('inventory', transaction)
1698
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1699
 
        # the revision id here is irrelevant: it will not be stored, and cannot
1700
 
        # already exist.
1701
 
        _revision_store.has_revision_id('A', transaction)
1702
 
        _revision_store.get_signature_file(transaction)
1703
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1704
 
 
1705
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1706
 
        """See RepositoryFormat.open().
1707
 
        
1708
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1709
 
                                    repository at a slightly different url
1710
 
                                    than normal. I.e. during 'upgrade'.
1711
 
        """
1712
 
        if not _found:
1713
 
            format = RepositoryFormat.find_format(a_bzrdir)
1714
 
            assert format.__class__ ==  self.__class__
1715
 
        if _override_transport is not None:
1716
 
            repo_transport = _override_transport
1717
 
        else:
1718
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1719
 
        control_files = lockable_files.LockableFiles(repo_transport,
1720
 
                                'lock', lockdir.LockDir)
1721
 
        text_store = self._get_text_store(repo_transport, control_files)
1722
 
        control_store = self._get_control_store(repo_transport, control_files)
1723
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1724
 
        return KnitRepository(_format=self,
1725
 
                              a_bzrdir=a_bzrdir,
1726
 
                              control_files=control_files,
1727
 
                              _revision_store=_revision_store,
1728
 
                              control_store=control_store,
1729
 
                              text_store=text_store)
1730
 
 
1731
 
 
1732
 
class RepositoryFormatKnit1(RepositoryFormatKnit):
1733
 
    """Bzr repository knit format 1.
1734
 
 
1735
 
    This repository format has:
1736
 
     - knits for file texts and inventory
1737
 
     - hash subdirectory based stores.
1738
 
     - knits for revisions and signatures
1739
 
     - TextStores for revisions and signatures.
1740
 
     - a format marker of its own
1741
 
     - an optional 'shared-storage' flag
1742
 
     - an optional 'no-working-trees' flag
1743
 
     - a LockDir lock
1744
 
 
1745
 
    This format was introduced in bzr 0.8.
1746
 
    """
1747
 
    def get_format_string(self):
1748
 
        """See RepositoryFormat.get_format_string()."""
1749
 
        return "Bazaar-NG Knit Repository Format 1"
1750
 
 
1751
 
    def get_format_description(self):
1752
 
        """See RepositoryFormat.get_format_description()."""
1753
 
        return "Knit repository format 1"
1754
 
 
1755
 
    def check_conversion_target(self, target_format):
1756
 
        pass
1757
 
 
1758
 
 
1759
 
class RepositoryFormatKnit2(RepositoryFormatKnit):
1760
 
    """Bzr repository knit format 2.
1761
 
 
1762
 
    THIS FORMAT IS EXPERIMENTAL
1763
 
    This repository format has:
1764
 
     - knits for file texts and inventory
1765
 
     - hash subdirectory based stores.
1766
 
     - knits for revisions and signatures
1767
 
     - TextStores for revisions and signatures.
1768
 
     - a format marker of its own
1769
 
     - an optional 'shared-storage' flag
1770
 
     - an optional 'no-working-trees' flag
1771
 
     - a LockDir lock
1772
 
     - Support for recording full info about the tree root
1773
 
 
1774
 
    """
1775
 
    
1776
 
    rich_root_data = True
1777
 
 
1778
 
    def get_format_string(self):
1779
 
        """See RepositoryFormat.get_format_string()."""
1780
 
        return "Bazaar Knit Repository Format 2\n"
1781
 
 
1782
 
    def get_format_description(self):
1783
 
        """See RepositoryFormat.get_format_description()."""
1784
 
        return "Knit repository format 2"
1785
 
 
1786
 
    def check_conversion_target(self, target_format):
1787
 
        if not target_format.rich_root_data:
1788
 
            raise errors.BadConversionTarget(
1789
 
                'Does not support rich root data.', target_format)
1790
 
 
1791
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1792
 
        """See RepositoryFormat.open().
1793
 
        
1794
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1795
 
                                    repository at a slightly different url
1796
 
                                    than normal. I.e. during 'upgrade'.
1797
 
        """
1798
 
        if not _found:
1799
 
            format = RepositoryFormat.find_format(a_bzrdir)
1800
 
            assert format.__class__ ==  self.__class__
1801
 
        if _override_transport is not None:
1802
 
            repo_transport = _override_transport
1803
 
        else:
1804
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1805
 
        control_files = lockable_files.LockableFiles(repo_transport, 'lock',
1806
 
                                                     lockdir.LockDir)
1807
 
        text_store = self._get_text_store(repo_transport, control_files)
1808
 
        control_store = self._get_control_store(repo_transport, control_files)
1809
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
1810
 
        return KnitRepository2(_format=self,
1811
 
                               a_bzrdir=a_bzrdir,
1812
 
                               control_files=control_files,
1813
 
                               _revision_store=_revision_store,
1814
 
                               control_store=control_store,
1815
 
                               text_store=text_store)
1816
 
 
1817
 
 
1818
 
 
1819
1232
# formats which have no format string are not discoverable
1820
 
# and not independently creatable, so are not registered.
1821
 
RepositoryFormat.register_format(RepositoryFormat7())
 
1233
# and not independently creatable, so are not registered.  They're 
 
1234
# all in bzrlib.repofmt.weaverepo now.  When an instance of one of these is
 
1235
# needed, it's constructed directly by the BzrDir.  Non-native formats where
 
1236
# the repository is not separately opened are similar.
 
1237
 
 
1238
format_registry.register_lazy(
 
1239
    'Bazaar-NG Repository format 7',
 
1240
    'bzrlib.repofmt.weaverepo',
 
1241
    'RepositoryFormat7'
 
1242
    )
1822
1243
# KEEP in sync with bzrdir.format_registry default, which controls the overall
1823
1244
# default control directory format
1824
 
_default_format = RepositoryFormatKnit1()
1825
 
RepositoryFormat.register_format(_default_format)
1826
 
RepositoryFormat.register_format(RepositoryFormatKnit2())
1827
 
RepositoryFormat._set_default_format(_default_format)
1828
 
_legacy_formats = [RepositoryFormat4(),
1829
 
                   RepositoryFormat5(),
1830
 
                   RepositoryFormat6()]
 
1245
 
 
1246
format_registry.register_lazy(
 
1247
    'Bazaar-NG Knit Repository Format 1',
 
1248
    'bzrlib.repofmt.knitrepo',
 
1249
    'RepositoryFormatKnit1',
 
1250
    )
 
1251
format_registry.default_key = 'Bazaar-NG Knit Repository Format 1'
 
1252
 
 
1253
format_registry.register_lazy(
 
1254
    'Bazaar Knit Repository Format 2\n',
 
1255
    'bzrlib.repofmt.knitrepo',
 
1256
    'RepositoryFormatKnit2',
 
1257
    )
1831
1258
 
1832
1259
 
1833
1260
class InterRepository(InterObject):
1875
1302
        # generic, possibly worst case, slow code path.
1876
1303
        target_ids = set(self.target.all_revision_ids())
1877
1304
        if revision_id is not None:
 
1305
            # TODO: jam 20070210 InterRepository is internal enough that it
 
1306
            #       should assume revision_ids are already utf-8
 
1307
            revision_id = osutils.safe_revision_id(revision_id)
1878
1308
            source_ids = self.source.get_ancestry(revision_id)
1879
1309
            assert source_ids[0] is None
1880
1310
            source_ids.pop(0)
1893
1323
    Data format and model must match for this to work.
1894
1324
    """
1895
1325
 
1896
 
    _matching_repo_format = RepositoryFormat4()
1897
 
    """Repository format for testing with."""
 
1326
    @classmethod
 
1327
    def _get_repo_format_to_test(self):
 
1328
        """Repository format for testing with."""
 
1329
        return RepositoryFormat.get_default_format()
1898
1330
 
1899
1331
    @staticmethod
1900
1332
    def is_compatible(source, target):
1922
1354
            self.target.set_make_working_trees(self.source.make_working_trees())
1923
1355
        except NotImplementedError:
1924
1356
            pass
 
1357
        # TODO: jam 20070210 This is fairly internal, so we should probably
 
1358
        #       just assert that revision_id is not unicode.
 
1359
        revision_id = osutils.safe_revision_id(revision_id)
1925
1360
        # grab the basis available data
1926
1361
        if basis is not None:
1927
1362
            self.target.fetch(basis, revision_id=revision_id)
1938
1373
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1939
1374
               self.source, self.source._format, self.target, 
1940
1375
               self.target._format)
 
1376
        # TODO: jam 20070210 This should be an assert, not a translate
 
1377
        revision_id = osutils.safe_revision_id(revision_id)
1941
1378
        f = GenericRepoFetcher(to_repository=self.target,
1942
1379
                               from_repository=self.source,
1943
1380
                               last_revision=revision_id,
1948
1385
class InterWeaveRepo(InterSameDataRepository):
1949
1386
    """Optimised code paths between Weave based repositories."""
1950
1387
 
1951
 
    _matching_repo_format = RepositoryFormat7()
1952
 
    """Repository format for testing with."""
 
1388
    @classmethod
 
1389
    def _get_repo_format_to_test(self):
 
1390
        from bzrlib.repofmt import weaverepo
 
1391
        return weaverepo.RepositoryFormat7()
1953
1392
 
1954
1393
    @staticmethod
1955
1394
    def is_compatible(source, target):
1959
1398
        could lead to confusing results, and there is no need to be 
1960
1399
        overly general.
1961
1400
        """
 
1401
        from bzrlib.repofmt.weaverepo import (
 
1402
                RepositoryFormat5,
 
1403
                RepositoryFormat6,
 
1404
                RepositoryFormat7,
 
1405
                )
1962
1406
        try:
1963
1407
            return (isinstance(source._format, (RepositoryFormat5,
1964
1408
                                                RepositoryFormat6,
1973
1417
    def copy_content(self, revision_id=None, basis=None):
1974
1418
        """See InterRepository.copy_content()."""
1975
1419
        # weave specific optimised path:
 
1420
        # TODO: jam 20070210 Internal, should be an assert, not translate
 
1421
        revision_id = osutils.safe_revision_id(revision_id)
1976
1422
        if basis is not None:
1977
1423
            # copy the basis in, then fetch remaining data.
1978
1424
            basis.copy_content_into(self.target, revision_id)
2015
1461
        from bzrlib.fetch import GenericRepoFetcher
2016
1462
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2017
1463
               self.source, self.source._format, self.target, self.target._format)
 
1464
        # TODO: jam 20070210 This should be an assert, not a translate
 
1465
        revision_id = osutils.safe_revision_id(revision_id)
2018
1466
        f = GenericRepoFetcher(to_repository=self.target,
2019
1467
                               from_repository=self.source,
2020
1468
                               last_revision=revision_id,
2066
1514
class InterKnitRepo(InterSameDataRepository):
2067
1515
    """Optimised code paths between Knit based repositories."""
2068
1516
 
2069
 
    _matching_repo_format = RepositoryFormatKnit1()
2070
 
    """Repository format for testing with."""
 
1517
    @classmethod
 
1518
    def _get_repo_format_to_test(self):
 
1519
        from bzrlib.repofmt import knitrepo
 
1520
        return knitrepo.RepositoryFormatKnit1()
2071
1521
 
2072
1522
    @staticmethod
2073
1523
    def is_compatible(source, target):
2077
1527
        could lead to confusing results, and there is no need to be 
2078
1528
        overly general.
2079
1529
        """
 
1530
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
2080
1531
        try:
2081
1532
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
2082
1533
                    isinstance(target._format, (RepositoryFormatKnit1)))
2089
1540
        from bzrlib.fetch import KnitRepoFetcher
2090
1541
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2091
1542
               self.source, self.source._format, self.target, self.target._format)
 
1543
        # TODO: jam 20070210 This should be an assert, not a translate
 
1544
        revision_id = osutils.safe_revision_id(revision_id)
2092
1545
        f = KnitRepoFetcher(to_repository=self.target,
2093
1546
                            from_repository=self.source,
2094
1547
                            last_revision=revision_id,
2128
1581
 
2129
1582
class InterModel1and2(InterRepository):
2130
1583
 
2131
 
    _matching_repo_format = None
 
1584
    @classmethod
 
1585
    def _get_repo_format_to_test(self):
 
1586
        return None
2132
1587
 
2133
1588
    @staticmethod
2134
1589
    def is_compatible(source, target):
2145
1600
    def fetch(self, revision_id=None, pb=None):
2146
1601
        """See InterRepository.fetch()."""
2147
1602
        from bzrlib.fetch import Model1toKnit2Fetcher
 
1603
        # TODO: jam 20070210 This should be an assert, not a translate
 
1604
        revision_id = osutils.safe_revision_id(revision_id)
2148
1605
        f = Model1toKnit2Fetcher(to_repository=self.target,
2149
1606
                                 from_repository=self.source,
2150
1607
                                 last_revision=revision_id,
2166
1623
            self.target.set_make_working_trees(self.source.make_working_trees())
2167
1624
        except NotImplementedError:
2168
1625
            pass
 
1626
        # TODO: jam 20070210 Internal, assert, don't translate
 
1627
        revision_id = osutils.safe_revision_id(revision_id)
2169
1628
        # grab the basis available data
2170
1629
        if basis is not None:
2171
1630
            self.target.fetch(basis, revision_id=revision_id)
2178
1637
 
2179
1638
class InterKnit1and2(InterKnitRepo):
2180
1639
 
2181
 
    _matching_repo_format = None
 
1640
    @classmethod
 
1641
    def _get_repo_format_to_test(self):
 
1642
        return None
2182
1643
 
2183
1644
    @staticmethod
2184
1645
    def is_compatible(source, target):
2185
1646
        """Be compatible with Knit1 source and Knit2 target"""
 
1647
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit2
2186
1648
        try:
 
1649
            from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1, \
 
1650
                    RepositoryFormatKnit2
2187
1651
            return (isinstance(source._format, (RepositoryFormatKnit1)) and
2188
1652
                    isinstance(target._format, (RepositoryFormatKnit2)))
2189
1653
        except AttributeError:
2196
1660
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2197
1661
               self.source, self.source._format, self.target, 
2198
1662
               self.target._format)
 
1663
        # TODO: jam 20070210 This should be an assert, not a translate
 
1664
        revision_id = osutils.safe_revision_id(revision_id)
2199
1665
        f = Knit1to2Fetcher(to_repository=self.target,
2200
1666
                            from_repository=self.source,
2201
1667
                            last_revision=revision_id,
2227
1693
    def adapt(self, test):
2228
1694
        result = unittest.TestSuite()
2229
1695
        for repository_format, bzrdir_format in self._formats:
 
1696
            from copy import deepcopy
2230
1697
            new_test = deepcopy(test)
2231
1698
            new_test.transport_server = self._transport_server
2232
1699
            new_test.transport_readonly_server = self._transport_readonly_server
2257
1724
    def adapt(self, test):
2258
1725
        result = unittest.TestSuite()
2259
1726
        for interrepo_class, repository_format, repository_format_to in self._formats:
 
1727
            from copy import deepcopy
2260
1728
            new_test = deepcopy(test)
2261
1729
            new_test.transport_server = self._transport_server
2262
1730
            new_test.transport_readonly_server = self._transport_readonly_server
2273
1741
    @staticmethod
2274
1742
    def default_test_list():
2275
1743
        """Generate the default list of interrepo permutations to test."""
 
1744
        from bzrlib.repofmt import knitrepo, weaverepo
2276
1745
        result = []
2277
1746
        # test the default InterRepository between format 6 and the current 
2278
1747
        # default format.
2281
1750
        #result.append((InterRepository,
2282
1751
        #               RepositoryFormat6(),
2283
1752
        #               RepositoryFormatKnit1()))
2284
 
        for optimiser in InterRepository._optimisers:
2285
 
            if optimiser._matching_repo_format is not None:
2286
 
                result.append((optimiser,
2287
 
                               optimiser._matching_repo_format,
2288
 
                               optimiser._matching_repo_format
2289
 
                               ))
 
1753
        for optimiser_class in InterRepository._optimisers:
 
1754
            format_to_test = optimiser_class._get_repo_format_to_test()
 
1755
            if format_to_test is not None:
 
1756
                result.append((optimiser_class,
 
1757
                               format_to_test, format_to_test))
2290
1758
        # if there are specific combinations we want to use, we can add them 
2291
1759
        # here.
2292
 
        result.append((InterModel1and2, RepositoryFormat5(),
2293
 
                       RepositoryFormatKnit2()))
2294
 
        result.append((InterKnit1and2, RepositoryFormatKnit1(),
2295
 
                       RepositoryFormatKnit2()))
 
1760
        result.append((InterModel1and2,
 
1761
                       weaverepo.RepositoryFormat5(),
 
1762
                       knitrepo.RepositoryFormatKnit2()))
 
1763
        result.append((InterKnit1and2,
 
1764
                       knitrepo.RepositoryFormatKnit1(),
 
1765
                       knitrepo.RepositoryFormatKnit2()))
2296
1766
        return result
2297
1767
 
2298
1768
 
2379
1849
            self._committer = committer
2380
1850
 
2381
1851
        self.new_inventory = Inventory(None)
2382
 
        self._new_revision_id = revision_id
 
1852
        self._new_revision_id = osutils.safe_revision_id(revision_id)
2383
1853
        self.parents = parents
2384
1854
        self.repository = repository
2385
1855
 
2393
1863
        self._timestamp = round(timestamp, 3)
2394
1864
 
2395
1865
        if timezone is None:
2396
 
            self._timezone = local_time_offset()
 
1866
            self._timezone = osutils.local_time_offset()
2397
1867
        else:
2398
1868
            self._timezone = int(timezone)
2399
1869