~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/knitrepo.py

  • Committer: Alexander Belchenko
  • Date: 2007-08-10 09:04:38 UTC
  • mto: This revision was merged to the branch mainline in revision 2694.
  • Revision ID: bialix@ukr.net-20070810090438-0835xdz0rl8825qv
fixes after Ian's review

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 bzrlib.lazy_import import lazy_import
18
 
lazy_import(globals(), """
19
 
from bzrlib import (
20
 
    debug,
21
 
    )
22
 
from bzrlib.store import revision
23
 
from bzrlib.store.revision.knit import KnitRevisionStore
24
 
""")
25
17
from bzrlib import (
26
18
    bzrdir,
27
19
    deprecated_graph,
30
22
    lockable_files,
31
23
    lockdir,
32
24
    osutils,
33
 
    symbol_versioning,
34
25
    transactions,
35
26
    xml5,
36
 
    xml6,
37
27
    xml7,
38
28
    )
39
29
 
40
30
from bzrlib.decorators import needs_read_lock, needs_write_lock
41
31
from bzrlib.repository import (
42
 
    CommitBuilder,
43
32
    MetaDirRepository,
44
33
    MetaDirRepositoryFormat,
45
34
    RepositoryFormat,
47
36
    )
48
37
import bzrlib.revision as _mod_revision
49
38
from bzrlib.store.versioned import VersionedFileStore
50
 
from bzrlib.trace import mutter, mutter_callsite
51
 
from bzrlib.util import bencode
 
39
from bzrlib.trace import mutter, note, warning
52
40
 
53
41
 
54
42
class _KnitParentsProvider(object):
59
47
    def __repr__(self):
60
48
        return 'KnitParentsProvider(%r)' % self._knit
61
49
 
62
 
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
63
50
    def get_parents(self, revision_ids):
64
 
        """See graph._StackedParentsProvider.get_parents"""
65
 
        parent_map = self.get_parent_map(revision_ids)
66
 
        return [parent_map.get(r, None) for r in revision_ids]
67
 
 
68
 
    def get_parent_map(self, keys):
69
 
        """See graph._StackedParentsProvider.get_parent_map"""
70
 
        parent_map = {}
71
 
        for revision_id in keys:
 
51
        parents_list = []
 
52
        for revision_id in revision_ids:
72
53
            if revision_id == _mod_revision.NULL_REVISION:
73
 
                parent_map[revision_id] = ()
 
54
                parents = []
74
55
            else:
75
56
                try:
76
 
                    parents = tuple(
77
 
                        self._knit.get_parents_with_ghosts(revision_id))
 
57
                    parents = self._knit.get_parents_with_ghosts(revision_id)
78
58
                except errors.RevisionNotPresent:
79
 
                    continue
 
59
                    parents = None
80
60
                else:
81
61
                    if len(parents) == 0:
82
 
                        parents = (_mod_revision.NULL_REVISION,)
83
 
                parent_map[revision_id] = parents
84
 
        return parent_map
 
62
                        parents = [_mod_revision.NULL_REVISION]
 
63
            parents_list.append(parents)
 
64
        return parents_list
85
65
 
86
66
 
87
67
class KnitRepository(MetaDirRepository):
88
68
    """Knit format repository."""
89
69
 
90
 
    # These attributes are inherited from the Repository base class. Setting
91
 
    # them to None ensures that if the constructor is changed to not initialize
92
 
    # them, or a subclass fails to call the constructor, that an error will
93
 
    # occur rather than the system working but generating incorrect data.
94
 
    _commit_builder_class = None
95
 
    _serializer = None
96
 
 
97
 
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
98
 
        control_store, text_store, _commit_builder_class, _serializer):
99
 
        MetaDirRepository.__init__(self, _format, a_bzrdir, control_files,
100
 
            _revision_store, control_store, text_store)
101
 
        self._commit_builder_class = _commit_builder_class
102
 
        self._serializer = _serializer
103
 
        self._reconcile_fixes_text_parents = True
 
70
    _serializer = xml5.serializer_v5
104
71
 
105
72
    def _warn_if_deprecated(self):
106
73
        # This class isn't deprecated
107
74
        pass
108
75
 
