~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

Add RepositoryFormats and allow bzrdir.open or create _repository to be used.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
 
17
from copy import deepcopy
17
18
from cStringIO import StringIO
 
19
from unittest import TestSuite
18
20
 
 
21
import bzrlib.bzrdir as bzrdir
19
22
from bzrlib.decorators import needs_read_lock, needs_write_lock
20
23
from bzrlib.errors import InvalidRevisionId
21
24
from bzrlib.lockable_files import LockableFiles
24
27
from bzrlib.store import copy_all
25
28
from bzrlib.store.weave import WeaveStore
26
29
from bzrlib.store.text import TextStore
 
30
from bzrlib.trace import mutter
27
31
from bzrlib.tree import RevisionTree
28
32
from bzrlib.testament import Testament
29
33
from bzrlib.tree import EmptyTree
43
47
    remote) disk.
44
48
    """
45
49
 
46
 
    def __init__(self, transport, branch_format):
47
 
        # circular dependencies:
48
 
        from bzrlib.branch import (BzrBranchFormat4,
49
 
                                   BzrBranchFormat5,
50
 
                                   BzrBranchFormat6,
51
 
                                   )
 
50
    @staticmethod
 
51
    def create(a_bzrdir):
 
52
        """Construct the current default format repository in a_bzrdir."""
 
53
        return RepositoryFormat.get_default_format().initialize(a_bzrdir)
 
54
 
 
55
    def __init__(self, transport, branch_format, _format=None, a_bzrdir=None):
52
56
        object.__init__(self)
53
 
        self.control_files = LockableFiles(transport.clone(bzrlib.BZRDIR), 'README')
 
57
        if transport is not None:
 
58
            self.control_files = LockableFiles(transport.clone(bzrlib.BZRDIR), 'README')
 
59
        else: 
 
60
            # TODO: clone into repository if needed
 
61
            self.control_files = LockableFiles(a_bzrdir.transport, 'README')
54
62
 
55
63
        dir_mode = self.control_files._dir_mode
56
64
        file_mode = self.control_files._file_mode
 
65
        self._format = _format
 
66
        self.bzrdir = a_bzrdir
57
67
 
58
68
        def get_weave(name, prefixed=False):
59
69
            if name:
60
 
                name = bzrlib.BZRDIR + '/' + safe_unicode(name)
 
70
                name = safe_unicode(name)
61
71
            else:
62
 
                name = bzrlib.BZRDIR
 
72
                name = ''
63
73
            relpath = self.control_files._escape(name)
64
 
            weave_transport = transport.clone(relpath)
 
74
            weave_transport = self.control_files._transport.clone(relpath)
65
75
            ws = WeaveStore(weave_transport, prefixed=prefixed,
66
76
                            dir_mode=dir_mode,
67
77
                            file_mode=file_mode)
76
86
            # some existing branches where there's a mixture; we probably 
77
87
            # still want the option to look for both.
78
88
            if name:
79
 
                name = bzrlib.BZRDIR + '/' + safe_unicode(name)
 
89
                name = safe_unicode(name)
80
90
            else:
81
 
                name = bzrlib.BZRDIR
 
91
                name = ''
82
92
            relpath = self.control_files._escape(name)
83
 
            store = TextStore(transport.clone(relpath),
 
93
            store = TextStore(self.control_files._transport.clone(relpath),
84
94
                              prefixed=prefixed, compressed=compressed,
85
95
                              dir_mode=dir_mode,
86
96
                              file_mode=file_mode)
90
100
            #    store = bzrlib.store.CachedStore(store, cache_path)
91
101
            return store
92
102
 
 
103
        if branch_format is not None:
 
104
            # circular dependencies:
 
105
            from bzrlib.branch import (BzrBranchFormat4,
 
106
                                       BzrBranchFormat5,
 
107
                                       BzrBranchFormat6,
 
108
                                       )
 
109
            if isinstance(branch_format, BzrBranchFormat4):
 
110
                self._format = RepositoryFormat4()
 
111
            elif isinstance(branch_format, BzrBranchFormat5):
 
112
                self._format = RepositoryFormat5()
 
113
            elif isinstance(branch_format, BzrBranchFormat6):
 
114
                self._format = RepositoryFormat6()
 
115
            
93
116
 
94
 
        if isinstance(branch_format, BzrBranchFormat4):
 
117
        if isinstance(self._format, RepositoryFormat4):
95
118
            self.inventory_store = get_store('inventory-store')
96
119
            self.text_store = get_store('text-store')
97
120
            self.revision_store = get_store('revision-store')
98
 
        elif isinstance(branch_format, BzrBranchFormat5):
 
121
        elif isinstance(self._format, RepositoryFormat5):
99
122
            self.control_weaves = get_weave('')
100
123
            self.weave_store = get_weave('weaves')
101
124
            self.revision_store = get_store('revision-store', compressed=False)
102
 
        elif isinstance(branch_format, BzrBranchFormat6):
 
125
        elif isinstance(self._format, RepositoryFormat6):
103
126
            self.control_weaves = get_weave('')
104
127
            self.weave_store = get_weave('weaves', prefixed=True)
105
128
            self.revision_store = get_store('revision-store', compressed=False,
112
135
    def lock_read(self):
113
136
        self.control_files.lock_read()
114
137
 
 
138
    @staticmethod
 
139
    def open(base):
 
140
        """Open the repository rooted at base.
 
