~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/groupcompress.py

add gettext to all the builtin commands outf usage

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2008-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
23
23
except ImportError:
24
24
    pylzma = None
25
25
 
 
26
from bzrlib.lazy_import import lazy_import
 
27
lazy_import(globals(), """
26
28
from bzrlib import (
27
29
    annotate,
 
30
    config,
28
31
    debug,
29
32
    errors,
30
33
    graph as _mod_graph,
31
 
    knit,
32
34
    osutils,
33
35
    pack,
34
36
    static_tuple,
35
37
    trace,
 
38
    tsort,
36
39
    )
 
40
 
 
41
from bzrlib.repofmt import pack_repo
 
42
""")
 
43
 
37
44
from bzrlib.btree_index import BTreeBuilder
38
45
from bzrlib.lru_cache import LRUSizeCache
39
 
from bzrlib.tsort import topo_sort
40
46
from bzrlib.versionedfile import (
 
47
    _KeyRefs,
41
48
    adapter_registry,
42
49
    AbsentContentFactory,
43
50
    ChunkedContentFactory,
44
51
    FulltextContentFactory,
45
 
    VersionedFiles,
 
52
    VersionedFilesWithFallbacks,
46
53
    )
47
54
 
48
55
# Minimum number of uncompressed bytes to try fetch at once when retrieving
77
84
 
78
85
    present_keys = []
79
86
    for prefix in sorted(per_prefix_map):
80
 
        present_keys.extend(reversed(topo_sort(per_prefix_map[prefix])))
 
87
        present_keys.extend(reversed(tsort.topo_sort(per_prefix_map[prefix])))
81
88
    return present_keys
82
89
 
83
90
 
101
108
    def __init__(self):
102
109
        # map by key? or just order in file?
103
110
        self._compressor_name = None
104
 
        self._z_content = None
 
111
        self._z_content_chunks = None
105
112
        self._z_content_decompressor = None
106
113
        self._z_content_length = None
107
114
        self._content_length = None
135
142
                self._content = ''.join(self._content_chunks)
136
143
                self._content_chunks = None
137
144
        if self._content is None:
138
 
            if self._z_content is None:
 
145
            # We join self._z_content_chunks here, because if we are
 
146
            # decompressing, then it is *very* likely that we have a single
 
147
            # chunk
 
148
            if self._z_content_chunks is None:
139
149
                raise AssertionError('No content to decompress')
140
 
            if self._z_content == '':
 
150
            z_content = ''.join(self._z_content_chunks)
 
151
            if z_content == '':
141
152
                self._content = ''
142
153
            elif self._compressor_name == 'lzma':
143
154
                # We don't do partial lzma decomp yet
144
 
                self._content = pylzma.decompress(self._z_content)
 
155
                self._content = pylzma.decompress(z_content)
145
156
            elif self._compressor_name == 'zlib':
146
157
                # Start a zlib decompressor
147
158
                if num_bytes * 4 > self._content_length * 3:
148
159
                    # If we are requesting more that 3/4ths of the content,
149
160
                    # just extract the whole thing in a single pass
150
161
                    num_bytes = self._content_length
151
 
                    self._content = zlib.decompress(self._z_content)
 
162
                    self._content = zlib.decompress(z_content)
152
163
                else:
153
164
                    self._z_content_decompressor = zlib.decompressobj()
154
165
                    # Seed the decompressor with the uncompressed bytes, so
155
166
                    # that the rest of the code is simplified
156
167
                    self._content = self._z_content_decompressor.decompress(
157
 
                        self._z_content, num_bytes + _ZLIB_DECOMP_WINDOW)
 
168
                        z_content, num_bytes + _ZLIB_DECOMP_WINDOW)
158
169
                    if not self._z_content_decompressor.unconsumed_tail:
159
170
                        self._z_content_decompressor = None
160
171
            else:
207
218
            # XXX: Define some GCCorrupt error ?
