104
109
# the default CommitBuilder does not manage trees whose root is versioned.
105
110
_versioned_root = False
107
def __init__(self, repository, parents, config, timestamp=None,
112
def __init__(self, repository, parents, config_stack, timestamp=None,
108
113
timezone=None, committer=None, revprops=None,
109
114
revision_id=None, lossy=False):
110
115
super(VersionedFileCommitBuilder, self).__init__(repository,
111
parents, config, timestamp, timezone, committer, revprops,
116
parents, config_stack, timestamp, timezone, committer, revprops,
112
117
revision_id, lossy)
114
119
basis_id = self.parents[0]
195
200
revision_id=self._new_revision_id,
196
201
properties=self._revprops)
197
202
rev.parent_ids = self.parents
198
self.repository.add_revision(self._new_revision_id, rev,
199
self.new_inventory, self._config)
203
if self._config_stack.get('create_signatures') == _mod_config.SIGN_ALWAYS:
204
testament = Testament(rev, self.revision_tree())
205
plaintext = testament.as_short_text()
206
self.repository.store_revision_signature(
207
gpg.GPGStrategy(self._config_stack), plaintext,
208
self._new_revision_id)
209
self.repository._add_revision(rev)
200
210
self._ensure_fallback_inventories()
201
211
self.repository.commit_write_group()
202
212
return self._new_revision_id
594
604
_mod_revision.NULL_REVISION))
595
605
# The basis inventory from a repository
597
basis_inv = revtrees[0].inventory
607
basis_tree = revtrees[0]
599
basis_inv = self.repository.revision_tree(
600
_mod_revision.NULL_REVISION).inventory
609
basis_tree = self.repository.revision_tree(
610
_mod_revision.NULL_REVISION)
611
basis_inv = basis_tree.root_inventory
601
612
if len(self.parents) > 0:
602
613
if basis_revision_id != self.parents[0] and not ghost_basis:
604
615
"arbitrary basis parents not yet supported with merges")
605
616
for revtree in revtrees[1:]:
606
for change in revtree.inventory._make_delta(basis_inv):
617
for change in revtree.root_inventory._make_delta(basis_inv):
607
618
if change[1] is None:
608
619
# Not present in this parent.
1011
1022
# return a new inventory, but as there is no revision tree cache in
1012
1023
# repository this is safe for now - RBC 20081013
1013
1024
if basis_inv is None:
1014
basis_inv = basis_tree.inventory
1025
basis_inv = basis_tree.root_inventory
1015
1026
basis_inv.apply_delta(delta)
1016
1027
basis_inv.revision_id = new_revision_id
1017
1028
return (self.add_inventory(new_revision_id, basis_inv, parents),
1028
1039
self.inventories._access.flush()
1031
def add_revision(self, revision_id, rev, inv=None, config=None):
1042
def add_revision(self, revision_id, rev, inv=None):
1032
1043
"""Add rev to the revision store as revision_id.
1034
1045
:param revision_id: the revision id to use.
1035
1046
:param rev: The revision object.
1036
1047
:param inv: The inventory for the revision. if None, it will be looked
1037
1048
up in the inventory storer
1038
:param config: If None no digital signature will be created.
1039
If supplied its signature_needed method will be used
1040
to determine if a signature should be made.
1042
1050
# TODO: jam 20070210 Shouldn't we check rev.revision_id and
1043
1051
# rev.parent_ids?
1044
1052
_mod_revision.check_not_reserved_id(revision_id)
1045
if config is not None and config.signature_needed():
1047
inv = self.get_inventory(revision_id)
1048
tree = InventoryRevisionTree(self, inv, revision_id)
1049
testament = Testament(rev, tree)
1050
plaintext = testament.as_short_text()
1051
self.store_revision_signature(
1052
gpg.GPGStrategy(config), plaintext, revision_id)
1053
1053
# check inventory present
1054
1054
if not self.inventories.get_parent_map([(revision_id,)]):
1055
1055
if inv is None:
1199
1199
"""Instantiate a VersionedFileRepository.
1201
1201
:param _format: The format of the repository on disk.
1202
:param a_bzrdir: The BzrDir of the repository.
1202
:param controldir: The ControlDir of the repository.
1203
1203
:param control_files: Control files to use for locking, etc.
1205
1205
# In the future we will have a single api for all stores for
1219
1219
# rather copying them?
1220
1220
self._safe_to_return_from_cache = False
1222
def fetch(self, source, revision_id=None, find_ghosts=False,
1224
"""Fetch the content required to construct revision_id from source.
1226
If revision_id is None and fetch_spec is None, then all content is
1229
fetch() may not be used when the repository is in a write group -
1230
either finish the current write group before using fetch, or use
1231
fetch before starting the write group.
1233
:param find_ghosts: Find and copy revisions in the source that are
1234
ghosts in the target (and not reachable directly by walking out to
1235
the first-present revision in target from revision_id).
1236
:param revision_id: If specified, all the content needed for this
1237
revision ID will be copied to the target. Fetch will determine for
1238
itself which content needs to be copied.
1239
:param fetch_spec: If specified, a SearchResult or
1240
PendingAncestryResult that describes which revisions to copy. This
1241
allows copying multiple heads at once. Mutually exclusive with
1244
if fetch_spec is not None and revision_id is not None:
1245
raise AssertionError(
1246
"fetch_spec and revision_id are mutually exclusive.")
1247
if self.is_in_write_group():
1248
raise errors.InternalBzrError(
1249
"May not fetch while in a write group.")
1250
# fast path same-url fetch operations
1251
# TODO: lift out to somewhere common with RemoteRepository
1252
# <https://bugs.launchpad.net/bzr/+bug/401646>
1253
if (self.has_same_location(source)
1254
and fetch_spec is None
1255
and self._has_same_fallbacks(source)):
1256
# check that last_revision is in 'from' and then return a
1258
if (revision_id is not None and
1259
not _mod_revision.is_null(revision_id)):
1260
self.get_revision(revision_id)
1262
inter = InterRepository.get(source, self)
1263
if (fetch_spec is not None and
1264
not getattr(inter, "supports_fetch_spec", False)):
1265
raise errors.UnsupportedOperation(
1266
"fetch_spec not supported for %r" % inter)
1267
return inter.fetch(revision_id=revision_id,
1268
find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1222
1270
@needs_read_lock
1223
1271
def gather_stats(self, revid=None, committers=None):
1224
1272
"""See Repository.gather_stats()."""
1233
1281
# result['size'] = t
1236
def get_commit_builder(self, branch, parents, config, timestamp=None,
1284
def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
1237
1285
timezone=None, committer=None, revprops=None,
1238
1286
revision_id=None, lossy=False):
1239
1287
"""Obtain a CommitBuilder for this repository.
1241
1289
:param branch: Branch to commit to.
1242
1290
:param parents: Revision ids of the parents of the new revision.
1243
:param config: Configuration to use.
1291
:param config_stack: Configuration stack to use.
1244
1292
:param timestamp: Optional timestamp recorded for commit.
1245
1293
:param timezone: Optional timezone for timestamp.
1246
1294
:param committer: Optional committer to set for commit.
1253
1301
raise errors.BzrError("Cannot commit directly to a stacked branch"
1254
1302
" in pre-2a formats. See "
1255
1303
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1256
result = self._commit_builder_class(self, parents, config,
1304
result = self._commit_builder_class(self, parents, config_stack,
1257
1305
timestamp, timezone, committer, revprops, revision_id,
1259
1307
self.start_write_group()
1515
1563
text_keys[(file_id, revision_id)] = callable_data
1516
1564
for record in self.texts.get_record_stream(text_keys, 'unordered', True):
1517
1565
if record.storage_kind == 'absent':
1518
raise errors.RevisionNotPresent(record.key, self)
1566
raise errors.RevisionNotPresent(record.key[1], record.key[0])
1519
1567
yield text_keys[record.key], record.get_bytes_as('chunked')
1521
1569
def _generate_text_key_index(self, text_key_references=None,
1699
1747
if ((None in revision_ids)
1700
1748
or (_mod_revision.NULL_REVISION in revision_ids)):
1701
1749
raise ValueError('cannot get null revision inventory')
1702
return self._iter_inventories(revision_ids, ordering)
1750
for inv, revid in self._iter_inventories(revision_ids, ordering):
1752
raise errors.NoSuchRevision(self, revid)
1704
1755
def _iter_inventories(self, revision_ids, ordering):
1705
1756
"""single-document based inventory iteration."""
1706
1757
inv_xmls = self._iter_inventory_xmls(revision_ids, ordering)
1707
1758
for text, revision_id in inv_xmls:
1708
yield self._deserialise_inventory(revision_id, text)
1760
yield None, revision_id
1762
yield self._deserialise_inventory(revision_id, text), revision_id
1710
1764
def _iter_inventory_xmls(self, revision_ids, ordering):
1711
1765
if ordering is None:
1730
1784
yield ''.join(chunks), record.key[-1]
1732
raise errors.NoSuchRevision(self, record.key)
1786
yield None, record.key[-1]
1733
1787
if order_as_requested:
1734
1788
# Yield as many results as we can while preserving order.
1735
1789
while next_key in text_chunks:
1764
1818
def _get_inventory_xml(self, revision_id):
1765
1819
"""Get serialized inventory as a string."""
1766
1820
texts = self._iter_inventory_xmls([revision_id], 'unordered')
1768
text, revision_id = texts.next()
1769
except StopIteration:
1770
raise errors.HistoryMissing(self, 'inventory', revision_id)
1821
text, revision_id = texts.next()
1823
raise errors.NoSuchRevision(self, revision_id)
1773
1826
@needs_read_lock
1848
1901
"""Return the graph walker for text revisions."""
1849
1902
return graph.Graph(self.texts)
1904
def revision_ids_to_search_result(self, result_set):
1905
"""Convert a set of revision ids to a graph SearchResult."""
1906
result_parents = set()
1907
for parents in self.get_graph().get_parent_map(
1908
result_set).itervalues():
1909
result_parents.update(parents)
1910
included_keys = result_set.intersection(result_parents)
1911
start_keys = result_set.difference(included_keys)
1912
exclude_keys = result_parents.difference(result_set)
1913
result = vf_search.SearchResult(start_keys, exclude_keys,
1914
len(result_set), result_set)
1851
1917
def _get_versioned_file_checker(self, text_key_references=None,
1852
1918
ancestors=None):
1853
1919
"""Return an object suitable for checking versioned files.
2376
2442
invs_sent_so_far = set([_mod_revision.NULL_REVISION])
2377
2443
inventory_cache = lru_cache.LRUCache(50)
2378
2444
null_inventory = from_repo.revision_tree(
2379
_mod_revision.NULL_REVISION).inventory
2445
_mod_revision.NULL_REVISION).root_inventory
2380
2446
# XXX: ideally the rich-root/tree-refs flags would be per-revision, not
2381
2447
# per-repo (e.g. streaming a non-rich-root revision out of a rich-root
2382
2448
# repo back into a non-rich-root repo ought to be allowed)
2570
2638
searcher.stop_searching_any(stop_revs)
2571
2639
if searcher_exhausted:
2573
return searcher.get_result()
2641
(started_keys, excludes, included_keys) = searcher.get_state()
2642
return vf_search.SearchResult(started_keys, excludes,
2643
len(included_keys), included_keys)
2575
2645
@needs_read_lock
2576
2646
def search_missing_revision_ids(self,
2727
2797
# Generate deltas against each tree, to find the shortest.
2798
# FIXME: Support nested trees
2728
2799
texts_possibly_new_in_tree = set()
2729
2800
for basis_id, basis_tree in possible_trees:
2730
delta = tree.inventory._make_delta(basis_tree.inventory)
2801
delta = tree.root_inventory._make_delta(basis_tree.root_inventory)
2731
2802
for old_path, new_path, file_id, new_entry in delta:
2732
2803
if new_path is None:
2733
2804
# This file_id isn't present in the new rev, so we don't
2770
2841
parents_parents = [key[-1] for key in parents_parents_keys]
2771
2842
basis_id = _mod_revision.NULL_REVISION
2772
2843
basis_tree = self.source.revision_tree(basis_id)
2773
delta = parent_tree.inventory._make_delta(basis_tree.inventory)
2844
delta = parent_tree.root_inventory._make_delta(
2845
basis_tree.root_inventory)
2774
2846
self.target.add_inventory_by_delta(
2775
2847
basis_id, delta, current_revision_id, parents_parents)
2776
2848
cache[current_revision_id] = parent_tree
2835
2907
kind = entry.kind
2836
2908
texts_possibly_new_in_tree.add((file_id, entry.revision))
2837
2909
for basis_id, basis_tree in possible_trees:
2838
basis_inv = basis_tree.inventory
2910
basis_inv = basis_tree.root_inventory
2839
2911
for file_key in list(texts_possibly_new_in_tree):
2840
2912
file_id, file_revision = file_key
3073
3145
parent_trees[p_id] = repository.revision_tree(
3074
3146
_mod_revision.NULL_REVISION)
3076
inv = revision_tree.inventory
3148
# FIXME: Support nested trees
3149
inv = revision_tree.root_inventory
3077
3150
entries = inv.iter_entries()
3078
3151
# backwards compatibility hack: skip the root id.
3079
3152
if not repository.supports_rich_root():