~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/knit.py

  • Committer: Tarmac
  • Author(s): Vincent Ladeuil
  • Date: 2017-01-30 14:42:05 UTC
  • mfrom: (6620.1.1 trunk)
  • Revision ID: tarmac-20170130144205-r8fh2xpmiuxyozpv
Merge  2.7 into trunk including fix for bug #1657238 [r=vila]

Show diffs side-by-side

added added

removed removed

Lines of Context:
51
51
 
52
52
"""
53
53
 
 
54
from __future__ import absolute_import
 
55
 
54
56
 
55
57
from cStringIO import StringIO
56
58
from itertools import izip
76
78
    )
77
79
 
78
80
from bzrlib.repofmt import pack_repo
 
81
from bzrlib.i18n import gettext
79
82
""")
80
83
from bzrlib import (
81
84
    annotate,
103
106
    ConstantMapper,
104
107
    ContentFactory,
105
108
    sort_groupcompress,
106
 
    VersionedFiles,
 
109
    VersionedFilesWithFallbacks,
107
110
    )
108
111
 
109
112
 
408
411
class KnitContent(object):
409
412
    """Content of a knit version to which deltas can be applied.
410
413
 
411
 
    This is always stored in memory as a list of lines with \n at the end,
 
414
    This is always stored in memory as a list of lines with \\n at the end,
412
415
    plus a flag saying if the final ending is really there or not, because that
413
416
    corresponds to the on-disk knit representation.
414
417
    """
845
848
                in all_build_index_memos.itervalues()])
846
849
 
847
850
 
848
 
class KnitVersionedFiles(VersionedFiles):
 
851
class KnitVersionedFiles(VersionedFilesWithFallbacks):
849
852
    """Storage for many versioned files using knit compression.
850
853
 
851
854
    Backend storage is managed by indices and data objects.
887
890
            self._index,
888
891
            self._access)
889
892
 
 
893
    def without_fallbacks(self):
 
894
        """Return a clone of this object without any fallbacks configured."""
 
895
        return KnitVersionedFiles(self._index, self._access,
 
896
            self._max_delta_chain, self._factory.annotated,
 
897
            self._reload_func)
 
898
 
890
899
    def add_fallback_versioned_files(self, a_versioned_files):
891
900
        """Add a source of texts for texts not present in this knit.
892
901
 
1149
1158
 
1150
1159
        A dict of key to (record_details, index_memo, next, parents) is
1151
1160
        returned.
1152
 
        method is the way referenced data should be applied.
1153
 
        index_memo is the handle to pass to the data access to actually get the
1154
 
            data
1155
 
        next is the build-parent of the version, or None for fulltexts.
1156
 
        parents is the version_ids of the parents of this version
1157
 
 
1158
 
        :param allow_missing: If True do not raise an error on a missing component,
1159
 
            just ignore it.
 
1161
 
 
1162
        * method is the way referenced data should be applied.
 
1163
        * index_memo is the handle to pass to the data access to actually get
 
1164
          the data
 
1165
        * next is the build-parent of the version, or None for fulltexts.
 
1166
        * parents is the version_ids of the parents of this version
 
1167
 
 
1168
        :param allow_missing: If True do not raise an error on a missing
 
1169
            component, just ignore it.
1160
1170
        """
1161
1171
        component_data = {}
1162
1172
        pending_components = keys
1188
1198
        generator = _VFContentMapGenerator(self, [key])
1189
1199
        return generator._get_content(key)
1190
1200
 
1191
 
    def get_known_graph_ancestry(self, keys):
1192
 
        """Get a KnownGraph instance with the ancestry of keys."""
1193
 
        parent_map, missing_keys = self._index.find_ancestry(keys)
1194
 
        for fallback in self._transitive_fallbacks():
1195
 
            if not missing_keys:
1196
 
                break
1197
 
            (f_parent_map, f_missing_keys) = fallback._index.find_ancestry(
1198
 
                                                missing_keys)
1199
 
            parent_map.update(f_parent_map)
1200
 
            missing_keys = f_missing_keys
1201
 
        kg = _mod_graph.KnownGraph(parent_map)
1202
 
        return kg
1203
 
 
1204
1201
    def get_parent_map(self, keys):
1205
1202
        """Get a map of the graph parents of keys.
1206
1203
 
