~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/knit.py

  • Committer: Vincent Ladeuil
  • Date: 2010-10-07 06:08:01 UTC
  • mto: This revision was merged to the branch mainline in revision 5491.
  • Revision ID: v.ladeuil+lp@free.fr-20101007060801-wfdhizfhfmctl8qa
Fix some typos and propose a release planning.

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
56
56
from itertools import izip
57
57
import operator
58
58
import os
 
59
import sys
59
60
 
60
61
from bzrlib.lazy_import import lazy_import
61
62
lazy_import(globals(), """
62
 
import gzip
63
 
 
64
63
from bzrlib import (
 
64
    annotate,
65
65
    debug,
66
66
    diff,
67
67
    graph as _mod_graph,
68
68
    index as _mod_index,
 
69
    lru_cache,
69
70
    pack,
70
71
    patiencediff,
 
72
    progress,
71
73
    static_tuple,
72
74
    trace,
73
75
    tsort,
74
76
    tuned_gzip,
75
77
    ui,
76
78
    )
77
 
 
78
 
from bzrlib.repofmt import pack_repo
79
 
from bzrlib.i18n import gettext
80
79
""")
81
80
from bzrlib import (
82
 
    annotate,
83
81
    errors,
84
82
    osutils,
85
83
    )
86
84
from bzrlib.errors import (
 
85
    FileExists,
87
86
    NoSuchFile,
 
87
    KnitError,
88
88
    InvalidRevisionId,
89
89
    KnitCorrupt,
90
90
    KnitHeaderError,
91
91
    RevisionNotPresent,
 
92
    RevisionAlreadyPresent,
92
93
    SHA1KnitCorrupt,
93
94
    )
94
95
from bzrlib.osutils import (
95
96
    contains_whitespace,
 
97
    contains_linebreaks,
96
98
    sha_string,
97
99
    sha_strings,
98
100
    split_lines,
99
101
    )
100
102
from bzrlib.versionedfile import (
101
 
    _KeyRefs,
102
103
    AbsentContentFactory,
103
104
    adapter_registry,
104
105
    ConstantMapper,
105
106
    ContentFactory,
 
107
    ChunkedContentFactory,
106
108
    sort_groupcompress,
107
 
    VersionedFilesWithFallbacks,
 
109
    VersionedFile,
 
110
    VersionedFiles,
108
111
    )
109
112
 
110
113
 
409
412
class KnitContent(object):
410
413
    """Content of a knit version to which deltas can be applied.
411
414
 
412
 
    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,
413
416
    plus a flag saying if the final ending is really there or not, because that
414
417
    corresponds to the on-disk knit representation.
415
418
    """
802
805
        writer.begin()
803
806
        index = _KnitGraphIndex(graph_index, lambda:True, parents=parents,
804
807
            deltas=delta, add_callback=graph_index.add_nodes)
805
 
        access = pack_repo._DirectPackAccess({})
 
808
        access = _DirectPackAccess({})
806
809
        access.set_writer(writer, graph_index, (transport, 'newpack'))
807
810
        result = KnitVersionedFiles(index, access,
808
811
            max_delta_chain=max_delta_chain)
846
849
                in all_build_index_memos.itervalues()])
847
850
 
848
851
 
849
 
class KnitVersionedFiles(VersionedFilesWithFallbacks):
 
852
class KnitVersionedFiles(VersionedFiles):
850
853
    """Storage for many versioned files using knit compression.
851
854
 
852
855
    Backend storage is managed by indices and data objects.
879
882
            self._factory = KnitAnnotateFactory()
880
883
        else:
881
884
            self._factory = KnitPlainFactory()
882
 
        self._immediate_fallback_vfs = []
 
885
        self._fallback_vfs = []
883
886
        self._reload_func = reload_func
884
887
 
885
888
    def __repr__(self):
888
891
            self._index,
889
892
            self._access)
890
893
 
891
 
    def without_fallbacks(self):
892
 
        """Return a clone of this object without any fallbacks configured."""
893
 
        return KnitVersionedFiles(self._index, self._access,
894
 
            self._max_delta_chain, self._factory.annotated,
895
 
            self._reload_func)
896
 
 
897
894
    def add_fallback_versioned_files(self, a_versioned_files):
898
895
        """Add a source of texts for texts not present in this knit.
899
896
 
900
897
        :param a_versioned_files: A VersionedFiles object.
