~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:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2006-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
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
57
59
import operator
58
60
import os
59
 
import sys
60
61
 
61
62
from bzrlib.lazy_import import lazy_import
62
63
lazy_import(globals(), """
 
64
import gzip
 
65
 
63
66
from bzrlib import (
64
 
    annotate,
65
67
    debug,
66
68
    diff,
67
69
    graph as _mod_graph,
68
70
    index as _mod_index,
69
 
    lru_cache,
70
71
    pack,
71
 
    progress,
 
72
    patiencediff,
 
73
    static_tuple,
72
74
    trace,
73
75
    tsort,
74
76
    tuned_gzip,
 
77
    ui,
75
78
    )
 
79
 
 
80
from bzrlib.repofmt import pack_repo
 
81
from bzrlib.i18n import gettext
76
82
""")
77
83
from bzrlib import (
 
84
    annotate,
78
85
    errors,
79
86
    osutils,
80
 
    patiencediff,
81
87
    )
82
88
from bzrlib.errors import (
83
 
    FileExists,
84
89
    NoSuchFile,
85
 
    KnitError,
86
90
    InvalidRevisionId,
87
91
    KnitCorrupt,
88
92
    KnitHeaderError,
89
93
    RevisionNotPresent,
90
 
    RevisionAlreadyPresent,
91
94
    SHA1KnitCorrupt,
92
95
    )
93
96
from bzrlib.osutils import (
94
97
    contains_whitespace,
95
 
    contains_linebreaks,
96
98
    sha_string,
97
99
    sha_strings,
98
100
    split_lines,
99
101
    )
100
102
from bzrlib.versionedfile import (
 
103
    _KeyRefs,
101
104
    AbsentContentFactory,
102
105
    adapter_registry,
103
106
    ConstantMapper,
104
107
    ContentFactory,
105
 
    ChunkedContentFactory,
106
108
    sort_groupcompress,
107
 
    VersionedFile,
108
 
    VersionedFiles,
 
109
    VersionedFilesWithFallbacks,
109
110
    )
110
111
 
111
112
 
410
411
class KnitContent(object):
411
412
    """Content of a knit version to which deltas can be applied.
412
413
 
413
 
    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,
414
415
    plus a flag saying if the final ending is really there or not, because that
415
416
    corresponds to the on-disk knit representation.
416
417
    """
803
804
        writer.begin()
804
805
        index = _KnitGraphIndex(graph_index, lambda:True, parents=parents,
805
806
            deltas=delta, add_callback=graph_index.add_nodes)
806
 
        access = _DirectPackAccess({})
 
807
        access = pack_repo._DirectPackAccess({})
807
808
        access.set_writer(writer, graph_index, (transport, 'newpack'))
808
809
        result = KnitVersionedFiles(index, access,
809
810
            max_delta_chain=max_delta_chain)
847
848
                in all_build_index_memos.itervalues()])
848
849
 
849
850
 
850
 
class KnitVersionedFiles(VersionedFiles):
 
851
class KnitVersionedFiles(VersionedFilesWithFallbacks):
851
852
    """Storage for many versioned files using knit compression.
852
853
 
853
854
    Backend storage is managed by indices and data objects.
880
881
            self._factory = KnitAnnotateFactory()
881
882
        else:
882
883
            self._factory = KnitPlainFactory()
883
 
        self._fallback_vfs = []
 
884
        self._immediate_fallback_vfs = []
884
885
        self._reload_func = reload_func
885
886
 
886
887
    def __repr__(self):
889
890
            self._index,
890
891
            self._access)
891
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
 
892
899
    def add_fallback_versioned_files(self, a_versioned_files):
893
900
        """Add a source of texts for texts not present in this knit.
894
901
 
895
902
        :param a_versioned_files: A VersionedFiles object.
