~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: Martin Pool
  • Date: 2006-04-20 02:37:21 UTC
  • mto: This revision was merged to the branch mainline in revision 1675.
  • Revision ID: mbp@sourcefrog.net-20060420023721-04d8a3b015987240
Add .hg to default ignore list

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
"""Deprecated weave-based repository formats.
18
 
 
19
 
Weave based formats scaled linearly with history size and could not represent
20
 
ghosts.
21
 
"""
22
 
 
23
 
from StringIO import StringIO
24
 
 
25
 
from bzrlib import (
26
 
    bzrdir,
27
 
    debug,
28
 
    errors,
29
 
    lockable_files,
30
 
    lockdir,
31
 
    osutils,
32
 
    revision as _mod_revision,
33
 
    weave,
34
 
    weavefile,
35
 
    xml5,
36
 
    )
37
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
38
 
from bzrlib.repository import (
39
 
    CommitBuilder,
40
 
    MetaDirVersionedFileRepository,
41
 
    MetaDirRepositoryFormat,
42
 
    Repository,
43
 
    RepositoryFormat,
44
 
    )
45
 
from bzrlib.store.text import TextStore
46
 
from bzrlib.trace import mutter
47
 
 
48
 
 
49
 
class AllInOneRepository(Repository):
50
 
    """Legacy support - the repository behaviour for all-in-one branches."""
51
 
 
52
 
    _serializer = xml5.serializer_v5
53
 
 
54
 
    def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
55
 
        dir_mode = a_bzrdir._get_dir_mode()
56
 
        file_mode = a_bzrdir._get_file_mode()
57
 
 
58
 
        def get_store(name, compressed=True, prefixed=False):
59
 
            # FIXME: This approach of assuming stores are all entirely compressed
60
 
            # or entirely uncompressed is tidy, but breaks upgrade from 
61
 
            # some existing branches where there's a mixture; we probably 
62
 
            # still want the option to look for both.
63
 
            relpath = a_bzrdir._control_files._escape(name)
64
 
            store = TextStore(a_bzrdir.transport.clone(relpath),
65
 
                              prefixed=prefixed, compressed=compressed,
66
 
                              dir_mode=dir_mode,
67
 
                              file_mode=file_mode)
68
 
            return store
69
 
 
70
 
        # not broken out yet because the controlweaves|inventory_store
71
 
        # and text_store | weave_store bits are still different.
72
 
        if isinstance(_format, RepositoryFormat4):
73
 
            # cannot remove these - there is still no consistent api 
74
 
            # which allows access to this old info.
75
 
            self.inventory_store = get_store('inventory-store')
76
 
            text_store = get_store('text-store')
77
 
        super(AllInOneRepository, self).__init__(_format,
78
 
            a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
79
 
        if control_store is not None:
80
 
            control_store.get_scope = self.get_transaction
81
 
        text_store.get_scope = self.get_transaction
82
 
 
83
 
    @needs_read_lock
84
 
    def _all_possible_ids(self):
85
 
        """Return all the possible revisions that we could find."""
86
 
        if 'evil' in debug.debug_flags:
87
 
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
88
 
        return self.get_inventory_weave().versions()
89
 
 
90
 
    @needs_read_lock
91
 
    def _all_revision_ids(self):
92
 
        """Returns a list of all the revision ids in the repository. 
93
 
 
94
 
        These are in as much topological order as the underlying store can 
95
 
        present: for weaves ghosts may lead to a lack of correctness until
96
 
        the reweave updates the parents list.
97
 
        """
98
 
        if self._revision_store.text_store.listable():
99
 
            return self._revision_store.all_revision_ids(self.get_transaction())
100
 
        result = self._all_possible_ids()
101
 
        # TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
102
 
        #       ids. (It should, since _revision_store's API should change to
103
 
        #       return utf8 revision_ids)
104
 
        return self._eliminate_revisions_not_present(result)
105
 
 
106
 
    def _check_revision_parents(self, revision, inventory):
107
 
        """Private to Repository and Fetch.
108
 
        
109
 
        This checks the parentage of revision in an inventory weave for 
110
 
        consistency and is only applicable to inventory-weave-for-ancestry
111
 
        using repository formats & fetchers.
112
 
        """
113
 
        weave_parents = inventory.get_parent_map(
114
 
            [revision.revision_id])[revision.revision_id]
115
 
        parent_map = inventory.get_parent_map(revision.parent_ids)
116
 
        for parent_id in revision.parent_ids:
117
 
            if parent_id in parent_map:
118
 
                # this parent must not be a ghost.
119
 
                if not parent_id in weave_parents:
120
 
                    # but it is a ghost
121
 
                    raise errors.CorruptRepository(self)
122
 
 
123
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
124
 
                           timezone=None, committer=None, revprops=None,
125
 
                           revision_id=None):