901
898
        """
902
 
        self._immediate_fallback_vfs.append(a_versioned_files)
 
899
        self._fallback_vfs.append(a_versioned_files)
903
900
 
904
901
    def add_lines(self, key, parents, lines, parent_texts=None,
905
902
        left_matching_blocks=None, nostore_sha=None, random_id=False,
1072
1069
                    raise errors.KnitCorrupt(self,
1073
1070
                        "Missing basis parent %s for %s" % (
1074
1071
                        compression_parent, key))
1075
 
        for fallback_vfs in self._immediate_fallback_vfs:
 
1072
        for fallback_vfs in self._fallback_vfs:
1076
1073
            fallback_vfs.check()
1077
1074
 
1078
1075
    def _check_add(self, key, lines, random_id, check_content):
1156
1153
 
1157
1154
        A dict of key to (record_details, index_memo, next, parents) is
1158
1155
        returned.
1159
 
 
1160
 
        * method is the way referenced data should be applied.
1161
 
        * index_memo is the handle to pass to the data access to actually get
1162
 
          the data
1163
 
        * next is the build-parent of the version, or None for fulltexts.
1164
 
        * parents is the version_ids of the parents of this version
1165
 
 
1166
 
        :param allow_missing: If True do not raise an error on a missing
1167
 
            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.
1168
1164
        """
1169
1165
        component_data = {}
1170
1166
        pending_components = keys
1196
1192
        generator = _VFContentMapGenerator(self, [key])
1197
1193
        return generator._get_content(key)
1198
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
 
1199
1208
    def get_parent_map(self, keys):
1200
1209
        """Get a map of the graph parents of keys.
1201
1210
 
1216
1225
            and so on.
1217
1226
        """
1218
1227
        result = {}
1219
 
        sources = [self._index] + self._immediate_fallback_vfs
 
1228
        sources = [self._index] + self._fallback_vfs
1220
1229
        source_results = []
1221
1230
        missing = set(keys)
1222
1231
        for source in sources:
1232
1241
        """Produce a dictionary of knit records.
1233
1242
 
1234
1243
        :return: {key:(record, record_details, digest, next)}
1235
 
 
1236
 
            * record: data returned from read_records (a KnitContentobject)
1237
 
            * record_details: opaque information to pass to parse_record
1238
 
            * digest: SHA1 digest of the full text after all steps are done
1239
 
            * 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.
1240
1252
                Will be None if the record is not a delta.
1241
 
 
1242
1253
        :param keys: The keys to build a map for
1243
1254
        :param allow_missing: If some records are missing, rather than
1244
1255
            error, just return the data that could be generated.
1514
1525
                        yield KnitContentFactory(key, global_map[key],
1515
1526
                            record_details, None, raw_data, self._factory.annotated, None)
1516
1527
                else:
1517
 
                    vf = self._immediate_fallback_vfs[parent_maps.index(source) - 1]
 
1528
                    vf = self._fallback_vfs[parent_maps.index(source) - 1]
1518
1529
                    for record in vf.get_record_stream(keys, ordering,
1519
1530
                        include_delta_closure):
1520
1531
                        yield record
1530
1541
            # record entry 2 is the 'digest'.
1531
1542
            result[key] = details[2]
1532
1543
        missing.difference_update(set(result))
1533
 
        for source in self._immediate_fallback_vfs:
 
1544
        for source in self._fallback_vfs:
1534
1545
            if not missing:
1535
1546
                break
1536
1547
            new_result = source.get_sha1s(missing)
1607
1618
                raise RevisionNotPresent([record.key], self)
1608
1619
            elif ((record.storage_kind in knit_types)
1609
1620
                  and (compression_parent is None
1610
 
                       or not self._immediate_fallback_vfs
 
1621
                       or not self._fallback_vfs
1611
1622
                       or self._index.has_key(compression_parent)
1612
1623
                       or not self.has_key(compression_parent))):
1613
1624
                # we can insert the knit record literally if either it has no
1761
1772
                        key_records.append((key, details[0]))
1762
1773
                records_iter = enumerate(self._read_records_iter(key_records))
1763
1774
                for (key_idx, (key, data, sha_value)) in records_iter:
1764
 
                    pb.update(gettext('Walking content'), key_idx, total)
 
1775
                    pb.update('Walking content', key_idx, total)
1765
1776
                    compression_parent = build_details[key][1]
1766
1777
                    if compression_parent is None:
1767
1778
                        # fulltext
1785
1796
        # vfs, and hope to find them there.  Note that if the keys are found
1786
1797
        # but had no changes or no content, the fallback may not return
1787
1798
        # anything.
1788
 
        if keys and not self._immediate_fallback_vfs:
 
1799
        if keys and not self._fallback_vfs:
1789
1800
            # XXX: strictly the second parameter is meant to be the file id
1790
1801
            # but it's not easily accessible here.
1791
1802
            raise RevisionNotPresent(keys, repr(self))
1792
 
        for source in self._immediate_fallback_vfs:
 
1803
        for source in self._fallback_vfs:
1793
1804
            if not keys:
1794
1805
                break
1795
1806
            source_keys = set()
1797
1808
                source_keys.add(key)
1798
1809
                yield line, key
1799
1810
            keys.difference_update(source_keys)
1800
 
        pb.update(gettext('Walking content'), total, total)
 
1811
        pb.update('Walking content', total, total)
1801
1812
 
1802
1813
    def _make_line_delta(self, delta_seq, new_content):
1803
1814
        """Generate a line delta from delta_seq and new_content."""
1868
1879
        :return: the header and the decompressor stream.
1869
1880
                 as (stream, header_record)
1870
1881
        """