208
219
            raise AssertionError('Invalid bytes: (%d) != %d + %d' %
209
220
                                 (len(bytes), pos, self._z_content_length))
210
 
        self._z_content = bytes[pos:]
 
221
        self._z_content_chunks = (bytes[pos:],)
 
222
 
 
223
    @property
 
224
    def _z_content(self):
 
225
        """Return z_content_chunks as a simple string.
 
226
 
 
227
        Meant only to be used by the test suite.
 
228
        """
 
229
        if self._z_content_chunks is not None:
 
230
            return ''.join(self._z_content_chunks)
 
231
        return None
211
232
 
212
233
    @classmethod
213
234
    def from_bytes(cls, bytes):
269
290
        self._content_length = length
270
291
        self._content_chunks = content_chunks
271
292
        self._content = None
272
 
        self._z_content = None
 
293
        self._z_content_chunks = None
273
294
 
274
295
    def set_content(self, content):
275
296
        """Set the content of this block."""
276
297
        self._content_length = len(content)
277
298
        self._content = content
278
 
        self._z_content = None
 
299
        self._z_content_chunks = None
279
300
 
280
301
    def _create_z_content_using_lzma(self):
281
302
        if self._content_chunks is not None:
283
304
            self._content_chunks = None
284
305
        if self._content is None:
285
306
            raise AssertionError('Nothing to compress')
286
 
        self._z_content = pylzma.compress(self._content)
287
 
        self._z_content_length = len(self._z_content)
 
307
        z_content = pylzma.compress(self._content)
 
308
        self._z_content_chunks = (z_content,)
 
309
        self._z_content_length = len(z_content)
288
310
 
289
 
    def _create_z_content_from_chunks(self):
 
311
    def _create_z_content_from_chunks(self, chunks):
290
312
        compressor = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION)
291
 
        compressed_chunks = map(compressor.compress, self._content_chunks)
 
313
        # Peak in this point is 1 fulltext, 1 compressed text, + zlib overhead
 
314
        # (measured peak is maybe 30MB over the above...)
 
315
        compressed_chunks = map(compressor.compress, chunks)
292
316
        compressed_chunks.append(compressor.flush())
293
 
        self._z_content = ''.join(compressed_chunks)
294
 
        self._z_content_length = len(self._z_content)
 
317
        # Ignore empty chunks
 
318
        self._z_content_chunks = [c for c in compressed_chunks if c]
 
319
        self._z_content_length = sum(map(len, self._z_content_chunks))
295
320
 
296
321
    def _create_z_content(self):
297
 
        if self._z_content is not None:
 
322
        if self._z_content_chunks is not None:
298
323
            return
299
324
        if _USE_LZMA:
300
325
            self._create_z_content_using_lzma()
301
326
            return
302
327
        if self._content_chunks is not None:
303
 
            self._create_z_content_from_chunks()
304
 
            return
305
 
        self._z_content = zlib.compress(self._content)
306
 
        self._z_content_length = len(self._z_content)
 
328
            chunks = self._content_chunks
 
329
        else:
 
330
            chunks = (self._content,)
 
331
        self._create_z_content_from_chunks(chunks)
307
332
 
308
 
    def to_bytes(self):
309
 
        """Encode the information into a byte stream."""
 
333
    def to_chunks(self):
 
334
        """Create the byte stream as a series of 'chunks'"""
310
335
        self._create_z_content()
311
336
        if _USE_LZMA:
312
337
            header = self.GCB_LZ_HEADER
313
338
        else:
314
339
            header = self.GCB_HEADER
