~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/knitrepo.py

  • Committer: mbp at sourcefrog
  • Date: 2005-03-28 04:16:10 UTC
  • Revision ID: mbp@sourcefrog.net-20050328041610-0b9dfa40f77c7671
fix up debug output for check command

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
from bzrlib.lazy_import import lazy_import
18
 
lazy_import(globals(), """
19
 
from bzrlib import (
20
 
    bzrdir,
21
 
    errors,
22
 
    knit as _mod_knit,
23
 
    lockable_files,
24
 
    lockdir,
25
 
    osutils,
26
 
    revision as _mod_revision,
27
 
    trace,
28
 
    transactions,
29
 
    versionedfile,
30
 
    xml5,
31
 
    xml6,
32
 
    xml7,
33
 
    )
34
 
""")
35
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
36
 
from bzrlib.repository import (
37
 
    CommitBuilder,
38
 
    InterRepository,
39
 
    InterSameDataRepository,
40
 
    IsInWriteGroupError,
41
 
    MetaDirRepository,
42
 
    MetaDirRepositoryFormat,
43
 
    RepositoryFormat,
44
 
    RootCommitBuilder,
45
 
    )
46
 
from bzrlib import symbol_versioning
47
 
 
48
 
 
49
 
class _KnitParentsProvider(object):
50
 
 
51
 
    def __init__(self, knit):
52
 
        self._knit = knit
53
 
 
54
 
    def __repr__(self):
55
 
        return 'KnitParentsProvider(%r)' % self._knit
56
 
 
57
 
    def get_parent_map(self, keys):
58
 
        """See graph.StackedParentsProvider.get_parent_map"""
59
 
        parent_map = {}
60
 
        for revision_id in keys:
61
 
            if revision_id is None:
62
 
                raise ValueError('get_parent_map(None) is not valid')
63
 
            if revision_id == _mod_revision.NULL_REVISION:
64
 
                parent_map[revision_id] = ()
65
 
            else:
66
 
                try:
67
 
                    parents = tuple(
68
 
                        self._knit.get_parents_with_ghosts(revision_id))
69
 
                except errors.RevisionNotPresent:
70
 
                    continue
71
 
                else:
72
 
                    if len(parents) == 0:
73
 
                        parents = (_mod_revision.NULL_REVISION,)
74
 
                parent_map[revision_id] = parents
75
 
        return parent_map
76
 
 
77
 
 
78
 
class _KnitsParentsProvider(object):
79
 
 
80
 
    def __init__(self, knit, prefix=()):
81
 
        """Create a parent provider for string keys mapped to tuple keys."""
82
 
        self._knit = knit
83
 
        self._prefix = prefix
84
 
 
85
 
    def __repr__(self):
86
 
        return 'KnitsParentsProvider(%r)' % self._knit
87
 
 
88
 
    def get_parent_map(self, keys):
89
 
        """See graph.StackedParentsProvider.get_parent_map"""
90
 
        parent_map = self._knit.get_parent_map(
91
 
            [self._prefix + (key,) for key in keys])
92
 
        result = {}
93
 
        for key, parents in parent_map.items():
94
 
            revid = key[-1]
95
 
            if len(parents) == 0:
96
 
                parents = (_mod_revision.NULL_REVISION,)
97
 
            else:
98
 
                parents = tuple(parent[-1] for parent in parents)
99
 
            result[revid] = parents
100
 
        for revision_id in keys:
101
 
            if revision_id == _mod_revision.NULL_REVISION:
102
 
                result[revision_id] = ()
103
 
        return result
104
 
 
105
 
 
106
 
class KnitRepository(MetaDirRepository):
107
 
    """Knit format repository."""
108
 
 
109
 
    # These attributes are inherited from the Repository base class. Setting
110
 
    # them to None ensures that if the constructor is changed to not initialize
111
 
    # them, or a subclass fails to call the constructor, that an error will
112
 
    # occur rather than the system working but generating incorrect data.
113
 
    _commit_builder_class = None
114
 
    _serializer = None
115
 
 
116
 
    def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
117
 
        _serializer):
118
 
        MetaDirRepository.__init__(self, _format, a_bzrdir, control_files)
119
 
        self._commit_builder_class = _commit_builder_class
120
 
        self._serializer = _serializer
121
 
        self._reconcile_fixes_text_parents = True
122
 
 
123
 
    @needs_read_lock
124
 
    def _all_revision_ids(self):
125
 
        """See Repository.all_revision_ids()."""
126
 
        return [key[0] for key in self.revisions.keys()]