896
903
        """
897
 
        self._fallback_vfs.append(a_versioned_files)
 
904
        self._immediate_fallback_vfs.append(a_versioned_files)
898
905
 
899
906
    def add_lines(self, key, parents, lines, parent_texts=None,
900
907
        left_matching_blocks=None, nostore_sha=None, random_id=False,
1045
1052
    def get_annotator(self):
1046
1053
        return _KnitAnnotator(self)
1047
1054
 
1048
 
    def check(self, progress_bar=None):
 
1055
    def check(self, progress_bar=None, keys=None):
1049
1056
        """See VersionedFiles.check()."""
 
1057
        if keys is None:
 
1058
            return self._logical_check()
 
1059
        else:
 
1060
            # At the moment, check does not extra work over get_record_stream
 
1061
            return self.get_record_stream(keys, 'unordered', True)
 
1062
 
 
1063
    def _logical_check(self):
1050
1064
        # This doesn't actually test extraction of everything, but that will
1051
1065
        # impact 'bzr check' substantially, and needs to be integrated with
1052
1066
        # care. However, it does check for the obvious problem of a delta with
1060
1074
                    raise errors.KnitCorrupt(self,
1061
1075
                        "Missing basis parent %s for %s" % (
1062
1076
                        compression_parent, key))
1063
 
        for fallback_vfs in self._fallback_vfs:
 
1077
        for fallback_vfs in self._immediate_fallback_vfs:
1064
1078
            fallback_vfs.check()
1065
1079
 
1066
1080
    def _check_add(self, key, lines, random_id, check_content):
1144
1158
 
1145
1159
        A dict of key to (record_details, index_memo, next, parents) is
1146
1160
        returned.
1147
 
        method is the way referenced data should be applied.
1148
 
        index_memo is the handle to pass to the data access to actually get the
1149
 
            data
1150
 
        next is the build-parent of the version, or None for fulltexts.
1151
 
        parents is the version_ids of the parents of this version
1152
 
 
1153
 
        :param allow_missing: If True do not raise an error on a missing component,
1154
 
            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.
1155
1170
        """
1156
1171
        component_data = {}
1157
1172
        pending_components = keys
1203
1218
            and so on.
1204
1219
        """
1205
1220
        result = {}
1206
 
        sources = [self._index] + self._fallback_vfs
 
1221
        sources = [self._index] + self._immediate_fallback_vfs
1207
1222
        source_results = []
1208
1223
        missing = set(keys)
1209
1224
        for source in sources:
1219
1234
        """Produce a dictionary of knit records.
1220
1235
 
1221
1236
        :return: {key:(record, record_details, digest, next)}
1222
 
            record
1223
 
                data returned from read_records (a KnitContentobject)
1224
 
            record_details
1225
 
                opaque information to pass to parse_record
1226
 
            digest
1227
 
                SHA1 digest of the full text after all steps are done
1228
 
            next
1229
 
                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.
1230
1242
                Will be None if the record is not a delta.
 
1243
 
1231
1244
        :param keys: The keys to build a map for
1232
1245
        :param allow_missing: If some records are missing, rather than
1233
1246
            error, just return the data that could be generated.
1498
1511
                if source is parent_maps[0]:
1499
1512
                    # this KnitVersionedFiles
1500
1513
                    records = [(key, positions[key][1]) for key in keys]
1501
 
                    for key, raw_data, sha1 in self._read_records_iter_raw(records):
 
1514
                    for key, raw_data in self._read_records_iter_unchecked(records):
1502
1515
                        (record_details, index_memo, _) = positions[key]
1503
1516
                        yield KnitContentFactory(key, global_map[key],
1504
 
                            record_details, sha1, raw_data, self._factory.annotated, None)
 
1517
                            record_details, None, raw_data, self._factory.annotated, None)
1505
1518
                else:
1506
 
                    vf = self._fallback_vfs[parent_maps.index(source) - 1]
 
1519
                    vf = self._immediate_fallback_vfs[parent_maps.index(source) - 1]
1507
1520
                    for record in vf.get_record_stream(keys, ordering,
1508
1521
                        include_delta_closure):
1509
1522
                        yield record
1519
1532
            # record entry 2 is the 'digest'.
1520
1533
            result[key] = details[2]
1521
1534
        missing.difference_update(set(result))
1522
 
        for source in self._fallback_vfs:
 
1535
        for source in self._immediate_fallback_vfs:
1523
1536
            if not missing:
1524
1537
                break
1525
1538
            new_result = source.get_sha1s(missing)
1576
1589
        # key = basis_parent, value = index entry to add
1577
1590
        buffered_index_entries = {}
1578
1591
        for record in stream:
 
1592
            kind = record.storage_kind
 
1593
            if kind.startswith('knit-') and kind.endswith('-gz'):
 
1594
                # Check that the ID in the header of the raw knit bytes matches
 
1595
                # the record metadata.
 
1596
                raw_data = record._raw_record
 
1597
                df, rec = self._parse_record_header(record.key, raw_data)
 
1598
                df.close()
1579
1599
            buffered = False
1580
1600
            parents = record.parents
1581
1601
            if record.storage_kind in delta_types:
1589
1609
                raise RevisionNotPresent([record.key], self)
1590
1610
            elif ((record.storage_kind in knit_types)
1591
1611
                  and (compression_parent is None
1592
 
                       or not self._fallback_vfs
 
1612
                       or not self._immediate_fallback_vfs
1593
1613
                       or self._index.has_key(compression_parent)
1594
1614
                       or not self.has_key(compression_parent))):
1595
1615
                # we can insert the knit record literally if either it has no
1683
1703
            # There were index entries buffered at the end of the stream,
1684
1704
            # So these need to be added (if the index supports holding such
1685
1705
            # entries for later insertion)
 
1706
            all_entries = []
1686
1707
            for key in buffered_index_entries:
1687
1708
                index_entries = buffered_index_entries[key]
1688
 
                self._index.add_records(index_entries,
1689
 
                    missing_compression_parents=True)
 
1709
                all_entries.extend(index_entries)
 
1710
            self._index.add_records(
 
1711
                all_entries, missing_compression_parents=True)
1690
1712
 
1691
1713
    def get_missing_compression_parent_keys(self):
1692
1714
        """Return an iterable of keys of missing compression parents.
1725
1747
        :return: An iterator over (line, key).
1726
1748
        """
1727
1749
        if pb is None:
1728
 
            pb = progress.DummyProgress()
 
1750
            pb = ui.ui_factory.nested_progress_bar()
1729
1751
        keys = set(keys)
1730
1752
        total = len(keys)
1731
1753
        done = False
1741
1763
                        key_records.append((key, details[0]))
1742
1764
                records_iter = enumerate(self._read_records_iter(key_records))
1743
1765
                for (key_idx, (key, data, sha_value)) in records_iter:
1744
 
                    pb.update('Walking content', key_idx, total)
 
1766
                    pb.update(gettext('Walking content'), key_idx, total)
1745
1767
                    compression_parent = build_details[key][1]
1746
1768
                    if compression_parent is None:
1747
1769
                        # fulltext
1765
1787
        # vfs, and hope to find them there.  Note that if the keys are found
1766
1788
        # but had no changes or no content, the fallback may not return
1767
1789
        # anything.
1768
 
        if keys and not self._fallback_vfs:
 
1790
        if keys and not self._immediate_fallback_vfs:
1769
1791
            # XXX: strictly the second parameter is meant to be the file id
1770
1792
            # but it's not easily accessible here.
1771
1793
            raise RevisionNotPresent(keys, repr(self))
1772
 
        for source in self._fallback_vfs:
 
1794
        for source in self._immediate_fallback_vfs:
1773
1795
            if not keys:
1774
1796
                break
1775
1797
            source_keys = set()
1777
1799
                source_keys.add(key)
1778
1800
                yield line, key
1779
1801
            keys.difference_update(source_keys)
1780
 
        pb.update('Walking content', total, total)
 
1802
        pb.update(gettext('Walking content'), total, total)
1781
1803
 
1782
1804
    def _make_line_delta(self, delta_seq, new_content):
1783
1805
        """Generate a line delta from delta_seq and new_content."""
1848
1870
        :return: the header and the decompressor stream.
1849
1871
                 as (stream, header_record)