126
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
127
 
        result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
128
 
                              committer, revprops, revision_id)
129
 
        self.start_write_group()
130
 
        return result
131
 
 
132
 
    @needs_read_lock
133
 
    def get_revisions(self, revision_ids):
134
 
        revs = self._get_revisions(revision_ids)
135
 
        # weave corruption can lead to absent revision markers that should be
136
 
        # present.
137
 
        # the following test is reasonably cheap (it needs a single weave read)
138
 
        # and the weave is cached in read transactions. In write transactions
139
 
        # it is not cached but typically we only read a small number of
140
 
        # revisions. For knits when they are introduced we will probably want
141
 
        # to ensure that caching write transactions are in use.
142
 
        inv = self.get_inventory_weave()
143
 
        for rev in revs:
144
 
            self._check_revision_parents(rev, inv)
145
 
        return revs
146
 
 
147
 
    def has_revisions(self, revision_ids):
148
 
        """See Repository.has_revisions()."""
149
 
        result = set()
150
 
        transaction = self.get_transaction()
151
 
        for revision_id in revision_ids:
152
 
            if self._revision_store.has_revision_id(revision_id, transaction):
153
 
                result.add(revision_id)
154
 
        return result
155
 
 
156
 
    @needs_read_lock
157
 
    def is_shared(self):
158
 
        """AllInOne repositories cannot be shared."""
159
 
        return False
160
 
 
161
 
    @needs_write_lock
162
 
    def set_make_working_trees(self, new_value):
163
 
        """Set the policy flag for making working trees when creating branches.
164
 
 
165
 
        This only applies to branches that use this repository.
166
 
 
167
 
        The default is 'True'.
168
 
        :param new_value: True to restore the default, False to disable making
169
 
                          working trees.
170
 
        """
171
 
        raise errors.RepositoryUpgradeRequired(self.bzrdir.root_transport.base)
172
 
 
173
 
    def make_working_trees(self):
174
 
        """Returns the policy for making working trees on new branches."""
175
 
        return True
176
 
 
177
 
    def revision_graph_can_have_wrong_parents(self):
178
 
        # XXX: This is an old format that we don't support full checking on, so
179
 
        # just claim that checking for this inconsistency is not required.
180
 
        return False
181
 
 
182
 
 
183
 
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
184
 
    """A subclass of MetaDirRepository to set weave specific policy."""
185
 
 
186
 
    _serializer = xml5.serializer_v5
187
 
 
188
 
    @needs_read_lock
189
 
    def _all_possible_ids(self):
190
 
        """Return all the possible revisions that we could find."""
191
 
        if 'evil' in debug.debug_flags:
192
 
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
193
 
        return self.get_inventory_weave().versions()
194
 
 
195
 
    @needs_read_lock
196
 
    def _all_revision_ids(self):
197
 
        """Returns a list of all the revision ids in the repository. 
198
 
 
199
 
        These are in as much topological order as the underlying store can 
200
 
        present: for weaves ghosts may lead to a lack of correctness until
201
 
        the reweave updates the parents list.
202
 
        """
203
 
        if self._revision_store.text_store.listable():
204
 
            return self._revision_store.all_revision_ids(self.get_transaction())
205
 
        result = self._all_possible_ids()
206
 
        # TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
207
 
        #       ids. (It should, since _revision_store's API should change to
208
 
        #       return utf8 revision_ids)
209
 
        return self._eliminate_revisions_not_present(result)
210
 
 
211
 
    def _check_revision_parents(self, revision, inventory):
212
 
        """Private to Repository and Fetch.
213
 
        
214
 
        This checks the parentage of revision in an inventory weave for 
215
 
        consistency and is only applicable to inventory-weave-for-ancestry
216
 
        using repository formats & fetchers.
217
 
        """
218
 
        weave_parents = inventory.get_parent_map(
219
 
            [revision.revision_id])[revision.revision_id]
220
 
        parent_map = inventory.get_parent_map(revision.parent_ids)
221
 
        for parent_id in revision.parent_ids:
222
 
            if parent_id in parent_map:
223
 
                # this parent must not be a ghost.
224
 
                if not parent_id in weave_parents:
225
 
                    # but it is a ghost
226
 
                    raise errors.CorruptRepository(self)