127
 
 
128
 
    def _activate_new_inventory(self):
129
 
        """Put a replacement inventory.new into use as inventories."""
130
 
        # Copy the content across
131
 
        t = self._transport
132
 
        t.copy('inventory.new.kndx', 'inventory.kndx')
133
 
        try:
134
 
            t.copy('inventory.new.knit', 'inventory.knit')
135
 
        except errors.NoSuchFile:
136
 
            # empty inventories knit
137
 
            t.delete('inventory.knit')
138
 
        # delete the temp inventory
139
 
        t.delete('inventory.new.kndx')
140
 
        try:
141
 
            t.delete('inventory.new.knit')
142
 
        except errors.NoSuchFile:
143
 
            # empty inventories knit
144
 
            pass
145
 
        # Force index reload (sanity check)
146
 
        self.inventories._index._reset_cache()
147
 
        self.inventories.keys()
148
 
 
149
 
    def _backup_inventory(self):
150
 
        t = self._transport
151
 
        t.copy('inventory.kndx', 'inventory.backup.kndx')
152
 
        t.copy('inventory.knit', 'inventory.backup.knit')
153
 
 
154
 
    def _move_file_id(self, from_id, to_id):
155
 
        t = self._transport.clone('knits')
156
 
        from_rel_url = self.texts._index._mapper.map((from_id, None))
157
 
        to_rel_url = self.texts._index._mapper.map((to_id, None))
158
 
        # We expect both files to always exist in this case.
159
 
        for suffix in ('.knit', '.kndx'):
160
 
            t.rename(from_rel_url + suffix, to_rel_url + suffix)
161
 
 
162
 
    def _remove_file_id(self, file_id):
163
 
        t = self._transport.clone('knits')
164
 
        rel_url = self.texts._index._mapper.map((file_id, None))
165
 
        for suffix in ('.kndx', '.knit'):
166
 
            try:
167
 
                t.delete(rel_url + suffix)
168
 
            except errors.NoSuchFile:
169
 
                pass
170
 
 
171
 
    def _temp_inventories(self):
172
 
        result = self._format._get_inventories(self._transport, self,
173
 
            'inventory.new')
174
 
        # Reconciling when the output has no revisions would result in no
175
 
        # writes - but we want to ensure there is an inventory for
176
 
        # compatibility with older clients that don't lazy-load.
177
 
        result.get_parent_map([('A',)])
178
 
        return result
179
 
 
180
 
    def fileid_involved_between_revs(self, from_revid, to_revid):
181
 
        """Find file_id(s) which are involved in the changes between revisions.
182
 
 
183
 
        This determines the set of revisions which are involved, and then
184
 
        finds all file ids affected by those revisions.
185
 
        """
186
 
        vf = self._get_revision_vf()
187
 
        from_set = set(vf.get_ancestry(from_revid))
188
 
        to_set = set(vf.get_ancestry(to_revid))
189
 
        changed = to_set.difference(from_set)
190
 
        return self._fileid_involved_by_set(changed)
191
 
 
192
 
    def fileid_involved(self, last_revid=None):
193
 
        """Find all file_ids modified in the ancestry of last_revid.
194
 
 
195
 
        :param last_revid: If None, last_revision() will be used.
196
 
        """
197
 
        if not last_revid:
198
 
            changed = set(self.all_revision_ids())
199
 
        else:
200
 
            changed = set(self.get_ancestry(last_revid))
201
 
        if None in changed:
202
 
            changed.remove(None)
203
 
        return self._fileid_involved_by_set(changed)
204
 
 
205
 
    @needs_read_lock
206
 
    def get_revision(self, revision_id):
207
 
        """Return the Revision object for a named revision"""
208
 
        revision_id = osutils.safe_revision_id(revision_id)
209
 
        return self.get_revision_reconcile(revision_id)
210
 
 
211
 
    def _refresh_data(self):
212
 
        if not self.is_locked():
213
 
            return
214
 
        if self.is_in_write_group():
215
 
            raise IsInWriteGroupError(self)
216
 
        # Create a new transaction to force all knits to see the scope change.
217
 
        # This is safe because we're outside a write group.
218
 
        self.control_files._finish_transaction()
219
 
        if self.is_write_locked():
220
 
            self.control_files._set_write_transaction()
221
 
        else:
222
 
            self.control_files._set_read_transaction()
223
 
 
224
 
    @needs_write_lock
225
 
    def reconcile(self, other=None, thorough=False):
226
 
        """Reconcile this repository."""