109
 
    def _inventory_add_lines(self, inv_vf, revid, parents, lines, check_content):
110
 
        return inv_vf.add_lines_with_ghosts(revid, parents, lines,
111
 
            check_content=check_content)[0]
 
76
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
 
77
        inv_vf.add_lines_with_ghosts(revid, parents, lines)
112
78
 
113
79
    @needs_read_lock
114
80
    def _all_revision_ids(self):
123
89
        This determines the set of revisions which are involved, and then
124
90
        finds all file ids affected by those revisions.
125
91
        """
 
92
        from_revid = osutils.safe_revision_id(from_revid)
 
93
        to_revid = osutils.safe_revision_id(to_revid)
126
94
        vf = self._get_revision_vf()
127
95
        from_set = set(vf.get_ancestry(from_revid))
128
96
        to_set = set(vf.get_ancestry(to_revid))
151
119
        """
152
120
        if _mod_revision.is_null(revision_id):
153
121
            return [None]
 
122
        revision_id = osutils.safe_revision_id(revision_id)
154
123
        vf = self._get_revision_vf()
155
124
        try:
156
125
            return [None] + vf.get_ancestry(revision_id, topo_sorted)
157
126
        except errors.RevisionNotPresent:
158
127
            raise errors.NoSuchRevision(self, revision_id)
159
128
 
160
 
    @symbol_versioning.deprecated_method(symbol_versioning.one_two)
161
 
    @needs_read_lock
162
 
    def get_data_stream(self, revision_ids):
163
 
        """See Repository.get_data_stream.
164
 
        
165
 
        Deprecated in 1.2 for get_data_stream_for_search.
166
 
        """
167
 
        search_result = self.revision_ids_to_search_result(set(revision_ids))
168
 
        return self.get_data_stream_for_search(search_result)
169
 
 
170
 
    @needs_read_lock
171
 
    def get_data_stream_for_search(self, search):
172
 
        """See Repository.get_data_stream_for_search."""
173
 
        item_keys = self.item_keys_introduced_by(search.get_keys())
174
 
        for knit_kind, file_id, versions in item_keys:
175
 
            name = (knit_kind,)
176
 
            if knit_kind == 'file':
177
 
                name = ('file', file_id)
178
 
                knit = self.weave_store.get_weave_or_empty(
179
 
                    file_id, self.get_transaction())
180
 
            elif knit_kind == 'inventory':
181
 
                knit = self.get_inventory_weave()
182
 
            elif knit_kind == 'revisions':
183
 
                knit = self._revision_store.get_revision_file(
184
 
                    self.get_transaction())
185
 
            elif knit_kind == 'signatures':
186
 
                knit = self._revision_store.get_signature_file(
187
 
                    self.get_transaction())
188
 
            else:
189
 
                raise AssertionError('Unknown knit kind %r' % (knit_kind,))
190
 
            yield name, _get_stream_as_bytes(knit, versions)
191
 
 
192
129
    @needs_read_lock
193
130
    def get_revision(self, revision_id):
194
131
        """Return the Revision object for a named revision"""
204
141
        operation and will be removed in the future.
205
142
        :return: a dictionary of revision_id->revision_parents_list.
206
143
        """
207
 
        if 'evil' in debug.debug_flags:
208
 
            mutter_callsite(3,
209
 
                "get_revision_graph scales with size of history.")
210
144
        # special case NULL_REVISION
211
145
        if revision_id == _mod_revision.NULL_REVISION:
212
146
            return {}
 
147
        revision_id = osutils.safe_revision_id(revision_id)
213
148
        a_weave = self._get_revision_vf()
214
149
        if revision_id is None:
215
150
            return a_weave.get_graph()
216
 
        if revision_id not in a_weave:
 
151
        elif revision_id not in a_weave:
217
152
            raise errors.NoSuchRevision(self, revision_id)
218
153
        else:
219
154
            # add what can be reached from revision_id
220
155
            return a_weave.get_graph([revision_id])
221
156
 
222
157
    @needs_read_lock
223
 
    @symbol_versioning.deprecated_method(symbol_versioning.one_three)
224
158
    def get_revision_graph_with_ghosts(self, revision_ids=None):
225
159
        """Return a graph of the revisions with ghosts marked as applicable.