227
 
 
228
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
229
 
                           timezone=None, committer=None, revprops=None,
230
 
                           revision_id=None):
231
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
232
 
        result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
233
 
                              committer, revprops, revision_id)
234
 
        self.start_write_group()
235
 
        return result
236
 
 
237
 
    @needs_read_lock
238
 
    def get_revision(self, revision_id):
239
 
        """Return the Revision object for a named revision"""
240
 
        # TODO: jam 20070210 get_revision_reconcile should do this for us
241
 
        r = self.get_revision_reconcile(revision_id)
242
 
        # weave corruption can lead to absent revision markers that should be
243
 
        # present.
244
 
        # the following test is reasonably cheap (it needs a single weave read)
245
 
        # and the weave is cached in read transactions. In write transactions
246
 
        # it is not cached but typically we only read a small number of
247
 
        # revisions. For knits when they are introduced we will probably want
248
 
        # to ensure that caching write transactions are in use.
249
 
        inv = self.get_inventory_weave()
250
 
        self._check_revision_parents(r, inv)
251
 
        return r
252
 
 
253
 
    def has_revisions(self, revision_ids):
254
 
        """See Repository.has_revisions()."""
255
 
        result = set()
256
 
        transaction = self.get_transaction()
257
 
        for revision_id in revision_ids:
258
 
            if self._revision_store.has_revision_id(revision_id, transaction):
259
 
                result.add(revision_id)
260
 
        return result
261
 
 
262
 
    def revision_graph_can_have_wrong_parents(self):
263
 
        # XXX: This is an old format that we don't support full checking on, so
264
 
        # just claim that checking for this inconsistency is not required.
265
 
        return False
266
 
 
267
 
 
268
 
class PreSplitOutRepositoryFormat(RepositoryFormat):
269
 
    """Base class for the pre split out repository formats."""
270
 
 
271
 
    rich_root_data = False
272
 
    supports_tree_reference = False
273
 
    supports_ghosts = False
274
 
    supports_external_lookups = False
275
 
 
276
 
    def initialize(self, a_bzrdir, shared=False, _internal=False):
277
 
        """Create a weave repository."""
278
 
        if shared:
279
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
280
 
 
281
 
        if not _internal:
282
 
            # always initialized when the bzrdir is.
283
 
            return self.open(a_bzrdir, _found=True)
284
 
        
285
 
        # Create an empty weave
286
 
        sio = StringIO()
287
 
        weavefile.write_weave_v5(weave.Weave(), sio)
288
 
        empty_weave = sio.getvalue()
289
 
 
290
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
291
 
        
292
 
        # FIXME: RBC 20060125 don't peek under the covers
293
 
        # NB: no need to escape relative paths that are url safe.
294
 
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
295
 
            'branch-lock', lockable_files.TransportLock)
296
 
        control_files.create_lock()
297
 
        control_files.lock_write()
298
 
        transport = a_bzrdir.transport
299
 
        try:
300
 
            transport.mkdir_multi(['revision-store', 'weaves'],
301
 
                mode=a_bzrdir._get_dir_mode())
302
 
            transport.put_bytes_non_atomic('inventory.weave', empty_weave)
303
 
        finally:
304
 
            control_files.unlock()
305
 
        return self.open(a_bzrdir, _found=True)
306
 
 
307
 
    def _get_control_store(self, repo_transport, control_files):
308
 
        """Return the control store for this repository."""
309
 
        return self._get_versioned_file_store('',
310
 
                                              repo_transport,
311
 
                                              control_files,
312
 
                                              prefixed=False)
313
 
 
314
 
    def _get_text_store(self, transport, control_files):
315
 
        """Get a store for file texts for this format."""
316
 
        raise NotImplementedError(self._get_text_store)
317
 
 
318
 
    def open(self, a_bzrdir, _found=False):
319
 
        """See RepositoryFormat.open()."""
320
 
        if not _found:
321
 
            # we are being called directly and must probe.
322
 
            raise NotImplementedError
323
 
 
324
 
        repo_transport = a_bzrdir.get_repository_transport(None)
325
 
        control_files = a_bzrdir._control_files
326
 
        text_store = self._get_text_store(repo_transport, control_files)
327
 
        control_store = self._get_control_store(repo_transport, control_files)
328
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
329
 
        return AllInOneRepository(_format=self,
330
 
                                  a_bzrdir=a_bzrdir,
331
 
                                  _revision_store=_revision_store,
332
 
                                  control_store=control_store,
333
 
                                  text_store=text_store)
334
 
 
335
 
    def check_conversion_target(self, target_format):
336
 
        pass
