14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from ..lazy_import import lazy_import
17
from bzrlib.lazy_import import lazy_import
18
18
lazy_import(globals(), """
26
26
revision as _mod_revision,
30
from breezy.bzr import (
41
from ..repository import (
45
from ..bzr.repository import (
46
RepositoryFormatMetaDir,
48
from ..bzr.vf_repository import (
49
InterSameDataRepository,
50
MetaDirVersionedFileRepository,
51
MetaDirVersionedFileRepositoryFormat,
52
VersionedFileCommitBuilder,
37
from bzrlib.decorators import needs_read_lock, needs_write_lock
38
from bzrlib.repository import (
41
MetaDirRepositoryFormat,
45
from bzrlib.trace import mutter, mutter_callsite
56
48
class _KnitParentsProvider(object):
120
112
_commit_builder_class = None
121
113
_serializer = None
123
def __init__(self, _format, a_controldir, control_files, _commit_builder_class,
125
super(KnitRepository, self).__init__(
126
_format, a_controldir, control_files)
115
def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
117
MetaDirRepository.__init__(self, _format, a_bzrdir, control_files)
127
118
self._commit_builder_class = _commit_builder_class
128
119
self._serializer = _serializer
129
120
self._reconcile_fixes_text_parents = True
131
123
def _all_revision_ids(self):
132
124
"""See Repository.all_revision_ids()."""
133
with self.lock_read():
134
return [key[0] for key in self.revisions.keys()]
125
return [key[0] for key in self.revisions.keys()]
136
127
def _activate_new_inventory(self):
137
128
"""Put a replacement inventory.new into use as inventories."""
179
170
def _temp_inventories(self):
180
171
result = self._format._get_inventories(self._transport, self,
182
173
# Reconciling when the output has no revisions would result in no
183
174
# writes - but we want to ensure there is an inventory for
184
175
# compatibility with older clients that don't lazy-load.
185
result.get_parent_map([(b'A',)])
176
result.get_parent_map([('A',)])
179
def fileid_involved_between_revs(self, from_revid, to_revid):
180
"""Find file_id(s) which are involved in the changes between revisions.
182
This determines the set of revisions which are involved, and then
183
finds all file ids affected by those revisions.
185
vf = self._get_revision_vf()
186
from_set = set(vf.get_ancestry(from_revid))
187
to_set = set(vf.get_ancestry(to_revid))
188
changed = to_set.difference(from_set)
189
return self._fileid_involved_by_set(changed)
191
def fileid_involved(self, last_revid=None):
192
"""Find all file_ids modified in the ancestry of last_revid.
194
:param last_revid: If None, last_revision() will be used.
197
changed = set(self.all_revision_ids())
199
changed = set(self.get_ancestry(last_revid))
202
return self._fileid_involved_by_set(changed)
188
205
def get_revision(self, revision_id):
189
206
"""Return the Revision object for a named revision"""
190
with self.lock_read():
191
return self.get_revision_reconcile(revision_id)
207
revision_id = osutils.safe_revision_id(revision_id)
208
return self.get_revision_reconcile(revision_id)
193
210
def _refresh_data(self):
194
211
if not self.is_locked():
196
if self.is_in_write_group():
197
raise IsInWriteGroupError(self)
198
213
# Create a new transaction to force all knits to see the scope change.
199
214
# This is safe because we're outside a write group.
200
215
self.control_files._finish_transaction()
204
219
self.control_files._set_read_transaction()
206
222
def reconcile(self, other=None, thorough=False):
207
223
"""Reconcile this repository."""
208
from .reconcile import KnitReconciler
209
with self.lock_write():
210
reconciler = KnitReconciler(self, thorough=thorough)
211
return reconciler.reconcile()
224
from bzrlib.reconcile import KnitReconciler
225
reconciler = KnitReconciler(self, thorough=thorough)
226
reconciler.reconcile()
213
229
def _make_parents_provider(self):
214
230
return _KnitsParentsProvider(self.revisions)
217
class RepositoryFormatKnit(MetaDirVersionedFileRepositoryFormat):
232
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
233
"""Find revisions with different parent lists in the revision object
234
and in the index graph.
236
:param revisions_iterator: None, or an iterator of (revid,
237
Revision-or-None). This iterator controls the revisions checked.
238
:returns: an iterator yielding tuples of (revison-id, parents-in-index,
239
parents-in-revision).
241
if not self.is_locked():
242
raise AssertionError()
244
if revisions_iterator is None:
245
revisions_iterator = self._iter_revisions(None)
246
for revid, revision in revisions_iterator:
249
parent_map = vf.get_parent_map([(revid,)])
250
parents_according_to_index = tuple(parent[-1] for parent in
251
parent_map[(revid,)])
252
parents_according_to_revision = tuple(revision.parent_ids)
253
if parents_according_to_index != parents_according_to_revision:
254
yield (revid, parents_according_to_index,
255
parents_according_to_revision)
257
def _check_for_inconsistent_revision_parents(self):
258
inconsistencies = list(self._find_inconsistent_revision_parents())
260
raise errors.BzrCheckError(
261
"Revision knit has inconsistent parents.")
263
def revision_graph_can_have_wrong_parents(self):
264
# The revision.kndx could potentially claim a revision has a different
265
# parent to the revision text.
269
class RepositoryFormatKnit(MetaDirRepositoryFormat):
218
270
"""Bzr repository knit format (generalized).
220
272
This repository format has:
250
301
_fetch_order = 'topological'
251
302
_fetch_uses_deltas = True
252
303
fast_deltas = False
253
supports_funky_characters = True
254
# The revision.kndx could potentially claim a revision has a different
255
# parent to the revision text.
256
revision_graph_can_have_wrong_parents = True
258
305
def _get_inventories(self, repo_transport, repo, name='inventory'):
259
306
mapper = versionedfile.ConstantMapper(name)
260
307
index = _mod_knit._KndxIndex(repo_transport, mapper,
261
repo.get_transaction, repo.is_write_locked, repo.is_locked)
308
repo.get_transaction, repo.is_write_locked, repo.is_locked)
262
309
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
263
310
return _mod_knit.KnitVersionedFiles(index, access, annotated=False)
265
312
def _get_revisions(self, repo_transport, repo):
266
313
mapper = versionedfile.ConstantMapper('revisions')
267
314
index = _mod_knit._KndxIndex(repo_transport, mapper,
268
repo.get_transaction, repo.is_write_locked, repo.is_locked)
315
repo.get_transaction, repo.is_write_locked, repo.is_locked)
269
316
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
270
317
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
273
320
def _get_signatures(self, repo_transport, repo):
274
321
mapper = versionedfile.ConstantMapper('signatures')
275
322
index = _mod_knit._KndxIndex(repo_transport, mapper,
276
repo.get_transaction, repo.is_write_locked, repo.is_locked)
323
repo.get_transaction, repo.is_write_locked, repo.is_locked)
277
324
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
278
325
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
281
328
def _get_texts(self, repo_transport, repo):
282
329
mapper = versionedfile.HashEscapedPrefixMapper()
283
330
base_transport = repo_transport.clone('knits')
284
331
index = _mod_knit._KndxIndex(base_transport, mapper,
285
repo.get_transaction, repo.is_write_locked, repo.is_locked)
332
repo.get_transaction, repo.is_write_locked, repo.is_locked)
286
333
access = _mod_knit._KnitKeyAccess(base_transport, mapper)
287
334
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=200,
290
def initialize(self, a_controldir, shared=False):
337
def initialize(self, a_bzrdir, shared=False):
291
338
"""Create a knit format 1 repository.
293
:param a_controldir: bzrdir to contain the new repository; must already
340
:param a_bzrdir: bzrdir to contain the new repository; must already
295
342
:param shared: If true the repository will be initialized as a shared
298
trace.mutter('creating repository in %s.', a_controldir.transport.base)
345
mutter('creating repository in %s.', a_bzrdir.transport.base)
301
348
utf8_files = [('format', self.get_format_string())]
303
self._upload_blank_content(
304
a_controldir, dirs, files, utf8_files, shared)
305
repo_transport = a_controldir.get_repository_transport(None)
350
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
351
repo_transport = a_bzrdir.get_repository_transport(None)
306
352
control_files = lockable_files.LockableFiles(repo_transport,
307
'lock', lockdir.LockDir)
353
'lock', lockdir.LockDir)
308
354
transaction = transactions.WriteTransaction()
309
result = self.open(a_controldir=a_controldir, _found=True)
355
result = self.open(a_bzrdir=a_bzrdir, _found=True)
310
356
result.lock_write()
311
357
# the revision id here is irrelevant: it will not be stored, and cannot
312
358
# already exist, we do this to create files on disk for older clients.
313
result.inventories.get_parent_map([(b'A',)])
314
result.revisions.get_parent_map([(b'A',)])
315
result.signatures.get_parent_map([(b'A',)])
359
result.inventories.get_parent_map([('A',)])
360
result.revisions.get_parent_map([('A',)])
361
result.signatures.get_parent_map([('A',)])
317
self._run_post_repo_init_hooks(result, a_controldir, shared)
320
def open(self, a_controldir, _found=False, _override_transport=None):
365
def open(self, a_bzrdir, _found=False, _override_transport=None):
321
366
"""See RepositoryFormat.open().
323
368
:param _override_transport: INTERNAL USE ONLY. Allows opening the
325
370
than normal. I.e. during 'upgrade'.
328
format = RepositoryFormatMetaDir.find_format(a_controldir)
373
format = RepositoryFormat.find_format(a_bzrdir)
329
374
if _override_transport is not None:
330
375
repo_transport = _override_transport
332
repo_transport = a_controldir.get_repository_transport(None)
377
repo_transport = a_bzrdir.get_repository_transport(None)
333
378
control_files = lockable_files.LockableFiles(repo_transport,
334
'lock', lockdir.LockDir)
379
'lock', lockdir.LockDir)
335
380
repo = self.repository_class(_format=self,
336
a_controldir=a_controldir,
337
control_files=control_files,
338
_commit_builder_class=self._commit_builder_class,
339
_serializer=self._serializer)
382
control_files=control_files,
383
_commit_builder_class=self._commit_builder_class,
384
_serializer=self._serializer)
340
385
repo.revisions = self._get_revisions(repo_transport, repo)
341
386
repo.signatures = self._get_signatures(repo_transport, repo)
342
387
repo.inventories = self._get_inventories(repo_transport, repo)
401
444
repository_class = KnitRepository
402
_commit_builder_class = VersionedFileCommitBuilder
445
_commit_builder_class = RootCommitBuilder
403
446
rich_root_data = True
405
447
supports_tree_reference = True
408
449
def _serializer(self):
409
450
return xml7.serializer_v7
411
452
def _get_matching_bzrdir(self):
412
return controldir.format_registry.make_controldir('dirstate-with-subtree')
453
return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
414
455
def _ignore_setting_bzrdir(self, format):
417
_matchingcontroldir = property(
418
_get_matching_bzrdir, _ignore_setting_bzrdir)
458
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
421
def get_format_string(cls):
460
def get_format_string(self):
422
461
"""See RepositoryFormat.get_format_string()."""
423
return b"Bazaar Knit Repository Format 3 (bzr 0.15)\n"
462
return "Bazaar Knit Repository Format 3 (bzr 0.15)\n"
425
464
def get_format_description(self):
426
465
"""See RepositoryFormat.get_format_description()."""
446
485
repository_class = KnitRepository
447
_commit_builder_class = VersionedFileCommitBuilder
486
_commit_builder_class = RootCommitBuilder
448
487
rich_root_data = True
449
488
supports_tree_reference = False
452
490
def _serializer(self):
453
491
return xml6.serializer_v6
455
493
def _get_matching_bzrdir(self):
456
return controldir.format_registry.make_controldir('rich-root')
494
return bzrdir.format_registry.make_bzrdir('rich-root')
458
496
def _ignore_setting_bzrdir(self, format):
461
_matchingcontroldir = property(
462
_get_matching_bzrdir, _ignore_setting_bzrdir)
499
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
465
def get_format_string(cls):
501
def get_format_string(self):
466
502
"""See RepositoryFormat.get_format_string()."""
467
return b'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
503
return 'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
469
505
def get_format_description(self):
470
506
"""See RepositoryFormat.get_format_description()."""
471
507
return "Knit repository format 4"
474
class InterKnitRepo(InterSameDataRepository):
475
"""Optimised code paths between Knit based repositories."""
478
def _get_repo_format_to_test(self):
479
return RepositoryFormatKnit1()
482
def is_compatible(source, target):
483
"""Be compatible with known Knit formats.
485
We don't test for the stores being of specific types because that
486
could lead to confusing results, and there is no need to be
490
are_knits = (isinstance(source._format, RepositoryFormatKnit)
491
and isinstance(target._format, RepositoryFormatKnit))
492
except AttributeError:
494
return are_knits and InterRepository._same_model(source, target)
496
def search_missing_revision_ids(self,
497
find_ghosts=True, revision_ids=None, if_present_ids=None,
499
"""See InterRepository.search_missing_revision_ids()."""
500
with self.lock_read():
501
source_ids_set = self._present_source_revisions_for(
502
revision_ids, if_present_ids)
503
# source_ids is the worst possible case we may need to pull.
504
# now we want to filter source_ids against what we actually
505
# have in target, but don't try to check for existence where we know
506
# we do not have a revision as that would be pointless.
507
target_ids = set(self.target.all_revision_ids())
508
possibly_present_revisions = target_ids.intersection(
510
actually_present_revisions = set(
511
self.target._eliminate_revisions_not_present(possibly_present_revisions))
512
required_revisions = source_ids_set.difference(
513
actually_present_revisions)
514
if revision_ids is not None:
515
# we used get_ancestry to determine source_ids then we are assured all
516
# revisions referenced are present as they are installed in topological order.
517
# and the tip revision was validated by get_ancestry.
518
result_set = required_revisions
520
# if we just grabbed the possibly available ids, then
521
# we only have an estimate of whats available and need to validate
522
# that against the revision records.
524
self.source._eliminate_revisions_not_present(required_revisions))
525
if limit is not None:
526
topo_ordered = self.source.get_graph().iter_topo_order(result_set)
527
result_set = set(itertools.islice(topo_ordered, limit))
528
return self.source.revision_ids_to_search_result(result_set)
531
InterRepository.register_optimiser(InterKnitRepo)