315
 
        chunks = [header,
316
 
                  '%d\n%d\n' % (self._z_content_length, self._content_length),
317
 
                  self._z_content,
 
340
        chunks = ['%s%d\n%d\n'
 
341
                  % (header, self._z_content_length, self._content_length),
318
342
                 ]
 
343
        chunks.extend(self._z_content_chunks)
 
344
        total_len = sum(map(len, chunks))
 
345
        return total_len, chunks
 
346
 
 
347
    def to_bytes(self):
 
348
        """Encode the information into a byte stream."""
 
349
        total_len, chunks = self.to_chunks()
319
350
        return ''.join(chunks)
320
351
 
321
352
    def _dump(self, include_text=False):
435
466
                # Grab and cache the raw bytes for this entry
436
467
                # and break the ref-cycle with _manager since we don't need it
437
468
                # anymore
438
 
                self._manager._prepare_for_extract()
 
469
                try:
 
470
                    self._manager._prepare_for_extract()
 
471
                except zlib.error as value:
 
472
                    raise errors.DecompressCorruption("zlib: " + str(value))
439
473
                block = self._manager._block
440
474
                self._bytes = block.extract(self.key, self._start, self._end)
441
475
                # There are code paths that first extract as fulltext, and then
460
494
    _full_enough_block_size = 3*1024*1024 # size at which we won't repack
461
495
    _full_enough_mixed_block_size = 2*768*1024 # 1.5MB
462
496
 
463
 
    def __init__(self, block):
 
497
    def __init__(self, block, get_compressor_settings=None):
464
498
        self._block = block
465
499
        # We need to preserve the ordering
466
500
        self._factories = []
467
501
        self._last_byte = 0
 
502
        self._get_settings = get_compressor_settings
 
503
        self._compressor_settings = None
 
504
 
 
505
    def _get_compressor_settings(self):
 
506
        if self._compressor_settings is not None:
 
507
            return self._compressor_settings
 
508
        settings = None
 
509
        if self._get_settings is not None:
 
510
            settings = self._get_settings()
 
511
        if settings is None:
 
512
            vf = GroupCompressVersionedFiles
 
513
            settings = vf._DEFAULT_COMPRESSOR_SETTINGS
 
514
        self._compressor_settings = settings
 
515
        return self._compressor_settings
468
516
 
469
517
    def add_factory(self, key, parents, start, end):
470
518
        if not self._factories:
503
551
        new_block.set_content(self._block._content[:last_byte])
504
552
        self._block = new_block
505
553
 
 
554
    def _make_group_compressor(self):
 
555
        return GroupCompressor(self._get_compressor_settings())
 
556
 
506
557
    def _rebuild_block(self):
507
558
        """Create a new GroupCompressBlock with only the referenced texts."""
508
 
        compressor = GroupCompressor()
 
559
        compressor = self._make_group_compressor()
509
560
        tstart = time.time()
510
561
        old_length = self._block._content_length
511
562
        end_point = 0
523
574
        #       block? It seems hard to come up with a method that it would
524
575
        #       expand, since we do full compression again. Perhaps based on a
525
576
        #       request that ends up poorly ordered?
 
577
        # TODO: If the content would have expanded, then we would want to
 
578
        #       handle a case where we need to split the block.
 
579
        #       Now that we have a user-tweakable option
 
580
        #       (max_bytes_to_index), it is possible that one person set it
 
581
        #       to a very low value, causing poor compression.
526
582
        delta = time.time() - tstart
527
583
        self._block = new_block
528
584
        trace.mutter('creating new compressed block on-the-fly in %.3fs'
679
735
        z_header_bytes = zlib.compress(header_bytes)
680
736
        del header_bytes
681
737
        z_header_bytes_len = len(z_header_bytes)
682
 
        block_bytes = self._block.to_bytes()
 
738
        block_bytes_len, block_chunks = self._block.to_chunks()
683
739
        lines.append('%d\n%d\n%d\n' % (z_header_bytes_len, header_bytes_len,
684
 
                                       len(block_bytes)))
 
740
                                       block_bytes_len))
685
741
        lines.append(z_header_bytes)
686
 
        lines.append(block_bytes)
687
 
        del z_header_bytes, block_bytes
 
742
        lines.extend(block_chunks)
 
743
        del z_header_bytes, block_chunks
 