337
 
 
338
 
 
339
 
class RepositoryFormat4(PreSplitOutRepositoryFormat):
340
 
    """Bzr repository format 4.
341
 
 
342
 
    This repository format has:
343
 
     - flat stores
344
 
     - TextStores for texts, inventories,revisions.
345
 
 
346
 
    This format is deprecated: it indexes texts using a text id which is
347
 
    removed in format 5; initialization and write support for this format
348
 
    has been removed.
349
 
    """
350
 
 
351
 
    _matchingbzrdir = bzrdir.BzrDirFormat4()
352
 
 
353
 
    def __init__(self):
354
 
        super(RepositoryFormat4, self).__init__()
355
 
 
356
 
    def get_format_description(self):
357
 
        """See RepositoryFormat.get_format_description()."""
358
 
        return "Repository format 4"
359
 
 
360
 
    def initialize(self, url, shared=False, _internal=False):
361
 
        """Format 4 branches cannot be created."""
362
 
        raise errors.UninitializableFormat(self)
363
 
 
364
 
    def is_supported(self):
365
 
        """Format 4 is not supported.
366
 
 
367
 
        It is not supported because the model changed from 4 to 5 and the
368
 
        conversion logic is expensive - so doing it on the fly was not 
369
 
        feasible.
370
 
        """
371
 
        return False
372
 
 
373
 
    def _get_control_store(self, repo_transport, control_files):
374
 
        """Format 4 repositories have no formal control store at this point.
375
 
        
376
 
        This will cause any control-file-needing apis to fail - this is desired.
377
 
        """
378
 
        return None
379
 
    
380
 
    def _get_revision_store(self, repo_transport, control_files):
381
 
        """See RepositoryFormat._get_revision_store()."""
382
 
        from bzrlib.xml4 import serializer_v4
383
 
        return self._get_text_rev_store(repo_transport,
384
 
                                        control_files,
385
 
                                        'revision-store',
386
 
                                        serializer=serializer_v4)
387
 
 
388
 
    def _get_text_store(self, transport, control_files):
389
 
        """See RepositoryFormat._get_text_store()."""
390
 
 
391
 
 
392
 
class RepositoryFormat5(PreSplitOutRepositoryFormat):
393
 
    """Bzr control format 5.
394
 
 
395
 
    This repository format has:
396
 
     - weaves for file texts and inventory
397
 
     - flat stores
398
 
     - TextStores for revisions and signatures.
399
 
    """
400
 
 
401
 
    _versionedfile_class = weave.WeaveFile
402
 
    _matchingbzrdir = bzrdir.BzrDirFormat5()
403
 
 
404
 
    def __init__(self):
405
 
        super(RepositoryFormat5, self).__init__()
406
 
 
407
 
    def get_format_description(self):
408
 
        """See RepositoryFormat.get_format_description()."""
409
 
        return "Weave repository format 5"
410
 
 
411
 
    def _get_revision_store(self, repo_transport, control_files):
412
 
        """See RepositoryFormat._get_revision_store()."""
413
 
        """Return the revision store object for this a_bzrdir."""
414
 
        return self._get_text_rev_store(repo_transport,
415
 
                                        control_files,
416
 
                                        'revision-store',
417
 
                                        compressed=False)
418
 
 
419
 
    def _get_text_store(self, transport, control_files):
420
 
        """See RepositoryFormat._get_text_store()."""
421
 
        return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
422
 
 
423
 
 
424
 
class RepositoryFormat6(PreSplitOutRepositoryFormat):
425
 
    """Bzr control format 6.
426
 
 
427
 
    This repository format has:
428
 
     - weaves for file texts and inventory
429
 
     - hash subdirectory based stores.
430
 
     - TextStores for revisions and signatures.
431
 
    """
432
 
 
433
 
    _versionedfile_class = weave.WeaveFile
434
 
    _matchingbzrdir = bzrdir.BzrDirFormat6()
435
 
 
436
 
    def __init__(self):
437
 
        super(RepositoryFormat6, self).__init__()
438
 
 
439
 
    def get_format_description(self):
440
 
        """See RepositoryFormat.get_format_description()."""
441
 
        return "Weave repository format 6"
442
 
 
443
 
    def _get_revision_store(self, repo_transport, control_files):
444
 
        """See RepositoryFormat._get_revision_store()."""
445
 
        return self._get_text_rev_store(repo_transport,
446
 
                                        control_files,
447
 
                                        'revision-store',
448
 
                                        compressed=False,
449
 
                                        prefixed=True)
