~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/knit.py

  • Committer: Dmitry Vasiliev
  • Date: 2006-12-01 10:44:42 UTC
  • mto: (2196.2.1 knit_index_optim)
  • mto: This revision was merged to the branch mainline in revision 2212.
  • Revision ID: dima@hlabs.spb.ru-20061201104442-57108aafbad6498c
KnitIndex tests/fixes/optimizations

 - Added _KnitIndex tests.
 - Fixed bug with undecoded version ids.
 - Extracted the knit index file parsing code from __init__() into _load_data()
   method.
 - Converted loops into list comprehensions where appropriate.
 - Localized attributes for faster access inside loops where appropriate.
 - Added forgotten import.
 - Removed trailing whitespaces.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
# Written by Martin Pool.
3
 
# Modified by Johan Rydberg <jrydberg@gnu.org>
4
 
# Modified by Robert Collins <robert.collins@canonical.com>
5
 
# Modified by Aaron Bentley <aaron.bentley@utoronto.ca>
6
2
#
7
3
# This program is free software; you can redistribute it and/or modify
8
4
# it under the terms of the GNU General Public License as published by
39
35
130,130,2
40
36
8         if elt.get('executable') == 'yes':
41
37
8             ie.executable = True
42
 
end robertc@robertcollins.net-20051003014215-ee2990904cc4c7ad 
 
38
end robertc@robertcollins.net-20051003014215-ee2990904cc4c7ad
43
39
 
44
40
 
45
41
whats in an index:
62
58
#                    always' approach.
63
59
# move sha1 out of the content so that join is faster at verifying parents
64
60
# record content length ?
65
 
                  
 
61
 
66
62
 
67
63
from copy import copy
68
64
from cStringIO import StringIO
79
75
    errors,
80
76
    patiencediff,
81
77
    progress,
82
 
    )
83
 
from bzrlib.errors import FileExists, NoSuchFile, KnitError, \
84
 
        InvalidRevisionId, KnitCorrupt, KnitHeaderError, \
85
 
        RevisionNotPresent, RevisionAlreadyPresent
 
78
    ui
 
79
    )
 
80
from bzrlib.errors import (
 
81
    FileExists,
 
82
    NoSuchFile,
 
83
    KnitError,
 
84
    InvalidRevisionId,
 
85
    KnitCorrupt,
 
86
    KnitHeaderError,
 
87
    RevisionNotPresent,
 
88
    RevisionAlreadyPresent,
 
89
    )
86
90
from bzrlib.tuned_gzip import GzipFile
87
91
from bzrlib.trace import mutter
88
 
from bzrlib.osutils import contains_whitespace, contains_linebreaks, \
89
 
     sha_strings
 
92
from bzrlib.osutils import (
 
93
    contains_whitespace,
 
94
    contains_linebreaks,
 
95
    sha_strings,
 
96
    )
90
97
from bzrlib.symbol_versioning import DEPRECATED_PARAMETER, deprecated_passed
91
98
from bzrlib.tsort import topo_sort
92
99
import bzrlib.weave
247
254
 
248
255
    def parse_line_delta(self, lines, version):
249
256
        return list(self.parse_line_delta_iter(lines, version))
250
 
    
 
257
 
251
258
    def lower_fulltext(self, content):
252
259
        return content.text()
253
260
 
285
292
                 create=False, create_parent_dir=False, delay_create=False,
286
293
                 dir_mode=None):
287
294
        """Construct a knit at location specified by relpath.
288
 
        
 
295
 
289
296
        :param create: If not True, only open an existing knit.
290
 
        :param create_parent_dir: If True, create the parent directory if 
291
 
            creating the file fails. (This is used for stores with 
 
297
        :param create_parent_dir: If True, create the parent directory if
 
298
            creating the file fails. (This is used for stores with
292
299
            hash-prefixes that may not exist yet)
293
 
        :param delay_create: The calling code is aware that the knit won't 
 
300
        :param delay_create: The calling code is aware that the knit won't
294
301
            actually be created until the first data is stored.
295
302
        """
296
303
        if deprecated_passed(basis_knit):