141
 
 
142
        For instance, if the repository is at URL/.bzr/repository,
 
143
        Repository.open(URL) -> a Repository instance.
 
144
        """
 
145
        control = bzrdir.BzrDir.open(base)
 
146
        return control.open_repository()
 
147
 
115
148
    def unlock(self):
116
149
        self.control_files.unlock()
117
150
 
280
313
    def sign_revision(self, revision_id, gpg_strategy):
281
314
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
282
315
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
 
316
 
 
317
 
 
318
class RepositoryFormat(object):
 
319
    """A repository format.
 
320
 
 
321
    Formats provide three things:
 
322
     * An initialization routine to construct repository data on disk.
 
323
     * a format string which is used when the BzrDir supports versioned
 
324
       children.
 
325
     * an open routine which returns a Repository instance.
 
326
 
 
327
    Formats are placed in an dict by their format string for reference 
 
328
    during opening. These should be subclasses of RepositoryFormat
 
329
    for consistency.
 
330
 
 
331
    Once a format is deprecated, just deprecate the initialize and open
 
332
    methods on the format class. Do not deprecate the object, as the 
 
333
    object will be created every system load.
 
334
 
 
335
    Common instance attributes:
 
336
    _matchingbzrdir - the bzrdir format that the repository format was
 
337
    originally written to work with. This can be used if manually
 
338
    constructing a bzrdir and repository, or more commonly for test suite
 
339
    parameterisation.
 
340
    """
 
341
 
 
342
    _default_format = None
 
343
    """The default format used for new branches."""
 
344
 
 
345
    _formats = {}
 
346
    """The known formats."""
 
347
 
 
348
    @classmethod
 
349
    def get_default_format(klass):
 
350
        """Return the current default format."""
 
351
        return klass._default_format
 
352
 
 
353
    def get_format_string(self):
 
354
        """Return the ASCII format string that identifies this format.
 
355
        
 
356
        Note that in pre format ?? repositories the format string is 
 
357
        not permitted nor written to disk.
 
358
        """
 
359
        raise NotImplementedError(self.get_format_string)
 
360
 
 
361
    def initialize(self, a_bzrdir):
 
362
        """Create a weave repository.
 
363
        
 
364
        TODO: when creating split out bzr branch formats, move this to a common
 
365
        base for Format5, Format6. or something like that.
 
366
        """
 
367
        from bzrlib.weavefile import write_weave_v5
 
368
        from bzrlib.weave import Weave
 
369
        
 
370
        # Create an empty weave
 
371
        sio = StringIO()
 
372
        bzrlib.weavefile.write_weave_v5(Weave(), sio)
 
373
        empty_weave = sio.getvalue()
 
374
 
 
375
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
376
        dirs = ['revision-store', 'weaves']
 
377
        lock_file = 'branch-lock'
 
378
        files = [('inventory.weave', StringIO(empty_weave)), 
 
379
                 ]
 
380
        
 
381
        # FIXME: RBC 20060125 dont peek under the covers
 
382
        # NB: no need to escape relative paths that are url safe.
 
383
        control_files = LockableFiles(a_bzrdir.transport, 'branch-lock')
 
384
        control_files.lock_write()
 
385
        control_files._transport.mkdir_multi(dirs,
 
386
                mode=control_files._dir_mode)
 
387
        try:
 
388
            for file, content in files:
 
389
                control_files.put(file, content)
 
390
        finally:
 
391
            control_files.unlock()
 
392
        return Repository(None, branch_format=None, _format=self, a_bzrdir=a_bzrdir)
 
393
 
 
394
    def is_supported(self):
 
395
        """Is this format supported?
 
396
 
 
397
        Supported formats must be initializable and openable.
 
398
        Unsupported formats may not support initialization or committing or 
 
399
        some other features depending on the reason for not being supported.
 
400
        """
 
401
        return True
 
402
 
 
403
    def open(self, a_bzrdir, _found=False):
 
404
        """Return an instance of this format for the bzrdir a_bzrdir.
 