227
 
        from bzrlib.reconcile import KnitReconciler
228
 
        reconciler = KnitReconciler(self, thorough=thorough)
229
 
        reconciler.reconcile()
230
 
        return reconciler
231
 
 
232
 
    def _make_parents_provider(self):
233
 
        return _KnitsParentsProvider(self.revisions)
234
 
 
235
 
 
236
 
class RepositoryFormatKnit(MetaDirRepositoryFormat):
237
 
    """Bzr repository knit format (generalized).
238
 
 
239
 
    This repository format has:
240
 
     - knits for file texts and inventory
241
 
     - hash subdirectory based stores.
242
 
     - knits for revisions and signatures
243
 
     - TextStores for revisions and signatures.
244
 
     - a format marker of its own
245
 
     - an optional 'shared-storage' flag
246
 
     - an optional 'no-working-trees' flag
247
 
     - a LockDir lock
248
 
    """
249
 
 
250
 
    # Set this attribute in derived classes to control the repository class
251
 
    # created by open and initialize.
252
 
    repository_class = None
253
 
    # Set this attribute in derived classes to control the
254
 
    # _commit_builder_class that the repository objects will have passed to
255
 
    # their constructor.
256
 
    _commit_builder_class = None
257
 
    # Set this attribute in derived clases to control the _serializer that the
258
 
    # repository objects will have passed to their constructor.
259
 
    @property
260
 
    def _serializer(self):
261
 
        return xml5.serializer_v5
262
 
    # Knit based repositories handle ghosts reasonably well.
263
 
    supports_ghosts = True
264
 
    # External lookups are not supported in this format.
265
 
    supports_external_lookups = False
266
 
    # No CHK support.
267
 
    supports_chks = False
268
 
    _fetch_order = 'topological'
269
 
    _fetch_uses_deltas = True
270
 
    fast_deltas = False
271
 
    supports_funky_characters = True
272
 
    supports_full_versioned_files = True
273
 
    # The revision.kndx could potentially claim a revision has a different
274
 
    # parent to the revision text.
275
 
    revision_graph_can_have_wrong_parents = True
276
 
 
277
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
278
 
        mapper = versionedfile.ConstantMapper(name)
279
 
        index = _mod_knit._KndxIndex(repo_transport, mapper,
280
 
            repo.get_transaction, repo.is_write_locked, repo.is_locked)
281
 
        access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
282
 
        return _mod_knit.KnitVersionedFiles(index, access, annotated=False)
283
 
 
284
 
    def _get_revisions(self, repo_transport, repo):
285
 
        mapper = versionedfile.ConstantMapper('revisions')
286
 
        index = _mod_knit._KndxIndex(repo_transport, mapper,
287
 
            repo.get_transaction, repo.is_write_locked, repo.is_locked)
288
 
        access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
289
 
        return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
290
 
            annotated=False)
291
 
 
292
 
    def _get_signatures(self, repo_transport, repo):
293
 
        mapper = versionedfile.ConstantMapper('signatures')
294
 
        index = _mod_knit._KndxIndex(repo_transport, mapper,
295
 
            repo.get_transaction, repo.is_write_locked, repo.is_locked)
296
 
        access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
297
 
        return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
298
 
            annotated=False)
299
 
 
300
 
    def _get_texts(self, repo_transport, repo):
301
 
        mapper = versionedfile.HashEscapedPrefixMapper()
302
 
        base_transport = repo_transport.clone('knits')
303
 
        index = _mod_knit._KndxIndex(base_transport, mapper,
304
 
            repo.get_transaction, repo.is_write_locked, repo.is_locked)
305
 
        access = _mod_knit._KnitKeyAccess(base_transport, mapper)
306
 
        return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=200,
307
 
            annotated=True)
308
 
 
309
 
    def initialize(self, a_bzrdir, shared=False):
310
 
        """Create a knit format 1 repository.
311
 
 
312
 
        :param a_bzrdir: bzrdir to contain the new repository; must already
313
 
            be initialized.
314
 
        :param shared: If true the repository will be initialized as a shared
315
 
                       repository.
316
 
        """
317
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
318
 
        dirs = ['knits']
319
 
        files = []
320
 
        utf8_files = [('format', self.get_format_string())]
321
 
 
322
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
323
 
        repo_transport = a_bzrdir.get_repository_transport(None)
324
 
        control_files = lockable_files.LockableFiles(repo_transport,
325
 
                                'lock', lockdir.LockDir)
326
 
        transaction = transactions.WriteTransaction()
327
 
        result = self.open(a_bzrdir=a_bzrdir, _found=True)