319
326
            dir_mode=dir_mode)
320
327
 
321
328
    def __repr__(self):
322
 
        return '%s(%s)' % (self.__class__.__name__, 
 
329
        return '%s(%s)' % (self.__class__.__name__,
323
330
                           self.transport.abspath(self.filename))
324
 
    
 
331
 
325
332
    def _check_should_delta(self, first_parents):
326
333
        """Iterate back through the parent listing, looking for a fulltext.
327
334
 
452
459
    def create_empty(self, name, transport, mode=None):
453
460
        return KnitVersionedFile(name, transport, factory=self.factory,
454
461
                                 delta=self.delta, create=True)
455
 
    
 
462
 
456
463
    def _fix_parents(self, version, new_parents):
457
464
        """Fix the parents list for version.
458
 
        
 
465
 
459
466
        This is done by appending a new version to the index
460
467
        with identical data except for the parents list.
461
468
        the parents list must be a superset of the current
464
471
        current_values = self._index._cache[version]
465
472
        assert set(current_values[4]).difference(set(new_parents)) == set()
466
473
        self._index.add_version(version,
467
 
                                current_values[1], 
 
474
                                current_values[1],
468
475
                                current_values[2],
469
476
                                current_values[3],
470
477
                                new_parents)
473
480
        """Get a delta for constructing version from some other version."""
474
481
        if not self.has_version(version_id):
475
482
            raise RevisionNotPresent(version_id, self.filename)
476
 
        
 
483
 
477
484
        parents = self.get_parents(version_id)
478
485
        if len(parents):
479
486
            parent = parents[0]
496
503
        else:
497
504
            delta = self.factory.parse_line_delta(data, version_idx)
498
505
            return parent, sha1, noeol, delta
499
 
        
 
506
 
500
507
    def get_graph_with_ghosts(self):
501
508
        """See VersionedFile.get_graph_with_ghosts()."""
502
509
        graph_items = self._index.get_graph()
506
513
        """See VersionedFile.get_sha1()."""
507
514
        record_map = self._get_record_map([version_id])
508
515
        method, content, digest, next = record_map[version_id]
509
 
        return digest 
 
516
        return digest
510
517
 
511
518
    @staticmethod
512
519
    def get_suffixes():
603
610
                component_data[cursor] = (method, data_pos, data_size, next)
604
611
                cursor = next
605
612
        return component_data
606
 
       
 
613
 
607
614
    def _get_content(self, version_id, parent_texts={}):
608
615
        """Returns a content object that makes up the specified
609
616
        version."""
619
626
 
620
627
    def _check_versions_present(self, version_ids):
621
628
        """Check that all specified versions are present."""
622
 
        version_ids = set(version_ids)
623
 
        for r in list(version_ids):
624
 
            if self._index.has_version(r):
625
 
                version_ids.remove(r)
626
 
        if version_ids:
627
 
            raise RevisionNotPresent(list(version_ids)[0], self.filename)
 
629
        self._index.check_versions_present(version_ids)
628
630
 
629
631
    def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts):
630
632
        """See VersionedFile.add_lines_with_ghosts()."""
726
728
 
727
729
    def _get_record_map(self, version_ids):
728
730
        """Produce a dictionary of knit records.
729
 
        
 
731
 
730
732
        The keys are version_ids, the values are tuples of (method, content,
731
733
        digest, next).
732
 
        method is the way the content should be applied.  
 
734
        method is the way the content should be applied.
733
735
        content is a KnitContent object.
734
736
        digest is the SHA1 digest of this version id after all steps are done
735
737
        next is the build-parent of the version, i.e. the leftmost ancestor.
743
745
                self._data.read_records_iter(records):
744
746
            method, position, size, next = position_map[component_id]
745
747
            record_map[component_id] = method, content, digest, next
746
 
                          
 
748
 
747
749
        return record_map
748
750
 
749
751
    def get_text(self, version_id):
760
762
 
761
763
    def _get_content_maps(self, version_ids):
762
764
        """Produce maps of text and KnitContents
763
 
        
 
765
 
