743
844
Sets self._text_filter appropriately.
745
raise NotImplementedError(self._copy_inventory_texts)
846
# select inventory keys
847
inv_keys = self._revision_keys # currently the same keyspace, and note that
848
# querying for keys here could introduce a bug where an inventory item
849
# is missed, so do not change it to query separately without cross
850
# checking like the text key check below.
851
inventory_index_map, inventory_indices = self._pack_map_and_index_list(
853
inv_nodes = self._index_contents(inventory_indices, inv_keys)
854
# copy inventory keys and adjust values
855
# XXX: Should be a helper function to allow different inv representation
857
self.pb.update("Copying inventory texts", 2)
858
total_items, readv_group_iter = self._least_readv_node_readv(inv_nodes)
859
# Only grab the output lines if we will be processing them
860
output_lines = bool(self.revision_ids)
861
inv_lines = self._copy_nodes_graph(inventory_index_map,
862
self.new_pack._writer, self.new_pack.inventory_index,
863
readv_group_iter, total_items, output_lines=output_lines)
864
if self.revision_ids:
865
self._process_inventory_lines(inv_lines)
867
# eat the iterator to cause it to execute.
869
self._text_filter = None
870
if 'pack' in debug.debug_flags:
871
mutter('%s: create_pack: inventories copied: %s%s %d items t+%6.3fs',
872
time.ctime(), self._pack_collection._upload_transport.base,
873
self.new_pack.random_name,
874
self.new_pack.inventory_index.key_count(),
875
time.time() - self.new_pack.start_time)
747
877
def _copy_text_texts(self):
748
raise NotImplementedError(self._copy_text_texts)
879
text_index_map, text_nodes = self._get_text_nodes()
880
if self._text_filter is not None:
881
# We could return the keys copied as part of the return value from
882
# _copy_nodes_graph but this doesn't work all that well with the
883
# need to get line output too, so we check separately, and as we're
884
# going to buffer everything anyway, we check beforehand, which
885
# saves reading knit data over the wire when we know there are
887
text_nodes = set(text_nodes)
888
present_text_keys = set(_node[1] for _node in text_nodes)
889
missing_text_keys = set(self._text_filter) - present_text_keys
890
if missing_text_keys:
891
# TODO: raise a specific error that can handle many missing
893
mutter("missing keys during fetch: %r", missing_text_keys)
894
a_missing_key = missing_text_keys.pop()
895
raise errors.RevisionNotPresent(a_missing_key[1],
897
# copy text keys and adjust values
898
self.pb.update("Copying content texts", 3)
899
total_items, readv_group_iter = self._least_readv_node_readv(text_nodes)
900
list(self._copy_nodes_graph(text_index_map, self.new_pack._writer,
901
self.new_pack.text_index, readv_group_iter, total_items))
902
self._log_copied_texts()
750
904
def _create_pack_from_packs(self):
751
raise NotImplementedError(self._create_pack_from_packs)
905
self.pb.update("Opening pack", 0, 5)
906
self.new_pack = self.open_pack()
907
new_pack = self.new_pack
908
# buffer data - we won't be reading-back during the pack creation and
909
# this makes a significant difference on sftp pushes.
910
new_pack.set_write_cache_size(1024*1024)
911
if 'pack' in debug.debug_flags:
912
plain_pack_list = ['%s%s' % (a_pack.pack_transport.base, a_pack.name)
913
for a_pack in self.packs]
914
if self.revision_ids is not None:
915
rev_count = len(self.revision_ids)
918
mutter('%s: create_pack: creating pack from source packs: '
919
'%s%s %s revisions wanted %s t=0',
920
time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
921
plain_pack_list, rev_count)
922
self._copy_revision_texts()
923
self._copy_inventory_texts()
924
self._copy_text_texts()
925
# select signature keys
926
signature_filter = self._revision_keys # same keyspace
927
signature_index_map, signature_indices = self._pack_map_and_index_list(
929
signature_nodes = self._index_contents(signature_indices,
931
# copy signature keys and adjust values
932
self.pb.update("Copying signature texts", 4)
933
self._copy_nodes(signature_nodes, signature_index_map, new_pack._writer,
934
new_pack.signature_index)
935
if 'pack' in debug.debug_flags:
936
mutter('%s: create_pack: revision signatures copied: %s%s %d items t+%6.3fs',
937
time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
938
new_pack.signature_index.key_count(),
939
time.time() - new_pack.start_time)
941
# NB XXX: how to check CHK references are present? perhaps by yielding
942
# the items? How should that interact with stacked repos?
943
if new_pack.chk_index is not None:
945
if 'pack' in debug.debug_flags:
946
mutter('%s: create_pack: chk content copied: %s%s %d items t+%6.3fs',
947
time.ctime(), self._pack_collection._upload_transport.base,
948
new_pack.random_name,
949
new_pack.chk_index.key_count(),
950
time.time() - new_pack.start_time)
951
new_pack._check_references()
952
if not self._use_pack(new_pack):
955
self.pb.update("Finishing pack", 5)
957
self._pack_collection.allocate(new_pack)
960
def _copy_chks(self, refs=None):
961
# XXX: Todo, recursive follow-pointers facility when fetching some
963
chk_index_map, chk_indices = self._pack_map_and_index_list(
965
chk_nodes = self._index_contents(chk_indices, refs)
967
# TODO: This isn't strictly tasteful as we are accessing some private
968
# variables (_serializer). Perhaps a better way would be to have
969
# Repository._deserialise_chk_node()
970
search_key_func = chk_map.search_key_registry.get(
971
self._pack_collection.repo._serializer.search_key_name)
972
def accumlate_refs(lines):
973
# XXX: move to a generic location
975
bytes = ''.join(lines)
976
node = chk_map._deserialise(bytes, ("unknown",), search_key_func)
977
new_refs.update(node.refs())
978
self._copy_nodes(chk_nodes, chk_index_map, self.new_pack._writer,
979
self.new_pack.chk_index, output_lines=accumlate_refs)
982
def _copy_nodes(self, nodes, index_map, writer, write_index,
984
"""Copy knit nodes between packs with no graph references.
986
:param output_lines: Output full texts of copied items.
988
pb = ui.ui_factory.nested_progress_bar()
990
return self._do_copy_nodes(nodes, index_map, writer,
991
write_index, pb, output_lines=output_lines)
995
def _do_copy_nodes(self, nodes, index_map, writer, write_index, pb,
997
# for record verification
998
knit = KnitVersionedFiles(None, None)
999
# plan a readv on each source pack:
1001
nodes = sorted(nodes)
1002
# how to map this into knit.py - or knit.py into this?
1003
# we don't want the typical knit logic, we want grouping by pack
1004
# at this point - perhaps a helper library for the following code
1005
# duplication points?
1007
for index, key, value in nodes:
1008
if index not in request_groups:
1009
request_groups[index] = []
1010
request_groups[index].append((key, value))
1012
pb.update("Copied record", record_index, len(nodes))
1013
for index, items in request_groups.iteritems():
1014
pack_readv_requests = []
1015
for key, value in items:
1016
# ---- KnitGraphIndex.get_position
1017
bits = value[1:].split(' ')
1018
offset, length = int(bits[0]), int(bits[1])
1019
pack_readv_requests.append((offset, length, (key, value[0])))
1020
# linear scan up the pack
1021
pack_readv_requests.sort()
1023
pack_obj = index_map[index]
1024
transport, path = pack_obj.access_tuple()
1026
reader = pack.make_readv_reader(transport, path,
1027
[offset[0:2] for offset in pack_readv_requests])
1028
except errors.NoSuchFile:
1029
if self._reload_func is not None:
1032
for (names, read_func), (_1, _2, (key, eol_flag)) in \
1033
izip(reader.iter_records(), pack_readv_requests):
1034
raw_data = read_func(None)
1035
# check the header only
1036
if output_lines is not None:
1037
output_lines(knit._parse_record(key[-1], raw_data)[0])
1039
df, _ = knit._parse_record_header(key, raw_data)
1041
pos, size = writer.add_bytes_record(raw_data, names)
1042
write_index.add_node(key, eol_flag + "%d %d" % (pos, size))
1043
pb.update("Copied record", record_index)
1046
def _copy_nodes_graph(self, index_map, writer, write_index,
1047
readv_group_iter, total_items, output_lines=False):
1048
"""Copy knit nodes between packs.
1050
:param output_lines: Return lines present in the copied data as
1051
an iterator of line,version_id.
1053
pb = ui.ui_factory.nested_progress_bar()
1055
for result in self._do_copy_nodes_graph(index_map, writer,
1056
write_index, output_lines, pb, readv_group_iter, total_items):
1059
# Python 2.4 does not permit try:finally: in a generator.
1065
def _do_copy_nodes_graph(self, index_map, writer, write_index,
1066
output_lines, pb, readv_group_iter, total_items):
1067
# for record verification
1068
knit = KnitVersionedFiles(None, None)
1069
# for line extraction when requested (inventories only)
1071
factory = KnitPlainFactory()
1073
pb.update("Copied record", record_index, total_items)
1074
for index, readv_vector, node_vector in readv_group_iter:
1076
pack_obj = index_map[index]
1077
transport, path = pack_obj.access_tuple()
1079
reader = pack.make_readv_reader(transport, path, readv_vector)
1080
except errors.NoSuchFile:
1081
if self._reload_func is not None:
1084
for (names, read_func), (key, eol_flag, references) in \
1085
izip(reader.iter_records(), node_vector):
1086
raw_data = read_func(None)
1088
# read the entire thing
1089
content, _ = knit._parse_record(key[-1], raw_data)
1090
if len(references[-1]) == 0:
1091
line_iterator = factory.get_fulltext_content(content)
1093
line_iterator = factory.get_linedelta_content(content)
1094
for line in line_iterator:
1097
# check the header only
1098
df, _ = knit._parse_record_header(key, raw_data)
1100
pos, size = writer.add_bytes_record(raw_data, names)
1101
write_index.add_node(key, eol_flag + "%d %d" % (pos, size), references)
1102
pb.update("Copied record", record_index)
1105
def _get_text_nodes(self):
1106
text_index_map, text_indices = self._pack_map_and_index_list(
1108
return text_index_map, self._index_contents(text_indices,
1111
def _least_readv_node_readv(self, nodes):
1112
"""Generate request groups for nodes using the least readv's.
1114
:param nodes: An iterable of graph index nodes.
1115
:return: Total node count and an iterator of the data needed to perform
1116
readvs to obtain the data for nodes. Each item yielded by the
1117
iterator is a tuple with:
1118
index, readv_vector, node_vector. readv_vector is a list ready to
1119
hand to the transport readv method, and node_vector is a list of
1120
(key, eol_flag, references) for the node retrieved by the
1121
matching readv_vector.
1123
# group by pack so we do one readv per pack
1124
nodes = sorted(nodes)
1127
for index, key, value, references in nodes:
1128
if index not in request_groups:
1129
request_groups[index] = []
1130
request_groups[index].append((key, value, references))
1132
for index, items in request_groups.iteritems():
1133
pack_readv_requests = []
1134
for key, value, references in items:
1135
# ---- KnitGraphIndex.get_position
1136
bits = value[1:].split(' ')
1137
offset, length = int(bits[0]), int(bits[1])
1138
pack_readv_requests.append(
1139
((offset, length), (key, value[0], references)))
1140
# linear scan up the pack to maximum range combining.
1141
pack_readv_requests.sort()
1142
# split out the readv and the node data.
1143
pack_readv = [readv for readv, node in pack_readv_requests]
1144
node_vector = [node for readv, node in pack_readv_requests]
1145
result.append((index, pack_readv, node_vector))
1146
return total, result
753
1148
def _log_copied_texts(self):
754
1149
if 'pack' in debug.debug_flags:
767
1181
return new_pack.data_inserted()
1184
class OptimisingPacker(Packer):
1185
"""A packer which spends more time to create better disk layouts."""
1187
def _revision_node_readv(self, revision_nodes):
1188
"""Return the total revisions and the readv's to issue.
1190
This sort places revisions in topological order with the ancestors
1193
:param revision_nodes: The revision index contents for the packs being
1194
incorporated into the new pack.
1195
:return: As per _least_readv_node_readv.
1197
# build an ancestors dict
1200
for index, key, value, references in revision_nodes:
1201
ancestors[key] = references[0]
1202
by_key[key] = (index, value, references)
1203
order = tsort.topo_sort(ancestors)
1205
# Single IO is pathological, but it will work as a starting point.
1207
for key in reversed(order):
1208
index, value, references = by_key[key]
1209
# ---- KnitGraphIndex.get_position
1210
bits = value[1:].split(' ')
1211
offset, length = int(bits[0]), int(bits[1])
1213
(index, [(offset, length)], [(key, value[0], references)]))
1214
# TODO: combine requests in the same index that are in ascending order.
1215
return total, requests
1217
def open_pack(self):
1218
"""Open a pack for the pack we are creating."""
1219
new_pack = super(OptimisingPacker, self).open_pack()
1220
# Turn on the optimization flags for all the index builders.
1221
new_pack.revision_index.set_optimize(for_size=True)
1222
new_pack.inventory_index.set_optimize(for_size=True)
1223
new_pack.text_index.set_optimize(for_size=True)
1224
new_pack.signature_index.set_optimize(for_size=True)
1228
class ReconcilePacker(Packer):
1229
"""A packer which regenerates indices etc as it copies.
1231
This is used by ``bzr reconcile`` to cause parent text pointers to be
1235
def _extra_init(self):
1236
self._data_changed = False
1238
def _process_inventory_lines(self, inv_lines):
1239
"""Generate a text key reference map rather for reconciling with."""
1240
repo = self._pack_collection.repo
1241
refs = repo._find_text_key_references_from_xml_inventory_lines(
1243
self._text_refs = refs
1244
# during reconcile we:
1245
# - convert unreferenced texts to full texts
1246
# - correct texts which reference a text not copied to be full texts
1247
# - copy all others as-is but with corrected parents.
1248
# - so at this point we don't know enough to decide what becomes a full
1250
self._text_filter = None
1252
def _copy_text_texts(self):
1253
"""generate what texts we should have and then copy."""
1254
self.pb.update("Copying content texts", 3)
1255
# we have three major tasks here:
1256
# 1) generate the ideal index
1257
repo = self._pack_collection.repo
1258
ancestors = dict([(key[0], tuple(ref[0] for ref in refs[0])) for
1259
_1, key, _2, refs in
1260
self.new_pack.revision_index.iter_all_entries()])
1261
ideal_index = repo._generate_text_key_index(self._text_refs, ancestors)
1262
# 2) generate a text_nodes list that contains all the deltas that can
1263
# be used as-is, with corrected parents.
1266
discarded_nodes = []
1267
NULL_REVISION = _mod_revision.NULL_REVISION
1268
text_index_map, text_nodes = self._get_text_nodes()
1269
for node in text_nodes:
1275
ideal_parents = tuple(ideal_index[node[1]])
1277
discarded_nodes.append(node)
1278
self._data_changed = True
1280
if ideal_parents == (NULL_REVISION,):
1282
if ideal_parents == node[3][0]:
1284
ok_nodes.append(node)
1285
elif ideal_parents[0:1] == node[3][0][0:1]:
1286
# the left most parent is the same, or there are no parents
1287
# today. Either way, we can preserve the representation as
1288
# long as we change the refs to be inserted.
1289
self._data_changed = True
1290
ok_nodes.append((node[0], node[1], node[2],
1291
(ideal_parents, node[3][1])))
1292
self._data_changed = True
1294
# Reinsert this text completely
1295
bad_texts.append((node[1], ideal_parents))
1296
self._data_changed = True
1297
# we're finished with some data.
1300
# 3) bulk copy the ok data
1301
total_items, readv_group_iter = self._least_readv_node_readv(ok_nodes)
1302
list(self._copy_nodes_graph(text_index_map, self.new_pack._writer,
1303
self.new_pack.text_index, readv_group_iter, total_items))
1304
# 4) adhoc copy all the other texts.
1305
# We have to topologically insert all texts otherwise we can fail to
1306
# reconcile when parts of a single delta chain are preserved intact,
1307
# and other parts are not. E.g. Discarded->d1->d2->d3. d1 will be
1308
# reinserted, and if d3 has incorrect parents it will also be
1309
# reinserted. If we insert d3 first, d2 is present (as it was bulk
1310
# copied), so we will try to delta, but d2 is not currently able to be
1311
# extracted because it's basis d1 is not present. Topologically sorting
1312
# addresses this. The following generates a sort for all the texts that
1313
# are being inserted without having to reference the entire text key
1314
# space (we only topo sort the revisions, which is smaller).
1315
topo_order = tsort.topo_sort(ancestors)
1316
rev_order = dict(zip(topo_order, range(len(topo_order))))
1317
bad_texts.sort(key=lambda key:rev_order.get(key[0][1], 0))
1318
transaction = repo.get_transaction()
1319
file_id_index = GraphIndexPrefixAdapter(
1320
self.new_pack.text_index,
1322
add_nodes_callback=self.new_pack.text_index.add_nodes)
1323
data_access = _DirectPackAccess(
1324
{self.new_pack.text_index:self.new_pack.access_tuple()})
1325
data_access.set_writer(self.new_pack._writer, self.new_pack.text_index,
1326
self.new_pack.access_tuple())
1327
output_texts = KnitVersionedFiles(
1328
_KnitGraphIndex(self.new_pack.text_index,
1329
add_callback=self.new_pack.text_index.add_nodes,
1330
deltas=True, parents=True, is_locked=repo.is_locked),
1331
data_access=data_access, max_delta_chain=200)
1332
for key, parent_keys in bad_texts:
1333
# We refer to the new pack to delta data being output.
1334
# A possible improvement would be to catch errors on short reads
1335
# and only flush then.
1336
self.new_pack.flush()
1338
for parent_key in parent_keys:
1339
if parent_key[0] != key[0]:
1340
# Graph parents must match the fileid
1341
raise errors.BzrError('Mismatched key parent %r:%r' %
1343
parents.append(parent_key[1])
1344
text_lines = osutils.split_lines(repo.texts.get_record_stream(
1345
[key], 'unordered', True).next().get_bytes_as('fulltext'))
1346
output_texts.add_lines(key, parent_keys, text_lines,
1347
random_id=True, check_content=False)
1348
# 5) check that nothing inserted has a reference outside the keyspace.
1349
missing_text_keys = self.new_pack.text_index._external_references()
1350
if missing_text_keys:
1351
raise errors.BzrCheckError('Reference to missing compression parents %r'
1352
% (missing_text_keys,))
1353
self._log_copied_texts()
1355
def _use_pack(self, new_pack):
1356
"""Override _use_pack to check for reconcile having changed content."""
1357
# XXX: we might be better checking this at the copy time.
1358
original_inventory_keys = set()
1359
inv_index = self._pack_collection.inventory_index.combined_index
1360
for entry in inv_index.iter_all_entries():
1361
original_inventory_keys.add(entry[1])
1362
new_inventory_keys = set()
1363
for entry in new_pack.inventory_index.iter_all_entries():
1364
new_inventory_keys.add(entry[1])
1365
if new_inventory_keys != original_inventory_keys:
1366
self._data_changed = True
1367
return new_pack.data_inserted() and self._data_changed
770
1370
class RepositoryPackCollection(object):
771
1371
"""Management of packs within a repository.
773
1373
:ivar _names: map of {pack_name: (index_size,)}
777
resumed_pack_factory = None
778
normal_packer_class = None
779
optimising_packer_class = None
1376
pack_factory = NewPack
1377
resumed_pack_factory = ResumedPack
781
1379
def __init__(self, repo, transport, index_transport, upload_transport,
782
1380
pack_transport, index_builder_class, index_class,
1681
# These attributes are inherited from the Repository base class. Setting
1682
# them to None ensures that if the constructor is changed to not initialize
1683
# them, or a subclass fails to call the constructor, that an error will
1684
# occur rather than the system working but generating incorrect data.
1685
_commit_builder_class = None
1688
2183
def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
1690
MetaDirRepository.__init__(self, _format, a_bzrdir, control_files)
1691
self._commit_builder_class = _commit_builder_class
1692
self._serializer = _serializer
2185
KnitRepository.__init__(self, _format, a_bzrdir, control_files,
2186
_commit_builder_class, _serializer)
2187
index_transport = self._transport.clone('indices')
2188
self._pack_collection = RepositoryPackCollection(self, self._transport,
2190
self._transport.clone('upload'),
2191
self._transport.clone('packs'),
2192
_format.index_builder_class,
2193
_format.index_class,
2194
use_chk_index=self._format.supports_chks,
2196
self.inventories = KnitVersionedFiles(
2197
_KnitGraphIndex(self._pack_collection.inventory_index.combined_index,
2198
add_callback=self._pack_collection.inventory_index.add_callback,
2199
deltas=True, parents=True, is_locked=self.is_locked),
2200
data_access=self._pack_collection.inventory_index.data_access,
2201
max_delta_chain=200)
2202
self.revisions = KnitVersionedFiles(
2203
_KnitGraphIndex(self._pack_collection.revision_index.combined_index,
2204
add_callback=self._pack_collection.revision_index.add_callback,
2205
deltas=False, parents=True, is_locked=self.is_locked,
2206
track_external_parent_refs=True),
2207
data_access=self._pack_collection.revision_index.data_access,
2209
self.signatures = KnitVersionedFiles(
2210
_KnitGraphIndex(self._pack_collection.signature_index.combined_index,
2211
add_callback=self._pack_collection.signature_index.add_callback,
2212
deltas=False, parents=False, is_locked=self.is_locked),
2213
data_access=self._pack_collection.signature_index.data_access,
2215
self.texts = KnitVersionedFiles(
2216
_KnitGraphIndex(self._pack_collection.text_index.combined_index,
2217
add_callback=self._pack_collection.text_index.add_callback,
2218
deltas=True, parents=True, is_locked=self.is_locked),
2219
data_access=self._pack_collection.text_index.data_access,
2220
max_delta_chain=200)
2221
if _format.supports_chks:
2222
# No graph, no compression:- references from chks are between
2223
# different objects not temporal versions of the same; and without
2224
# some sort of temporal structure knit compression will just fail.
2225
self.chk_bytes = KnitVersionedFiles(
2226
_KnitGraphIndex(self._pack_collection.chk_index.combined_index,
2227
add_callback=self._pack_collection.chk_index.add_callback,
2228
deltas=False, parents=False, is_locked=self.is_locked),
2229
data_access=self._pack_collection.chk_index.data_access,
2232
self.chk_bytes = None
2233
# True when the repository object is 'write locked' (as opposed to the
2234
# physical lock only taken out around changes to the pack-names list.)
2235
# Another way to represent this would be a decorator around the control
2236
# files object that presents logical locks as physical ones - if this
2237
# gets ugly consider that alternative design. RBC 20071011
2238
self._write_lock_count = 0
2239
self._transaction = None
2241
self._reconcile_does_inventory_gc = True
1693
2242
self._reconcile_fixes_text_parents = True
1694
if self._format.supports_external_lookups:
1695
self._unstacked_provider = graph.CachingParentsProvider(
1696
self._make_parents_provider_unstacked())
1698
self._unstacked_provider = graph.CachingParentsProvider(self)
1699
self._unstacked_provider.disable_cache()
2243
self._reconcile_backsup_inventory = False
1702
def _all_revision_ids(self):
1703
"""See Repository.all_revision_ids()."""
1704
return [key[0] for key in self.revisions.keys()]
2245
def _warn_if_deprecated(self, branch=None):
2246
# This class isn't deprecated, but one sub-format is
2247
if isinstance(self._format, RepositoryFormatKnitPack5RichRootBroken):
2248
super(KnitPackRepository, self)._warn_if_deprecated(branch)
1706
2250
def _abort_write_group(self):
1707
2251
self.revisions._index._key_dependencies.clear()
1708
2252
self._pack_collection._abort_write_group()
2254
def _get_source(self, to_format):
2255
if to_format.network_name() == self._format.network_name():
2256
return KnitPackStreamSource(self, to_format)
2257
return super(KnitPackRepository, self)._get_source(to_format)
1710
2259
def _make_parents_provider(self):
1711
if not self._format.supports_external_lookups:
1712
return self._unstacked_provider
1713
return graph.StackedParentsProvider(_LazyListJoin(
1714
[self._unstacked_provider], self._fallback_repositories))
2260
return graph.CachingParentsProvider(self)
1716
2262
def _refresh_data(self):
1717
2263
if not self.is_locked():
1719
2265
self._pack_collection.reload_pack_names()
1720
self._unstacked_provider.disable_cache()
1721
self._unstacked_provider.enable_cache()
1723
2267
def _start_write_group(self):
1724
2268
self._pack_collection._start_write_group()
1851
2379
self.control_files.unlock()
1853
2381
if not self.is_locked():
1854
self._unstacked_provider.disable_cache()
1855
2382
for repo in self._fallback_repositories:
1859
class RepositoryFormatPack(MetaDirVersionedFileRepositoryFormat):
2386
class KnitPackStreamSource(StreamSource):
2387
"""A StreamSource used to transfer data between same-format KnitPack repos.
2389
This source assumes:
2390
1) Same serialization format for all objects
2391
2) Same root information
2392
3) XML format inventories
2393
4) Atomic inserts (so we can stream inventory texts before text
2398
def __init__(self, from_repository, to_format):
2399
super(KnitPackStreamSource, self).__init__(from_repository, to_format)
2400
self._text_keys = None
2401
self._text_fetch_order = 'unordered'
2403
def _get_filtered_inv_stream(self, revision_ids):
2404
from_repo = self.from_repository
2405
parent_ids = from_repo._find_parent_ids_of_revisions(revision_ids)
2406
parent_keys = [(p,) for p in parent_ids]
2407
find_text_keys = from_repo._find_text_key_references_from_xml_inventory_lines
2408
parent_text_keys = set(find_text_keys(
2409
from_repo._inventory_xml_lines_for_keys(parent_keys)))
2410
content_text_keys = set()
2411
knit = KnitVersionedFiles(None, None)
2412
factory = KnitPlainFactory()
2413
def find_text_keys_from_content(record):
2414
if record.storage_kind not in ('knit-delta-gz', 'knit-ft-gz'):
2415
raise ValueError("Unknown content storage kind for"
2416
" inventory text: %s" % (record.storage_kind,))
2417
# It's a knit record, it has a _raw_record field (even if it was
2418
# reconstituted from a network stream).
2419
raw_data = record._raw_record
2420
# read the entire thing
2421
revision_id = record.key[-1]
2422
content, _ = knit._parse_record(revision_id, raw_data)
2423
if record.storage_kind == 'knit-delta-gz':
2424
line_iterator = factory.get_linedelta_content(content)
2425
elif record.storage_kind == 'knit-ft-gz':
2426
line_iterator = factory.get_fulltext_content(content)
2427
content_text_keys.update(find_text_keys(
2428
[(line, revision_id) for line in line_iterator]))
2429
revision_keys = [(r,) for r in revision_ids]
2430
def _filtered_inv_stream():
2431
source_vf = from_repo.inventories
2432
stream = source_vf.get_record_stream(revision_keys,
2434
for record in stream:
2435
if record.storage_kind == 'absent':
2436
raise errors.NoSuchRevision(from_repo, record.key)
2437
find_text_keys_from_content(record)
2439
self._text_keys = content_text_keys - parent_text_keys
2440
return ('inventories', _filtered_inv_stream())
2442
def _get_text_stream(self):
2443
# Note: We know we don't have to handle adding root keys, because both
2444
# the source and target are the identical network name.
2445
text_stream = self.from_repository.texts.get_record_stream(
2446
self._text_keys, self._text_fetch_order, False)
2447
return ('texts', text_stream)
2449
def get_stream(self, search):
2450
revision_ids = search.get_keys()
2451
for stream_info in self._fetch_revision_texts(revision_ids):
2453
self._revision_keys = [(rev_id,) for rev_id in revision_ids]
2454
yield self._get_filtered_inv_stream(revision_ids)
2455
yield self._get_text_stream()
2459
class RepositoryFormatPack(MetaDirRepositoryFormat):
1860
2460
"""Format logic for pack structured repositories.
1862
2462
This repository format has:
1936
2532
_serializer=self._serializer)
1939
class RetryPackOperations(errors.RetryWithNewPacks):
1940
"""Raised when we are packing and we find a missing file.
1942
Meant as a signaling exception, to tell the RepositoryPackCollection.pack
1943
code it should try again.
1946
internal_error = True
1948
_fmt = ("Pack files have changed, reload and try pack again."
1949
" context: %(context)s %(orig_error)s")
1952
class _DirectPackAccess(object):
1953
"""Access to data in one or more packs with less translation."""
1955
def __init__(self, index_to_packs, reload_func=None, flush_func=None):
1956
"""Create a _DirectPackAccess object.
1958
:param index_to_packs: A dict mapping index objects to the transport
1959
and file names for obtaining data.
1960
:param reload_func: A function to call if we determine that the pack
1961
files have moved and we need to reload our caches. See
1962
bzrlib.repo_fmt.pack_repo.AggregateIndex for more details.
1964
self._container_writer = None
1965
self._write_index = None
1966
self._indices = index_to_packs
1967
self._reload_func = reload_func
1968
self._flush_func = flush_func
1970
def add_raw_records(self, key_sizes, raw_data):
1971
"""Add raw knit bytes to a storage area.
1973
The data is spooled to the container writer in one bytes-record per
1976
:param sizes: An iterable of tuples containing the key and size of each
1978
:param raw_data: A bytestring containing the data.
1979
:return: A list of memos to retrieve the record later. Each memo is an
1980
opaque index memo. For _DirectPackAccess the memo is (index, pos,
1981
length), where the index field is the write_index object supplied
1982
to the PackAccess object.
1984
if type(raw_data) is not str:
1985
raise AssertionError(
1986
'data must be plain bytes was %s' % type(raw_data))
1989
for key, size in key_sizes:
1990
p_offset, p_length = self._container_writer.add_bytes_record(
1991
raw_data[offset:offset+size], [])
1993
result.append((self._write_index, p_offset, p_length))
1997
"""Flush pending writes on this access object.
1999
This will flush any buffered writes to a NewPack.
2001
if self._flush_func is not None:
2004
def get_raw_records(self, memos_for_retrieval):
2005
"""Get the raw bytes for a records.
2007
:param memos_for_retrieval: An iterable containing the (index, pos,
2008
length) memo for retrieving the bytes. The Pack access method
2009
looks up the pack to use for a given record in its index_to_pack
2011
:return: An iterator over the bytes of the records.
2013
# first pass, group into same-index requests
2015
current_index = None
2016
for (index, offset, length) in memos_for_retrieval:
2017
if current_index == index:
2018
current_list.append((offset, length))
2020
if current_index is not None:
2021
request_lists.append((current_index, current_list))
2022
current_index = index
2023
current_list = [(offset, length)]
2024
# handle the last entry
2025
if current_index is not None:
2026
request_lists.append((current_index, current_list))
2027
for index, offsets in request_lists:
2029
transport, path = self._indices[index]
2031
# A KeyError here indicates that someone has triggered an index
2032
# reload, and this index has gone missing, we need to start
2034
if self._reload_func is None:
2035
# If we don't have a _reload_func there is nothing that can
2038
raise errors.RetryWithNewPacks(index,
2039
reload_occurred=True,
2040
exc_info=sys.exc_info())
2042
reader = pack.make_readv_reader(transport, path, offsets)
2043
for names, read_func in reader.iter_records():
2044
yield read_func(None)
2045
except errors.NoSuchFile:
2046
# A NoSuchFile error indicates that a pack file has gone
2047
# missing on disk, we need to trigger a reload, and start over.
2048
if self._reload_func is None:
2050
raise errors.RetryWithNewPacks(transport.abspath(path),
2051
reload_occurred=False,
2052
exc_info=sys.exc_info())
2054
def set_writer(self, writer, index, transport_packname):
2055
"""Set a writer to use for adding data."""
2056
if index is not None:
2057
self._indices[index] = transport_packname
2058
self._container_writer = writer
2059
self._write_index = index
2061
def reload_or_raise(self, retry_exc):
2062
"""Try calling the reload function, or re-raise the original exception.
2064
This should be called after _DirectPackAccess raises a
2065
RetryWithNewPacks exception. This function will handle the common logic
2066
of determining when the error is fatal versus being temporary.
2067
It will also make sure that the original exception is raised, rather
2068
than the RetryWithNewPacks exception.
2070
If this function returns, then the calling function should retry
2071
whatever operation was being performed. Otherwise an exception will
2074
:param retry_exc: A RetryWithNewPacks exception.
2077
if self._reload_func is None:
2079
elif not self._reload_func():
2080
# The reload claimed that nothing changed
2081
if not retry_exc.reload_occurred:
2082
# If there wasn't an earlier reload, then we really were
2083
# expecting to find changes. We didn't find them, so this is a
2087
exc_class, exc_value, exc_traceback = retry_exc.exc_info
2088
raise exc_class, exc_value, exc_traceback
2535
class RepositoryFormatKnitPack1(RepositoryFormatPack):
2536
"""A no-subtrees parameterized Pack repository.
2538
This format was introduced in 0.92.
2541
repository_class = KnitPackRepository
2542
_commit_builder_class = PackCommitBuilder
2544
def _serializer(self):
2545
return xml5.serializer_v5
2546
# What index classes to use
2547
index_builder_class = InMemoryGraphIndex
2548
index_class = GraphIndex
2550
def _get_matching_bzrdir(self):
2551
return bzrdir.format_registry.make_bzrdir('pack-0.92')
2553
def _ignore_setting_bzrdir(self, format):
2556
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2558
def get_format_string(self):
2559
"""See RepositoryFormat.get_format_string()."""
2560
return "Bazaar pack repository format 1 (needs bzr 0.92)\n"
2562
def get_format_description(self):
2563
"""See RepositoryFormat.get_format_description()."""
2564
return "Packs containing knits without subtree support"
2567
class RepositoryFormatKnitPack3(RepositoryFormatPack):
2568
"""A subtrees parameterized Pack repository.
2570
This repository format uses the xml7 serializer to get:
2571
- support for recording full info about the tree root
2572
- support for recording tree-references
2574
This format was introduced in 0.92.
2577
repository_class = KnitPackRepository
2578
_commit_builder_class = PackRootCommitBuilder
2579
rich_root_data = True
2580
supports_tree_reference = True
2582
def _serializer(self):
2583
return xml7.serializer_v7
2584
# What index classes to use
2585
index_builder_class = InMemoryGraphIndex
2586
index_class = GraphIndex
2588
def _get_matching_bzrdir(self):
2589
return bzrdir.format_registry.make_bzrdir(
2590
'pack-0.92-subtree')
2592
def _ignore_setting_bzrdir(self, format):
2595
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2597
def get_format_string(self):
2598
"""See RepositoryFormat.get_format_string()."""
2599
return "Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n"
2601
def get_format_description(self):
2602
"""See RepositoryFormat.get_format_description()."""
2603
return "Packs containing knits with subtree support\n"
2606
class RepositoryFormatKnitPack4(RepositoryFormatPack):
2607
"""A rich-root, no subtrees parameterized Pack repository.
2609
This repository format uses the xml6 serializer to get:
2610
- support for recording full info about the tree root
2612
This format was introduced in 1.0.
2615
repository_class = KnitPackRepository
2616
_commit_builder_class = PackRootCommitBuilder
2617
rich_root_data = True
2618
supports_tree_reference = False
2620
def _serializer(self):
2621
return xml6.serializer_v6
2622
# What index classes to use
2623
index_builder_class = InMemoryGraphIndex
2624
index_class = GraphIndex
2626
def _get_matching_bzrdir(self):
2627
return bzrdir.format_registry.make_bzrdir(
2630
def _ignore_setting_bzrdir(self, format):
2633
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2635
def get_format_string(self):
2636
"""See RepositoryFormat.get_format_string()."""
2637
return ("Bazaar pack repository format 1 with rich root"
2638
" (needs bzr 1.0)\n")
2640
def get_format_description(self):
2641
"""See RepositoryFormat.get_format_description()."""
2642
return "Packs containing knits with rich root support\n"
2645
class RepositoryFormatKnitPack5(RepositoryFormatPack):
2646
"""Repository that supports external references to allow stacking.
2650
Supports external lookups, which results in non-truncated ghosts after
2651
reconcile compared to pack-0.92 formats.
2654
repository_class = KnitPackRepository
2655
_commit_builder_class = PackCommitBuilder
2656
supports_external_lookups = True
2657
# What index classes to use
2658
index_builder_class = InMemoryGraphIndex
2659
index_class = GraphIndex
2662
def _serializer(self):
2663
return xml5.serializer_v5
2665
def _get_matching_bzrdir(self):
2666
return bzrdir.format_registry.make_bzrdir('1.6')
2668
def _ignore_setting_bzrdir(self, format):
2671
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2673
def get_format_string(self):
2674
"""See RepositoryFormat.get_format_string()."""
2675
return "Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n"
2677
def get_format_description(self):
2678
"""See RepositoryFormat.get_format_description()."""
2679
return "Packs 5 (adds stacking support, requires bzr 1.6)"
2682
class RepositoryFormatKnitPack5RichRoot(RepositoryFormatPack):
2683
"""A repository with rich roots and stacking.
2685
New in release 1.6.1.
2687
Supports stacking on other repositories, allowing data to be accessed
2688
without being stored locally.
2691
repository_class = KnitPackRepository
2692
_commit_builder_class = PackRootCommitBuilder
2693
rich_root_data = True
2694
supports_tree_reference = False # no subtrees
2695
supports_external_lookups = True
2696
# What index classes to use
2697
index_builder_class = InMemoryGraphIndex
2698
index_class = GraphIndex
2701
def _serializer(self):
2702
return xml6.serializer_v6
2704
def _get_matching_bzrdir(self):
2705
return bzrdir.format_registry.make_bzrdir(
2708
def _ignore_setting_bzrdir(self, format):
2711
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2713
def get_format_string(self):
2714
"""See RepositoryFormat.get_format_string()."""
2715
return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n"
2717
def get_format_description(self):
2718
return "Packs 5 rich-root (adds stacking support, requires bzr 1.6.1)"
2721
class RepositoryFormatKnitPack5RichRootBroken(RepositoryFormatPack):
2722
"""A repository with rich roots and external references.
2726
Supports external lookups, which results in non-truncated ghosts after
2727
reconcile compared to pack-0.92 formats.
2729
This format was deprecated because the serializer it uses accidentally
2730
supported subtrees, when the format was not intended to. This meant that
2731
someone could accidentally fetch from an incorrect repository.
2734
repository_class = KnitPackRepository
2735
_commit_builder_class = PackRootCommitBuilder
2736
rich_root_data = True
2737
supports_tree_reference = False # no subtrees
2739
supports_external_lookups = True
2740
# What index classes to use
2741
index_builder_class = InMemoryGraphIndex
2742
index_class = GraphIndex
2745
def _serializer(self):
2746
return xml7.serializer_v7
2748
def _get_matching_bzrdir(self):
2749
matching = bzrdir.format_registry.make_bzrdir(
2751
matching.repository_format = self
2754
def _ignore_setting_bzrdir(self, format):
2757
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2759
def get_format_string(self):
2760
"""See RepositoryFormat.get_format_string()."""
2761
return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n"
2763
def get_format_description(self):
2764
return ("Packs 5 rich-root (adds stacking support, requires bzr 1.6)"
2768
class RepositoryFormatKnitPack6(RepositoryFormatPack):
2769
"""A repository with stacking and btree indexes,
2770
without rich roots or subtrees.
2772
This is equivalent to pack-1.6 with B+Tree indices.
2775
repository_class = KnitPackRepository
2776
_commit_builder_class = PackCommitBuilder
2777
supports_external_lookups = True
2778
# What index classes to use
2779
index_builder_class = BTreeBuilder
2780
index_class = BTreeGraphIndex
2783
def _serializer(self):
2784
return xml5.serializer_v5
2786
def _get_matching_bzrdir(self):
2787
return bzrdir.format_registry.make_bzrdir('1.9')
2789
def _ignore_setting_bzrdir(self, format):
2792
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2794
def get_format_string(self):
2795
"""See RepositoryFormat.get_format_string()."""
2796
return "Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n"
2798
def get_format_description(self):
2799
"""See RepositoryFormat.get_format_description()."""
2800
return "Packs 6 (uses btree indexes, requires bzr 1.9)"
2803
class RepositoryFormatKnitPack6RichRoot(RepositoryFormatPack):
2804
"""A repository with rich roots, no subtrees, stacking and btree indexes.
2806
1.6-rich-root with B+Tree indices.
2809
repository_class = KnitPackRepository
2810
_commit_builder_class = PackRootCommitBuilder
2811
rich_root_data = True
2812
supports_tree_reference = False # no subtrees
2813
supports_external_lookups = True
2814
# What index classes to use
2815
index_builder_class = BTreeBuilder
2816
index_class = BTreeGraphIndex
2819
def _serializer(self):
2820
return xml6.serializer_v6
2822
def _get_matching_bzrdir(self):
2823
return bzrdir.format_registry.make_bzrdir(
2826
def _ignore_setting_bzrdir(self, format):
2829
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2831
def get_format_string(self):
2832
"""See RepositoryFormat.get_format_string()."""
2833
return "Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n"
2835
def get_format_description(self):
2836
return "Packs 6 rich-root (uses btree indexes, requires bzr 1.9)"
2839
class RepositoryFormatPackDevelopment2Subtree(RepositoryFormatPack):
2840
"""A subtrees development repository.
2842
This format should be retained until the second release after bzr 1.7.
2844
1.6.1-subtree[as it might have been] with B+Tree indices.
2846
This is [now] retained until we have a CHK based subtree format in
2850
repository_class = KnitPackRepository
2851
_commit_builder_class = PackRootCommitBuilder
2852
rich_root_data = True
2853
supports_tree_reference = True
2854
supports_external_lookups = True
2855
# What index classes to use
2856
index_builder_class = BTreeBuilder
2857
index_class = BTreeGraphIndex
2860
def _serializer(self):
2861
return xml7.serializer_v7
2863
def _get_matching_bzrdir(self):
2864
return bzrdir.format_registry.make_bzrdir(
2865
'development-subtree')
2867
def _ignore_setting_bzrdir(self, format):
2870
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2872
def get_format_string(self):
2873
"""See RepositoryFormat.get_format_string()."""
2874
return ("Bazaar development format 2 with subtree support "
2875
"(needs bzr.dev from before 1.8)\n")
2877
def get_format_description(self):
2878
"""See RepositoryFormat.get_format_description()."""
2879
return ("Development repository format, currently the same as "
2880
"1.6.1-subtree with B+Tree indices.\n")