226
160
 
227
161
        :param revision_ids: an iterable of revisions to graph or None for all.
228
162
        :return: a Graph object with the graph reachable from revision_ids.
229
163
        """
230
 
        if 'evil' in debug.debug_flags:
231
 
            mutter_callsite(3,
232
 
                "get_revision_graph_with_ghosts scales with size of history.")
233
164
        result = deprecated_graph.Graph()
234
165
        vf = self._get_revision_vf()
235
166
        versions = set(vf.versions())
237
168
            pending = set(self.all_revision_ids())
238
169
            required = set([])
239
170
        else:
240
 
            pending = set(revision_ids)
 
171
            pending = set(osutils.safe_revision_id(r) for r in revision_ids)
241
172
            # special case NULL_REVISION
242
173
            if _mod_revision.NULL_REVISION in pending:
243
174
                pending.remove(_mod_revision.NULL_REVISION)
276
207
        """
277
208
        return self._get_revision_vf()
278
209
 
279
 
    def has_revisions(self, revision_ids):
280
 
        """See Repository.has_revisions()."""
281
 
        result = set()
282
 
        transaction = self.get_transaction()
283
 
        for revision_id in revision_ids:
284
 
            if self._revision_store.has_revision_id(revision_id, transaction):
285
 
                result.add(revision_id)
286
 
        return result
287
 
 
288
210
    @needs_write_lock
289
211
    def reconcile(self, other=None, thorough=False):
290
212
        """Reconcile this repository."""
294
216
        return reconciler
295
217
    
296
218
    def revision_parents(self, revision_id):
 
219
        revision_id = osutils.safe_revision_id(revision_id)
297
220
        return self._get_revision_vf().get_parents(revision_id)
298
221
 
299
222
    def _make_parents_provider(self):
300
223
        return _KnitParentsProvider(self._get_revision_vf())
301
224
 
302
 
    def _find_inconsistent_revision_parents(self):
303
 
        """Find revisions with different parent lists in the revision object
304
 
        and in the index graph.
305
 
 
306
 
        :returns: an iterator yielding tuples of (revison-id, parents-in-index,
307
 
            parents-in-revision).
308
 
        """
309
 
        assert self.is_locked()
310
 
        vf = self._get_revision_vf()
311
 
        for index_version in vf.versions():
312
 
            parents_according_to_index = tuple(vf.get_parents_with_ghosts(
313
 
                index_version))
314
 
            revision = self.get_revision(index_version)
315
 
            parents_according_to_revision = tuple(revision.parent_ids)
316
 
            if parents_according_to_index != parents_according_to_revision:
317
 
                yield (index_version, parents_according_to_index,
318
 
                    parents_according_to_revision)
319
 
 
320
 
    def _check_for_inconsistent_revision_parents(self):
321
 
        inconsistencies = list(self._find_inconsistent_revision_parents())
322
 
        if inconsistencies:
323
 
            raise errors.BzrCheckError(
324
 
                "Revision knit has inconsistent parents.")
325
 
 
326
 
    def revision_graph_can_have_wrong_parents(self):
327
 
        # The revision.kndx could potentially claim a revision has a different
328
 
        # parent to the revision text.
329
 
        return True
 
225
 
 
226
class KnitRepository3(KnitRepository):
 
227
 
 
228
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
 
229
                 control_store, text_store):
 
230
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
 
231
                              _revision_store, control_store, text_store)
 
232
        self._serializer = xml7.serializer_v7
 
233
 
 
234
    def deserialise_inventory(self, revision_id, xml):
 
235
        """Transform the xml into an inventory object. 
 
236
 
 
237
        :param revision_id: The expected revision id of the inventory.
 
238
        :param xml: A serialised inventory.
 
239
        """
 
240
        result = self._serializer.read_inventory_from_string(xml)
 
241
        assert result.root.revision is not None
 
242
        return result
 
243
 
 
244
    def serialise_inventory(self, inv):
 
245
        """Transform the inventory object into XML text.
 
246
 
 
247
        :param revision_id: The expected revision id of the inventory.
 
248
        :param xml: A serialised inventory.
 
249
        """
 
250
        assert inv.revision_id is not None
 
251
        assert inv.root.revision is not None
 