744
        # TODO: This is a point where we will double the memory consumption. To
 
745
        #       avoid this, we probably have to switch to a 'chunked' api
688
746
        return ''.join(lines)
689
747
 
690
748
    @classmethod
691
749
    def from_bytes(cls, bytes):
692
750
        # TODO: This does extra string copying, probably better to do it a
693
 
        #       different way
 
751
        #       different way. At a minimum this creates 2 copies of the
 
752
        #       compressed content
694
753
        (storage_kind, z_header_len, header_len,
695
754
         block_len, rest) = bytes.split('\n', 4)
696
755
        del bytes
748
807
 
749
808
class _CommonGroupCompressor(object):
750
809
 
751
 
    def __init__(self):
 
810
    def __init__(self, settings=None):
752
811
        """Create a GroupCompressor."""
753
812
        self.chunks = []
754
813
        self._last = None
757
816
        self.labels_deltas = {}
758
817
        self._delta_index = None # Set by the children
759
818
        self._block = GroupCompressBlock()
 
819
        if settings is None:
 
820
            self._settings = {}
 
821
        else:
 
822
            self._settings = settings
760
823
 
761
824
    def compress(self, key, bytes, expected_sha, nostore_sha=None, soft=False):
762
825
        """Compress lines with label key.
854
917
 
855
918
        After calling this, the compressor should no longer be used
856
919
        """
857
 
        # TODO: this causes us to 'bloat' to 2x the size of content in the
858
 
        #       group. This has an impact for 'commit' of large objects.
859
 
        #       One possibility is to use self._content_chunks, and be lazy and
860
 
        #       only fill out self._content as a full string when we actually
861
 
        #       need it. That would at least drop the peak memory consumption
862
 
        #       for 'commit' down to ~1x the size of the largest file, at a
863
 
        #       cost of increased complexity within this code. 2x is still <<
864
 
        #       3x the size of the largest file, so we are doing ok.
865
920
        self._block.set_chunked_content(self.chunks, self.endpoint)
866
921
        self.chunks = None
867
922
        self._delta_index = None
885
940
 
886
941
class PythonGroupCompressor(_CommonGroupCompressor):
887
942
 
888
 
    def __init__(self):
 
943
    def __init__(self, settings=None):
889
944
        """Create a GroupCompressor.
890
945
 
891
946
        Used only if the pyrex version is not available.
892
947
        """
893
 
        super(PythonGroupCompressor, self).__init__()
 
948
        super(PythonGroupCompressor, self).__init__(settings)
894
949
        self._delta_index = LinesDeltaIndex([])
895
950
        # The actual content is managed by LinesDeltaIndex
896
951
        self.chunks = self._delta_index.lines
933
988
 
934
989
    It contains code very similar to SequenceMatcher because of having a similar
935
990
    task. However some key differences apply:
936
 
     - there is no junk, we want a minimal edit not a human readable diff.
937
 
     - we don't filter very common lines (because we don't know where a good
938
 
       range will start, and after the first text we want to be emitting minmal
939
 
       edits only.
940
 
     - we chain the left side, not the right side
941
 
     - we incrementally update the adjacency matrix as new lines are provided.
942
 
     - we look for matches in all of the left side, so the routine which does
943
 
       the analagous task of find_longest_match does not need to filter on the
944
 
       left side.
 
991
 
 
992
    * there is no junk, we want a minimal edit not a human readable diff.
 
993
    * we don't filter very common lines (because we don't know where a good
 
994
      range will start, and after the first text we want to be emitting minmal
 
995
      edits only.
 
996
    * we chain the left side, not the right side
 
997
    * we incrementally update the adjacency matrix as new lines are provided.
 
998
    * we look for matches in all of the left side, so the routine which does
 
999
      the analagous task of find_longest_match does not need to filter on the
 
1000
      left side.