1850
1872
        """
1851
 
        df = tuned_gzip.GzipFile(mode='rb', fileobj=StringIO(raw_data))
 
1873
        df = gzip.GzipFile(mode='rb', fileobj=StringIO(raw_data))
1852
1874
        try:
1853
1875
            # Current serialise
1854
1876
            rec = self._check_header(key, df.readline())
1863
1885
        # 4168 calls in 2880 217 internal
1864
1886
        # 4168 calls to _parse_record_header in 2121
1865
1887
        # 4168 calls to readlines in 330
1866
 
        df = tuned_gzip.GzipFile(mode='rb', fileobj=StringIO(data))
 
1888
        df = gzip.GzipFile(mode='rb', fileobj=StringIO(data))
1867
1889
        try:
1868
1890
            record_contents = df.readlines()
1869
1891
        except Exception, e:
1891
1913
        The result will be returned in whatever is the fastest to read.
1892
1914
        Not by the order requested. Also, multiple requests for the same
1893
1915
        record will only yield 1 response.
 
1916
 
1894
1917
        :param records: A list of (key, access_memo) entries
1895
1918
        :return: Yields (key, contents, digest) in the order
1896
1919
                 read, not the order requested
1954
1977
        :param key: The key of the record. Currently keys are always serialised
1955
1978
            using just the trailing component.
1956
1979
        :param dense_lines: The bytes of lines but in a denser form. For
1957
 
            instance, if lines is a list of 1000 bytestrings each ending in \n,
1958
 
            dense_lines may be a list with one line in it, containing all the
1959
 
            1000's lines and their \n's. Using dense_lines if it is already
1960
 
            known is a win because the string join to create bytes in this
1961
 
            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.
1962
1985
        :return: (len, a StringIO instance with the raw data ready to read.)
1963
1986
        """
1964
1987
        chunks = ["version %s %d %s\n" % (key[-1], len(lines), digest)]
1984
2007
        """See VersionedFiles.keys."""
1985
2008
        if 'evil' in debug.debug_flags:
1986
2009
            trace.mutter_callsite(2, "keys scales with size of history")
1987
 
        sources = [self._index] + self._fallback_vfs
 
2010
        sources = [self._index] + self._immediate_fallback_vfs
1988
2011
        result = set()
1989
2012
        for source in sources:
1990
2013
            result.update(source.keys())
2030
2053
 
2031
2054
        missing_keys = set(nonlocal_keys)
2032
2055
        # Read from remote versioned file instances and provide to our caller.
2033
 
        for source in self.vf._fallback_vfs:
 
2056
        for source in self.vf._immediate_fallback_vfs:
2034
2057
            if not missing_keys:
2035
2058
                break
2036
2059
            # Loop over fallback repositories asking them for texts - ignore
2338
2361
    FLAGS is a comma separated list of flags about the record. Values include
2339
2362
        no-eol, line-delta, fulltext.
2340
2363
    BYTE_OFFSET is the ascii representation of the byte offset in the data file
2341
 
        that the the compressed data starts at.
 
2364
        that the compressed data starts at.
2342
2365
    LENGTH is the ascii representation of the length of the data file.
2343
2366
    PARENT_ID a utf-8 revision id prefixed by a '.' that is a parent of
2344
2367
        REVISION_ID.
2553
2576
        except KeyError:
2554
2577
            raise RevisionNotPresent(key, self)
2555
2578
 
 
2579
    def find_ancestry(self, keys):
 
2580
        """See CombinedGraphIndex.find_ancestry()"""
 
2581
        prefixes = set(key[:-1] for key in keys)
 
2582
        self._load_prefixes(prefixes)
 
2583
        result = {}
 
2584
        parent_map = {}
 
2585
        missing_keys = set()
 
2586
        pending_keys = list(keys)
 
2587
        # This assumes that keys will not reference parents in a different
 
2588
        # prefix, which is accurate so far.
 
2589
        while pending_keys:
 
2590
            key = pending_keys.pop()
 
2591
            if key in parent_map:
 
2592
                continue
 
2593
            prefix = key[:-1]
 
2594
            try:
 
2595
                suffix_parents = self._kndx_cache[prefix][0][key[-1]][4]
 
2596
            except KeyError:
 
