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 __future__ import absolute_import
19
from ..lazy_import import lazy_import
17
from bzrlib.lazy_import import lazy_import
20
18
lazy_import(globals(), """
28
26
revision as _mod_revision,
32
from breezy.bzr import (
43
from ..repository import (
38
from bzrlib.decorators import needs_read_lock, needs_write_lock
39
from bzrlib.repository import (
45
41
IsInWriteGroupError,
47
from ..bzr.repository import (
48
RepositoryFormatMetaDir,
50
from ..bzr.vf_repository import (
51
InterSameDataRepository,
52
MetaDirVersionedFileRepository,
53
MetaDirVersionedFileRepositoryFormat,
54
VersionedFileCommitBuilder,
43
MetaDirRepositoryFormat,
122
113
_commit_builder_class = None
123
114
_serializer = None
125
def __init__(self, _format, a_controldir, control_files, _commit_builder_class,
127
super(KnitRepository, self).__init__(
128
_format, a_controldir, control_files)
116
def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
118
MetaDirRepository.__init__(self, _format, a_bzrdir, control_files)
129
119
self._commit_builder_class = _commit_builder_class
130
120
self._serializer = _serializer
131
121
self._reconcile_fixes_text_parents = True
133
124
def _all_revision_ids(self):
134
125
"""See Repository.all_revision_ids()."""
135
with self.lock_read():
136
return [key[0] for key in self.revisions.keys()]
126
return [key[0] for key in self.revisions.keys()]
138
128
def _activate_new_inventory(self):
139
129
"""Put a replacement inventory.new into use as inventories."""
181
171
def _temp_inventories(self):
182
172
result = self._format._get_inventories(self._transport, self,
184
174
# Reconciling when the output has no revisions would result in no
185
175
# writes - but we want to ensure there is an inventory for
186
176
# compatibility with older clients that don't lazy-load.
187
result.get_parent_map([(b'A',)])
177
result.get_parent_map([('A',)])
180
def fileid_involved_between_revs(self, from_revid, to_revid):
181
"""Find file_id(s) which are involved in the changes between revisions.
183
This determines the set of revisions which are involved, and then
184
finds all file ids affected by those revisions.
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)
192
def fileid_involved(self, last_revid=None):
193
"""Find all file_ids modified in the ancestry of last_revid.
195
:param last_revid: If None, last_revision() will be used.
198
changed = set(self.all_revision_ids())
200
changed = set(self.get_ancestry(last_revid))
203
return self._fileid_involved_by_set(changed)
190
206
def get_revision(self, revision_id):
191
207
"""Return the Revision object for a named revision"""
192
208
revision_id = osutils.safe_revision_id(revision_id)
193
with self.lock_read():
194
return self.get_revision_reconcile(revision_id)
209
return self.get_revision_reconcile(revision_id)
196
211
def _refresh_data(self):
197
212
if not self.is_locked():
207
222
self.control_files._set_read_transaction()
209
225
def reconcile(self, other=None, thorough=False):
210
226
"""Reconcile this repository."""
211
from .reconcile import KnitReconciler
212
with self.lock_write():
213
reconciler = KnitReconciler(self, thorough=thorough)
214
return reconciler.reconcile()
227
from bzrlib.reconcile import KnitReconciler
228
reconciler = KnitReconciler(self, thorough=thorough)
229
reconciler.reconcile()
216
232
def _make_parents_provider(self):
217
233
return _KnitsParentsProvider(self.revisions)
220
class RepositoryFormatKnit(MetaDirVersionedFileRepositoryFormat):
235
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
236
"""Find revisions with different parent lists in the revision object
237
and in the index graph.
239
:param revisions_iterator: None, or an iterator of (revid,
240
Revision-or-None). This iterator controls the revisions checked.
241
:returns: an iterator yielding tuples of (revison-id, parents-in-index,
242
parents-in-revision).
244
if not self.is_locked():
245
raise AssertionError()
247
if revisions_iterator is None:
248
revisions_iterator = self._iter_revisions(None)
249
for revid, revision in revisions_iterator:
252
parent_map = vf.get_parent_map([(revid,)])
253
parents_according_to_index = tuple(parent[-1] for parent in
254
parent_map[(revid,)])
255
parents_according_to_revision = tuple(revision.parent_ids)
256
if parents_according_to_index != parents_according_to_revision:
257
yield (revid, parents_according_to_index,
258
parents_according_to_revision)
260
def _check_for_inconsistent_revision_parents(self):
261
inconsistencies = list(self._find_inconsistent_revision_parents())
263
raise errors.BzrCheckError(
264
"Revision knit has inconsistent parents.")
266
def revision_graph_can_have_wrong_parents(self):
267
# The revision.kndx could potentially claim a revision has a different
268
# parent to the revision text.
272
class RepositoryFormatKnit(MetaDirRepositoryFormat):
221
273
"""Bzr repository knit format (generalized).
223
275
This repository format has:
253
304
_fetch_order = 'topological'
254
305
_fetch_uses_deltas = True
255
306
fast_deltas = False
256
supports_funky_characters = True
257
# The revision.kndx could potentially claim a revision has a different
258
# parent to the revision text.
259
revision_graph_can_have_wrong_parents = True
261
308
def _get_inventories(self, repo_transport, repo, name='inventory'):
262
309
mapper = versionedfile.ConstantMapper(name)
263
310
index = _mod_knit._KndxIndex(repo_transport, mapper,
264
repo.get_transaction, repo.is_write_locked, repo.is_locked)
311
repo.get_transaction, repo.is_write_locked, repo.is_locked)
265
312
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
266
313
return _mod_knit.KnitVersionedFiles(index, access, annotated=False)
268
315
def _get_revisions(self, repo_transport, repo):
269
316
mapper = versionedfile.ConstantMapper('revisions')
270
317
index = _mod_knit._KndxIndex(repo_transport, mapper,
271
repo.get_transaction, repo.is_write_locked, repo.is_locked)
318
repo.get_transaction, repo.is_write_locked, repo.is_locked)
272
319
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
273
320
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
276
323
def _get_signatures(self, repo_transport, repo):
277
324
mapper = versionedfile.ConstantMapper('signatures')
278
325
index = _mod_knit._KndxIndex(repo_transport, mapper,
279
repo.get_transaction, repo.is_write_locked, repo.is_locked)
326
repo.get_transaction, repo.is_write_locked, repo.is_locked)
280
327
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
281
328
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
284
331
def _get_texts(self, repo_transport, repo):
285
332
mapper = versionedfile.HashEscapedPrefixMapper()
286
333
base_transport = repo_transport.clone('knits')
287
334
index = _mod_knit._KndxIndex(base_transport, mapper,
288
repo.get_transaction, repo.is_write_locked, repo.is_locked)
335
repo.get_transaction, repo.is_write_locked, repo.is_locked)
289
336
access = _mod_knit._KnitKeyAccess(base_transport, mapper)
290
337
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=200,
293
def initialize(self, a_controldir, shared=False):
340
def initialize(self, a_bzrdir, shared=False):
294
341
"""Create a knit format 1 repository.
296
:param a_controldir: bzrdir to contain the new repository; must already
343
:param a_bzrdir: bzrdir to contain the new repository; must already
298
345
:param shared: If true the repository will be initialized as a shared
301
trace.mutter('creating repository in %s.', a_controldir.transport.base)
348
trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
304
351
utf8_files = [('format', self.get_format_string())]
306
self._upload_blank_content(
307
a_controldir, dirs, files, utf8_files, shared)
308
repo_transport = a_controldir.get_repository_transport(None)
353
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
354
repo_transport = a_bzrdir.get_repository_transport(None)
309
355
control_files = lockable_files.LockableFiles(repo_transport,
310
'lock', lockdir.LockDir)
356
'lock', lockdir.LockDir)
311
357
transaction = transactions.WriteTransaction()
312
result = self.open(a_controldir=a_controldir, _found=True)
358
result = self.open(a_bzrdir=a_bzrdir, _found=True)
313
359
result.lock_write()
314
360
# the revision id here is irrelevant: it will not be stored, and cannot
315
361
# already exist, we do this to create files on disk for older clients.
316
result.inventories.get_parent_map([(b'A',)])
317
result.revisions.get_parent_map([(b'A',)])
318
result.signatures.get_parent_map([(b'A',)])
362
result.inventories.get_parent_map([('A',)])
363
result.revisions.get_parent_map([('A',)])
364
result.signatures.get_parent_map([('A',)])
320
self._run_post_repo_init_hooks(result, a_controldir, shared)
366
self._run_post_repo_init_hooks(result, a_bzrdir, shared)
323
def open(self, a_controldir, _found=False, _override_transport=None):
369
def open(self, a_bzrdir, _found=False, _override_transport=None):
324
370
"""See RepositoryFormat.open().
326
372
:param _override_transport: INTERNAL USE ONLY. Allows opening the
328
374
than normal. I.e. during 'upgrade'.
331
format = RepositoryFormatMetaDir.find_format(a_controldir)
377
format = RepositoryFormat.find_format(a_bzrdir)
332
378
if _override_transport is not None:
333
379
repo_transport = _override_transport
335
repo_transport = a_controldir.get_repository_transport(None)
381
repo_transport = a_bzrdir.get_repository_transport(None)
336
382
control_files = lockable_files.LockableFiles(repo_transport,
337
'lock', lockdir.LockDir)
383
'lock', lockdir.LockDir)
338
384
repo = self.repository_class(_format=self,
339
a_controldir=a_controldir,
340
control_files=control_files,
341
_commit_builder_class=self._commit_builder_class,
342
_serializer=self._serializer)
386
control_files=control_files,
387
_commit_builder_class=self._commit_builder_class,
388
_serializer=self._serializer)
343
389
repo.revisions = self._get_revisions(repo_transport, repo)
344
390
repo.signatures = self._get_signatures(repo_transport, repo)
345
391
repo.inventories = self._get_inventories(repo_transport, repo)
404
448
repository_class = KnitRepository
405
_commit_builder_class = VersionedFileCommitBuilder
449
_commit_builder_class = RootCommitBuilder
406
450
rich_root_data = True
407
451
experimental = True
408
452
supports_tree_reference = True
411
454
def _serializer(self):
412
455
return xml7.serializer_v7
414
457
def _get_matching_bzrdir(self):
415
return controldir.format_registry.make_controldir('dirstate-with-subtree')
458
return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
417
460
def _ignore_setting_bzrdir(self, format):
420
_matchingcontroldir = property(
421
_get_matching_bzrdir, _ignore_setting_bzrdir)
463
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
424
def get_format_string(cls):
465
def get_format_string(self):
425
466
"""See RepositoryFormat.get_format_string()."""
426
return b"Bazaar Knit Repository Format 3 (bzr 0.15)\n"
467
return "Bazaar Knit Repository Format 3 (bzr 0.15)\n"
428
469
def get_format_description(self):
429
470
"""See RepositoryFormat.get_format_description()."""
449
490
repository_class = KnitRepository
450
_commit_builder_class = VersionedFileCommitBuilder
491
_commit_builder_class = RootCommitBuilder
451
492
rich_root_data = True
452
493
supports_tree_reference = False
455
495
def _serializer(self):
456
496
return xml6.serializer_v6
458
498
def _get_matching_bzrdir(self):
459
return controldir.format_registry.make_controldir('rich-root')
499
return bzrdir.format_registry.make_bzrdir('rich-root')
461
501
def _ignore_setting_bzrdir(self, format):
464
_matchingcontroldir = property(
465
_get_matching_bzrdir, _ignore_setting_bzrdir)
504
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
468
def get_format_string(cls):
506
def get_format_string(self):
469
507
"""See RepositoryFormat.get_format_string()."""
470
return b'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
508
return 'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
472
510
def get_format_description(self):
473
511
"""See RepositoryFormat.get_format_description()."""
474
512
return "Knit repository format 4"
477
class InterKnitRepo(InterSameDataRepository):
478
"""Optimised code paths between Knit based repositories."""
481
def _get_repo_format_to_test(self):
482
return RepositoryFormatKnit1()
485
def is_compatible(source, target):
486
"""Be compatible with known Knit formats.
488
We don't test for the stores being of specific types because that
489
could lead to confusing results, and there is no need to be
493
are_knits = (isinstance(source._format, RepositoryFormatKnit)
494
and isinstance(target._format, RepositoryFormatKnit))
495
except AttributeError:
497
return are_knits and InterRepository._same_model(source, target)
499
def search_missing_revision_ids(self,
500
find_ghosts=True, revision_ids=None, if_present_ids=None,
502
"""See InterRepository.search_missing_revision_ids()."""
503
with self.lock_read():
504
source_ids_set = self._present_source_revisions_for(
505
revision_ids, if_present_ids)
506
# source_ids is the worst possible case we may need to pull.
507
# now we want to filter source_ids against what we actually
508
# have in target, but don't try to check for existence where we know
509
# we do not have a revision as that would be pointless.
510
target_ids = set(self.target.all_revision_ids())
511
possibly_present_revisions = target_ids.intersection(
513
actually_present_revisions = set(
514
self.target._eliminate_revisions_not_present(possibly_present_revisions))
515
required_revisions = source_ids_set.difference(
516
actually_present_revisions)
517
if revision_ids is not None:
518
# we used get_ancestry to determine source_ids then we are assured all
519
# revisions referenced are present as they are installed in topological order.
520
# and the tip revision was validated by get_ancestry.
521
result_set = required_revisions
523
# if we just grabbed the possibly available ids, then
524
# we only have an estimate of whats available and need to validate
525
# that against the revision records.
527
self.source._eliminate_revisions_not_present(required_revisions))
528
if limit is not None:
529
topo_ordered = self.source.get_graph().iter_topo_order(result_set)
530
result_set = set(itertools.islice(topo_ordered, limit))
531
return self.source.revision_ids_to_search_result(result_set)
534
InterRepository.register_optimiser(InterKnitRepo)