~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: 2010-09-01 08:02:42 UTC
  • mfrom: (5390.3.3 faster-revert-593560)
  • Revision ID: pqm@pqm.ubuntu.com-20100901080242-esg62ody4frwmy66
(spiv) Avoid repeatedly calling self.target.all_file_ids() in
 InterTree.iter_changes. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

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