2597
                missing_keys.add(key)
 
2598
            else:
 
2599
                parent_keys = tuple([prefix + (suffix,)
 
2600
                                     for suffix in suffix_parents])
 
2601
                parent_map[key] = parent_keys
 
2602
                pending_keys.extend([p for p in parent_keys
 
2603
                                        if p not in parent_map])
 
2604
        return parent_map, missing_keys
 
2605
 
2556
2606
    def get_parent_map(self, keys):
2557
2607
        """Get a map of the parents of keys.
2558
2608
 
2728
2778
        return key[:-1], key[-1]
2729
2779
 
2730
2780
 
2731
 
class _KeyRefs(object):
2732
 
 
2733
 
    def __init__(self):
2734
 
        # dict mapping 'key' to 'set of keys referring to that key'
2735
 
        self.refs = {}
2736
 
 
2737
 
    def add_references(self, key, refs):
2738
 
        # Record the new references
2739
 
        for referenced in refs:
2740
 
            try:
2741
 
                needed_by = self.refs[referenced]
2742
 
            except KeyError:
2743
 
                needed_by = self.refs[referenced] = set()
2744
 
            needed_by.add(key)
2745
 
        # Discard references satisfied by the new key
2746
 
        self.add_key(key)
2747
 
 
2748
 
    def get_unsatisfied_refs(self):
2749
 
        return self.refs.iterkeys()
2750
 
 
2751
 
    def add_key(self, key):
2752
 
        try:
2753
 
            del self.refs[key]
2754
 
        except KeyError:
2755
 
            # No keys depended on this key.  That's ok.
2756
 
            pass
2757
 
 
2758
 
    def add_keys(self, keys):
2759
 
        for key in keys:
2760
 
            self.add_key(key)
2761
 
 
2762
 
    def get_referrers(self):
2763
 
        result = set()
2764
 
        for referrers in self.refs.itervalues():
2765
 
            result.update(referrers)
2766
 
        return result
2767
 
 
2768
 
 
2769
2781
class _KnitGraphIndex(object):
2770
2782
    """A KnitVersionedFiles index layered on GraphIndex."""
2771
2783
 
2868
2880
        if not random_id:
2869
2881
            present_nodes = self._get_entries(keys)
2870
2882
            for (index, key, value, node_refs) in present_nodes:
 
2883
                parents = node_refs[:1]
 
2884
                # Sometimes these are passed as a list rather than a tuple
 
2885
                passed = static_tuple.as_tuples(keys[key])
 
2886
                passed_parents = passed[1][:1]
2871
2887
                if (value[0] != keys[key][0][0] or
2872
 
                    node_refs[:1] != keys[key][1][:1]):
 
2888
                    parents != passed_parents):
 
2889
                    node_refs = static_tuple.as_tuples(node_refs)
2873
2890
                    raise KnitCorrupt(self, "inconsistent details in add_records"
2874
 
                        ": %s %s" % ((value, node_refs), keys[key]))
 
2891
                        ": %s %s" % ((value, node_refs), passed))
2875
2892
                del keys[key]
2876
2893
        result = []
2877
2894
        if self._parents:
2925
2942
        # If updating this, you should also update
2926
2943
        # groupcompress._GCGraphIndex.get_missing_parents
2927
2944
        # We may have false positives, so filter those out.
2928
 
        self._key_dependencies.add_keys(
 
2945
        self._key_dependencies.satisfy_refs_for_keys(
2929
2946
            self.get_parent_map(self._key_dependencies.get_unsatisfied_refs()))
2930
2947
        return frozenset(self._key_dependencies.get_unsatisfied_refs())
2931
2948
 
3042
3059
            options.append('no-eol')
3043
3060
        return options
3044
3061
 
 
3062
    def find_ancestry(self, keys):
 
3063
        """See CombinedGraphIndex.find_ancestry()"""
 
3064
        return self._graph_index.find_ancestry(keys, 0)
 
3065
 
3045
3066
    def get_parent_map(self, keys):
3046
3067
        """Get a map of the parents of keys.
3047
3068
 
3191
3212
                yield data