764
766
        :return: (text_map, content_map) where text_map contains the texts for
765
767
        the requested versions and content_map contains the KnitContents.
766
768
        Both dicts take version_ids as their keys.
793
795
                        assert content is None
794
796
                        content = self.factory.parse_fulltext(data, version_idx)
795
797
                    elif method == 'line-delta':
796
 
                        delta = self.factory.parse_line_delta(data[:], 
 
798
                        delta = self.factory.parse_line_delta(data[:],
797
799
                                                              version_idx)
798
800
                        content = content.copy()
799
 
                        content._lines = self._apply_delta(content._lines, 
 
801
                        content._lines = self._apply_delta(content._lines,
800
802
                                                           delta)
801
803
                    content_map[component_id] = content
802
804
 
809
811
            # digest here is the digest from the last applied component.
810
812
            text = content.text()
811
813
            if sha_strings(text) != digest:
812
 
                raise KnitCorrupt(self.filename, 
 
814
                raise KnitCorrupt(self.filename,
813
815
                                  'sha-1 does not match %s' % version_id)
814
816
 
815
 
            text_map[version_id] = text 
816
 
        return text_map, final_content 
 
817
            text_map[version_id] = text
 
818
        return text_map, final_content
817
819
 
818
 
    def iter_lines_added_or_present_in_versions(self, version_ids=None, 
 
820
    def iter_lines_added_or_present_in_versions(self, version_ids=None,
819
821
                                                pb=None):
820
822
        """See VersionedFile.iter_lines_added_or_present_in_versions()."""
821
823
        if version_ids is None:
856
858
                    for origin, line in lines:
857
859
                        yield line
858
860
        pb.update('Walking content.', total, total)
859
 
        
 
861
 
860
862
    def num_versions(self):
861
863
        """See VersionedFile.num_versions()."""
862
864
        return self._index.num_versions()
892
894
            versions = [versions]
893
895
        if not versions:
894
896
            return []
895
 
        self._check_versions_present(versions)
896
897
        return self._index.get_ancestry(versions)
897
898
 
898
899
    def get_ancestry_with_ghosts(self, versions):
901
902
            versions = [versions]
902
903
        if not versions:
903
904
            return []
904
 
        self._check_versions_present(versions)
905
905
        return self._index.get_ancestry_with_ghosts(versions)
906
906
 
907
907
    #@deprecated_method(zero_eight)
917
917
        ancestry = self.get_ancestry(version_ids)
918
918
        sorted_graph = topo_sort(self._index.get_graph())
919
919
        version_list = [vid for vid in sorted_graph if vid in ancestry]
920
 
        
 
920
 
921
921
        for version_id in version_list:
922
922
            lines = self.get_lines(version_id)
923
923
            w.add_lines(version_id, self.get_parents(version_id), lines)
933
933
                return 'killed-b', text
934
934
            else:
935
935
                return 'new-a', text
936
 
        
 
936
 
937
937
        ancestors_a = set(self.get_ancestry(ver_a))
938
938
        def status_b(revision, text):
939
939
            if revision in ancestors_a:
1009
1009
 
1010
1010
    Duplicate entries may be written to the index for a single version id
1011
1011
    if this is done then the latter one completely replaces the former:
1012
 
    this allows updates to correct version and parent information. 
 
1012
    this allows updates to correct version and parent information.
1013
1013
    Note that the two entries may share the delta, and that successive
1014
1014
    annotations and references MUST point to the first entry.
1015
1015
 
1016
1016
    The index file on disc contains a header, followed by one line per knit
1017
1017
    record. The same revision can be present in an index file more than once.
1018
 
    The first occurrence gets assigned a sequence number starting from 0. 
1019
 
    
 
1018
    The first occurrence gets assigned a sequence number starting from 0.
 
1019
 
1020
1020
    The format of a single line is
1021
1021
    REVISION_ID FLAGS BYTE_OFFSET LENGTH( PARENT_ID|PARENT_SEQUENCE_ID)* :\n
1022
1022
    REVISION_ID is a utf8-encoded revision id
1023
 
    FLAGS is a comma separated list of flags about the record. Values include 
 
1023
    FLAGS is a comma separated list of flags about the record. Values include
1024
1024
        no-eol, line-delta, fulltext.
1025
1025
    BYTE_OFFSET is the ascii representation of the byte offset in the data file
1026
1026
        that the the compressed data starts at.
1030
1030
    PARENT_SEQUENCE_ID the ascii representation of the sequence number of a
1031
1031
        revision id already in the knit that is a parent of REVISION_ID.
1032
1032
    The ' :' marker is the end of record marker.
1033
 
    
 
1033
 
1034
1034
    partial writes:
1035
 
    when a write is interrupted to the index file, it will result in a line that
1036
 
    does not end in ' :'. If the ' :' is not present at the end of a line, or at
1037
 
    the end of the file, then the record that is missing it will be ignored by
1038
 
    the parser.
 
1035
    when a write is interrupted to the index file, it will result in a line
 
1036
    that does not end in ' :'. If the ' :' is not present at the end of a line,
 
1037
    or at the end of the file, then the record that is missing it will be
 
1038
    ignored by the parser.
1039
1039
 
1040
1040
    When writing new records to the index file, the data is preceded by '\n'
1041
1041
    to ensure that records always start on new lines even if the last write was
1050
1050
 
1051
1051
    def _cache_version(self, version_id, options, pos, size, parents):
1052
1052
        """Cache a version record in the history array and index cache.
1053
 
        
1054
 
        This is inlined into __init__ for performance. KEEP IN SYNC.
 
1053
 
 
1054
        This is inlined into _load_data for performance. KEEP IN SYNC.
1055
1055
        (It saves 60ms, 25% of the __init__ overhead on local 4000 record
1056
1056
         indexes).
1057
1057
        """
1062
1062
            self._history.append(version_id)
1063
1063
        else:
1064
1064
            index = self._cache[version_id][5]
1065
 
        self._cache[version_id] = (version_id, 
 
1065
        self._cache[version_id] = (version_id,
1066
1066
                                   options,
1067
1067
                                   pos,
1068
1068
                                   size,
1081
1081
        # so - wc -l of a knit index is != the number of unique names
1082
1082
        # in the knit.
1083
1083
        self._history = []
1084
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1084
        pb = ui.ui_factory.nested_progress_bar()
1085
1085
        try:
1086
 
            count = 0
1087
 
            total = 1
 
1086
            pb.update('read knit index', 0, 1)
1088
1087
            try:
1089
 
                pb.update('read knit index', count, total)
1090
1088
                fp = self._transport.get(self._filename)
 
1089
            except NoSuchFile:
 
1090
                if mode != 'w' or not create:
 
1091
                    raise
 
1092
                elif delay_create:
 
1093
                    self._need_to_create = True
 
1094
                else:
 
1095
                    self._transport.put_bytes_non_atomic(
 
1096
                        self._filename, self.HEADER, mode=self._file_mode)
 
1097
            else:
1091
1098
                try:
1092
 
                    self.check_header(fp)
1093
 
                    # readlines reads the whole file at once:
1094
 
                    # bad for transports like http, good for local disk
1095
 
                    # we save 60 ms doing this one change (
1096
 
                    # from calling readline each time to calling
1097
 
                    # readlines once.
1098
 
                    # probably what we want for nice behaviour on
1099
 
                    # http is a incremental readlines that yields, or
1100
 
                    # a check for local vs non local indexes,
1101
 
                    for l in fp.readlines():
1102
 
                        rec = l.split()
1103
 
                        if len(rec) < 5 or rec[-1] != ':':
1104
 
                            # corrupt line.
1105
 
                            # FIXME: in the future we should determine if its a
1106
 
                            # short write - and ignore it 
1107
 
                            # or a different failure, and raise. RBC 20060407
1108
 
                            continue
1109
 
                        count += 1
1110
 
                        total += 1
1111
 
                        #pb.update('read knit index', count, total)
1112
 
                        # See self._parse_parents
1113
 
                        parents = []
1114
 
                        for value in rec[4:-1]:
1115
 
                            if '.' == value[0]:
1116
 
                                # uncompressed reference
1117
 
                                parents.append(value[1:])
1118
 
                            else:
1119
 
                                # this is 15/4000ms faster than isinstance,
1120
 
                                # (in lsprof)
1121
 
                                # this function is called thousands of times a 
1122
 
                                # second so small variations add up.
1123
 
                                assert value.__class__ is str
1124
 
                                parents.append(self._history[int(value)])
1125
 
                        # end self._parse_parents
1126
 
                        # self._cache_version(rec[0], 
1127
 
                        #                     rec[1].split(','),
1128
 
                        #                     int(rec[2]),
1129
 
                        #                     int(rec[3]),
1130
 
                        #                     parents)
1131
 
                        # --- self._cache_version
1132
 
                        # only want the _history index to reference the 1st 
1133
 
                        # index entry for version_id
1134
 
                        version_id = rec[0]
1135
 
                        if version_id not in self._cache:
1136
 
                            index = len(self._history)
1137
 
                            self._history.append(version_id)
1138
 
                        else:
1139
 
                            index = self._cache[version_id][5]
1140
 
                        self._cache[version_id] = (version_id,
1141
 
                                                   rec[1].split(','),
1142
 
                                                   int(rec[2]),
1143
 
                                                   int(rec[3]),
1144
 
                                                   parents,
1145
 
                                                   index)
1146
 
                        # --- self._cache_version 
 
1099
                    self._load_data(fp)
1147
1100
                finally:
1148
1101
                    fp.close()
1149
 
            except NoSuchFile, e:
1150
 
                if mode != 'w' or not create:
1151
 
                    raise
1152
 
                if delay_create:
1153
 
                    self._need_to_create = True
1154
 
                else:
1155
 
                    self._transport.put_bytes_non_atomic(self._filename,
1156
 
                        self.HEADER, mode=self._file_mode)
1157
 
 
1158
1102
        finally:
1159
 
            pb.update('read knit index', total, total)
 
1103
            pb.update('read knit index', 1, 1)
1160
1104
            pb.finished()
1161
1105
 
1162
 
    def _parse_parents(self, compressed_parents):
1163
 
        """convert a list of string parent values into version ids.
1164
 
 
1165
 
        ints are looked up in the index.
1166
 
        .FOO values are ghosts and converted in to FOO.
1167
 
 
1168
 
        NOTE: the function is retained here for clarity, and for possible
1169
 
              use in partial index reads. However bulk processing now has
1170
 
              it inlined in __init__ for inner-loop optimisation.
1171
 
        """
1172
 
        result = []
1173
 
        for value in compressed_parents:
1174
 
            if value[-1] == '.':
1175
 
                # uncompressed reference
1176
 
                result.append(value[1:])
 
1106
    def _load_data(self, fp):
 
1107
        cache = self._cache
 
1108
        history = self._history
 
1109
        decode_utf8 = cache_utf8.decode
 
1110
 
 
1111
        self.check_header(fp)
 
1112
        # readlines reads the whole file at once:
 
1113
        # bad for transports like http, good for local disk
 
1114
        # we save 60 ms doing this one change (
 
1115
        # from calling readline each time to calling
 
1116
        # readlines once.
 
1117
        # probably what we want for nice behaviour on
 
1118
        # http is a incremental readlines that yields, or
 
1119
        # a check for local vs non local indexes,
 
1120
        history_top = len(history) - 1
 
1121
        for line in fp.readlines():
 
1122
            rec = line.split()
 
1123
            if len(rec) < 5 or rec[-1] != ':':
 
1124
                # corrupt line.
 
1125
                # FIXME: in the future we should determine if its a
 
1126
                # short write - and ignore it 
 
1127
                # or a different failure, and raise. RBC 20060407
 
1128
                continue
 
1129
 
 
1130
            parents = []
 
1131
            for value in rec[4:-1]:
 
1132
                if value[0] == '.':
 
1133
                    # uncompressed reference
 
1134
                    parents.append(decode_utf8(value[1:]))
 
1135
                else:
 
1136
                    parents.append(history[int(value)])
 
1137
 
 
1138
            version_id, options, pos, size = rec[:4]
 
1139
            version_id = decode_utf8(version_id)
 
1140
 
 
1141
            # See self._cache_version
 
1142
            # only want the _history index to reference the 1st 
 
1143
            # index entry for version_id
 
1144
            if version_id not in cache:
 
1145
                history_top += 1
 
1146
                index = history_top
 
1147
                history.append(version_id)
1177
1148
            else:
1178
 
                # this is 15/4000ms faster than isinstance,
1179
 
                # this function is called thousands of times a 
1180
 
                # second so small variations add up.
1181
 
                assert value.__class__ is str
1182
 
                result.append(self._history[int(value)])
1183
 
        return result
 
1149
                index = cache[version_id][5]
 
1150
            cache[version_id] = (version_id,
 
1151
                                 options.split(','),
 
1152
                                 int(pos),
 
1153
                                 int(size),
 
1154
                                 parents,
 
1155
                                 index)
 
1156
            # end self._cache_version 
1184
1157
 
1185
1158
    def get_graph(self):
1186
 
        graph = []
1187
 
        for version_id, index in self._cache.iteritems():
1188
 
            graph.append((version_id, index[4]))
1189
 
        return graph
 
1159
        return [(vid, idx[4]) for vid, idx in self._cache.iteritems()]
1190
1160
 
1191
1161
    def get_ancestry(self, versions):
1192
1162
        """See VersionedFile.get_ancestry."""
1193
1163
        # get a graph of all the mentioned versions:
1194
1164
        graph = {}
1195
1165
        pending = set(versions)
1196
 
        while len(pending):
 
1166
        cache = self._cache
 
1167
        while pending:
1197
1168
            version = pending.pop()
1198
 
            parents = self._cache[version][4]
1199
 
            # got the parents ok
1200
1169
            # trim ghosts
1201
 
            parents = [parent for parent in parents if parent in self._cache]
1202
 
            for parent in parents:
1203
 
                # if not completed and not a ghost
1204
 
                if parent not in graph:
1205
 
                    pending.add(parent)
 
1170
            try:
 
1171
                parents = [p for p in cache[version][4] if p in cache]
 
1172
            except KeyError:
 
1173
                raise RevisionNotPresent(version, self._filename)
 
1174
            # if not completed and not a ghost
 
1175
            pending.update([p for p in parents if p not in graph])
1206
1176
            graph[version] = parents
1207
1177
        return topo_sort(graph.items())
1208
1178
 
1209
1179
    def get_ancestry_with_ghosts(self, versions):
1210
1180
        """See VersionedFile.get_ancestry_with_ghosts."""
1211
1181
        # get a graph of all the mentioned versions:
 
1182
        self.check_versions_present(versions)
 
1183
        cache = self._cache
1212
1184
        graph = {}
1213
1185
        pending = set(versions)
1214
 
        while len(pending):
 
1186
        while pending:
1215
1187
            version = pending.pop()
1216
1188
            try:
1217
 
                parents = self._cache[version][4]
 
1189
                parents = cache[version][4]
1218
1190
            except KeyError:
1219
1191
                # ghost, fake it
1220
1192
                graph[version] = []
1221
 
                pass
1222
1193
            else:
1223
 
                # got the parents ok
1224
 
                for parent in parents:
1225
 
                    if parent not in graph:
1226
 
                        pending.add(parent)
 
1194
                # if not completed
 
1195
                pending.update([p for p in parents if p not in graph])
1227
1196
                graph[version] = parents
1228
1197
        return topo_sort(graph.items())
1229
1198
 
1245
1214
    def _version_list_to_index(self, versions):
1246
1215
        encode_utf8 = cache_utf8.encode
1247
1216
        result_list = []
 
1217
        cache = self._cache
1248
1218
        for version in versions:
1249
 
            if version in self._cache:
 
1219
            if version in cache:
1250
1220
                # -- inlined lookup() --
1251
 
                result_list.append(str(self._cache[version][5]))
 
1221
                result_list.append(str(cache[version][5]))
1252
1222
                # -- end lookup () --
1253
1223
            else:
1254
1224
                result_list.append('.' + encode_utf8(version))
1260
1230
 
1261
1231
    def add_versions(self, versions):
1262
1232
        """Add multiple versions to the index.
1263
 
        
 
1233
 
1264
1234
        :param versions: a list of tuples:
1265
1235
                         (version_id, options, pos, size, parents).
1266
1236
        """
1300
1270
 
1301
1271
    def has_version(self, version_id):
1302
1272
        """True if the version is in the index."""
1303
 
        return (version_id in self._cache)
 
1273
        return version_id in self._cache
1304
1274
 
1305
1275
    def get_position(self, version_id):
1306
1276
        """Return data position and size of specified version."""
1307
 
        return (self._cache[version_id][2], \
1308
 
                self._cache[version_id][3])
 
1277
        entry = self._cache[version_id]
 
1278
        return entry[2], entry[3]
1309
1279
 
1310
1280
    def get_method(self, version_id):
1311
1281
        """Return compression method of specified version."""
1321
1291
 
1322
1292
    def get_parents(self, version_id):
1323
1293
        """Return parents of specified version ignoring ghosts."""
1324
 
        return [parent for parent in self._cache[version_id][4] 
 
1294
        return [parent for parent in self._cache[version_id][4]
1325
1295
                if parent in self._cache]
1326
1296
 
1327
1297
    def get_parents_with_ghosts(self, version_id):
1328
1298
        """Return parents of specified version with ghosts."""
1329
 
        return self._cache[version_id][4] 
 
1299
        return self._cache[version_id][4]
1330
1300
 
1331
1301
    def check_versions_present(self, version_ids):
1332
1302
        """Check that all specified versions are present."""
1333
 
        version_ids = set(version_ids)
1334
 
        for version_id in list(version_ids):
1335
 
            if version_id in self._cache:
1336
 
                version_ids.remove(version_id)
1337
 
        if version_ids:
1338
 
            raise RevisionNotPresent(list(version_ids)[0], self.filename)
 
1303
        cache = self._cache
 
1304
        for version_id in version_ids:
 
1305
            if version_id not in cache:
 
1306
                raise RevisionNotPresent(version_id, self._filename)
1339
1307
 
1340
1308
 
1341
1309
class _KnitData(_KnitComponentFile):
1380
1348
 
1381
1349
    def _record_to_data(self, version_id, digest, lines):
1382
1350
        """Convert version_id, digest, lines into a raw data block.
1383
 
        
 
1351
 
1384
1352
        :return: (len, a StringIO instance with the raw data ready to read.)
1385
1353
        """
1386
1354
        sio = StringIO()
1401
1369
 
1402
1370
    def add_raw_record(self, raw_data):
1403
1371
        """Append a prepared record to the data file.
1404
 
        
 
1372
 
1405
1373
        :return: the offset in the data file raw_data was written.
1406
1374
        """
1407
1375
        assert isinstance(raw_data, str), 'data must be plain bytes'
1414
1382
                                   dir_mode=self._dir_mode)
1415
1383
            self._need_to_create = False
1416
1384
            return 0
1417
 
        
 
1385
 
1418
1386
    def add_record(self, version_id, digest, lines):
1419
1387
        """Write new text record to disk.  Returns the position in the
1420
1388
        file where it was written."""
1444
1412
        if len(rec) != 4:
1445
1413
            raise KnitCorrupt(self._filename, 'unexpected number of elements in record header')
1446
1414
        if cache_utf8.decode(rec[1]) != version_id:
1447
 
            raise KnitCorrupt(self._filename, 
 
1415
            raise KnitCorrupt(self._filename,
1448
1416
                              'unexpected version, wanted %r, got %r' % (
1449
1417
                                version_id, rec[1]))
1450
1418
        return df, rec
1484
1452
                                               in records]
1485
1453
 
1486
1454
            raw_records = self._transport.readv(self._filename, needed_offsets)
1487
 
                
1488
1455
 
1489
1456
        for version_id, pos, size in records:
1490
1457
            if version_id in self._cache:
1557
1524
 
1558
1525
class InterKnit(InterVersionedFile):
1559
1526
    """Optimised code paths for knit to knit operations."""
1560
 
    
 
1527
 
1561
1528
    _matching_file_from_factory = KnitVersionedFile
1562
1529
    _matching_file_to_factory = KnitVersionedFile
1563
 
    
 
1530
 
1564
1531
    @staticmethod
1565
1532
    def is_compatible(source, target):
1566
1533
        """Be compatible with knits.  """
1580
1547
        if not version_ids:
1581
1548
            return 0
1582
1549
 
1583
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1550
        pb = ui.ui_factory.nested_progress_bar()
1584
1551
        try:
1585
1552
            version_ids = list(version_ids)
1586
1553
            if None in version_ids:
1587
1554
                version_ids.remove(None)
1588
 
    
 
1555
 
1589
1556
            self.source_ancestry = set(self.source.get_ancestry(version_ids))
1590
1557
            this_versions = set(self.target._index.get_versions())
1591
1558
            needed_versions = self.source_ancestry - this_versions
1611
1578
                    new_parents = n2.difference(n1)
1612
1579
                    needed_versions.update(new_parents.difference(this_versions))
1613
1580
                    mismatched_versions.add(version)
1614
 
    
 
1581
 
1615
1582
            if not needed_versions and not mismatched_versions:
1616
1583
                return 0
1617
1584
            full_list = topo_sort(self.source.get_graph())
1618
 
    
 
1585
 
1619
1586
            version_list = [i for i in full_list if (not self.target.has_version(i)
1620
1587
                            and i in needed_versions)]
1621
 
    
 
1588
 
1622
1589
            # plan the join:
1623
1590
            copy_queue = []
1624
1591
            copy_queue_records = []
1674
1641
 
1675
1642
class WeaveToKnit(InterVersionedFile):
1676
1643
    """Optimised code paths for weave to knit operations."""
1677
 
    
 
1644
 
1678
1645
    _matching_file_from_factory = bzrlib.weave.WeaveFile
1679
1646
    _matching_file_to_factory = KnitVersionedFile
1680
 
    
 
1647
 
1681
1648
    @staticmethod
1682
1649
    def is_compatible(source, target):
1683
1650
        """Be compatible with weaves to knits."""
1697
1664
        if not version_ids:
1698
1665
            return 0
1699
1666
 
1700
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1667
        pb = ui.ui_factory.nested_progress_bar()
1701
1668
        try:
1702
1669
            version_ids = list(version_ids)
1703
 
    
 
1670
 
1704
1671
            self.source_ancestry = set(self.source.get_ancestry(version_ids))
1705
1672
            this_versions = set(self.target._index.get_versions())
1706
1673
            needed_versions = self.source_ancestry - this_versions
1727
1694
                    new_parents = n2.difference(n1)
1728
1695
                    needed_versions.update(new_parents.difference(this_versions))
1729
1696
                    mismatched_versions.add(version)
1730
 
    
 
1697
 
1731
1698
            if not needed_versions and not mismatched_versions:
1732
1699
                return 0
1733
1700
            full_list = topo_sort(self.source.get_graph())
1734
 
    
 
1701
 
1735
1702
            version_list = [i for i in full_list if (not self.target.has_version(i)
1736
1703
                            and i in needed_versions)]
1737
 
    
 
1704
 
1738
1705
            # do the join:
1739
1706
            count = 0
1740
1707
            total = len(version_list)
1841
1808
            # b2j has no junk keys, the loop is skipped if a[i] is junk
1842
1809
            j2lenget = j2len.get
1843
1810
            newj2len = {}
1844
 
            
 
1811
 
1845
1812
            # changing b2j.get(a[i], nothing) to a try:KeyError pair produced the
1846
1813
            # following improvement
1847
1814
            #     704  0   4650.5320   2620.7410   bzrlib.knit:1336(find_longest_match)
1897
1864
            bestsize = bestsize + 1
1898
1865
 
1899
1866
        return besti, bestj, bestsize
1900