~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/knitrepo.py

  • Committer: Martin Pool
  • Date: 2009-01-13 03:06:36 UTC
  • mfrom: (3932.2.3 1.11)
  • mto: This revision was merged to the branch mainline in revision 3937.
  • Revision ID: mbp@sourcefrog.net-20090113030636-dqx4t8yaaqgdvam5
MergeĀ 1.11rc1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
from __future__ import absolute_import
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
16
 
19
17
from bzrlib.lazy_import import lazy_import
20
18
lazy_import(globals(), """
21
 
import itertools
22
 
 
23
19
from bzrlib import (
24
 
    controldir,
 
20
    bzrdir,
25
21
    errors,
26
22
    knit as _mod_knit,
27
23
    lockable_files,
28
24
    lockdir,
29
25
    osutils,
30
26
    revision as _mod_revision,
31
 
    trace,
32
27
    transactions,
33
28
    versionedfile,
34
29
    xml5,
36
31
    xml7,
37
32
    )
38
33
""")
 
34
from bzrlib import (
 
35
    symbol_versioning,
 
36
    )
39
37
from bzrlib.decorators import needs_read_lock, needs_write_lock
40
38
from bzrlib.repository import (
41
 
    InterRepository,
42
 
    IsInWriteGroupError,
43
 
    RepositoryFormatMetaDir,
44
 
    )
45
 
from bzrlib.vf_repository import (
46
 
    InterSameDataRepository,
47
 
    MetaDirVersionedFileRepository,
48
 
    MetaDirVersionedFileRepositoryFormat,
49
 
    VersionedFileCommitBuilder,
50
 
    VersionedFileRootCommitBuilder,
51
 
    )
52
 
from bzrlib import symbol_versioning
 
39
    CommitBuilder,
 
40
    MetaDirRepository,
 
41
    MetaDirRepositoryFormat,
 
42
    RepositoryFormat,
 
43
    RootCommitBuilder,
 
44
    )
 
45
from bzrlib.trace import mutter, mutter_callsite
53
46
 
54
47
 
55
48
class _KnitParentsProvider(object):
60
53
    def __repr__(self):
61
54
        return 'KnitParentsProvider(%r)' % self._knit
62
55
 
 
56
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
 
57
    def get_parents(self, revision_ids):
 
58
        """See graph._StackedParentsProvider.get_parents"""
 
59
        parent_map = self.get_parent_map(revision_ids)
 
60
        return [parent_map.get(r, None) for r in revision_ids]
 
61
 
63
62
    def get_parent_map(self, keys):
64
 
        """See graph.StackedParentsProvider.get_parent_map"""
 
63
        """See graph._StackedParentsProvider.get_parent_map"""
65
64
        parent_map = {}
66
65
        for revision_id in keys:
67
66
            if revision_id is None:
92
91
        return 'KnitsParentsProvider(%r)' % self._knit
93
92
 
94
93
    def get_parent_map(self, keys):
95
 
        """See graph.StackedParentsProvider.get_parent_map"""
 
94
        """See graph._StackedParentsProvider.get_parent_map"""
96
95
        parent_map = self._knit.get_parent_map(
97
96
            [self._prefix + (key,) for key in keys])
98
97
        result = {}
109
108
        return result
110
109
 
111
110
 
112
 
class KnitRepository(MetaDirVersionedFileRepository):
 
111
class KnitRepository(MetaDirRepository):
113
112
    """Knit format repository."""
114
113
 
115
114
    # These attributes are inherited from the Repository base class. Setting
121
120
 
122
121
    def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
123
122
        _serializer):
124
 
        super(KnitRepository, self).__init__(_format, a_bzrdir, control_files)
 
123
        MetaDirRepository.__init__(self, _format, a_bzrdir, control_files)
125
124
        self._commit_builder_class = _commit_builder_class
126
125
        self._serializer = _serializer
127
126
        self._reconcile_fixes_text_parents = True
 
127
        self._fetch_uses_deltas = True
 
128
        self._fetch_order = 'topological'
128
129
 
129
130
    @needs_read_lock
130
131
    def _all_revision_ids(self):
183
184
        result.get_parent_map([('A',)])
184
185
        return result
185
186
 
 
187
    def fileid_involved_between_revs(self, from_revid, to_revid):
 