3192
3213
 
3193
3214
 
3194
 
class _DirectPackAccess(object):
3195
 
    """Access to data in one or more packs with less translation."""
3196
 
 
3197
 
    def __init__(self, index_to_packs, reload_func=None, flush_func=None):
3198
 
        """Create a _DirectPackAccess object.
3199
 
 
3200
 
        :param index_to_packs: A dict mapping index objects to the transport
3201
 
            and file names for obtaining data.
3202
 
        :param reload_func: A function to call if we determine that the pack
3203
 
            files have moved and we need to reload our caches. See
3204
 
            bzrlib.repo_fmt.pack_repo.AggregateIndex for more details.
3205
 
        """
3206
 
        self._container_writer = None
3207
 
        self._write_index = None
3208
 
        self._indices = index_to_packs
3209
 
        self._reload_func = reload_func
3210
 
        self._flush_func = flush_func
3211
 
 
3212
 
    def add_raw_records(self, key_sizes, raw_data):
3213
 
        """Add raw knit bytes to a storage area.
3214
 
 
3215
 
        The data is spooled to the container writer in one bytes-record per
3216
 
        raw data item.
3217
 
 
3218
 
        :param sizes: An iterable of tuples containing the key and size of each
3219
 
            raw data segment.
3220
 
        :param raw_data: A bytestring containing the data.
3221
 
        :return: A list of memos to retrieve the record later. Each memo is an
3222
 
            opaque index memo. For _DirectPackAccess the memo is (index, pos,
3223
 
            length), where the index field is the write_index object supplied
3224
 
            to the PackAccess object.
3225
 
        """
3226
 
        if type(raw_data) is not str:
3227
 
            raise AssertionError(
3228
 
                'data must be plain bytes was %s' % type(raw_data))
3229
 
        result = []
3230
 
        offset = 0
3231
 
        for key, size in key_sizes:
3232
 
            p_offset, p_length = self._container_writer.add_bytes_record(
3233
 
                raw_data[offset:offset+size], [])
3234
 
            offset += size
3235
 
            result.append((self._write_index, p_offset, p_length))
3236
 
        return result
3237
 
 
3238
 
    def flush(self):
3239
 
        """Flush pending writes on this access object.
3240
 
 
3241
 
        This will flush any buffered writes to a NewPack.
3242
 
        """
3243
 
        if self._flush_func is not None:
3244
 
            self._flush_func()
3245
 
            
3246
 
    def get_raw_records(self, memos_for_retrieval):
3247
 
        """Get the raw bytes for a records.
3248
 
 
3249
 
        :param memos_for_retrieval: An iterable containing the (index, pos,
3250
 
            length) memo for retrieving the bytes. The Pack access method
3251
 
            looks up the pack to use for a given record in its index_to_pack
3252
 
            map.
3253
 
        :return: An iterator over the bytes of the records.
3254
 
        """
3255
 
        # first pass, group into same-index requests
3256
 
        request_lists = []
3257
 
        current_index = None
3258
 
        for (index, offset, length) in memos_for_retrieval:
3259
 
            if current_index == index:
3260
 
                current_list.append((offset, length))
3261
 
            else:
3262
 
                if current_index is not None:
3263
 
                    request_lists.append((current_index, current_list))
3264
 
                current_index = index
3265
 
                current_list = [(offset, length)]
3266
 
        # handle the last entry
3267
 
        if current_index is not None:
3268
 
            request_lists.append((current_index, current_list))
3269
 
        for index, offsets in request_lists:
3270
 
            try:
3271
 
                transport, path = self._indices[index]
3272
 
            except KeyError:
3273
 
                # A KeyError here indicates that someone has triggered an index
3274
 
                # reload, and this index has gone missing, we need to start
3275
 
                # over.
3276
 
                if self._reload_func is None:
3277
 
                    # If we don't have a _reload_func there is nothing that can
3278
 
                    # be done
3279
 
                    raise
3280
 
                raise errors.RetryWithNewPacks(index,
3281
 
                                               reload_occurred=True,
3282
 
                                               exc_info=sys.exc_info())
