~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/knit.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-10-13 06:08:53 UTC
  • mfrom: (4737.1.1 merge-2.0-into-devel)
  • Revision ID: pqm@pqm.ubuntu.com-20091013060853-erk2aaj80fnkrv25
(andrew) Merge lp:bzr/2.0 into lp:bzr, including fixes for #322807,
        #389413, #402623 and documentation improvements.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 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
 
 
56
54
 
57
55
from cStringIO import StringIO
58
56
from itertools import izip
59
57
import operator
60
58
import os
 
59
import sys
61
60
 
62
61
from bzrlib.lazy_import import lazy_import
63
62
lazy_import(globals(), """
64
 
import gzip
65
 
 
66
63
from bzrlib import (
 
64
    annotate,
67
65
    debug,
68
66
    diff,
69
67
    graph as _mod_graph,
70
68
    index as _mod_index,
 
69
    lru_cache,
71
70
    pack,
72
 
    patiencediff,
73
 
    static_tuple,
 
71
    progress,
74
72
    trace,
75
73
    tsort,
76
74
    tuned_gzip,
77
 
    ui,
78
75
    )
79
 
 
80
 
from bzrlib.repofmt import pack_repo
81
 
from bzrlib.i18n import gettext
82
76
""")
83
77
from bzrlib import (
84
 
    annotate,
85
78
    errors,
86
79
    osutils,
 
80
    patiencediff,
87
81
    )
88
82
from bzrlib.errors import (
 
83
    FileExists,
89
84
    NoSuchFile,
 
85
    KnitError,
90
86
    InvalidRevisionId,
91
87
    KnitCorrupt,
92
88
    KnitHeaderError,
93
89
    RevisionNotPresent,
 
90
    RevisionAlreadyPresent,
94
91
    SHA1KnitCorrupt,
95
92
    )
96
93
from bzrlib.osutils import (
97
94
    contains_whitespace,
 
95
    contains_linebreaks,
98
96
    sha_string,
99
97
    sha_strings,
100
98
    split_lines,
101
99
    )
102
100
from bzrlib.versionedfile import (
103
 
    _KeyRefs,
104
101
    AbsentContentFactory,
105
102
    adapter_registry,
106
103
    ConstantMapper,
107
104
    ContentFactory,
 
105
    ChunkedContentFactory,
108
106
    sort_groupcompress,
109
 
    VersionedFilesWithFallbacks,
 
107
    VersionedFile,
 
108
    VersionedFiles,
110
109
    )
111
110
 
112
111
 
411
410
class KnitContent(object):
412
411
    """Content of a knit version to which deltas can be applied.
413
412
 
414
 
    This is always stored in memory as a list of lines with \\n at the end,
 
413
    This is always stored in memory as a list of lines with \n at the end,
415
414
    plus a flag saying if the final ending is really there or not, because that
416
415
    corresponds to the on-disk knit representation.
417
416
    """
804
803
        writer.begin()
805
804
        index = _KnitGraphIndex(graph_index, lambda:True, parents=parents,
806
805
            deltas=delta, add_callback=graph_index.add_nodes)
807
 
        access = pack_repo._DirectPackAccess({})
 
806
        access = _DirectPackAccess({})
808
807
        access.set_writer(writer, graph_index, (transport, 'newpack'))
809
808
        result = KnitVersionedFiles(index, access,
810
809
            max_delta_chain=max_delta_chain)
848
847
                in all_build_index_memos.itervalues()])
849
848
 
850
849
 
851
 
class KnitVersionedFiles(VersionedFilesWithFallbacks):
 
850
class KnitVersionedFiles(VersionedFiles):
852
851
    """Storage for many versioned files using knit compression.
853
852
 
854
853
    Backend storage is managed by indices and data objects.
881
880
            self._factory = KnitAnnotateFactory()
882
881
        else:
883
882
            self._factory = KnitPlainFactory()
884
 
        self._immediate_fallback_vfs = []
 
883
        self._fallback_vfs = []
885
884
        self._reload_func = reload_func
886
885
 
887
886
    def __repr__(self):
890
889
            self._index,
891
890
            self._access)
892
891
 
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
 
 
899
892
    def add_fallback_versioned_files(self, a_versioned_files):
900
893
        """Add a source of texts for texts not present in this knit.
901
894
 