945
1001
    """
946
1002
 
947
 
    def __init__(self):
948
 
        super(PyrexGroupCompressor, self).__init__()
949
 
        self._delta_index = DeltaIndex()
 
1003
    def __init__(self, settings=None):
 
1004
        super(PyrexGroupCompressor, self).__init__(settings)
 
1005
        max_bytes_to_index = self._settings.get('max_bytes_to_index', 0)
 
1006
        self._delta_index = DeltaIndex(max_bytes_to_index=max_bytes_to_index)
950
1007
 
951
1008
    def _compress(self, key, bytes, max_delta_size, soft=False):
952
1009
        """see _CommonGroupCompressor._compress"""
1027
1084
        index = _GCGraphIndex(graph_index, lambda:True, parents=parents,
1028
1085
            add_callback=graph_index.add_nodes,
1029
1086
            inconsistency_fatal=inconsistency_fatal)
1030
 
        access = knit._DirectPackAccess({})
 
1087
        access = pack_repo._DirectPackAccess({})
1031
1088
        access.set_writer(writer, graph_index, (transport, 'newpack'))
1032
1089
        result = GroupCompressVersionedFiles(index, access, delta)
1033
1090
        result.stream = stream
1043
1100
 
1044
1101
class _BatchingBlockFetcher(object):
1045
1102
    """Fetch group compress blocks in batches.
1046
 
    
 
1103
 
1047
1104
    :ivar total_bytes: int of expected number of bytes needed to fetch the
1048
1105
        currently pending batch.
1049
1106
    """
1050
1107
 
1051
 
    def __init__(self, gcvf, locations):
 
1108
    def __init__(self, gcvf, locations, get_compressor_settings=None):
1052
1109
        self.gcvf = gcvf
1053
1110
        self.locations = locations
1054
1111
        self.keys = []
1057
1114
        self.total_bytes = 0
1058
1115
        self.last_read_memo = None
1059
1116
        self.manager = None
 
1117
        self._get_compressor_settings = get_compressor_settings
1060
1118
 
1061
1119
    def add_key(self, key):
1062
1120
        """Add another to key to fetch.
1063
 
        
 
1121
 
1064
1122
        :return: The estimated number of bytes needed to fetch the batch so
1065
1123
            far.
1066
1124
        """
1091
1149
            # and then.
1092
1150
            self.batch_memos[read_memo] = cached_block
1093
1151
        return self.total_bytes
1094
 
        
 
1152
 
1095
1153
    def _flush_manager(self):
1096
1154
        if self.manager is not None:
1097
1155
            for factory in self.manager.get_record_stream():
1102
1160
    def yield_factories(self, full_flush=False):
1103
1161
        """Yield factories for keys added since the last yield.  They will be
1104
1162
        returned in the order they were added via add_key.
1105
 
        
 
1163
 
1106
1164
        :param full_flush: by default, some results may not be returned in case
1107
1165
            they can be part of the next batch.  If full_flush is True, then
1108
1166
            all results are returned.
1136
1194
                    memos_to_get_stack.pop()
1137
1195
                else:
1138
1196
                    block = self.batch_memos[read_memo]
1139
 
                self.manager = _LazyGroupContentManager(block)
 
1197
                self.manager = _LazyGroupContentManager(block,
 
1198
                    get_compressor_settings=self._get_compressor_settings)
1140
1199
                self.last_read_memo = read_memo
1141
1200
            start, end = index_memo[3:5]
1142
1201
            self.manager.add_factory(key, parents, start, end)
1149
1208
        self.total_bytes = 0
1150
1209
 
1151
1210
 
1152
 
class GroupCompressVersionedFiles(VersionedFiles):
 
1211
class GroupCompressVersionedFiles(VersionedFilesWithFallbacks):
1153
1212
    """A group-compress based VersionedFiles implementation."""
1154
1213
 
1155
 
    def __init__(self, index, access, delta=True, _unadded_refs=None):
 
1214
    # This controls how the GroupCompress DeltaIndex works. Basically, we
 
1215
    # compute hash pointers into the source blocks (so hash(text) => text).
 