3283
 
            try:
3284
 
                reader = pack.make_readv_reader(transport, path, offsets)
3285
 
                for names, read_func in reader.iter_records():
3286
 
                    yield read_func(None)
3287
 
            except errors.NoSuchFile:
3288
 
                # A NoSuchFile error indicates that a pack file has gone
3289
 
                # missing on disk, we need to trigger a reload, and start over.
3290
 
                if self._reload_func is None:
3291
 
                    raise
3292
 
                raise errors.RetryWithNewPacks(transport.abspath(path),
3293
 
                                               reload_occurred=False,
3294
 
                                               exc_info=sys.exc_info())
3295
 
 
3296
 
    def set_writer(self, writer, index, transport_packname):
3297
 
        """Set a writer to use for adding data."""
3298
 
        if index is not None:
3299
 
            self._indices[index] = transport_packname
3300
 
        self._container_writer = writer
3301
 
        self._write_index = index
3302
 
 
3303
 
    def reload_or_raise(self, retry_exc):
3304
 
        """Try calling the reload function, or re-raise the original exception.
3305
 
 
3306
 
        This should be called after _DirectPackAccess raises a
3307
 
        RetryWithNewPacks exception. This function will handle the common logic
3308
 
        of determining when the error is fatal versus being temporary.
3309
 
        It will also make sure that the original exception is raised, rather
3310
 
        than the RetryWithNewPacks exception.
3311
 
 
3312
 
        If this function returns, then the calling function should retry
3313
 
        whatever operation was being performed. Otherwise an exception will
3314
 
        be raised.
3315
 
 
3316
 
        :param retry_exc: A RetryWithNewPacks exception.
3317
 
        """
3318
 
        is_error = False
3319
 
        if self._reload_func is None:
3320
 
            is_error = True
3321
 
        elif not self._reload_func():
3322
 
            # The reload claimed that nothing changed
3323
 
            if not retry_exc.reload_occurred:
3324
 
                # If there wasn't an earlier reload, then we really were
3325
 
                # expecting to find changes. We didn't find them, so this is a
3326
 
                # hard error
3327
 
                is_error = True
3328
 
        if is_error:
3329
 
            exc_class, exc_value, exc_traceback = retry_exc.exc_info
3330
 
            raise exc_class, exc_value, exc_traceback
3331
 
 
3332
 
 
3333
 
# Deprecated, use PatienceSequenceMatcher instead
3334
 
KnitSequenceMatcher = patiencediff.PatienceSequenceMatcher
3335
 
 
3336
 
 
3337
3215
def annotate_knit(knit, revision_id):
3338
3216
    """Annotate a knit with no cached annotations.
3339
3217
 
3437
3315
        return records, ann_keys
3438
3316
 
3439
3317
    def _get_needed_texts(self, key, pb=None):
3440
 
        # if True or len(self._vf._fallback_vfs) > 0:
3441
 
        if len(self._vf._fallback_vfs) > 0:
 
3318
        # if True or len(self._vf._immediate_fallback_vfs) > 0:
 
3319
        if len(self._vf._immediate_fallback_vfs) > 0:
3442
3320
            # If we have fallbacks, go to the generic path
3443
3321
            for v in annotate.Annotator._get_needed_texts(self, key, pb=pb):
3444
3322
                yield v
3449
3327
                for idx, (sub_key, text, num_lines) in enumerate(
3450
3328
                                                self._extract_texts(records)):
3451
3329
                    if pb is not None:
3452
 
                        pb.update('annotating', idx, len(records))
 
3330
                        pb.update(gettext('annotating'), idx, len(records))
3453
3331
                    yield sub_key, text, num_lines
3454
3332
                for sub_key in ann_keys:
3455
3333
                    text = self._text_cache[sub_key]
3622
3500
 
3623
3501
try:
3624
3502
    from bzrlib._knit_load_data_pyx import _load_data_c as _load_data
3625
 
except ImportError:
 
3503
except ImportError, e:
 
3504
    osutils.failed_to_load_extension(e)
3626
3505
    from bzrlib._knit_load_data_py import _load_data_py as _load_data