902
895
        :param a_versioned_files: A VersionedFiles object.
903
896
        """
904
 
        self._immediate_fallback_vfs.append(a_versioned_files)
 
897
        self._fallback_vfs.append(a_versioned_files)
905
898
 
906
899
    def add_lines(self, key, parents, lines, parent_texts=None,
907
900
        left_matching_blocks=None, nostore_sha=None, random_id=False,
1074
1067
                    raise errors.KnitCorrupt(self,
1075
1068
                        "Missing basis parent %s for %s" % (
1076
1069
                        compression_parent, key))
1077
 
        for fallback_vfs in self._immediate_fallback_vfs:
 
1070
        for fallback_vfs in self._fallback_vfs:
1078
1071
            fallback_vfs.check()
1079
1072
 
1080
1073
    def _check_add(self, key, lines, random_id, check_content):
1158
1151
 
1159
1152
        A dict of key to (record_details, index_memo, next, parents) is
1160
1153
        returned.
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.
 
1154
        method is the way referenced data should be applied.
 
1155
        index_memo is the handle to pass to the data access to actually get the
 
1156
            data
 
1157
        next is the build-parent of the version, or None for fulltexts.
 
1158
        parents is the version_ids of the parents of this version
 
1159
 
 
1160
        :param allow_missing: If True do not raise an error on a missing component,
 
1161
            just ignore it.
1170
1162
        """
1171
1163
        component_data = {}
1172
1164
        pending_components = keys
1198
1190
        generator = _VFContentMapGenerator(self, [key])
1199
1191
        return generator._get_content(key)
1200
1192
 
 
1193
    def get_known_graph_ancestry(self, keys):
 
1194
        """Get a KnownGraph instance with the ancestry of keys."""
 
1195
        parent_map, missing_keys = self._index.find_ancestry(keys)
 
1196
        for fallback in self._fallback_vfs:
 
1197
            if not missing_keys:
 
1198
                break
 
1199
            (f_parent_map, f_missing_keys) = fallback._index.find_ancestry(
 
1200
                                                missing_keys)
 
1201
            parent_map.update(f_parent_map)
 
1202
            missing_keys = f_missing_keys
 
1203
        kg = _mod_graph.KnownGraph(parent_map)
 
1204
        return kg
 
1205
 
1201
1206
    def get_parent_map(self, keys):
1202
1207
        """Get a map of the graph parents of keys.
1203
1208
 
1218
1223
            and so on.
1219
1224
        """
1220
1225
        result = {}
1221
 
        sources = [self._index] + self._immediate_fallback_vfs
 
1226
        sources = [self._index] + self._fallback_vfs
1222
1227
        source_results = []
1223
1228
        missing = set(keys)
1224
1229
        for source in sources:
1234
1239
        """Produce a dictionary of knit records.
1235
1240
 
1236
1241
        :return: {key:(record, record_details, digest, next)}
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.
 
1242
            record
 
1243
                data returned from read_records (a KnitContentobject)
 
1244
            record_details
 
1245
                opaque information to pass to parse_record
 
1246
            digest
 
1247
                SHA1 digest of the full text after all steps are done
 
1248
            next
 
1249
                build-parent of the version, i.e. the leftmost ancestor.
1242
1250
                Will be None if the record is not a delta.
1243
 
 
1244
1251
        :param keys: The keys to build a map for
1245
1252
        :param allow_missing: If some records are missing, rather than
1246
1253
            error, just return the data that could be generated.
1516
1523
                        yield KnitContentFactory(key, global_map[key],
1517
1524
                            record_details, None, raw_data, self._factory.annotated, None)
1518
1525
                else:
1519
 
                    vf = self._immediate_fallback_vfs[parent_maps.index(source) - 1]
 
1526
                    vf = self._fallback_vfs[parent_maps.index(source) - 1]
1520
1527
                    for record in vf.get_record_stream(keys, ordering,
1521
1528
                        include_delta_closure):
1522
1529
                        yield record
1532
1539
            # record entry 2 is the 'digest'.
1533
1540
            result[key] = details[2]
1534
1541
        missing.difference_update(set(result))
1535
 
        for source in self._immediate_fallback_vfs:
 
1542
        for source in self._fallback_vfs:
1536
1543
            if not missing:
1537
1544
                break
1538
1545
            new_result = source.get_sha1s(missing)
1609
1616
                raise RevisionNotPresent([record.key], self)
1610
1617
            elif ((record.storage_kind in knit_types)
1611
1618
                  and (compression_parent is None
1612
 
                       or not self._immediate_fallback_vfs
 
1619
                       or not self._fallback_vfs
1613
1620
                       or self._index.has_key(compression_parent)
1614
1621
                       or not self.has_key(compression_parent))):
1615
1622
                # we can insert the knit record literally if either it has no
1703
1710
            # There were index entries buffered at the end of the stream,
1704
1711
            # So these need to be added (if the index supports holding such
1705
1712
            # entries for later insertion)
1706
 
            all_entries = []
1707
1713
            for key in buffered_index_entries:
1708
1714
                index_entries = buffered_index_entries[key]
1709
 
                all_entries.extend(index_entries)
1710
 
            self._index.add_records(
1711
 
                all_entries, missing_compression_parents=True)
 
1715
                self._index.add_records(index_entries,
 
1716
                    missing_compression_parents=True)
1712
1717
 
1713
1718
    def get_missing_compression_parent_keys(self):
1714
1719
        """Return an iterable of keys of missing compression parents.