405
        
 
406
        _found is a private parameter, do not use it.
 
407
        """
 
408
        if not _found:
 
409
            raise NotImplementedError
 
410
        return Repository(None, branch_format=None, _format=self, a_bzrdir=a_bzrdir)
 
411
 
 
412
    @classmethod
 
413
    def register_format(klass, format):
 
414
        klass._formats[format.get_format_string()] = format
 
415
 
 
416
    @classmethod
 
417
    def set_default_format(klass, format):
 
418
        klass._default_format = format
 
419
 
 
420
    @classmethod
 
421
    def unregister_format(klass, format):
 
422
        assert klass._formats[format.get_format_string()] is format
 
423
        del klass._formats[format.get_format_string()]
 
424
 
 
425
 
 
426
class RepositoryFormat4(RepositoryFormat):
 
427
    """Bzr repository format 4.
 
428
 
 
429
    This repository format has:
 
430
     - flat stores
 
431
     - TextStores for texts, inventories,revisions.
 
432
 
 
433
    This format is deprecated: it indexes texts using a text id which is
 
434
    removed in format 5; initializationa and write support for this format
 
435
    has been removed.
 
436
    """
 
437
 
 
438
    def __init__(self):
 
439
        super(RepositoryFormat4, self).__init__()
 
440
        self._matchingbzrdir = bzrdir.BzrDirFormat4()
 
441
 
 
442
    def initialize(self, url):
 
443
        """Format 4 branches cannot be created."""
 
444
        raise errors.UninitializableFormat(self)
 
445
 
 
446
    def is_supported(self):
 
447
        """Format 4 is not supported.
 
448
 
 
449
        It is not supported because the model changed from 4 to 5 and the
 
450
        conversion logic is expensive - so doing it on the fly was not 
 
451
        feasible.
 
452
        """
 
453
        return False
 
454
 
 
455
 
 
456
class RepositoryFormat5(RepositoryFormat):
 
457
    """Bzr control format 5.
 
458
 
 
459
    This repository format has:
 
460
     - weaves for file texts and inventory
 
461
     - flat stores
 
462
     - TextStores for revisions and signatures.
 
463
    """
 
464
 
 
465
    def __init__(self):
 
466
        super(RepositoryFormat5, self).__init__()
 
467
        self._matchingbzrdir = bzrdir.BzrDirFormat5()
 
468
 
 
469
 
 
470
class RepositoryFormat6(RepositoryFormat):
 
471
    """Bzr control format 6.
 
472
 
 
473
    This repository format has:
 
474
     - weaves for file texts and inventory
 
475
     - hash subdirectory based stores.
 
476
     - TextStores for revisions and signatures.
 
477
    """
 
478
 
 
479
    def __init__(self):
 
480
        super(RepositoryFormat6, self).__init__()
 
481
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
 
482
 
 
483
# formats which have no format string are not discoverable
 
484
# and not independently creatable, so are not registered.
 
485
# __default_format = RepositoryFormatXXX()
 
486
# RepositoryFormat.register_format(__default_format)
 
487
# RepositoryFormat.set_default_format(__default_format)
 
488
_legacy_formats = [RepositoryFormat4(),
 
489
                   RepositoryFormat5(),
 
490
                   RepositoryFormat6()]
 
491
 
 
492
 
 
493
class RepositoryTestProviderAdapter(object):
 
494
    """A tool to generate a suite testing multiple repository formats at once.
 
495
 
 
496
    This is done by copying the test once for each transport and injecting
 
497
    the transport_server, transport_readonly_server, and bzrdir_format and
 
498
    repository_format classes into each copy. Each copy is also given a new id()
 
499
    to make it easy to identify.
 
500
    """
 
501
 
 
502
    def __init__(self, transport_server, transport_readonly_server, formats):
 
503
        self._transport_server = transport_server
 
504
        self._transport_readonly_server = transport_readonly_server
 
505
        self._formats = formats
 
506
    
 
507
    def adapt(self, test):
 
508
        result = TestSuite()
 
509
        for repository_format, bzrdir_format in self._formats:
 
510
            new_test = deepcopy(test)
 
511
            new_test.transport_server = self._transport_server
 
512
            new_test.transport_readonly_server = self._transport_readonly_server
 
513
            new_test.bzrdir_format = bzrdir_format
 
514
            new_test.repository_format = repository_format
 
515
            def make_new_test_id():
 
516
                new_id = "%s(%s)" % (new_test.id(), repository_format.__class__.__name__)
 
517
                return lambda: new_id
 
518
            new_test.id = make_new_test_id()
 
519
            result.addTest(new_test)
 
520
        return result