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(), """
29
26
revision as _mod_revision,
33
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,
38
from bzrlib.decorators import needs_read_lock, needs_write_lock
39
from bzrlib.repository import (
42
MetaDirRepositoryFormat,
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
207
revision_id = osutils.safe_revision_id(revision_id)
191
with self.lock_read():
192
return self.get_revision_reconcile(revision_id)
208
return self.get_revision_reconcile(revision_id)
194
210
def _refresh_data(self):
195
211
if not self.is_locked():
197
if self.is_in_write_group():
198
raise IsInWriteGroupError(self)
199
213
# Create a new transaction to force all knits to see the scope change.
200
214
# This is safe because we're outside a write group.
201
215
self.control_files._finish_transaction()
205
219
self.control_files._set_read_transaction()
207
222
def reconcile(self, other=None, thorough=False):
208
223
"""Reconcile this repository."""
209
from breezy.reconcile import KnitReconciler
210
with self.lock_write():
211
reconciler = KnitReconciler(self, thorough=thorough)
212
reconciler.reconcile()
224
from bzrlib.reconcile import KnitReconciler
225
reconciler = KnitReconciler(self, thorough=thorough)
226
reconciler.reconcile()
215
229
def _make_parents_provider(self):
216
230
return _KnitsParentsProvider(self.revisions)
219
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):
220
270
"""Bzr repository knit format (generalized).
222
272
This repository format has:
252
301
_fetch_order = 'topological'
253
302
_fetch_uses_deltas = True
254
303
fast_deltas = False
255
supports_funky_characters = True
256
# The revision.kndx could potentially claim a revision has a different
257
# parent to the revision text.
258
revision_graph_can_have_wrong_parents = True
260
305
def _get_inventories(self, repo_transport, repo, name='inventory'):
261
306
mapper = versionedfile.ConstantMapper(name)
262
307
index = _mod_knit._KndxIndex(repo_transport, mapper,
263
repo.get_transaction, repo.is_write_locked, repo.is_locked)
308
repo.get_transaction, repo.is_write_locked, repo.is_locked)
264
309
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
265
310
return _mod_knit.KnitVersionedFiles(index, access, annotated=False)
267
312
def _get_revisions(self, repo_transport, repo):
268
313
mapper = versionedfile.ConstantMapper('revisions')
269
314
index = _mod_knit._KndxIndex(repo_transport, mapper,
270
repo.get_transaction, repo.is_write_locked, repo.is_locked)
315
repo.get_transaction, repo.is_write_locked, repo.is_locked)
271
316
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
272
317
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
275
320
def _get_signatures(self, repo_transport, repo):
276
321
mapper = versionedfile.ConstantMapper('signatures')
277
322
index = _mod_knit._KndxIndex(repo_transport, mapper,
278
repo.get_transaction, repo.is_write_locked, repo.is_locked)
323
repo.get_transaction, repo.is_write_locked, repo.is_locked)
279
324
access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
280
325
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
283
328
def _get_texts(self, repo_transport, repo):
284
329
mapper = versionedfile.HashEscapedPrefixMapper()
285
330
base_transport = repo_transport.clone('knits')
286
331
index = _mod_knit._KndxIndex(base_transport, mapper,
287
repo.get_transaction, repo.is_write_locked, repo.is_locked)
332
repo.get_transaction, repo.is_write_locked, repo.is_locked)
288
333
access = _mod_knit._KnitKeyAccess(base_transport, mapper)
289
334
return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=200,
292
def initialize(self, a_controldir, shared=False):
337
def initialize(self, a_bzrdir, shared=False):
293
338
"""Create a knit format 1 repository.
295
:param a_controldir: bzrdir to contain the new repository; must already
340
:param a_bzrdir: bzrdir to contain the new repository; must already
297
342
:param shared: If true the repository will be initialized as a shared
300
trace.mutter('creating repository in %s.', a_controldir.transport.base)
345
trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
303
348
utf8_files = [('format', self.get_format_string())]
305
self._upload_blank_content(
306
a_controldir, dirs, files, utf8_files, shared)
307
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)
308
352
control_files = lockable_files.LockableFiles(repo_transport,
309
'lock', lockdir.LockDir)
353
'lock', lockdir.LockDir)
310
354
transaction = transactions.WriteTransaction()
311
result = self.open(a_controldir=a_controldir, _found=True)
355
result = self.open(a_bzrdir=a_bzrdir, _found=True)
312
356
result.lock_write()
313
357
# the revision id here is irrelevant: it will not be stored, and cannot
314
358
# already exist, we do this to create files on disk for older clients.
315
result.inventories.get_parent_map([(b'A',)])
316
result.revisions.get_parent_map([(b'A',)])
317
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',)])
319
self._run_post_repo_init_hooks(result, a_controldir, shared)
363
self._run_post_repo_init_hooks(result, a_bzrdir, shared)
322
def open(self, a_controldir, _found=False, _override_transport=None):
366
def open(self, a_bzrdir, _found=False, _override_transport=None):
323
367
"""See RepositoryFormat.open().
325
369
:param _override_transport: INTERNAL USE ONLY. Allows opening the
327
371
than normal. I.e. during 'upgrade'.
330
format = RepositoryFormatMetaDir.find_format(a_controldir)
374
format = RepositoryFormat.find_format(a_bzrdir)
331
375
if _override_transport is not None:
332
376
repo_transport = _override_transport
334
repo_transport = a_controldir.get_repository_transport(None)
378
repo_transport = a_bzrdir.get_repository_transport(None)
335
379
control_files = lockable_files.LockableFiles(repo_transport,
336
'lock', lockdir.LockDir)
380
'lock', lockdir.LockDir)
337
381
repo = self.repository_class(_format=self,
338
a_controldir=a_controldir,
339
control_files=control_files,
340
_commit_builder_class=self._commit_builder_class,
341
_serializer=self._serializer)
383
control_files=control_files,
384
_commit_builder_class=self._commit_builder_class,
385
_serializer=self._serializer)
342
386
repo.revisions = self._get_revisions(repo_transport, repo)
343
387
repo.signatures = self._get_signatures(repo_transport, repo)
344
388
repo.inventories = self._get_inventories(repo_transport, repo)
403
445
repository_class = KnitRepository
404
_commit_builder_class = VersionedFileCommitBuilder
446
_commit_builder_class = RootCommitBuilder
405
447
rich_root_data = True
406
448
experimental = True
407
449
supports_tree_reference = True
410
451
def _serializer(self):
411
452
return xml7.serializer_v7
413
454
def _get_matching_bzrdir(self):
414
return controldir.format_registry.make_controldir('dirstate-with-subtree')
455
return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
416
457
def _ignore_setting_bzrdir(self, format):
419
_matchingcontroldir = property(
420
_get_matching_bzrdir, _ignore_setting_bzrdir)
460
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
423
def get_format_string(cls):
462
def get_format_string(self):
424
463
"""See RepositoryFormat.get_format_string()."""
425
return b"Bazaar Knit Repository Format 3 (bzr 0.15)\n"
464
return "Bazaar Knit Repository Format 3 (bzr 0.15)\n"
427
466
def get_format_description(self):
428
467
"""See RepositoryFormat.get_format_description()."""
448
487
repository_class = KnitRepository
449
_commit_builder_class = VersionedFileCommitBuilder
488
_commit_builder_class = RootCommitBuilder
450
489
rich_root_data = True
451
490
supports_tree_reference = False
454
492
def _serializer(self):
455
493
return xml6.serializer_v6
457
495
def _get_matching_bzrdir(self):
458
return controldir.format_registry.make_controldir('rich-root')
496
return bzrdir.format_registry.make_bzrdir('rich-root')
460
498
def _ignore_setting_bzrdir(self, format):
463
_matchingcontroldir = property(
464
_get_matching_bzrdir, _ignore_setting_bzrdir)
501
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
467
def get_format_string(cls):
503
def get_format_string(self):
468
504
"""See RepositoryFormat.get_format_string()."""
469
return b'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
505
return 'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
471
507
def get_format_description(self):
472
508
"""See RepositoryFormat.get_format_description()."""
473
509
return "Knit repository format 4"
476
class InterKnitRepo(InterSameDataRepository):
477
"""Optimised code paths between Knit based repositories."""
480
def _get_repo_format_to_test(self):
481
return RepositoryFormatKnit1()
484
def is_compatible(source, target):
485
"""Be compatible with known Knit formats.
487
We don't test for the stores being of specific types because that
488
could lead to confusing results, and there is no need to be
492
are_knits = (isinstance(source._format, RepositoryFormatKnit)
493
and isinstance(target._format, RepositoryFormatKnit))
494
except AttributeError:
496
return are_knits and InterRepository._same_model(source, target)
498
def search_missing_revision_ids(self,
499
find_ghosts=True, revision_ids=None, if_present_ids=None,
501
"""See InterRepository.search_missing_revision_ids()."""
502
with self.lock_read():
503
source_ids_set = self._present_source_revisions_for(
504
revision_ids, if_present_ids)
505
# source_ids is the worst possible case we may need to pull.
506
# now we want to filter source_ids against what we actually
507
# have in target, but don't try to check for existence where we know
508
# we do not have a revision as that would be pointless.
509
target_ids = set(self.target.all_revision_ids())
510
possibly_present_revisions = target_ids.intersection(
512
actually_present_revisions = set(
513
self.target._eliminate_revisions_not_present(possibly_present_revisions))
514
required_revisions = source_ids_set.difference(
515
actually_present_revisions)
516
if revision_ids is not None:
517
# we used get_ancestry to determine source_ids then we are assured all
518
# revisions referenced are present as they are installed in topological order.
519
# and the tip revision was validated by get_ancestry.
520
result_set = required_revisions
522
# if we just grabbed the possibly available ids, then
523
# we only have an estimate of whats available and need to validate
524
# that against the revision records.
526
self.source._eliminate_revisions_not_present(required_revisions))
527
if limit is not None:
528
topo_ordered = self.source.get_graph().iter_topo_order(result_set)
529
result_set = set(itertools.islice(topo_ordered, limit))
530
return self.source.revision_ids_to_search_result(result_set)
533
InterRepository.register_optimiser(InterKnitRepo)