188
        """Find file_id(s) which are involved in the changes between revisions.
 
189
 
 
190
        This determines the set of revisions which are involved, and then
 
191
        finds all file ids affected by those revisions.
 
192
        """
 
193
        vf = self._get_revision_vf()
 
194
        from_set = set(vf.get_ancestry(from_revid))
 
195
        to_set = set(vf.get_ancestry(to_revid))
 
196
        changed = to_set.difference(from_set)
 
197
        return self._fileid_involved_by_set(changed)
 
198
 
 
199
    def fileid_involved(self, last_revid=None):
 
200
        """Find all file_ids modified in the ancestry of last_revid.
 
201
 
 
202
        :param last_revid: If None, last_revision() will be used.
 
203
        """
 
204
        if not last_revid:
 
205
            changed = set(self.all_revision_ids())
 
206
        else:
 
207
            changed = set(self.get_ancestry(last_revid))
 
208
        if None in changed:
 
209
            changed.remove(None)
 
210
        return self._fileid_involved_by_set(changed)
 
211
 
186
212
    @needs_read_lock
187
213
    def get_revision(self, revision_id):
188
214
        """Return the Revision object for a named revision"""
189
215
        revision_id = osutils.safe_revision_id(revision_id)
190
216
        return self.get_revision_reconcile(revision_id)
191
217
 
192
 
    def _refresh_data(self):
193
 
        if not self.is_locked():
194
 
            return
195
 
        if self.is_in_write_group():
196
 
            raise IsInWriteGroupError(self)
197
 
        # Create a new transaction to force all knits to see the scope change.
198
 
        # This is safe because we're outside a write group.
199
 
        self.control_files._finish_transaction()
200
 
        if self.is_write_locked():
201
 
            self.control_files._set_write_transaction()
202
 
        else:
203
 
            self.control_files._set_read_transaction()
204
 
 
205
218
    @needs_write_lock
206
219
    def reconcile(self, other=None, thorough=False):
207
220
        """Reconcile this repository."""
209
222
        reconciler = KnitReconciler(self, thorough=thorough)
210
223
        reconciler.reconcile()
211
224
        return reconciler
212
 
 
 
225
    
213
226
    def _make_parents_provider(self):
214
227
        return _KnitsParentsProvider(self.revisions)
215
228
 
216
 
 
217
 
class RepositoryFormatKnit(MetaDirVersionedFileRepositoryFormat):
218
 
    """Bzr repository knit format (generalized).
 
229
    def _find_inconsistent_revision_parents(self):
 
230
        """Find revisions with different parent lists in the revision object
 
231
        and in the index graph.
 
232
 
 
233
        :returns: an iterator yielding tuples of (revison-id, parents-in-index,
 
234
            parents-in-revision).
 
235
        """
 
236
        if not self.is_locked():
 
237
            raise AssertionError()
 
238
        vf = self.revisions
 
239
        for index_version in vf.keys():
 
240
            parent_map = vf.get_parent_map([index_version])
 
241
            parents_according_to_index = tuple(parent[-1] for parent in
 
242
                parent_map[index_version])
 
243
            revision = self.get_revision(index_version[-1])
 
244
            parents_according_to_revision = tuple(revision.parent_ids)
 
245
            if parents_according_to_index != parents_according_to_revision:
 
246
                yield (index_version[-1], parents_according_to_index,
 
247
                    parents_according_to_revision)
 
248
 
 
249
    def _check_for_inconsistent_revision_parents(self):
 
250
        inconsistencies = list(self._find_inconsistent_revision_parents())
 
251
        if inconsistencies:
 
252
            raise errors.BzrCheckError(
 
253
                "Revision knit has inconsistent parents.")
 
254
 
 
255
    def revision_graph_can_have_wrong_parents(self):
 
256
        # The revision.kndx could potentially claim a revision has a different
 
257
        # parent to the revision text.
 
258
        return True
 
259
 
 
260
 
 
261
class RepositoryFormatKnit(MetaDirRepositoryFormat):
 
262
    """Bzr repository knit format (generalized). 
219
263
 
220
264
    This repository format has:
221
265
     - knits for file texts and inventory
244
288
    supports_ghosts = True
245
289
    # External lookups are not supported in this format.
246
290
    supports_external_lookups = False