1747
1752
        :return: An iterator over (line, key).
1748
1753
        """
1749
1754
        if pb is None:
1750
 
            pb = ui.ui_factory.nested_progress_bar()
 
1755
            pb = progress.DummyProgress()
1751
1756
        keys = set(keys)
1752
1757
        total = len(keys)
1753
1758
        done = False
1763
1768
                        key_records.append((key, details[0]))
1764
1769
                records_iter = enumerate(self._read_records_iter(key_records))
1765
1770
                for (key_idx, (key, data, sha_value)) in records_iter:
1766
 
                    pb.update(gettext('Walking content'), key_idx, total)
 
1771
                    pb.update('Walking content', key_idx, total)
1767
1772
                    compression_parent = build_details[key][1]
1768
1773
                    if compression_parent is None:
1769
1774
                        # fulltext
1787
1792
        # vfs, and hope to find them there.  Note that if the keys are found
1788
1793
        # but had no changes or no content, the fallback may not return
1789
1794
        # anything.
1790
 
        if keys and not self._immediate_fallback_vfs:
 
1795
        if keys and not self._fallback_vfs:
1791
1796
            # XXX: strictly the second parameter is meant to be the file id
1792
1797
            # but it's not easily accessible here.
1793
1798
            raise RevisionNotPresent(keys, repr(self))
1794
 
        for source in self._immediate_fallback_vfs:
 
1799
        for source in self._fallback_vfs:
1795
1800
            if not keys:
1796
1801
                break
1797
1802
            source_keys = set()
1799
1804
                source_keys.add(key)
1800
1805
                yield line, key
1801
1806
            keys.difference_update(source_keys)
1802
 
        pb.update(gettext('Walking content'), total, total)
 
1807
        pb.update('Walking content', total, total)
1803
1808
 
1804
1809
    def _make_line_delta(self, delta_seq, new_content):
1805
1810
        """Generate a line delta from delta_seq and new_content."""
1870
1875
        :return: the header and the decompressor stream.
1871
1876
                 as (stream, header_record)
1872
1877
        """
1873
 
        df = gzip.GzipFile(mode='rb', fileobj=StringIO(raw_data))
 
1878
        df = tuned_gzip.GzipFile(mode='rb', fileobj=StringIO(raw_data))
1874
1879
        try:
1875
1880
            # Current serialise
1876
1881
            rec = self._check_header(key, df.readline())
1885
1890
        # 4168 calls in 2880 217 internal
1886
1891
        # 4168 calls to _parse_record_header in 2121
1887
1892
        # 4168 calls to readlines in 330
1888
 
        df = gzip.GzipFile(mode='rb', fileobj=StringIO(data))
 
1893
        df = tuned_gzip.GzipFile(mode='rb', fileobj=StringIO(data))
1889
1894
        try:
1890
1895
            record_contents = df.readlines()
1891
1896
        except Exception, e:
1913
1918
        The result will be returned in whatever is the fastest to read.
1914
1919
        Not by the order requested. Also, multiple requests for the same
1915
1920
        record will only yield 1 response.
1916
 
 
1917
1921
        :param records: A list of (key, access_memo) entries