328
 
        result.lock_write()
329
 
        # the revision id here is irrelevant: it will not be stored, and cannot
330
 
        # already exist, we do this to create files on disk for older clients.
331
 
        result.inventories.get_parent_map([('A',)])
332
 
        result.revisions.get_parent_map([('A',)])
333
 
        result.signatures.get_parent_map([('A',)])
334
 
        result.unlock()
335
 
        self._run_post_repo_init_hooks(result, a_bzrdir, shared)
336
 
        return result
337
 
 
338
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
339
 
        """See RepositoryFormat.open().
340
 
 
341
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
342
 
                                    repository at a slightly different url
343
 
                                    than normal. I.e. during 'upgrade'.
344
 
        """
345
 
        if not _found:
346
 
            format = RepositoryFormat.find_format(a_bzrdir)
347
 
        if _override_transport is not None:
348
 
            repo_transport = _override_transport
349
 
        else:
350
 
            repo_transport = a_bzrdir.get_repository_transport(None)
351
 
        control_files = lockable_files.LockableFiles(repo_transport,
352
 
                                'lock', lockdir.LockDir)
353
 
        repo = self.repository_class(_format=self,
354
 
                              a_bzrdir=a_bzrdir,
355
 
                              control_files=control_files,
356
 
                              _commit_builder_class=self._commit_builder_class,
357
 
                              _serializer=self._serializer)
358
 
        repo.revisions = self._get_revisions(repo_transport, repo)
359
 
        repo.signatures = self._get_signatures(repo_transport, repo)
360
 
        repo.inventories = self._get_inventories(repo_transport, repo)
361
 
        repo.texts = self._get_texts(repo_transport, repo)
362
 
        repo.chk_bytes = None
363
 
        repo._transport = repo_transport
364
 
        return repo
365
 
 
366
 
 
367
 
class RepositoryFormatKnit1(RepositoryFormatKnit):
368
 
    """Bzr repository knit format 1.
369
 
 
370
 
    This repository format has:
371
 
     - knits for file texts and inventory
372
 
     - hash subdirectory based stores.
373
 
     - knits for revisions and signatures
374
 
     - TextStores for revisions and signatures.
375
 
     - a format marker of its own
376
 
     - an optional 'shared-storage' flag
377
 
     - an optional 'no-working-trees' flag
378
 
     - a LockDir lock
379
 
 
380
 
    This format was introduced in bzr 0.8.
381
 
    """
382
 
 
383
 
    repository_class = KnitRepository
384
 
    _commit_builder_class = CommitBuilder
385
 
    @property
386
 
    def _serializer(self):
387
 
        return xml5.serializer_v5
388
 
 
389
 
    def __ne__(self, other):
390
 
        return self.__class__ is not other.__class__
391
 
 
392
 
    def get_format_string(self):
393
 
        """See RepositoryFormat.get_format_string()."""
394
 
        return "Bazaar-NG Knit Repository Format 1"
395
 
 
396
 
    def get_format_description(self):
397
 
        """See RepositoryFormat.get_format_description()."""
398
 
        return "Knit repository format 1"
399
 
 
400
 
 
401
 
class RepositoryFormatKnit3(RepositoryFormatKnit):
402
 
    """Bzr repository knit format 3.
403
 
 
404
 
    This repository format has:
405
 
     - knits for file texts and inventory
406
 
     - hash subdirectory based stores.
407
 
     - knits for revisions and signatures
408
 
     - TextStores for revisions and signatures.
409
 
     - a format marker of its own
410
 
     - an optional 'shared-storage' flag
411
 
     - an optional 'no-working-trees' flag
412
 
     - a LockDir lock
413
 
     - support for recording full info about the tree root
414
 
     - support for recording tree-references
415
 
    """
416
 
 
417
 
    repository_class = KnitRepository
418
 
    _commit_builder_class = RootCommitBuilder
419
 
    rich_root_data = True
420
 
    experimental = True
421
 
    supports_tree_reference = True
422
 
    @property
423
 
    def _serializer(self):
424
 
        return xml7.serializer_v7
425
 
 
426
 
    def _get_matching_bzrdir(self):
427
 
        return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
428
 
 
429
 
    def _ignore_setting_bzrdir(self, format):
430
 
        pass
431
 
 
432
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
433
 
 
434
 
    def get_format_string(self):
435
 
        """See RepositoryFormat.get_format_string()."""
436
 
        return "Bazaar Knit Repository Format 3 (bzr 0.15)\n"