1237
1234
        """Produce a dictionary of knit records.
1238
1235
 
1239
1236
        :return: {key:(record, record_details, digest, next)}
1240
 
            record
1241
 
                data returned from read_records (a KnitContentobject)
1242
 
            record_details
1243
 
                opaque information to pass to parse_record
1244
 
            digest
1245
 
                SHA1 digest of the full text after all steps are done
1246
 
            next
1247
 
                build-parent of the version, i.e. the leftmost ancestor.
 
1237
 
 
1238
            * record: data returned from read_records (a KnitContentobject)
 
1239
            * record_details: opaque information to pass to parse_record
 
1240
            * digest: SHA1 digest of the full text after all steps are done
 
1241
            * next: build-parent of the version, i.e. the leftmost ancestor.
1248
1242
                Will be None if the record is not a delta.
 
1243
 
1249
1244
        :param keys: The keys to build a map for
1250
1245
        :param allow_missing: If some records are missing, rather than
1251
1246
            error, just return the data that could be generated.
1768
1763
                        key_records.append((key, details[0]))
1769
1764
                records_iter = enumerate(self._read_records_iter(key_records))
1770
1765
                for (key_idx, (key, data, sha_value)) in records_iter:
1771
 
                    pb.update('Walking content', key_idx, total)
 
1766
                    pb.update(gettext('Walking content'), key_idx, total)
1772
1767
                    compression_parent = build_details[key][1]
1773
1768
                    if compression_parent is None:
1774
1769
                        # fulltext
1804
1799
                source_keys.add(key)
1805
1800
                yield line, key
1806
1801
            keys.difference_update(source_keys)
1807
 
        pb.update('Walking content', total, total)
 
1802
        pb.update(gettext('Walking content'), total, total)
1808
1803
 
1809
1804
    def _make_line_delta(self, delta_seq, new_content):
1810
1805
        """Generate a line delta from delta_seq and new_content."""
1918
1913
        The result will be returned in whatever is the fastest to read.
1919
1914
        Not by the order requested. Also, multiple requests for the same
1920
1915
        record will only yield 1 response.
 
1916
 
1921
1917
        :param records: A list of (key, access_memo) entries
1922
1918
        :return: Yields (key, contents, digest) in the order
1923
1919
                 read, not the order requested
1981
1977
        :param key: The key of the record. Currently keys are always serialised
1982
1978
            using just the trailing component.
1983
1979
        :param dense_lines: The bytes of lines but in a denser form. For
1984
 
            instance, if lines is a list of 1000 bytestrings each ending in \n,
1985
 
            dense_lines may be a list with one line in it, containing all the
1986
 
            1000's lines and their \n's. Using dense_lines if it is already
1987
 
            known is a win because the string join to create bytes in this
1988
 
            function spends less time resizing the final string.
 
1980
            instance, if lines is a list of 1000 bytestrings each ending in
 
1981
            \\n, dense_lines may be a list with one line in it, containing all
 
1982
            the 1000's lines and their \\n's. Using dense_lines if it is
 
1983
            already known is a win because the string join to create bytes in
 
1984
            this function spends less time resizing the final string.
1989
1985
        :return: (len, a StringIO instance with the raw data ready to read.)
1990
1986
        """
1991
1987
        chunks = ["version %s %d %s\n" % (key[-1], len(lines), digest)]
3216
3212
                yield data
3217
3213
 
3218
3214
 
3219
 
class _DirectPackAccess(object):
3220
 
    """Access to data in one or more packs with less translation."""
3221
 
 
3222
 
    def __init__(self, index_to_packs, reload_func=None, flush_func=None):
3223
 
        """Create a _DirectPackAccess object.
3224
 
 
3225
 
        :param index_to_packs: A dict mapping index objects to the transport
3226
 
            and file names for obtaining data.
3227
 
        :param reload_func: A function to call if we determine that the pack
3228
 
            files have moved and we need to reload our caches. See
3229
 
            bzrlib.repo_fmt.pack_repo.AggregateIndex for more details.
3230
 
        """
3231
 
        self._container_writer = None
3232
 
        self._write_index = None
3233
 
        self._indices = index_to_packs
3234
 
        self._reload_func = reload_func
3235
 
        self._flush_func = flush_func
3236
 
 
3237
 
    def add_raw_records(self, key_sizes, raw_data):
3238
 
        """Add raw knit bytes to a storage area.
3239
 
 
3240
 
        The data is spooled to the container writer in one bytes-record per
3241
 
        raw data item.
3242
 
 
3243
 
        :param sizes: An iterable of tuples containing the key and size of each
3244
 
            raw data segment.
3245
 
        :param raw_data: A bytestring containing the data.
3246
 
        :return: A list of memos to retrieve the record later. Each memo is an
3247
 
            opaque index memo. For _DirectPackAccess the memo is (index, pos,
3248
 
            length), where the index field is the write_index object supplied
3249
 
            to the PackAccess object.
3250
 
        """
3251
 
        if type(raw_data) is not str:
3252
 
            raise AssertionError(
3253
 
                'data must be plain bytes was %s' % type(raw_data))
3254
 
        result = []
3255
 
        offset = 0
3256
 
        for key, size in key_sizes:
3257
 
            p_offset, p_length = self._container_writer.add_bytes_record(
3258
 
                raw_data[offset:offset+size], [])
3259
 
            offset += size
3260
 
            result.append((self._write_index, p_offset, p_length))
3261
 
        return result