247
 
    # No CHK support.
248
 
    supports_chks = False
249
 
    _fetch_order = 'topological'
250
 
    _fetch_uses_deltas = True
251
 
    fast_deltas = False
252
 
    supports_funky_characters = True
253
 
    # The revision.kndx could potentially claim a revision has a different
254
 
    # parent to the revision text.
255
 
    revision_graph_can_have_wrong_parents = True
256
291
 
257
292
    def _get_inventories(self, repo_transport, repo, name='inventory'):
258
293
        mapper = versionedfile.ConstantMapper(name)
294
329
        :param shared: If true the repository will be initialized as a shared
295
330
                       repository.
296
331
        """
297
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
 
332
        mutter('creating repository in %s.', a_bzrdir.transport.base)
298
333
        dirs = ['knits']
299
334
        files = []
300
335
        utf8_files = [('format', self.get_format_string())]
301
 
 
 
336
        
302
337
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
303
338
        repo_transport = a_bzrdir.get_repository_transport(None)
304
339
        control_files = lockable_files.LockableFiles(repo_transport,
312
347
        result.revisions.get_parent_map([('A',)])
313
348
        result.signatures.get_parent_map([('A',)])
314
349
        result.unlock()
315
 
        self._run_post_repo_init_hooks(result, a_bzrdir, shared)
316
350
        return result
317
351
 
318
352
    def open(self, a_bzrdir, _found=False, _override_transport=None):
319
353
        """See RepositoryFormat.open().
320
 
 
 
354
        
321
355
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
322
356
                                    repository at a slightly different url
323
357
                                    than normal. I.e. during 'upgrade'.
324
358
        """
325
359
        if not _found:
326
 
            format = RepositoryFormatMetaDir.find_format(a_bzrdir)
 
360
            format = RepositoryFormat.find_format(a_bzrdir)
327
361
        if _override_transport is not None:
328
362
            repo_transport = _override_transport
329
363
        else:
339
373
        repo.signatures = self._get_signatures(repo_transport, repo)
340
374
        repo.inventories = self._get_inventories(repo_transport, repo)
341
375
        repo.texts = self._get_texts(repo_transport, repo)
342
 
        repo.chk_bytes = None
343
376
        repo._transport = repo_transport
344
377
        return repo
345
378
 
361
394
    """
362
395
 
363
396
    repository_class = KnitRepository
364
 
    _commit_builder_class = VersionedFileCommitBuilder
 
397
    _commit_builder_class = CommitBuilder
365
398
    @property
366
399
    def _serializer(self):
367
400
        return xml5.serializer_v5
369
402
    def __ne__(self, other):
370
403
        return self.__class__ is not other.__class__
371
404
 
372
 
    @classmethod
373
 
    def get_format_string(cls):
 
405
    def get_format_string(self):
374
406
        """See RepositoryFormat.get_format_string()."""
375
407
        return "Bazaar-NG Knit Repository Format 1"
376
408
 
378
410
        """See RepositoryFormat.get_format_description()."""
379
411
        return "Knit repository format 1"
380
412
 
 
413
    def check_conversion_target(self, target_format):
 
414
        pass
 
415
 
381
416
 
382
417
class RepositoryFormatKnit3(RepositoryFormatKnit):
383
418
    """Bzr repository knit format 3.
396
431
    """
397
432
 
398
433
    repository_class = KnitRepository
399
 
    _commit_builder_class = VersionedFileRootCommitBuilder
 
434
    _commit_builder_class = RootCommitBuilder
400
435
    rich_root_data = True
401
 
    experimental = True
402
436
    supports_tree_reference = True
403
437
    @property
404
438
    def _serializer(self):
405
439
        return xml7.serializer_v7
406
440
 
407
441
    def _get_matching_bzrdir(self):
408
 
        return controldir.format_registry.make_bzrdir('dirstate-with-subtree')
 
442
        return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
409
443
 
410
444
    def _ignore_setting_bzrdir(self, format):
411
445
        pass
412
446
 
413
447
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
414
448
 
415
 
    @classmethod
416
 
    def get_format_string(cls):
 
449
    def check_conversion_target(self, target_format):
 
450
        if not target_format.rich_root_data:
 
451
            raise errors.BadConversionTarget(
 
452
                'Does not support rich root data.', target_format)
 