1871
 
        df = gzip.GzipFile(mode='rb', fileobj=StringIO(raw_data))
 
1882
        df = tuned_gzip.GzipFile(mode='rb', fileobj=StringIO(raw_data))
1872
1883
        try:
1873
1884
            # Current serialise
1874
1885
            rec = self._check_header(key, df.readline())
1883
1894
        # 4168 calls in 2880 217 internal
1884
1895
        # 4168 calls to _parse_record_header in 2121
1885
1896
        # 4168 calls to readlines in 330
1886
 
        df = gzip.GzipFile(mode='rb', fileobj=StringIO(data))
 
1897
        df = tuned_gzip.GzipFile(mode='rb', fileobj=StringIO(data))
1887
1898
        try:
1888
1899
            record_contents = df.readlines()
1889
1900
        except Exception, e:
1911
1922
        The result will be returned in whatever is the fastest to read.
1912
1923
        Not by the order requested. Also, multiple requests for the same
1913
1924
        record will only yield 1 response.
1914
 
 
1915
1925
        :param records: A list of (key, access_memo) entries
1916
1926
        :return: Yields (key, contents, digest) in the order
1917
1927
                 read, not the order requested
1975
1985
        :param key: The key of the record. Currently keys are always serialised
1976
1986
            using just the trailing component.
1977
1987
        :param dense_lines: The bytes of lines but in a denser form. For
1978
 
            instance, if lines is a list of 1000 bytestrings each ending in
1979
 
            \\n, dense_lines may be a list with one line in it, containing all
1980
 
            the 1000's lines and their \\n's. Using dense_lines if it is
1981
 
            already known is a win because the string join to create bytes in
1982
 
            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.
1983
1993
        :return: (len, a StringIO instance with the raw data ready to read.)
1984
1994
        """
1985
1995
        chunks = ["version %s %d %s\n" % (key[-1], len(lines), digest)]
2005
2015
        """See VersionedFiles.keys."""
2006
2016
        if 'evil' in debug.debug_flags:
2007
2017
            trace.mutter_callsite(2, "keys scales with size of history")
2008
 
        sources = [self._index] + self._immediate_fallback_vfs
 
2018
        sources = [self._index] + self._fallback_vfs
2009
2019
        result = set()
2010
2020
        for source in sources:
2011
2021
            result.update(source.keys())
2051
2061
 
2052
2062
        missing_keys = set(nonlocal_keys)
2053
2063
        # Read from remote versioned file instances and provide to our caller.
2054
 
        for source in self.vf._immediate_fallback_vfs:
 
2064
        for source in self.vf._fallback_vfs:
2055
2065
            if not missing_keys:
2056
2066
                break
2057
2067
            # Loop over fallback repositories asking them for texts - ignore
2776
2786
        return key[:-1], key[-1]
2777
2787
 
2778
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
 
2779
2847
class _KnitGraphIndex(object):
2780
2848
    """A KnitVersionedFiles index layered on GraphIndex."""
2781
2849
 
3210
3278
                yield data
3211
3279
 
3212
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
 
3213
3420
def annotate_knit(knit, revision_id):
3214
3421
    """Annotate a knit with no cached annotations.
3215
3422
 
3313
3520
        return records, ann_keys
3314
3521
 
3315
3522
    def _get_needed_texts(self, key, pb=None):
3316
 
        # if True or len(self._vf._immediate_fallback_vfs) > 0:
3317
 
        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:
3318
3525
            # If we have fallbacks, go to the generic path
3319
3526
            for v in annotate.Annotator._get_needed_texts(self, key, pb=pb):
3320
3527
                yield v
3325
3532
                for idx, (sub_key, text, num_lines) in enumerate(
3326
3533
                                                self._extract_texts(records)):
3327
3534
                    if pb is not None:
3328
 
                        pb.update(gettext('annotating'), idx, len(records))
 
3535
                        pb.update('annotating', idx, len(records))
3329
3536
                    yield sub_key, text, num_lines
3330
3537
                for sub_key in ann_keys:
3331
3538
                    text = self._text_cache[sub_key]