3262
 
 
3263
 
    def flush(self):
3264
 
        """Flush pending writes on this access object.
3265
 
 
3266
 
        This will flush any buffered writes to a NewPack.
3267
 
        """
3268
 
        if self._flush_func is not None:
3269
 
            self._flush_func()
3270
 
            
3271
 
    def get_raw_records(self, memos_for_retrieval):
3272
 
        """Get the raw bytes for a records.
3273
 
 
3274
 
        :param memos_for_retrieval: An iterable containing the (index, pos,
3275
 
            length) memo for retrieving the bytes. The Pack access method
3276
 
            looks up the pack to use for a given record in its index_to_pack
3277
 
            map.
3278
 
        :return: An iterator over the bytes of the records.
3279
 
        """
3280
 
        # first pass, group into same-index requests
3281
 
        request_lists = []
3282
 
        current_index = None
3283
 
        for (index, offset, length) in memos_for_retrieval:
3284
 
            if current_index == index:
3285
 
                current_list.append((offset, length))
3286
 
            else:
3287
 
                if current_index is not None:
3288
 
                    request_lists.append((current_index, current_list))
3289
 
                current_index = index
3290
 
                current_list = [(offset, length)]
3291
 
        # handle the last entry
3292
 
        if current_index is not None:
3293
 
            request_lists.append((current_index, current_list))
3294
 
        for index, offsets in request_lists:
3295
 
            try:
3296
 
                transport, path = self._indices[index]
3297
 
            except KeyError:
3298
 
                # A KeyError here indicates that someone has triggered an index
3299
 
                # reload, and this index has gone missing, we need to start
3300
 
                # over.
3301
 
                if self._reload_func is None:
3302
 
                    # If we don't have a _reload_func there is nothing that can
3303
 
                    # be done
3304
 
                    raise
3305
 
                raise errors.RetryWithNewPacks(index,
3306
 
                                               reload_occurred=True,
3307
 
                                               exc_info=sys.exc_info())
3308
 
            try:
3309
 
                reader = pack.make_readv_reader(transport, path, offsets)
3310
 
                for names, read_func in reader.iter_records():
3311
 
                    yield read_func(None)
3312
 
            except errors.NoSuchFile:
3313
 
                # A NoSuchFile error indicates that a pack file has gone
3314
 
                # missing on disk, we need to trigger a reload, and start over.
3315
 
                if self._reload_func is None:
3316
 
                    raise
3317
 
                raise errors.RetryWithNewPacks(transport.abspath(path),
3318
 
                                               reload_occurred=False,
3319
 
                                               exc_info=sys.exc_info())
3320
 
 
3321
 
    def set_writer(self, writer, index, transport_packname):
3322
 
        """Set a writer to use for adding data."""
3323
 
        if index is not None:
3324
 
            self._indices[index] = transport_packname
3325
 
        self._container_writer = writer
3326
 
        self._write_index = index
3327
 
 
3328
 
    def reload_or_raise(self, retry_exc):
3329
 
        """Try calling the reload function, or re-raise the original exception.
3330
 
 
3331
 
        This should be called after _DirectPackAccess raises a
3332
 
        RetryWithNewPacks exception. This function will handle the common logic
3333
 
        of determining when the error is fatal versus being temporary.
3334
 
        It will also make sure that the original exception is raised, rather
3335
 
        than the RetryWithNewPacks exception.
3336
 
 
3337
 
        If this function returns, then the calling function should retry
3338
 
        whatever operation was being performed. Otherwise an exception will
3339
 
        be raised.
3340
 
 
3341
 
        :param retry_exc: A RetryWithNewPacks exception.
3342
 
        """
3343
 
        is_error = False
3344
 
        if self._reload_func is None:
3345
 
            is_error = True
3346
 
        elif not self._reload_func():
3347
 
            # The reload claimed that nothing changed
3348
 
            if not retry_exc.reload_occurred:
3349
 
                # If there wasn't an earlier reload, then we really were
3350
 
                # expecting to find changes. We didn't find them, so this is a
3351
 
                # hard error
3352
 
                is_error = True
3353
 
        if is_error:
3354
 
            exc_class, exc_value, exc_traceback = retry_exc.exc_info
3355
 
            raise exc_class, exc_value, exc_traceback
3356
 
 
3357
 
 
3358
3215
def annotate_knit(knit, revision_id):
3359
3216
    """Annotate a knit with no cached annotations.
3360
3217
 
3470
3327
                for idx, (sub_key, text, num_lines) in enumerate(
3471
3328
                                                self._extract_texts(records)):
3472
3329
                    if pb is not None:
3473
 
                        pb.update('annotating', idx, len(records))
 
3330
                        pb.update(gettext('annotating'), idx, len(records))
3474
3331
                    yield sub_key, text, num_lines
3475
3332
                for sub_key in ann_keys:
3476
3333
                    text = self._text_cache[sub_key]