453
        if not getattr(target_format, 'supports_tree_reference', False):
 
454
            raise errors.BadConversionTarget(
 
455
                'Does not support nested trees', target_format)
 
456
            
 
457
    def get_format_string(self):
417
458
        """See RepositoryFormat.get_format_string()."""
418
459
        return "Bazaar Knit Repository Format 3 (bzr 0.15)\n"
419
460
 
439
480
    """
440
481
 
441
482
    repository_class = KnitRepository
442
 
    _commit_builder_class = VersionedFileRootCommitBuilder
 
483
    _commit_builder_class = RootCommitBuilder
443
484
    rich_root_data = True
444
485
    supports_tree_reference = False
445
486
    @property
447
488
        return xml6.serializer_v6
448
489
 
449
490
    def _get_matching_bzrdir(self):
450
 
        return controldir.format_registry.make_bzrdir('rich-root')
 
491
        return bzrdir.format_registry.make_bzrdir('rich-root')
451
492
 
452
493
    def _ignore_setting_bzrdir(self, format):
453
494
        pass
454
495
 
455
496
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
456
497
 
457
 
    @classmethod
458
 
    def get_format_string(cls):
 
498
    def check_conversion_target(self, target_format):
 
499
        if not target_format.rich_root_data:
 
500
            raise errors.BadConversionTarget(
 
501
                'Does not support rich root data.', target_format)
 
502
 
 
503
    def get_format_string(self):
459
504
        """See RepositoryFormat.get_format_string()."""
460
505
        return 'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
461
506
 
462
507
    def get_format_description(self):
463
508
        """See RepositoryFormat.get_format_description()."""
464
509
        return "Knit repository format 4"
465
 
 
466
 
 
467
 
class InterKnitRepo(InterSameDataRepository):
468
 
    """Optimised code paths between Knit based repositories."""
469
 
 
470
 
    @classmethod
471
 
    def _get_repo_format_to_test(self):
472
 
        return RepositoryFormatKnit1()
473
 
 
474
 
    @staticmethod
475
 
    def is_compatible(source, target):
476
 
        """Be compatible with known Knit formats.
477
 
 
478
 
        We don't test for the stores being of specific types because that
479
 
        could lead to confusing results, and there is no need to be
480
 
        overly general.
481
 
        """
482
 
        try:
483
 
            are_knits = (isinstance(source._format, RepositoryFormatKnit) and
484
 
                isinstance(target._format, RepositoryFormatKnit))
485
 
        except AttributeError:
486
 
            return False
487
 
        return are_knits and InterRepository._same_model(source, target)
488
 
 
489
 
    @needs_read_lock
490
 
    def search_missing_revision_ids(self,
491
 
            find_ghosts=True, revision_ids=None, if_present_ids=None,
492
 
            limit=None):
493
 
        """See InterRepository.search_missing_revision_ids()."""
494
 
        source_ids_set = self._present_source_revisions_for(
495
 
            revision_ids, if_present_ids)
496
 
        # source_ids is the worst possible case we may need to pull.
497
 
        # now we want to filter source_ids against what we actually
498
 
        # have in target, but don't try to check for existence where we know
499
 
        # we do not have a revision as that would be pointless.
500
 
        target_ids = set(self.target.all_revision_ids())
501
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
502
 
        actually_present_revisions = set(
503
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
504
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
505
 
        if revision_ids is not None:
506
 
            # we used get_ancestry to determine source_ids then we are assured all
507
 
            # revisions referenced are present as they are installed in topological order.
508
 
            # and the tip revision was validated by get_ancestry.
509
 
            result_set = required_revisions
510
 
        else:
511
 
            # if we just grabbed the possibly available ids, then
512
 
            # we only have an estimate of whats available and need to validate
513
 
            # that against the revision records.
514
 
            result_set = set(
515
 
                self.source._eliminate_revisions_not_present(required_revisions))
516
 
        if limit is not None:
517
 
            topo_ordered = self.source.get_graph().iter_topo_order(result_set)
518
 
            result_set = set(itertools.islice(topo_ordered, limit))
519
 
        return self.source.revision_ids_to_search_result(result_set)
520
 
 
521
 
 
522
 
InterRepository.register_optimiser(InterKnitRepo)