450
 
 
451
 
    def _get_text_store(self, transport, control_files):
452
 
        """See RepositoryFormat._get_text_store()."""
453
 
        return self._get_versioned_file_store('weaves', transport, control_files)
454
 
 
455
 
class RepositoryFormat7(MetaDirRepositoryFormat):
456
 
    """Bzr repository 7.
457
 
 
458
 
    This repository format has:
459
 
     - weaves for file texts and inventory
460
 
     - hash subdirectory based stores.
461
 
     - TextStores for revisions and signatures.
462
 
     - a format marker of its own
463
 
     - an optional 'shared-storage' flag
464
 
     - an optional 'no-working-trees' flag
465
 
    """
466
 
 
467
 
    _versionedfile_class = weave.WeaveFile
468
 
    supports_ghosts = False
469
 
 
470
 
    def _get_control_store(self, repo_transport, control_files):
471
 
        """Return the control store for this repository."""
472
 
        return self._get_versioned_file_store('',
473
 
                                              repo_transport,
474
 
                                              control_files,
475
 
                                              prefixed=False)
476
 
 
477
 
    def get_format_string(self):
478
 
        """See RepositoryFormat.get_format_string()."""
479
 
        return "Bazaar-NG Repository format 7"
480
 
 
481
 
    def get_format_description(self):
482
 
        """See RepositoryFormat.get_format_description()."""
483
 
        return "Weave repository format 7"
484
 
 
485
 
    def check_conversion_target(self, target_format):
486
 
        pass
487
 
 
488
 
    def _get_revision_store(self, repo_transport, control_files):
489
 
        """See RepositoryFormat._get_revision_store()."""
490
 
        return self._get_text_rev_store(repo_transport,
491
 
                                        control_files,
492
 
                                        'revision-store',
493
 
                                        compressed=False,
494
 
                                        prefixed=True,
495
 
                                        )
496
 
 
497
 
    def _get_text_store(self, transport, control_files):
498
 
        """See RepositoryFormat._get_text_store()."""
499
 
        return self._get_versioned_file_store('weaves',
500
 
                                              transport,
501
 
                                              control_files)
502
 
 
503
 
    def initialize(self, a_bzrdir, shared=False):
504
 
        """Create a weave repository.
505
 
 
506
 
        :param shared: If true the repository will be initialized as a shared
507
 
                       repository.
508
 
        """
509
 
        # Create an empty weave
510
 
        sio = StringIO()
511
 
        weavefile.write_weave_v5(weave.Weave(), sio)
512
 
        empty_weave = sio.getvalue()
513
 
 
514
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
515
 
        dirs = ['revision-store', 'weaves']
516
 
        files = [('inventory.weave', StringIO(empty_weave)), 
517
 
                 ]
518
 
        utf8_files = [('format', self.get_format_string())]
519
 
 
520
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
521
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
522
 
 
523
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
524
 
        """See RepositoryFormat.open().
525
 
        
526
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
527
 
                                    repository at a slightly different url
528
 
                                    than normal. I.e. during 'upgrade'.
529
 
        """
530
 
        if not _found:
531
 
            format = RepositoryFormat.find_format(a_bzrdir)
532
 
        if _override_transport is not None:
533
 
            repo_transport = _override_transport
534
 
        else:
535
 
            repo_transport = a_bzrdir.get_repository_transport(None)
536
 
        control_files = lockable_files.LockableFiles(repo_transport,
537
 
                                'lock', lockdir.LockDir)
538
 
        text_store = self._get_text_store(repo_transport, control_files)
539
 
        control_store = self._get_control_store(repo_transport, control_files)
540
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
541
 
        return WeaveMetaDirRepository(_format=self,
542
 
            a_bzrdir=a_bzrdir,
543
 
            control_files=control_files,
544
 
            _revision_store=_revision_store,
545
 
            control_store=control_store,
546
 
            text_store=text_store)
547
 
 
548
 
 
549
 
class WeaveCommitBuilder(CommitBuilder):
550
 
    """A builder for weave based repos that don't support ghosts."""
551
 
 
552
 
    def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
553
 
        versionedfile = self.repository.weave_store.get_weave_or_empty(
554
 
            file_id, self.repository.get_transaction())
555
 
        result = versionedfile.add_lines(
556
 
            self._new_revision_id, parents, new_lines,
557
 
            nostore_sha=nostore_sha)[0:2]
558
 
        return result
559
 
 
560
 
 
561
 
_legacy_formats = [RepositoryFormat4(),
562
 
                   RepositoryFormat5(),
563
 
                   RepositoryFormat6()]