252
        return KnitRepository.serialise_inventory(self, inv)
 
253
 
 
254
    def get_commit_builder(self, branch, parents, config, timestamp=None,
 
255
                           timezone=None, committer=None, revprops=None,
 
256
                           revision_id=None):
 
257
        """Obtain a CommitBuilder for this repository.
 
258
        
 
259
        :param branch: Branch to commit to.
 
260
        :param parents: Revision ids of the parents of the new revision.
 
261
        :param config: Configuration to use.
 
262
        :param timestamp: Optional timestamp recorded for commit.
 
263
        :param timezone: Optional timezone for timestamp.
 
264
        :param committer: Optional committer to set for commit.
 
265
        :param revprops: Optional dictionary of revision properties.
 
266
        :param revision_id: Optional revision id.
 
267
        """
 
268
        revision_id = osutils.safe_revision_id(revision_id)
 
269
        result = RootCommitBuilder(self, parents, config, timestamp, timezone,
 
270
                                 committer, revprops, revision_id)
 
271
        self.start_write_group()
 
272
        return result
330
273
 
331
274
 
332
275
class RepositoryFormatKnit(MetaDirRepositoryFormat):
343
286
     - a LockDir lock
344
287
    """
345
288
 
346
 
    # Set this attribute in derived classes to control the repository class
347
 
    # created by open and initialize.
348
 
    repository_class = None
349
 
    # Set this attribute in derived classes to control the
350
 
    # _commit_builder_class that the repository objects will have passed to
351
 
    # their constructor.
352
 
    _commit_builder_class = None
353
 
    # Set this attribute in derived clases to control the _serializer that the
354
 
    # repository objects will have passed to their constructor.
355
 
    _serializer = xml5.serializer_v5
356
 
    # Knit based repositories handle ghosts reasonably well.
357
 
    supports_ghosts = True
358
 
    # External lookups are not supported in this format.
359
 
    supports_external_lookups = False
360
 
 
361
289
    def _get_control_store(self, repo_transport, control_files):
362
290
        """Return the control store for this repository."""
363
291
        return VersionedFileStore(
370
298
 
371
299
    def _get_revision_store(self, repo_transport, control_files):
372
300
        """See RepositoryFormat._get_revision_store()."""
 
301
        from bzrlib.store.revision.knit import KnitRevisionStore
373
302
        versioned_file_store = VersionedFileStore(
374
303
            repo_transport,
375
304
            file_mode=control_files._file_mode,
405
334
                       repository.
406
335
        """
407
336
        mutter('creating repository in %s.', a_bzrdir.transport.base)
408
 
        dirs = ['knits']
 
337
        dirs = ['revision-store', 'knits']
409
338
        files = []
410
339
        utf8_files = [('format', self.get_format_string())]
411
340
        
443
372
        text_store = self._get_text_store(repo_transport, control_files)
444
373
        control_store = self._get_control_store(repo_transport, control_files)
445
374
        _revision_store = self._get_revision_store(repo_transport, control_files)
446
 
        return self.repository_class(_format=self,
 
375
        return KnitRepository(_format=self,
447
376
                              a_bzrdir=a_bzrdir,
448
377
                              control_files=control_files,
449
378
                              _revision_store=_revision_store,
450
379
                              control_store=control_store,
451
 
                              text_store=text_store,
452
 
                              _commit_builder_class=self._commit_builder_class,
453
 
                              _serializer=self._serializer)
 
380
                              text_store=text_store)
454
381
 
455
382
 
456
383
class RepositoryFormatKnit1(RepositoryFormatKnit):
469
396
    This format was introduced in bzr 0.8.
470
397
    """
471
398
 
472
 
    repository_class = KnitRepository
473
 
    _commit_builder_class = CommitBuilder
474
 
    _serializer = xml5.serializer_v5
475
 
 
476
399
    def __ne__(self, other):
477
400
        return self.__class__ is not other.__class__
478
401
 
489
412
 
490
413
 
491
414
class RepositoryFormatKnit3(RepositoryFormatKnit):
492
 
    """Bzr repository knit format 3.
 
415
    """Bzr repository knit format 2.
493
416
 
494
417
    This repository format has:
495
418
     - knits for file texts and inventory
504
427
     - support for recording tree-references
505
428
    """