437
 
 
438
 
    def get_format_description(self):
439
 
        """See RepositoryFormat.get_format_description()."""
440
 
        return "Knit repository format 3"
441
 
 
442
 
 
443
 
class RepositoryFormatKnit4(RepositoryFormatKnit):
444
 
    """Bzr repository knit format 4.
445
 
 
446
 
    This repository format has everything in format 3, except for
447
 
    tree-references:
448
 
     - knits for file texts and inventory
449
 
     - hash subdirectory based stores.
450
 
     - knits for revisions and signatures
451
 
     - TextStores for revisions and signatures.
452
 
     - a format marker of its own
453
 
     - an optional 'shared-storage' flag
454
 
     - an optional 'no-working-trees' flag
455
 
     - a LockDir lock
456
 
     - support for recording full info about the tree root
457
 
    """
458
 
 
459
 
    repository_class = KnitRepository
460
 
    _commit_builder_class = RootCommitBuilder
461
 
    rich_root_data = True
462
 
    supports_tree_reference = False
463
 
    @property
464
 
    def _serializer(self):
465
 
        return xml6.serializer_v6
466
 
 
467
 
    def _get_matching_bzrdir(self):
468
 
        return bzrdir.format_registry.make_bzrdir('rich-root')
469
 
 
470
 
    def _ignore_setting_bzrdir(self, format):
471
 
        pass
472
 
 
473
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
474
 
 
475
 
    def get_format_string(self):
476
 
        """See RepositoryFormat.get_format_string()."""
477
 
        return 'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
478
 
 
479
 
    def get_format_description(self):
480
 
        """See RepositoryFormat.get_format_description()."""
481
 
        return "Knit repository format 4"
482
 
 
483
 
 
484
 
class InterKnitRepo(InterSameDataRepository):
485
 
    """Optimised code paths between Knit based repositories."""
486
 
 
487
 
    @classmethod
488
 
    def _get_repo_format_to_test(self):
489
 
        return RepositoryFormatKnit1()
490
 
 
491
 
    @staticmethod
492
 
    def is_compatible(source, target):
493
 
        """Be compatible with known Knit formats.
494
 
 
495
 
        We don't test for the stores being of specific types because that
496
 
        could lead to confusing results, and there is no need to be
497
 
        overly general.
498
 
        """
499
 
        try:
500
 
            are_knits = (isinstance(source._format, RepositoryFormatKnit) and
501
 
                isinstance(target._format, RepositoryFormatKnit))
502
 
        except AttributeError:
503
 
            return False
504
 
        return are_knits and InterRepository._same_model(source, target)
505
 
 
506
 
    @needs_read_lock
507
 
    def search_missing_revision_ids(self,
508
 
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
509
 
            find_ghosts=True, revision_ids=None, if_present_ids=None):
510
 
        """See InterRepository.search_missing_revision_ids()."""
511
 
        if symbol_versioning.deprecated_passed(revision_id):
512
 
            symbol_versioning.warn(
513
 
                'search_missing_revision_ids(revision_id=...) was '
514
 
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
515
 
                DeprecationWarning, stacklevel=2)
516
 
            if revision_ids is not None:
517
 
                raise AssertionError(
518
 
                    'revision_ids is mutually exclusive with revision_id')
519
 
            if revision_id is not None:
520
 
                revision_ids = [revision_id]
521
 
        del revision_id
522
 
        source_ids_set = self._present_source_revisions_for(
523
 
            revision_ids, if_present_ids)
524
 
        # source_ids is the worst possible case we may need to pull.
525
 
        # now we want to filter source_ids against what we actually
526
 
        # have in target, but don't try to check for existence where we know
527
 
        # we do not have a revision as that would be pointless.
528
 
        target_ids = set(self.target.all_revision_ids())
529
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
530
 
        actually_present_revisions = set(
531
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
532
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
533
 
        if revision_ids is not None:
534
 
            # we used get_ancestry to determine source_ids then we are assured all
535
 
            # revisions referenced are present as they are installed in topological order.
536
 
            # and the tip revision was validated by get_ancestry.
537
 
            result_set = required_revisions
538
 
        else:
539
 
            # if we just grabbed the possibly available ids, then
540
 
            # we only have an estimate of whats available and need to validate
541
 
            # that against the revision records.
542
 
            result_set = set(
543
 
                self.source._eliminate_revisions_not_present(required_revisions))
544
 
        return self.source.revision_ids_to_search_result(result_set)
545
 
 
546
 
 
547
 
InterRepository.register_optimiser(InterKnitRepo)