1918
1922
        :return: Yields (key, contents, digest) in the order
1919
1923
                 read, not the order requested
1977
1981
        :param key: The key of the record. Currently keys are always serialised
1978
1982
            using just the trailing component.
1979
1983
        :param dense_lines: The bytes of lines but in a denser form. For
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.
 
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.
1985
1989
        :return: (len, a StringIO instance with the raw data ready to read.)
1986
1990
        """
1987
1991
        chunks = ["version %s %d %s\n" % (key[-1], len(lines), digest)]
2007
2011
        """See VersionedFiles.keys."""
2008
2012
        if 'evil' in debug.debug_flags:
2009
2013
            trace.mutter_callsite(2, "keys scales with size of history")
2010
 
        sources = [self._index] + self._immediate_fallback_vfs
 
2014
        sources = [self._index] + self._fallback_vfs
2011
2015
        result = set()
2012
2016
        for source in sources:
2013
2017
            result.update(source.keys())
2053
2057
 
2054
2058
        missing_keys = set(nonlocal_keys)
2055
2059
        # Read from remote versioned file instances and provide to our caller.
2056
 
        for source in self.vf._immediate_fallback_vfs:
 
2060
        for source in self.vf._fallback_vfs:
2057
2061
            if not missing_keys:
2058
2062
                break
2059
2063
            # Loop over fallback repositories asking them for texts - ignore
2361
2365
    FLAGS is a comma separated list of flags about the record. Values include
2362
2366
        no-eol, line-delta, fulltext.
2363
2367
    BYTE_OFFSET is the ascii representation of the byte offset in the data file
2364
 
        that the compressed data starts at.
 
2368
        that the the compressed data starts at.
2365
2369
    LENGTH is the ascii representation of the length of the data file.
2366
2370
    PARENT_ID a utf-8 revision id prefixed by a '.' that is a parent of
2367
2371
        REVISION_ID.
2778
2782
        return key[:-1], key[-1]
2779
2783
 
2780
2784
 
 
2785
class _KeyRefs(object):
 
2786
 
 
2787
    def __init__(self, track_new_keys=False):
 
2788
        # dict mapping 'key' to 'set of keys referring to that key'
 
2789
        self.refs = {}
 
2790
        if track_new_keys:
 
2791
            # set remembering all new keys
 
2792
            self.new_keys = set()
 
2793
        else:
 
2794
            self.new_keys = None
 
2795
 
 
2796
    def clear(self):
 
2797
        if self.refs:
 
2798
            self.refs.clear()
 
2799
        if self.new_keys:
 
2800
            self.new_keys.clear()
 
2801
 
 
2802
    def add_references(self, key, refs):
 
2803
        # Record the new references
 
2804
        for referenced in refs:
 
2805
            try:
 
2806
                needed_by = self.refs[referenced]
 
2807
            except KeyError:
 
2808
                needed_by = self.refs[referenced] = set()
 
2809
            needed_by.add(key)
 
2810
        # Discard references satisfied by the new key
 
2811
        self.add_key(key)
 
2812
 
 
2813
    def get_new_keys(self):
 
2814
        return self.new_keys
 
2815
    
 
2816
    def get_unsatisfied_refs(self):
 
2817
        return self.refs.iterkeys()
 
2818
 
 
2819
    def _satisfy_refs_for_key(self, key):
 
2820
        try:
 
2821
            del self.refs[key]
 
2822
        except KeyError:
 
2823
            # No keys depended on this key.  That's ok.
 
2824
            pass
 
2825
 
 
2826
    def add_key(self, key):
 
2827
        # satisfy refs for key, and remember that we've seen this key.
 
2828
        self._satisfy_refs_for_key(key)
 
2829
        if self.new_keys is not None:
 
2830
            self.new_keys.add(key)
 
2831
 
 
2832
    def satisfy_refs_for_keys(self, keys):
 
2833
        for key in keys:
 
2834
            self._satisfy_refs_for_key(key)
 
2835
 
 
2836
    def get_referrers(self):
 
2837
        result = set()
 
2838
        for referrers in self.refs.itervalues():
 
2839
            result.update(referrers)
 
2840
        return result
 
2841
 
 
2842
 
2781
2843
class _KnitGraphIndex(object):
2782
2844
    """A KnitVersionedFiles index layered on GraphIndex."""
2783
2845
 
2880
2942
        if not random_id:
2881
2943
            present_nodes = self._get_entries(keys)
2882
2944
            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]
2887
2945
                if (value[0] != keys[key][0][0] or
2888
 
                    parents != passed_parents):
2889
 
                    node_refs = static_tuple.as_tuples(node_refs)
 
2946
                    node_refs[:1] != keys[key][1][:1]):
2890
2947
                    raise KnitCorrupt(self, "inconsistent details in add_records"
2891
 
                        ": %s %s" % ((value, node_refs), passed))
 
2948
                        ": %s %s" % ((value, node_refs), keys[key]))
2892
2949
                del keys[key]
2893
2950
        result = []
2894
2951
        if self._parents:
3212
3269
                yield data
3213
3270
 
3214
3271
 
 
3272
class _DirectPackAccess(object):
 
3273
    """Access to data in one or more packs with less translation."""
 
3274
 
 
3275
    def __init__(self, index_to_packs, reload_func=None, flush_func=None):
 
3276
        """Create a _DirectPackAccess object.
 
