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 (
47
from ..bzr.repository import (
48
RepositoryFormatMetaDir,
50
from ..bzr.vf_repository import (
51
InterSameDataRepository,
52
MetaDirVersionedFileRepository,
53
MetaDirVersionedFileRepositoryFormat,
54
VersionedFileCommitBuilder,
38
from bzrlib.decorators import needs_read_lock, needs_write_lock
39
from bzrlib.repository import (
42
MetaDirRepositoryFormat,
122
112
_commit_builder_class = None
123
113
_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)
115
def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
117
MetaDirRepository.__init__(self, _format, a_bzrdir, control_files)
129
118
self._commit_builder_class = _commit_builder_class
130
119
self._serializer = _serializer
131
120
self._reconcile_fixes_text_parents = True
133
123
def _all_revision_ids(self):
134
124
"""See Repository.all_revision_ids()."""
135
with self.lock_read():
136
return [key[0] for key in self.revisions.keys()]
125
return [key[0] for key in self.revisions.keys()]
138
127
def _activate_new_inventory(self):
139
128
"""Put a replacement inventory.new into use as inventories."""
181
170
def _temp_inventories(self):
182
171
result = self._format._get_inventories(self._transport, self,
184
173
# Reconciling when the output has no revisions would result in no
185
174
# writes - but we want to ensure there is an inventory for
186
175
# compatibility with older clients that don't lazy-load.
187
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)
190
205
def get_revision(self, revision_id):
191
206
"""Return the Revision object for a named revision"""
192
207
revision_id = osutils.safe_revision_id(revision_id)
193
with self.lock_read():
194
return self.get_revision_reconcile(revision_id)
208
return self.get_revision_reconcile(revision_id)
196
210
def _refresh_data(self):
197
211
if not self.is_locked():
199
if self.is_in_write_group():
200
raise IsInWriteGroupError(self)
201
213
# Create a new transaction to force all knits to see the scope change.
202
214
# This is safe because we're outside a write group.
203
215
self.control_files._finish_transaction()
207
219
self.control_files._set_read_transaction()
209
222
def reconcile(self, other=None, thorough=False):
210
223
"""Reconcile this repository."""
211
from .reconcile import KnitReconciler
212
with self.lock_write():
213
reconciler = KnitReconciler(self, thorough=thorough)
214
return reconciler.reconcile()
224
from bzrlib.reconcile import KnitReconciler
225
reconciler = KnitReconciler(self, thorough=thorough)
226
reconciler.reconcile()
216
229
def _make_parents_provider(self):
217
230
return _KnitsParentsProvider(self.revisions)
220
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):
221
270
"""Bzr repository knit format (generalized).
223
272
This repository format has:
253
301
_fetch_order = 'topological'
254
302
_fetch_uses_deltas = True
255
303
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
305
def _get_inventories(self, repo_transport, repo, name='inventory'):
262
306
mapper = versionedfile.ConstantMapper(name)
263
307
index = _mod_knit._KndxIndex(repo_transport, mapper,
264
repo.get_transaction, repo.is_write_locked, repo.is_locked)
308
repo.get_transaction, repo.is_write_locked, repo.is_locked)
265
309
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
266
310
return _mod_knit.KnitVersionedFiles(index, access, annotated=False)
268
312
def _get_revisions(self, repo_transport, repo):
269
313
mapper = versionedfile.ConstantMapper('revisions')
270
314
index = _mod_knit._KndxIndex(repo_transport, mapper,
271
repo.get_transaction, repo.is_write_locked, repo.is_locked)
315
repo.get_transaction, repo.is_write_locked, repo.is_locked)
272
316
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
273
317
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
276
320
def _get_signatures(self, repo_transport, repo):
277
321
mapper = versionedfile.ConstantMapper('signatures')
278
322
index = _mod_knit._KndxIndex(repo_transport, mapper,
279
repo.get_transaction, repo.is_write_locked, repo.is_locked)
323
repo.get_transaction, repo.is_write_locked, repo.is_locked)
280
324
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
281
325
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
284
328
def _get_texts(self, repo_transport, repo):
285
329
mapper = versionedfile.HashEscapedPrefixMapper()
286
330
base_transport = repo_transport.clone('knits')
287
331
index = _mod_knit._KndxIndex(base_transport, mapper,
288
repo.get_transaction, repo.is_write_locked, repo.is_locked)
332
repo.get_transaction, repo.is_write_locked, repo.is_locked)
289
333
access = _mod_knit._KnitKeyAccess(base_transport, mapper)
290
334
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=200,
293
def initialize(self, a_controldir, shared=False):
337
def initialize(self, a_bzrdir, shared=False):
294
338
"""Create a knit format 1 repository.
296
:param a_controldir: bzrdir to contain the new repository; must already
340
:param a_bzrdir: bzrdir to contain the new repository; must already
298
342
:param shared: If true the repository will be initialized as a shared
301
trace.mutter('creating repository in %s.', a_controldir.transport.base)
345
trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
304
348
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)
350
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
351
repo_transport = a_bzrdir.get_repository_transport(None)
309
352
control_files = lockable_files.LockableFiles(repo_transport,
310
'lock', lockdir.LockDir)
353
'lock', lockdir.LockDir)
311
354
transaction = transactions.WriteTransaction()
312
result = self.open(a_controldir=a_controldir, _found=True)
355
result = self.open(a_bzrdir=a_bzrdir, _found=True)
313
356
result.lock_write()
314
357
# the revision id here is irrelevant: it will not be stored, and cannot
315
358
# 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',)])
359
result.inventories.get_parent_map([('A',)])
360
result.revisions.get_parent_map([('A',)])
361
result.signatures.get_parent_map([('A',)])
320
self._run_post_repo_init_hooks(result, a_controldir, shared)
363
self._run_post_repo_init_hooks(result, a_bzrdir, shared)
323
def open(self, a_controldir, _found=False, _override_transport=None):
366
def open(self, a_bzrdir, _found=False, _override_transport=None):
324
367
"""See RepositoryFormat.open().
326
369
:param _override_transport: INTERNAL USE ONLY. Allows opening the
328
371
than normal. I.e. during 'upgrade'.
331
format = RepositoryFormatMetaDir.find_format(a_controldir)
374
format = RepositoryFormat.find_format(a_bzrdir)
332
375
if _override_transport is not None:
333
376
repo_transport = _override_transport
335
repo_transport = a_controldir.get_repository_transport(None)
378
repo_transport = a_bzrdir.get_repository_transport(None)
336
379
control_files = lockable_files.LockableFiles(repo_transport,
337
'lock', lockdir.LockDir)
380
'lock', lockdir.LockDir)
338
381
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)
383
control_files=control_files,
384
_commit_builder_class=self._commit_builder_class,
385
_serializer=self._serializer)
343
386
repo.revisions = self._get_revisions(repo_transport, repo)
344
387
repo.signatures = self._get_signatures(repo_transport, repo)
345
388
repo.inventories = self._get_inventories(repo_transport, repo)
404
445
repository_class = KnitRepository
405
_commit_builder_class = VersionedFileCommitBuilder
446
_commit_builder_class = RootCommitBuilder
406
447
rich_root_data = True
407
448
experimental = True
408
449
supports_tree_reference = True
411
451
def _serializer(self):
412
452
return xml7.serializer_v7
414
454
def _get_matching_bzrdir(self):
415
return controldir.format_registry.make_controldir('dirstate-with-subtree')
455
return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
417
457
def _ignore_setting_bzrdir(self, format):
420
_matchingcontroldir = property(
421
_get_matching_bzrdir, _ignore_setting_bzrdir)
460
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
424
def get_format_string(cls):
462
def get_format_string(self):
425
463
"""See RepositoryFormat.get_format_string()."""
426
return b"Bazaar Knit Repository Format 3 (bzr 0.15)\n"
464
return "Bazaar Knit Repository Format 3 (bzr 0.15)\n"
428
466
def get_format_description(self):
429
467
"""See RepositoryFormat.get_format_description()."""
449
487
repository_class = KnitRepository
450
_commit_builder_class = VersionedFileCommitBuilder
488
_commit_builder_class = RootCommitBuilder
451
489
rich_root_data = True
452
490
supports_tree_reference = False
455
492
def _serializer(self):
456
493
return xml6.serializer_v6
458
495
def _get_matching_bzrdir(self):
459
return controldir.format_registry.make_controldir('rich-root')
496
return bzrdir.format_registry.make_bzrdir('rich-root')
461
498
def _ignore_setting_bzrdir(self, format):
464
_matchingcontroldir = property(
465
_get_matching_bzrdir, _ignore_setting_bzrdir)
501
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
468
def get_format_string(cls):
503
def get_format_string(self):
469
504
"""See RepositoryFormat.get_format_string()."""
470
return b'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
505
return 'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
472
507
def get_format_description(self):
473
508
"""See RepositoryFormat.get_format_description()."""
474
509
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)