1216
    # However each of these references costs some memory in trade against a
 
1217
    # more accurate match result. For very large files, they either are
 
1218
    # pre-compressed and change in bulk whenever they change, or change in just
 
1219
    # local blocks. Either way, 'improved resolution' is not very helpful,
 
1220
    # versus running out of memory trying to track everything. The default max
 
1221
    # gives 100% sampling of a 1MB file.
 
1222
    _DEFAULT_MAX_BYTES_TO_INDEX = 1024 * 1024
 
1223
    _DEFAULT_COMPRESSOR_SETTINGS = {'max_bytes_to_index':
 
1224
                                     _DEFAULT_MAX_BYTES_TO_INDEX}
 
1225
 
 
1226
    def __init__(self, index, access, delta=True, _unadded_refs=None,
 
1227
                 _group_cache=None):
1156
1228
        """Create a GroupCompressVersionedFiles object.
1157
1229
 
1158
1230
        :param index: The index object storing access and graph data.
1159
1231
        :param access: The access object storing raw data.
1160
1232
        :param delta: Whether to delta compress or just entropy compress.
1161
1233
        :param _unadded_refs: private parameter, don't use.
 
1234
        :param _group_cache: private parameter, don't use.
1162
1235
        """
1163
1236
        self._index = index
1164
1237
        self._access = access
1166
1239
        if _unadded_refs is None:
1167
1240
            _unadded_refs = {}
1168
1241
        self._unadded_refs = _unadded_refs
1169
 
        self._group_cache = LRUSizeCache(max_size=50*1024*1024)
1170
 
        self._fallback_vfs = []
 
1242
        if _group_cache is None:
 
1243
            _group_cache = LRUSizeCache(max_size=50*1024*1024)
 
1244
        self._group_cache = _group_cache
 
1245
        self._immediate_fallback_vfs = []
 
1246
        self._max_bytes_to_index = None
1171
1247
 
1172
1248
    def without_fallbacks(self):
1173
1249
        """Return a clone of this object without any fallbacks configured."""
1174
1250
        return GroupCompressVersionedFiles(self._index, self._access,
1175
 
            self._delta, _unadded_refs=dict(self._unadded_refs))
 
1251
            self._delta, _unadded_refs=dict(self._unadded_refs),
 
1252
            _group_cache=self._group_cache)
1176
1253
 