3277
 
 
3278
        :param index_to_packs: A dict mapping index objects to the transport
 
3279
            and file names for obtaining data.
 
3280
        :param reload_func: A function to call if we determine that the pack
 
3281
            files have moved and we need to reload our caches. See
 
3282
            bzrlib.repo_fmt.pack_repo.AggregateIndex for more details.
 
3283
        """
 
3284
        self._container_writer = None
 
3285
        self._write_index = None
 
3286
        self._indices = index_to_packs
 
3287
        self._reload_func = reload_func
 
3288
        self._flush_func = flush_func
 
3289
 
 
3290
    def add_raw_records(self, key_sizes, raw_data):
 
3291
        """Add raw knit bytes to a storage area.
 
3292
 
 
3293
        The data is spooled to the container writer in one bytes-record per
 
3294
        raw data item.
 
3295
 
 
3296
        :param sizes: An iterable of tuples containing the key and size of each
 
3297
            raw data segment.
 
3298
        :param raw_data: A bytestring containing the data.
 
3299
        :return: A list of memos to retrieve the record later. Each memo is an
 
3300
            opaque index memo. For _DirectPackAccess the memo is (index, pos,
 
3301
            length), where the index field is the write_index object supplied
 
3302
            to the PackAccess object.
 
3303
        """
 
3304
        if type(raw_data) is not str:
 
3305
            raise AssertionError(
 
3306
                'data must be plain bytes was %s' % type(raw_data))
 
3307
        result = []
 
3308
        offset = 0
 
3309
        for key, size in key_sizes:
 
3310
            p_offset, p_length = self._container_writer.add_bytes_record(
 
3311
                raw_data[offset:offset+size], [])
 
3312
            offset += size
 
3313
            result.append((self._write_index, p_offset, p_length))
 
3314
        return result
 
3315
 
 
3316
    def flush(self):
 
3317
        """Flush pending writes on this access object.
 
3318
 
 
3319
        This will flush any buffered writes to a NewPack.
 
3320
        """
 
3321
        if self._flush_func is not None:
 
3322
            self._flush_func()
 
3323
            
 
3324
    def get_raw_records(self, memos_for_retrieval):
 
3325
        """Get the raw bytes for a records.
 
3326
 
 
3327
        :param memos_for_retrieval: An iterable containing the (index, pos,
 
3328
            length) memo for retrieving the bytes. The Pack access method
 
3329
            looks up the pack to use for a given record in its index_to_pack
 
3330
            map.
 
3331
        :return: An iterator over the bytes of the records.
 