506
429
 
507
 
    repository_class = KnitRepository
508
 
    _commit_builder_class = RootCommitBuilder
 
430
    repository_class = KnitRepository3
509
431
    rich_root_data = True
510
432
    supports_tree_reference = True
511
 
    _serializer = xml7.serializer_v7
512
433
 
513
434
    def _get_matching_bzrdir(self):
514
435
        return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
534
455
        """See RepositoryFormat.get_format_description()."""
535
456
        return "Knit repository format 3"
536
457
 
537
 
 
538
 
class RepositoryFormatKnit4(RepositoryFormatKnit):
539
 
    """Bzr repository knit format 4.
540
 
 
541
 
    This repository format has everything in format 3, except for
542
 
    tree-references:
543
 
     - knits for file texts and inventory
544
 
     - hash subdirectory based stores.
545
 
     - knits for revisions and signatures
546
 
     - TextStores for revisions and signatures.
547
 
     - a format marker of its own
548
 
     - an optional 'shared-storage' flag
549
 
     - an optional 'no-working-trees' flag
550
 
     - a LockDir lock
551
 
     - support for recording full info about the tree root
552
 
    """
553
 
 
554
 
    repository_class = KnitRepository
555
 
    _commit_builder_class = RootCommitBuilder
556
 
    rich_root_data = True
557
 
    supports_tree_reference = False
558
 
    _serializer = xml6.serializer_v6
559
 
 
560
 
    def _get_matching_bzrdir(self):
561
 
        return bzrdir.format_registry.make_bzrdir('rich-root')
562
 
 
563
 
    def _ignore_setting_bzrdir(self, format):
564
 
        pass
565
 
 
566
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
567
 
 
568
 
    def check_conversion_target(self, target_format):
569
 
        if not target_format.rich_root_data:
570
 
            raise errors.BadConversionTarget(
571
 
                'Does not support rich root data.', target_format)
572
 
 
573
 
    def get_format_string(self):
574
 
        """See RepositoryFormat.get_format_string()."""
575
 
        return 'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
576
 
 
577
 
    def get_format_description(self):
578
 
        """See RepositoryFormat.get_format_description()."""
579
 
        return "Knit repository format 4"
580
 
 
581
 
 
582
 
def _get_stream_as_bytes(knit, required_versions):
583
 
    """Generate a serialised data stream.
584
 
 
585
 
    The format is a bencoding of a list.  The first element of the list is a
586
 
    string of the format signature, then each subsequent element is a list
587
 
    corresponding to a record.  Those lists contain:
588
 
 
589
 
      * a version id
590
 
      * a list of options
591
 
      * a list of parents
592
 
      * the bytes
593
 
 
594
 
    :returns: a bencoded list.
595
 
    """
596
 
    knit_stream = knit.get_data_stream(required_versions)
597
 
    format_signature, data_list, callable = knit_stream
598
 
    data = []
599
 
    data.append(format_signature)
600
 
    for version, options, length, parents in data_list:
601
 
        data.append([version, options, parents, callable(length)])
602
 
    return bencode.bencode(data)
 
458
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
459
        """See RepositoryFormat.open().
 
460
        
 
461
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
462
                                    repository at a slightly different url
 
463
                                    than normal. I.e. during 'upgrade'.
 
464
        """
 
465
        if not _found:
 
466
            format = RepositoryFormat.find_format(a_bzrdir)
 
467
            assert format.__class__ ==  self.__class__
 
468
        if _override_transport is not None:
 
469
            repo_transport = _override_transport
 
470
        else:
 
471
            repo_transport = a_bzrdir.get_repository_transport(None)
 
472
        control_files = lockable_files.LockableFiles(repo_transport, 'lock',
 
473
                                                     lockdir.LockDir)
 
474
        text_store = self._get_text_store(repo_transport, control_files)
 
475
        control_store = self._get_control_store(repo_transport, control_files)
 
476
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
477
        return self.repository_class(_format=self,
 
478
                                     a_bzrdir=a_bzrdir,
 
479
                                     control_files=control_files,
 
480
                                     _revision_store=_revision_store,
 
481
                                     control_store=control_store,
 
482
                                     text_store=text_store)