1177
1254
    def add_lines(self, key, parents, lines, parent_texts=None,
1178
1255
        left_matching_blocks=None, nostore_sha=None, random_id=False,
1182
1259
        :param key: The key tuple of the text to add.
1183
1260
        :param parents: The parents key tuples of the text to add.
1184
1261
        :param lines: A list of lines. Each line must be a bytestring. And all
1185
 
            of them except the last must be terminated with \n and contain no
1186
 
            other \n's. The last line may either contain no \n's or a single
1187
 
            terminating \n. If the lines list does meet this constraint the add
1188
 
            routine may error or may succeed - but you will be unable to read
1189
 
            the data back accurately. (Checking the lines have been split
 
1262
            of them except the last must be terminated with \\n and contain no
 
1263
            other \\n's. The last line may either contain no \\n's or a single
 
1264
            terminating \\n. If the lines list does meet this constraint the
 
1265
            add routine may error or may succeed - but you will be unable to
 
1266
            read the data back accurately. (Checking the lines have been split
1190
1267
            correctly is expensive and extremely unlikely to catch bugs so it
1191
1268
            is not done at runtime unless check_content is True.)
1192
1269
        :param parent_texts: An optional dictionary containing the opaque
1247
1324
 
1248
1325
        :param a_versioned_files: A VersionedFiles object.
1249
1326
        """
1250
 
        self._fallback_vfs.append(a_versioned_files)
 
1327
        self._immediate_fallback_vfs.append(a_versioned_files)
1251
1328
 
1252
1329
    def annotate(self, key):
1253
1330
        """See VersionedFiles.annotate."""
1287
1364
            self._check_lines_not_unicode(lines)
1288
1365
            self._check_lines_are_lines(lines)
1289
1366
 
1290
 
    def get_known_graph_ancestry(self, keys):
1291
 
        """Get a KnownGraph instance with the ancestry of keys."""
1292
 
        # Note that this is identical to
1293
 
        # KnitVersionedFiles.get_known_graph_ancestry, but they don't share
1294
 
        # ancestry.
1295
 
        parent_map, missing_keys = self._index.find_ancestry(keys)
1296
 
        for fallback in self._fallback_vfs:
1297
 
            if not missing_keys:
1298
 
                break
1299
 
            (f_parent_map, f_missing_keys) = fallback._index.find_ancestry(
1300
 
                                                missing_keys)
1301
 
            parent_map.update(f_parent_map)
1302
 
            missing_keys = f_missing_keys
1303
 
        kg = _mod_graph.KnownGraph(parent_map)
1304
 
        return kg
1305
 
 
1306
1367
    def get_parent_map(self, keys):
1307
1368
        """Get a map of the graph parents of keys.
1308
1369
 
1323
1384
            and so on.
1324
1385
        """
1325
1386
        result = {}
1326
 
        sources = [self._index] + self._fallback_vfs
 
1387
        sources = [self._index] + self._immediate_fallback_vfs
1327
1388
        source_results = []
1328
1389
        missing = set(keys)
1329
1390
        for source in sources:
1430
1491
        parent_map = {}
1431
1492
        key_to_source_map = {}
1432
1493
        source_results = []
1433
 
        for source in self._fallback_vfs:
 
1494
        for source in self._immediate_fallback_vfs:
1434
1495
            if not missing:
1435
1496
                break
1436
1497
            source_parents = source.get_parent_map(missing)
1446
1507
 
1447
1508
        The returned objects should be in the order defined by 'ordering',
1448
1509
        which can weave between different sources.
 
1510
 
1449
1511
        :param ordering: Must be one of 'topological' or 'groupcompress'
1450
1512
        :return: List of [(source, [keys])] tuples, such that all keys are in
1451
1513
            the defined order, regardless of source.
1452
1514
        """
1453
1515
        if ordering == 'topological':
1454
 
            present_keys = topo_sort(parent_map)
 
1516
            present_keys = tsort.topo_sort(parent_map)
1455
1517
        else:
1456
1518
            # ordering == 'groupcompress'
1457
1519
            # XXX: This only optimizes for the target ordering. We may need
1546
1608
        #  - we encounter an unadded ref, or
1547
1609
        #  - we run out of keys, or
1548
1610
        #  - the total bytes to retrieve for this batch > BATCH_SIZE
1549
 
        batcher = _BatchingBlockFetcher(self, locations)
 
1611
        batcher = _BatchingBlockFetcher(self, locations,
 
1612
            get_compressor_settings=self._get_compressor_settings)
1550
1613
        for source, keys in source_keys:
1551
1614
            if source is self:
1552
1615
                for key in keys:
1598
1661
        for _ in self._insert_record_stream(stream, random_id=False):
1599
1662
            pass
1600
1663
 
 
1664
    def _get_compressor_settings(self):
 
1665
        if self._max_bytes_to_index is None:
 
1666
            # TODO: VersionedFiles don't know about their containing
 
1667
            #       repository, so they don't have much of an idea about their
 
1668
            #       location. So for now, this is only a global option.
 
1669
            c = config.GlobalConfig()
 
1670
            val = c.get_user_option('bzr.groupcompress.max_bytes_to_index')
 
1671
            if val is not None:
 
1672
                try:
 
1673
                    val = int(val)
 
1674
                except ValueError, e:
 
1675
                    trace.warning('Value for '
 
1676
                                  '"bzr.groupcompress.max_bytes_to_index"'
 
1677
                                  ' %r is not an integer'
 
1678
                                  % (val,))
 
1679
                    val = None
 
1680
            if val is None:
 
1681
                val = self._DEFAULT_MAX_BYTES_TO_INDEX
 
1682
            self._max_bytes_to_index = val
 
1683
        return {'max_bytes_to_index': self._max_bytes_to_index}
 
1684
 
 
1685
    def _make_group_compressor(self):
 
1686
        return GroupCompressor(self._get_compressor_settings())
 
1687
 
1601
1688
    def _insert_record_stream(self, stream, random_id=False, nostore_sha=None,
1602
1689
                              reuse_blocks=True):
1603
1690
        """Internal core to insert a record stream into this container.
1626
1713
                return adapter
1627
1714
        # This will go up to fulltexts for gc to gc fetching, which isn't
1628
1715
        # ideal.
1629
 
        self._compressor = GroupCompressor()
 
1716
        self._compressor = self._make_group_compressor()
1630
1717
        self._unadded_refs = {}
1631
1718
        keys_to_add = []
1632
1719
        def flush():
1633
 
            bytes = self._compressor.flush().to_bytes()
1634
 
            self._compressor = GroupCompressor()
 
1720
            bytes_len, chunks = self._compressor.flush().to_chunks()
 
1721
            self._compressor = self._make_group_compressor()
 
1722
            # Note: At this point we still have 1 copy of the fulltext (in
 
1723
            #       record and the var 'bytes'), and this generates 2 copies of
 
1724
            #       the compressed text (one for bytes, one in chunks)
 
1725
            # TODO: Push 'chunks' down into the _access api, so that we don't
 
1726
            #       have to double compressed memory here
 
1727
            # TODO: Figure out how to indicate that we would be happy to free
 
1728
            #       the fulltext content at this point. Note that sometimes we
 
1729
            #       will want it later (streaming CHK pages), but most of the
 
1730
            #       time we won't (everything else)
 
1731
            bytes = ''.join(chunks)
 
1732
            del chunks
1635
1733
            index, start, length = self._access.add_raw_records(
1636
1734
                [(None, len(bytes))], bytes)[0]
1637
1735
            nodes = []
1802
1900
        """See VersionedFiles.keys."""
1803
1901
        if 'evil' in debug.debug_flags:
1804
1902
            trace.mutter_callsite(2, "keys scales with size of history")
1805
 
        sources = [self._index] + self._fallback_vfs
 
1903
        sources = [self._index] + self._immediate_fallback_vfs
1806
1904
        result = set()
1807
1905
        for source in sources:
1808
1906
            result.update(source.keys())
1891
1989
        # repeated over and over, this creates a surplus of ints
1892
1990
        self._int_cache = {}
1893
1991
        if track_external_parent_refs:
1894
 
            self._key_dependencies = knit._KeyRefs(
 
1992
            self._key_dependencies = _KeyRefs(
1895
1993
                track_new_keys=track_new_keys)
1896
1994
        else:
1897
1995
            self._key_dependencies = None
2037
2135
        :param keys: An iterable of keys.
2038
2136
        :return: A dict of key:
2039
2137
            (index_memo, compression_parent, parents, record_details).
2040
 
            index_memo
2041
 
                opaque structure to pass to read_records to extract the raw
2042
 
                data
2043
 
            compression_parent
2044
 
                Content that this record is built upon, may be None
2045
 
            parents
2046
 
                Logical parents of this node
2047
 
            record_details
2048
 
                extra information about the content which needs to be passed to
2049
 
                Factory.parse_record
 
2138
 
 
2139
            * index_memo: opaque structure to pass to read_records to extract
 
2140
              the raw data
 
2141
            * compression_parent: Content that this record is built upon, may
 
2142
              be None
 
2143
            * parents: Logical parents of this node
 
2144
            * record_details: extra information about the content which needs
 
2145
              to be passed to Factory.parse_record
2050
2146
        """
2051
2147
        self._check_read()
2052
2148
        result = {}