103
109
# the default CommitBuilder does not manage trees whose root is versioned.
104
110
_versioned_root = False
106
def __init__(self, repository, parents, config, timestamp=None,
112
def __init__(self, repository, parents, config_stack, timestamp=None,
107
113
timezone=None, committer=None, revprops=None,
108
114
revision_id=None, lossy=False):
109
115
super(VersionedFileCommitBuilder, self).__init__(repository,
110
parents, config, timestamp, timezone, committer, revprops,
116
parents, config_stack, timestamp, timezone, committer, revprops,
111
117
revision_id, lossy)
113
119
basis_id = self.parents[0]
195
201
properties=self._revprops)
196
202
rev.parent_ids = self.parents
197
203
self.repository.add_revision(self._new_revision_id, rev,
198
self.new_inventory, self._config)
204
self.new_inventory, self._config_stack)
199
205
self._ensure_fallback_inventories()
200
206
self.repository.commit_write_group()
201
207
return self._new_revision_id
419
425
return None, False, None
420
426
# XXX: Friction: parent_candidates should return a list not a dict
421
427
# so that we don't have to walk the inventories again.
422
parent_candiate_entries = ie.parent_candidates(parent_invs)
423
head_set = self._heads(ie.file_id, parent_candiate_entries.keys())
428
parent_candidate_entries = ie.parent_candidates(parent_invs)
429
head_set = self._heads(ie.file_id, parent_candidate_entries.keys())
425
431
for inv in parent_invs:
426
432
if inv.has_id(ie.file_id):
443
449
# There is a single head, look it up for comparison
444
parent_entry = parent_candiate_entries[heads[0]]
450
parent_entry = parent_candidate_entries[heads[0]]
445
451
# if the non-content specific data has changed, we'll be writing a
447
453
if (parent_entry.parent_id != ie.parent_id or
559
565
:param iter_changes: An iter_changes iterator with the changes to apply
560
566
to basis_revision_id. The iterator must not include any items with
561
567
a current kind of None - missing items must be either filtered out
562
or errored-on beefore record_iter_changes sees the item.
568
or errored-on before record_iter_changes sees the item.
563
569
:param _entry_factory: Private method to bind entry_factory locally for
565
571
:return: A generator of (file_id, relpath, fs_hash) tuples for use with
919
925
if not self._format.supports_external_lookups:
920
926
raise errors.UnstackableRepositoryFormat(self._format, self.base)
927
# This can raise an exception, so should be done before we lock the
928
# fallback repository.
929
self._check_fallback_repository(repository)
921
930
if self.is_locked():
922
931
# This repository will call fallback.unlock() when we transition to
923
932
# the unlocked state, so we make sure to increment the lock count
924
933
repository.lock_read()
925
self._check_fallback_repository(repository)
926
934
self._fallback_repositories.append(repository)
927
935
self.texts.add_fallback_versioned_files(repository.texts)
928
936
self.inventories.add_fallback_versioned_files(repository.inventories)
1039
1047
# TODO: jam 20070210 Shouldn't we check rev.revision_id and
1040
1048
# rev.parent_ids?
1041
1049
_mod_revision.check_not_reserved_id(revision_id)
1042
if config is not None and config.signature_needed():
1050
if (config is not None and
1051
config.get('create_signatures') == _mod_config.SIGN_ALWAYS):
1043
1052
if inv is None:
1044
1053
inv = self.get_inventory(revision_id)
1045
1054
tree = InventoryRevisionTree(self, inv, revision_id)
1085
1094
keys = {'chk_bytes':set(), 'inventories':set(), 'texts':set()}
1086
1095
kinds = ['chk_bytes', 'texts']
1087
1096
count = len(checker.pending_keys)
1088
bar.update("inventories", 0, 2)
1097
bar.update(gettext("inventories"), 0, 2)
1089
1098
current_keys = checker.pending_keys
1090
1099
checker.pending_keys = {}
1091
1100
# Accumulate current checks.
1180
1189
'sha1 mismatch: %s has sha1 %s expected %s referenced by %s' %
1181
1190
(record.key, sha1, item_data[1], item_data[2]))
1193
def _eliminate_revisions_not_present(self, revision_ids):
1194
"""Check every revision id in revision_ids to see if we have it.
1196
Returns a set of the present revisions.
1199
graph = self.get_graph()
1200
parent_map = graph.get_parent_map(revision_ids)
1201
# The old API returned a list, should this actually be a set?
1202
return parent_map.keys()
1183
1204
def __init__(self, _format, a_bzrdir, control_files):
1184
1205
"""Instantiate a VersionedFileRepository.
1186
1207
:param _format: The format of the repository on disk.
1187
:param a_bzrdir: The BzrDir of the repository.
1208
:param controldir: The ControlDir of the repository.
1188
1209
:param control_files: Control files to use for locking, etc.
1190
1211
# In the future we will have a single api for all stores for
1192
1213
# this construct will accept instances of those things.
1193
1214
super(VersionedFileRepository, self).__init__(_format, a_bzrdir,
1216
self._transport = control_files._transport
1217
self.base = self._transport.base
1196
1219
self._reconcile_does_inventory_gc = True
1197
1220
self._reconcile_fixes_text_parents = False
1202
1225
# rather copying them?
1203
1226
self._safe_to_return_from_cache = False
1228
def fetch(self, source, revision_id=None, find_ghosts=False,
1230
"""Fetch the content required to construct revision_id from source.
1232
If revision_id is None and fetch_spec is None, then all content is
1235
fetch() may not be used when the repository is in a write group -
1236
either finish the current write group before using fetch, or use
1237
fetch before starting the write group.
1239
:param find_ghosts: Find and copy revisions in the source that are
1240
ghosts in the target (and not reachable directly by walking out to
1241
the first-present revision in target from revision_id).
1242
:param revision_id: If specified, all the content needed for this
1243
revision ID will be copied to the target. Fetch will determine for
1244
itself which content needs to be copied.
1245
:param fetch_spec: If specified, a SearchResult or
1246
PendingAncestryResult that describes which revisions to copy. This
1247
allows copying multiple heads at once. Mutually exclusive with
1250
if fetch_spec is not None and revision_id is not None:
1251
raise AssertionError(
1252
"fetch_spec and revision_id are mutually exclusive.")
1253
if self.is_in_write_group():
1254
raise errors.InternalBzrError(
1255
"May not fetch while in a write group.")
1256
# fast path same-url fetch operations
1257
# TODO: lift out to somewhere common with RemoteRepository
1258
# <https://bugs.launchpad.net/bzr/+bug/401646>
1259
if (self.has_same_location(source)
1260
and fetch_spec is None
1261
and self._has_same_fallbacks(source)):
1262
# check that last_revision is in 'from' and then return a
1264
if (revision_id is not None and
1265
not _mod_revision.is_null(revision_id)):
1266
self.get_revision(revision_id)
1268
inter = InterRepository.get(source, self)
1269
if (fetch_spec is not None and
1270
not getattr(inter, "supports_fetch_spec", False)):
1271
raise errors.UnsupportedOperation(
1272
"fetch_spec not supported for %r" % inter)
1273
return inter.fetch(revision_id=revision_id,
1274
find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1205
1276
@needs_read_lock
1206
1277
def gather_stats(self, revid=None, committers=None):
1207
1278
"""See Repository.gather_stats()."""
1216
1287
# result['size'] = t
1219
def get_commit_builder(self, branch, parents, config, timestamp=None,
1290
def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
1220
1291
timezone=None, committer=None, revprops=None,
1221
1292
revision_id=None, lossy=False):
1222
1293
"""Obtain a CommitBuilder for this repository.
1224
1295
:param branch: Branch to commit to.
1225
1296
:param parents: Revision ids of the parents of the new revision.
1226
:param config: Configuration to use.
1297
:param config_stack: Configuration stack to use.
1227
1298
:param timestamp: Optional timestamp recorded for commit.
1228
1299
:param timezone: Optional timezone for timestamp.
1229
1300
:param committer: Optional committer to set for commit.
1236
1307
raise errors.BzrError("Cannot commit directly to a stacked branch"
1237
1308
" in pre-2a formats. See "
1238
1309
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1239
result = self._commit_builder_class(self, parents, config,
1310
result = self._commit_builder_class(self, parents, config_stack,
1240
1311
timestamp, timezone, committer, revprops, revision_id,
1242
1313
self.start_write_group()
1498
1569
text_keys[(file_id, revision_id)] = callable_data
1499
1570
for record in self.texts.get_record_stream(text_keys, 'unordered', True):
1500
1571
if record.storage_kind == 'absent':
1501
raise errors.RevisionNotPresent(record.key, self)
1572
raise errors.RevisionNotPresent(record.key[1], record.key[0])
1502
1573
yield text_keys[record.key], record.get_bytes_as('chunked')
1504
1575
def _generate_text_key_index(self, text_key_references=None,
1554
1625
batch_size = 10 # should be ~150MB on a 55K path tree
1555
1626
batch_count = len(revision_order) / batch_size + 1
1556
1627
processed_texts = 0
1557
pb.update("Calculating text parents", processed_texts, text_count)
1628
pb.update(gettext("Calculating text parents"), processed_texts, text_count)
1558
1629
for offset in xrange(batch_count):
1559
1630
to_query = revision_order[offset * batch_size:(offset + 1) *
1563
1634
for revision_id in to_query:
1564
1635
parent_ids = ancestors[revision_id]
1565
1636
for text_key in revision_keys[revision_id]:
1566
pb.update("Calculating text parents", processed_texts)
1637
pb.update(gettext("Calculating text parents"), processed_texts)
1567
1638
processed_texts += 1
1568
1639
candidate_parents = []
1569
1640
for parent_id in parent_ids:
1639
1710
num_file_ids = len(file_ids)
1640
1711
for file_id, altered_versions in file_ids.iteritems():
1641
1712
if pb is not None:
1642
pb.update("Fetch texts", count, num_file_ids)
1713
pb.update(gettext("Fetch texts"), count, num_file_ids)
1644
1715
yield ("file", file_id, altered_versions)
1682
1753
if ((None in revision_ids)
1683
1754
or (_mod_revision.NULL_REVISION in revision_ids)):
1684
1755
raise ValueError('cannot get null revision inventory')
1685
return self._iter_inventories(revision_ids, ordering)
1756
for inv, revid in self._iter_inventories(revision_ids, ordering):
1758
raise errors.NoSuchRevision(self, revid)
1687
1761
def _iter_inventories(self, revision_ids, ordering):
1688
1762
"""single-document based inventory iteration."""
1689
1763
inv_xmls = self._iter_inventory_xmls(revision_ids, ordering)
1690
1764
for text, revision_id in inv_xmls:
1691
yield self._deserialise_inventory(revision_id, text)
1766
yield None, revision_id
1768
yield self._deserialise_inventory(revision_id, text), revision_id
1693
1770
def _iter_inventory_xmls(self, revision_ids, ordering):
1694
1771
if ordering is None:
1713
1790
yield ''.join(chunks), record.key[-1]
1715
raise errors.NoSuchRevision(self, record.key)
1792
yield None, record.key[-1]
1716
1793
if order_as_requested:
1717
1794
# Yield as many results as we can while preserving order.
1718
1795
while next_key in text_chunks:
1747
1824
def _get_inventory_xml(self, revision_id):
1748
1825
"""Get serialized inventory as a string."""
1749
1826
texts = self._iter_inventory_xmls([revision_id], 'unordered')
1751
text, revision_id = texts.next()
1752
except StopIteration:
1753
raise errors.HistoryMissing(self, 'inventory', revision_id)
1827
text, revision_id = texts.next()
1829
raise errors.NoSuchRevision(self, revision_id)
1756
1832
@needs_read_lock
1831
1907
"""Return the graph walker for text revisions."""
1832
1908
return graph.Graph(self.texts)
1910
def revision_ids_to_search_result(self, result_set):
1911
"""Convert a set of revision ids to a graph SearchResult."""
1912
result_parents = set()
1913
for parents in self.get_graph().get_parent_map(
1914
result_set).itervalues():
1915
result_parents.update(parents)
1916
included_keys = result_set.intersection(result_parents)
1917
start_keys = result_set.difference(included_keys)
1918
exclude_keys = result_parents.difference(result_set)
1919
result = vf_search.SearchResult(start_keys, exclude_keys,
1920
len(result_set), result_set)
1834
1923
def _get_versioned_file_checker(self, text_key_references=None,
1835
1924
ancestors=None):
1836
1925
"""Return an object suitable for checking versioned files.
2450
2539
self.text_index.iterkeys()])
2451
2540
# text keys is now grouped by file_id
2452
2541
n_versions = len(self.text_index)
2453
progress_bar.update('loading text store', 0, n_versions)
2542
progress_bar.update(gettext('loading text store'), 0, n_versions)
2454
2543
parent_map = self.repository.texts.get_parent_map(self.text_index)
2455
2544
# On unlistable transports this could well be empty/error...
2456
2545
text_keys = self.repository.texts.keys()
2457
2546
unused_keys = frozenset(text_keys) - set(self.text_index)
2458
2547
for num, key in enumerate(self.text_index.iterkeys()):
2459
progress_bar.update('checking text graph', num, n_versions)
2548
progress_bar.update(gettext('checking text graph'), num, n_versions)
2460
2549
correct_parents = self.calculate_file_version_parents(key)
2462
2551
knit_parents = parent_map[key]
2483
2574
content is copied.
2486
ui.ui_factory.warn_experimental_format_fetch(self)
2577
if self.target._format.experimental:
2578
ui.ui_factory.show_user_warning('experimental_format_fetch',
2579
from_format=self.source._format,
2580
to_format=self.target._format)
2487
2581
from bzrlib.fetch import RepoFetcher
2488
2582
# See <https://launchpad.net/bugs/456077> asking for a warning here
2489
2583
if self.source._format.network_name() != self.target._format.network_name():
2550
2644
searcher.stop_searching_any(stop_revs)
2551
2645
if searcher_exhausted:
2553
return searcher.get_result()
2647
(started_keys, excludes, included_keys) = searcher.get_state()
2648
return vf_search.SearchResult(started_keys, excludes,
2649
len(included_keys), included_keys)
2555
2651
@needs_read_lock
2556
2652
def search_missing_revision_ids(self,
2903
2999
for offset in range(0, len(revision_ids), batch_size):
2904
3000
self.target.start_write_group()
2906
pb.update('Transferring revisions', offset,
3002
pb.update(gettext('Transferring revisions'), offset,
2907
3003
len(revision_ids))
2908
3004
batch = revision_ids[offset:offset+batch_size]
2909
3005
basis_id = self._fetch_batch(batch, basis_id, cache)
2928
3024
revision_ids = fetch_spec.get_keys()
2930
3026
revision_ids = None
2931
ui.ui_factory.warn_experimental_format_fetch(self)
3027
if self.source._format.experimental:
3028
ui.ui_factory.show_user_warning('experimental_format_fetch',
3029
from_format=self.source._format,
3030
to_format=self.target._format)
2932
3031
if (not self.source.supports_rich_root()
2933
3032
and self.target.supports_rich_root()):
2934
3033
self._converting_to_rich_root = True
3029
3128
_install_revision(repository, revision, revision_tree, signature,
3030
3129
inventory_cache)
3031
3130
if pb is not None:
3032
pb.update('Transferring revisions', n + 1, num_revisions)
3131
pb.update(gettext('Transferring revisions'), n + 1, num_revisions)
3034
3133
repository.abort_write_group()