3332
        """
 
3333
        # first pass, group into same-index requests
 
3334
        request_lists = []
 
3335
        current_index = None
 
3336
        for (index, offset, length) in memos_for_retrieval:
 
3337
            if current_index == index:
 
3338
                current_list.append((offset, length))
 
3339
            else:
 
3340
                if current_index is not None:
 
3341
                    request_lists.append((current_index, current_list))
 
3342
                current_index = index
 
3343
                current_list = [(offset, length)]
 
3344
        # handle the last entry
 
3345
        if current_index is not None:
 
3346
            request_lists.append((current_index, current_list))
 
3347
        for index, offsets in request_lists:
 
3348
            try:
 
3349
                transport, path = self._indices[index]
 
3350
            except KeyError:
 
3351
                # A KeyError here indicates that someone has triggered an index
 
3352
                # reload, and this index has gone missing, we need to start
 
3353
                # over.
 
3354
                if self._reload_func is None:
 
3355
                    # If we don't have a _reload_func there is nothing that can
 
3356
                    # be done
 
3357
                    raise
 
3358
                raise errors.RetryWithNewPacks(index,
 
3359
                                               reload_occurred=True,
 
3360
                                               exc_info=sys.exc_info())
 
3361
            try:
 
3362
                reader = pack.make_readv_reader(transport, path, offsets)
 
3363
                for names, read_func in reader.iter_records():
 
3364
                    yield read_func(None)
 
3365
            except errors.NoSuchFile:
 
3366
                # A NoSuchFile error indicates that a pack file has gone
 
3367
                # missing on disk, we need to trigger a reload, and start over.
 
3368
                if self._reload_func is None:
 
3369
                    raise
 
3370
                raise errors.RetryWithNewPacks(transport.abspath(path),
 
3371
                                               reload_occurred=False,
 
3372
                                               exc_info=sys.exc_info())
 
3373
 
 
3374
    def set_writer(self, writer, index, transport_packname):
 
3375
        """Set a writer to use for adding data."""
 
3376
        if index is not None:
 
3377
            self._indices[index] = transport_packname
 
3378
        self._container_writer = writer
 
3379
        self._write_index = index
 
3380
 
 
3381
    def reload_or_raise(self, retry_exc):
 
3382
        """Try calling the reload function, or re-raise the original exception.
 
3383
 
 
3384
        This should be called after _DirectPackAccess raises a
 
3385
        RetryWithNewPacks exception. This function will handle the common logic
 
3386
        of determining when the error is fatal versus being temporary.
 
3387
        It will also make sure that the original exception is raised, rather
 
3388
        than the RetryWithNewPacks exception.
 
3389
 
 
3390
        If this function returns, then the calling function should retry
 
3391
        whatever operation was being performed. Otherwise an exception will
 
3392
        be raised.
 
3393
 
 
3394
        :param retry_exc: A RetryWithNewPacks exception.
 
3395
        """
 
3396
        is_error = False
 
3397
        if self._reload_func is None:
 
3398
            is_error = True
 
3399
        elif not self._reload_func():
 
3400
            # The reload claimed that nothing changed
 
3401
            if not retry_exc.reload_occurred:
 
3402
                # If there wasn't an earlier reload, then we really were
 
3403
                # expecting to find changes. We didn't find them, so this is a
 
3404
                # hard error
 
3405
                is_error = True
 
3406
        if is_error:
 
3407
            exc_class, exc_value, exc_traceback = retry_exc.exc_info
 
3408
            raise exc_class, exc_value, exc_traceback
 
3409
 
 
3410
 
 
3411
# Deprecated, use PatienceSequenceMatcher instead
 
3412
KnitSequenceMatcher = patiencediff.PatienceSequenceMatcher
 
3413
 
 
3414
 
3215
3415
def annotate_knit(knit, revision_id):
3216
3416
    """Annotate a knit with no cached annotations.
3217
3417
 
3315
3515
        return records, ann_keys
3316
3516
 
3317
3517
    def _get_needed_texts(self, key, pb=None):
3318
 
        # if True or len(self._vf._immediate_fallback_vfs) > 0:
3319
 
        if len(self._vf._immediate_fallback_vfs) > 0:
 
3518
        # if True or len(self._vf._fallback_vfs) > 0:
 
3519
        if len(self._vf._fallback_vfs) > 0:
3320
3520
            # If we have fallbacks, go to the generic path
3321
3521
            for v in annotate.Annotator._get_needed_texts(self, key, pb=pb):
3322
3522
                yield v
3327
3527
                for idx, (sub_key, text, num_lines) in enumerate(
3328
3528
                                                self._extract_texts(records)):
3329
3529
                    if pb is not None:
3330
 
                        pb.update(gettext('annotating'), idx, len(records))
 
3530
                        pb.update('annotating', idx, len(records))
3331
3531
                    yield sub_key, text, num_lines
3332
3532
                for sub_key in ann_keys:
3333
3533
                    text = self._text_cache[sub_key]