~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
2
 
#
3
 
# Authors:
4
 
#   Johan Rydberg <jrydberg@gnu.org>
 
1
# Copyright (C) 2006-2011 Canonical Ltd
5
2
#
6
3
# This program is free software; you can redistribute it and/or modify
7
4
# it under the terms of the GNU General Public License as published by
15
12
#
16
13
# You should have received a copy of the GNU General Public License
17
14
# along with this program; if not, write to the Free Software
18
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
16
 
20
17
"""Versioned text file storage api."""
21
18
 
22
19
from copy import copy
23
20
from cStringIO import StringIO
24
21
import os
 
22
import struct
25
23
from zlib import adler32
26
24
 
27
25
from bzrlib.lazy_import import lazy_import
29
27
import urllib
30
28
 
31
29
from bzrlib import (
 
30
    annotate,
 
31
    bencode,
32
32
    errors,
 
33
    graph as _mod_graph,
 
34
    groupcompress,
33
35
    index,
 
36
    knit,
34
37
    osutils,
35
38
    multiparent,
36
39
    tsort,
37
40
    revision,
38
 
    ui,
39
41
    )
40
 
from bzrlib.graph import DictParentsProvider, Graph, _StackedParentsProvider
41
 
from bzrlib.transport.memory import MemoryTransport
42
42
""")
43
 
from bzrlib.inter import InterObject
44
43
from bzrlib.registry import Registry
45
 
from bzrlib.symbol_versioning import *
46
44
from bzrlib.textmerge import TextMerge
47
45
 
48
46
 
65
63
 
66
64
class ContentFactory(object):
67
65
    """Abstract interface for insertion and retrieval from a VersionedFile.
68
 
    
 
66
 
69
67
    :ivar sha1: None, or the sha1 of the content fulltext.
70
68
    :ivar storage_kind: The native storage kind of this factory. One of
71
69
        'mpdiff', 'knit-annotated-ft', 'knit-annotated-delta', 'knit-ft',
155
153
 
156
154
class AbsentContentFactory(ContentFactory):
157
155
    """A placeholder content factory for unavailable texts.
158
 
    
 
156
 
159
157
    :ivar sha1: None.
160
158
    :ivar storage_kind: 'absent'.
161
159
    :ivar key: The key of this content. Each key is a tuple with a single
170
168
        self.key = key
171
169
        self.parents = None
172
170
 
 
171
    def get_bytes_as(self, storage_kind):
 
172
        raise ValueError('A request was made for key: %s, but that'
 
173
                         ' content is not available, and the calling'
 
174
                         ' code does not handle if it is missing.'
 
175
                         % (self.key,))
 
176
 
173
177
 
174
178
class AdapterFactory(ContentFactory):
175
179
    """A content factory to adapt between key prefix's."""
195
199
            yield record
196
200
 
197
201
 
 
202
class _MPDiffGenerator(object):
 
203
    """Pull out the functionality for generating mp_diffs."""
 
204
 
 
205
    def __init__(self, vf, keys):
 
206
        self.vf = vf
 
207
        # This is the order the keys were requested in
 
208
        self.ordered_keys = tuple(keys)
 
209
        # keys + their parents, what we need to compute the diffs
 
210
        self.needed_keys = ()
 
211
        # Map from key: mp_diff
 
212
        self.diffs = {}
 
213
        # Map from key: parents_needed (may have ghosts)
 
214
        self.parent_map = {}
 
215
        # Parents that aren't present
 
216
        self.ghost_parents = ()
 
217
        # Map from parent_key => number of children for this text
 
218
        self.refcounts = {}
 
219
        # Content chunks that are cached while we still need them
 
220
        self.chunks = {}
 
221
 
 
222
    def _find_needed_keys(self):
 
223
        """Find the set of keys we need to request.
 
224
 
 
225
        This includes all the original keys passed in, and the non-ghost
 
226
        parents of those keys.
 
227
 
 
228
        :return: (needed_keys, refcounts)
 
229
            needed_keys is the set of all texts we need to extract
 
230
            refcounts is a dict of {key: num_children} letting us know when we
 
231
                no longer need to cache a given parent text
 
232
        """
 
233
        # All the keys and their parents
 
234
        needed_keys = set(self.ordered_keys)
 
235
        parent_map = self.vf.get_parent_map(needed_keys)
 
236
        self.parent_map = parent_map
 
237
        # TODO: Should we be using a different construct here? I think this
 
238
        #       uses difference_update internally, and we expect the result to
 
239
        #       be tiny
 
240
        missing_keys = needed_keys.difference(parent_map)
 
241
        if missing_keys:
 
242
            raise errors.RevisionNotPresent(list(missing_keys)[0], self.vf)
 
243
        # Parents that might be missing. They are allowed to be ghosts, but we
 
244
        # should check for them
 
245
        refcounts = {}
 
246
        setdefault = refcounts.setdefault
 
247
        just_parents = set()
 
248
        for child_key, parent_keys in parent_map.iteritems():
 
249
            if not parent_keys:
 
250
                # parent_keys may be None if a given VersionedFile claims to
 
251
                # not support graph operations.
 
252
                continue
 
253
            just_parents.update(parent_keys)
 
254
            needed_keys.update(parent_keys)
 
255
            for p in parent_keys:
 
256
                refcounts[p] = setdefault(p, 0) + 1
 
257
        just_parents.difference_update(parent_map)
 
258
        # Remove any parents that are actually ghosts from the needed set
 
259
        self.present_parents = set(self.vf.get_parent_map(just_parents))
 
260
        self.ghost_parents = just_parents.difference(self.present_parents)
 
261
        needed_keys.difference_update(self.ghost_parents)
 
262
        self.needed_keys = needed_keys
 
263
        self.refcounts = refcounts
 
264
        return needed_keys, refcounts
 
265
 
 
266
    def _compute_diff(self, key, parent_lines, lines):
 
267
        """Compute a single mp_diff, and store it in self._diffs"""
 
268
        if len(parent_lines) > 0:
 
269
            # XXX: _extract_blocks is not usefully defined anywhere...
 
270
            #      It was meant to extract the left-parent diff without
 
271
            #      having to recompute it for Knit content (pack-0.92,
 
272
            #      etc). That seems to have regressed somewhere
 
273
            left_parent_blocks = self.vf._extract_blocks(key,
 
274
                parent_lines[0], lines)
 
275
        else:
 
276
            left_parent_blocks = None
 
277
        diff = multiparent.MultiParent.from_lines(lines,
 
278
                    parent_lines, left_parent_blocks)
 
279
        self.diffs[key] = diff
 
280
 
 
281
    def _process_one_record(self, key, this_chunks):
 
282
        parent_keys = None
 
283
        if key in self.parent_map:
 
284
            # This record should be ready to diff, since we requested
 
285
            # content in 'topological' order
 
286
            parent_keys = self.parent_map.pop(key)
 
287
            # If a VersionedFile claims 'no-graph' support, then it may return
 
288
            # None for any parent request, so we replace it with an empty tuple
 
289
            if parent_keys is None:
 
290
                parent_keys = ()
 
291
            parent_lines = []
 
292
            for p in parent_keys:
 
293
                # Alternatively we could check p not in self.needed_keys, but
 
294
                # ghost_parents should be tiny versus huge
 
295
                if p in self.ghost_parents:
 
296
                    continue
 
297
                refcount = self.refcounts[p]
 
298
                if refcount == 1: # Last child reference
 
299
                    self.refcounts.pop(p)
 
300
                    parent_chunks = self.chunks.pop(p)
 
301
                else:
 
302
                    self.refcounts[p] = refcount - 1
 
303
                    parent_chunks = self.chunks[p]
 
304
                p_lines = osutils.chunks_to_lines(parent_chunks)
 
305
                # TODO: Should we cache the line form? We did the
 
306
                #       computation to get it, but storing it this way will
 
307
                #       be less memory efficient...
 
308
                parent_lines.append(p_lines)
 
309
                del p_lines
 
310
            lines = osutils.chunks_to_lines(this_chunks)
 
311
            # Since we needed the lines, we'll go ahead and cache them this way
 
312
            this_chunks = lines
 
313
            self._compute_diff(key, parent_lines, lines)
 
314
            del lines
 
315
        # Is this content required for any more children?
 
316
        if key in self.refcounts:
 
317
            self.chunks[key] = this_chunks
 
318
 
 
319
    def _extract_diffs(self):
 
320
        needed_keys, refcounts = self._find_needed_keys()
 
321
        for record in self.vf.get_record_stream(needed_keys,
 
322
                                                'topological', True):
 
323
            if record.storage_kind == 'absent':
 
324
                raise errors.RevisionNotPresent(record.key, self.vf)
 
325
            self._process_one_record(record.key,
 
326
                                     record.get_bytes_as('chunked'))
 
327
        
 
328
    def compute_diffs(self):
 
329
        self._extract_diffs()
 
330
        dpop = self.diffs.pop
 
331
        return [dpop(k) for k in self.ordered_keys]
 
332
 
 
333
 
198
334
class VersionedFile(object):
199
335
    """Versioned text file storage.
200
 
    
 
336
 
201
337
    A versioned file manages versions of line-based text files,
202
338
    keeping track of the originating version for each line.
203
339
 
241
377
    def insert_record_stream(self, stream):
242
378
        """Insert a record stream into this versioned file.
243
379
 
244
 
        :param stream: A stream of records to insert. 
 
380
        :param stream: A stream of records to insert.
245
381
        :return: None
246
382
        :seealso VersionedFile.get_record_stream:
247
383
        """
266
402
            the data back accurately. (Checking the lines have been split
267
403
            correctly is expensive and extremely unlikely to catch bugs so it
268
404
            is not done at runtime unless check_content is True.)
269
 
        :param parent_texts: An optional dictionary containing the opaque 
 
405
        :param parent_texts: An optional dictionary containing the opaque
270
406
            representations of some or all of the parents of version_id to
271
407
            allow delta optimisations.  VERY IMPORTANT: the texts must be those
272
408
            returned by add_lines or data corruption can be caused.
300
436
        parent_texts=None, nostore_sha=None, random_id=False,
301
437
        check_content=True, left_matching_blocks=None):
302
438
        """Add lines to the versioned file, allowing ghosts to be present.
303
 
        
 
439
 
304
440
        This takes the same parameters as add_lines and returns the same.
305
441
        """
306
442
        self._check_write_ok()
330
466
 
331
467
    def get_format_signature(self):
332
468
        """Get a text description of the data encoding in this file.
333
 
        
 
469
 
334
470
        :since: 0.90
335
471
        """
336
472
        raise NotImplementedError(self.get_format_signature)
337
473
 
338
474
    def make_mpdiffs(self, version_ids):
339
475
        """Create multiparent diffs for specified versions."""
 
476
        # XXX: Can't use _MPDiffGenerator just yet. This is because version_ids
 
477
        #      is a list of strings, not keys. And while self.get_record_stream
 
478
        #      is supported, it takes *keys*, while self.get_parent_map() takes
 
479
        #      strings... *sigh*
340
480
        knit_versions = set()
341
481
        knit_versions.update(version_ids)
342
482
        parent_map = self.get_parent_map(version_ids)
457
597
        if isinstance(version_ids, basestring):
458
598
            version_ids = [version_ids]
459
599
        raise NotImplementedError(self.get_ancestry)
460
 
        
 
600
 
461
601
    def get_ancestry_with_ghosts(self, version_ids):
462
602
        """Return a list of all ancestors of given version(s). This
463
603
        will not include the null revision.
464
604
 
465
605
        Must raise RevisionNotPresent if any of the given versions are
466
606
        not present in file history.
467
 
        
 
607
 
468
608
        Ghosts that are known about will be included in ancestry list,
469
609
        but are not explicitly marked.
470
610
        """
471
611
        raise NotImplementedError(self.get_ancestry_with_ghosts)
472
 
    
 
612
 
473
613
    def get_parent_map(self, version_ids):
474
614
        """Get a map of the parents of version_ids.
475
615
 
538
678
        unchanged   Alive in both a and b (possibly created in both)
539
679
        new-a       Created in a
540
680
        new-b       Created in b
541
 
        ghost-a     Killed in a, unborn in b    
 
681
        ghost-a     Killed in a, unborn in b
542
682
        ghost-b     Killed in b, unborn in a
543
683
        irrelevant  Not in either revision
544
684
        """
545
685
        raise NotImplementedError(VersionedFile.plan_merge)
546
 
        
 
686
 
547
687
    def weave_merge(self, plan, a_marker=TextMerge.A_MARKER,
548
688
                    b_marker=TextMerge.B_MARKER):
549
689
        return PlanWeaveMerge(plan, a_marker, b_marker).merge_lines()[0]
551
691
 
552
692
class RecordingVersionedFilesDecorator(object):
553
693
    """A minimal versioned files that records calls made on it.
554
 
    
 
694
 
555
695
    Only enough methods have been added to support tests using it to date.
556
696
 
557
697
    :ivar calls: A list of the calls made; can be reset at any time by
560
700
 
561
701
    def __init__(self, backing_vf):
562
702
        """Create a RecordingVersionedFilesDecorator decorating backing_vf.
563
 
        
 
703
 
564
704
        :param backing_vf: The versioned file to answer all methods.
565
705
        """
566
706
        self._backing_vf = backing_vf
652
792
 
653
793
    def unmap(self, partition_id):
654
794
        """Map a partitioned storage id back to a key prefix.
655
 
        
 
795
 
656
796
        :param partition_id: The underlying partition id.
657
797
        :return: As much of a key (or prefix) as is derivable from the partition
658
798
            id.
690
830
 
691
831
class PrefixMapper(URLEscapeMapper):
692
832
    """A key mapper that extracts the first component of a key.
693
 
    
 
833
 
694
834
    This mapper is for use with a transport based backend.
695
835
    """
696
836
 
729
869
 
730
870
class HashEscapedPrefixMapper(HashPrefixMapper):
731
871
    """Combines the escaped first component of a key with a hash.
732
 
    
 
872
 
733
873
    This mapper is for use with a transport based backend.
734
874
    """
735
875
 
784
924
 
785
925
    The use of tuples allows a single code base to support several different
786
926
    uses with only the mapping logic changing from instance to instance.
 
927
 
 
928
    :ivar _immediate_fallback_vfs: For subclasses that support stacking,
 
929
        this is a list of other VersionedFiles immediately underneath this
 
930
        one.  They may in turn each have further fallbacks.
787
931
    """
788
932
 
789
933
    def add_lines(self, key, parents, lines, parent_texts=None,
791
935
        check_content=True):
792
936
        """Add a text to the store.
793
937
 
794
 
        :param key: The key tuple of the text to add.
 
938
        :param key: The key tuple of the text to add. If the last element is
 
939
            None, a CHK string will be generated during the addition.
795
940
        :param parents: The parents key tuples of the text to add.
796
941
        :param lines: A list of lines. Each line must be a bytestring. And all
797
942
            of them except the last must be terminated with \n and contain no
801
946
            the data back accurately. (Checking the lines have been split
802
947
            correctly is expensive and extremely unlikely to catch bugs so it
803
948
            is not done at runtime unless check_content is True.)
804
 
        :param parent_texts: An optional dictionary containing the opaque 
 
949
        :param parent_texts: An optional dictionary containing the opaque
805
950
            representations of some or all of the parents of version_id to
806
951
            allow delta optimisations.  VERY IMPORTANT: the texts must be those
807
952
            returned by add_lines or data corruption can be caused.
824
969
        """
825
970
        raise NotImplementedError(self.add_lines)
826
971
 
 
972
    def _add_text(self, key, parents, text, nostore_sha=None, random_id=False):
 
973
        """Add a text to the store.
 
974
 
 
975
        This is a private function for use by VersionedFileCommitBuilder.
 
976
 
 
977
        :param key: The key tuple of the text to add. If the last element is
 
978
            None, a CHK string will be generated during the addition.
 
979
        :param parents: The parents key tuples of the text to add.
 
980
        :param text: A string containing the text to be committed.
 
981
        :param nostore_sha: Raise ExistingContent and do not add the lines to
 
982
            the versioned file if the digest of the lines matches this.
 
983
        :param random_id: If True a random id has been selected rather than
 
984
            an id determined by some deterministic process such as a converter
 
985
            from a foreign VCS. When True the backend may choose not to check
 
986
            for uniqueness of the resulting key within the versioned file, so
 
987
            this should only be done when the result is expected to be unique
 
988
            anyway.
 
989
        :param check_content: If True, the lines supplied are verified to be
 
990
            bytestrings that are correctly formed lines.
 
991
        :return: The text sha1, the number of bytes in the text, and an opaque
 
992
                 representation of the inserted version which can be provided
 
993
                 back to future _add_text calls in the parent_texts dictionary.
 
994
        """
 
995
        # The default implementation just thunks over to .add_lines(),
 
996
        # inefficient, but it works.
 
997
        return self.add_lines(key, parents, osutils.split_lines(text),
 
998
                              nostore_sha=nostore_sha,
 
999
                              random_id=random_id,
 
1000
                              check_content=True)
 
1001
 
827
1002
    def add_mpdiffs(self, records):
828
1003
        """Add mpdiffs to this VersionedFile.
829
1004
 
871
1046
        raise NotImplementedError(self.annotate)
872
1047
 
873
1048
    def check(self, progress_bar=None):
874
 
        """Check this object for integrity."""
 
1049
        """Check this object for integrity.
 
1050
        
 
1051
        :param progress_bar: A progress bar to output as the check progresses.
 
1052
        :param keys: Specific keys within the VersionedFiles to check. When
 
1053
            this parameter is not None, check() becomes a generator as per
 
1054
            get_record_stream. The difference to get_record_stream is that
 
1055
            more or deeper checks will be performed.
 
1056
        :return: None, or if keys was supplied a generator as per
 
1057
            get_record_stream.
 
1058
        """
875
1059
        raise NotImplementedError(self.check)
876
1060
 
877
1061
    @staticmethod
878
1062
    def check_not_reserved_id(version_id):
879
1063
        revision.check_not_reserved_id(version_id)
880
1064
 
 
1065
    def clear_cache(self):
 
1066
        """Clear whatever caches this VersionedFile holds.
 
1067
 
 
1068
        This is generally called after an operation has been performed, when we
 
1069
        don't expect to be using this versioned file again soon.
 
1070
        """
 
1071
 
881
1072
    def _check_lines_not_unicode(self, lines):
882
1073
        """Check that lines being added to a versioned file are not unicode."""
883
1074
        for line in lines:
890
1081
            if '\n' in line[:-1]:
891
1082
                raise errors.BzrBadParameterContainsNewline("lines")
892
1083
 
 
1084
    def get_known_graph_ancestry(self, keys):
 
1085
        """Get a KnownGraph instance with the ancestry of keys."""
 
1086
        # most basic implementation is a loop around get_parent_map
 
1087
        pending = set(keys)
 
1088
        parent_map = {}
 
1089
        while pending:
 
1090
            this_parent_map = self.get_parent_map(pending)
 
1091
            parent_map.update(this_parent_map)
 
1092
            pending = set()
 
1093
            map(pending.update, this_parent_map.itervalues())
 
1094
            pending = pending.difference(parent_map)
 
1095
        kg = _mod_graph.KnownGraph(parent_map)
 
1096
        return kg
 
1097
 
893
1098
    def get_parent_map(self, keys):
894
1099
        """Get a map of the parents of keys.
895
1100
 
925
1130
 
926
1131
    has_key = index._has_key_from_parent_map
927
1132
 
 
1133
    def get_missing_compression_parent_keys(self):
 
1134
        """Return an iterable of keys of missing compression parents.
 
1135
 
 
1136
        Check this after calling insert_record_stream to find out if there are
 
1137
        any missing compression parents.  If there are, the records that
 
1138
        depend on them are not able to be inserted safely. The precise
 
1139
        behaviour depends on the concrete VersionedFiles class in use.
 
1140
 
 
1141
        Classes that do not support this will raise NotImplementedError.
 
1142
        """
 
1143
        raise NotImplementedError(self.get_missing_compression_parent_keys)
 
1144
 
928
1145
    def insert_record_stream(self, stream):
929
1146
        """Insert a record stream into this container.
930
1147
 
931
 
        :param stream: A stream of records to insert. 
 
1148
        :param stream: A stream of records to insert.
932
1149
        :return: None
933
1150
        :seealso VersionedFile.get_record_stream:
934
1151
        """
963
1180
 
964
1181
    def make_mpdiffs(self, keys):
965
1182
        """Create multiparent diffs for specified keys."""
966
 
        keys_order = tuple(keys)
967
 
        keys = frozenset(keys)
968
 
        knit_keys = set(keys)
969
 
        parent_map = self.get_parent_map(keys)
970
 
        for parent_keys in parent_map.itervalues():
971
 
            if parent_keys:
972
 
                knit_keys.update(parent_keys)
973
 
        missing_keys = keys - set(parent_map)
974
 
        if missing_keys:
975
 
            raise errors.RevisionNotPresent(list(missing_keys)[0], self)
976
 
        # We need to filter out ghosts, because we can't diff against them.
977
 
        maybe_ghosts = knit_keys - keys
978
 
        ghosts = maybe_ghosts - set(self.get_parent_map(maybe_ghosts))
979
 
        knit_keys.difference_update(ghosts)
980
 
        lines = {}
981
 
        chunks_to_lines = osutils.chunks_to_lines
982
 
        for record in self.get_record_stream(knit_keys, 'topological', True):
983
 
            lines[record.key] = chunks_to_lines(record.get_bytes_as('chunked'))
984
 
            # line_block_dict = {}
985
 
            # for parent, blocks in record.extract_line_blocks():
986
 
            #   line_blocks[parent] = blocks
987
 
            # line_blocks[record.key] = line_block_dict
988
 
        diffs = []
989
 
        for key in keys_order:
990
 
            target = lines[key]
991
 
            parents = parent_map[key] or []
992
 
            # Note that filtering knit_keys can lead to a parent difference
993
 
            # between the creation and the application of the mpdiff.
994
 
            parent_lines = [lines[p] for p in parents if p in knit_keys]
995
 
            if len(parent_lines) > 0:
996
 
                left_parent_blocks = self._extract_blocks(key, parent_lines[0],
997
 
                    target)
998
 
            else:
999
 
                left_parent_blocks = None
1000
 
            diffs.append(multiparent.MultiParent.from_lines(target,
1001
 
                parent_lines, left_parent_blocks))
1002
 
        return diffs
 
1183
        generator = _MPDiffGenerator(self, keys)
 
1184
        return generator.compute_diffs()
 
1185
 
 
1186
    def get_annotator(self):
 
1187
        return annotate.Annotator(self)
1003
1188
 
1004
1189
    missing_keys = index._missing_keys_from_parent_map
1005
1190
 
1006
1191
    def _extract_blocks(self, version_id, source, target):
1007
1192
        return None
1008
1193
 
 
1194
    def _transitive_fallbacks(self):
 
1195
        """Return the whole stack of fallback versionedfiles.
 
1196
 
 
1197
        This VersionedFiles may have a list of fallbacks, but it doesn't
 
1198
        necessarily know about the whole stack going down, and it can't know
 
1199
        at open time because they may change after the objects are opened.
 
1200
        """
 
1201
        all_fallbacks = []
 
1202
        for a_vfs in self._immediate_fallback_vfs:
 
1203
            all_fallbacks.append(a_vfs)
 
1204
            all_fallbacks.extend(a_vfs._transitive_fallbacks())
 
1205
        return all_fallbacks
 
1206
 
1009
1207
 
1010
1208
class ThunkedVersionedFiles(VersionedFiles):
1011
1209
    """Storage for many versioned files thunked onto a 'VersionedFile' class.
1075
1273
            result.append((prefix + (origin,), line))
1076
1274
        return result
1077
1275
 
1078
 
    def check(self, progress_bar=None):
 
1276
    def check(self, progress_bar=None, keys=None):
1079
1277
        """See VersionedFiles.check()."""
 
1278
        # XXX: This is over-enthusiastic but as we only thunk for Weaves today
 
1279
        # this is tolerable. Ideally we'd pass keys down to check() and 
 
1280
        # have the older VersiondFile interface updated too.
1080
1281
        for prefix, vf in self._iter_all_components():
1081
1282
            vf.check()
 
1283
        if keys is not None:
 
1284
            return self.get_record_stream(keys, 'unordered', True)
1082
1285
 
1083
1286
    def get_parent_map(self, keys):
1084
1287
        """Get a map of the parents of keys.
1162
1365
    def insert_record_stream(self, stream):
1163
1366
        """Insert a record stream into this container.
1164
1367
 
1165
 
        :param stream: A stream of records to insert. 
 
1368
        :param stream: A stream of records to insert.
1166
1369
        :return: None
1167
1370
        :seealso VersionedFile.get_record_stream:
1168
1371
        """
1219
1422
        return result
1220
1423
 
1221
1424
 
 
1425
class VersionedFilesWithFallbacks(VersionedFiles):
 
1426
 
 
1427
    def without_fallbacks(self):
 
1428
        """Return a clone of this object without any fallbacks configured."""
 
1429
        raise NotImplementedError(self.without_fallbacks)
 
1430
 
 
1431
    def add_fallback_versioned_files(self, a_versioned_files):
 
1432
        """Add a source of texts for texts not present in this knit.
 
1433
 
 
1434
        :param a_versioned_files: A VersionedFiles object.
 
1435
        """
 
1436
        raise NotImplementedError(self.add_fallback_versioned_files)
 
1437
 
 
1438
    def get_known_graph_ancestry(self, keys):
 
1439
        """Get a KnownGraph instance with the ancestry of keys."""
 
1440
        parent_map, missing_keys = self._index.find_ancestry(keys)
 
1441
        for fallback in self._transitive_fallbacks():
 
1442
            if not missing_keys:
 
1443
                break
 
1444
            (f_parent_map, f_missing_keys) = fallback._index.find_ancestry(
 
1445
                                                missing_keys)
 
1446
            parent_map.update(f_parent_map)
 
1447
            missing_keys = f_missing_keys
 
1448
        kg = _mod_graph.KnownGraph(parent_map)
 
1449
        return kg
 
1450
 
 
1451
 
1222
1452
class _PlanMergeVersionedFile(VersionedFiles):
1223
1453
    """A VersionedFile for uncommitted and committed texts.
1224
1454
 
1245
1475
        # line data for locally held keys.
1246
1476
        self._lines = {}
1247
1477
        # key lookup providers
1248
 
        self._providers = [DictParentsProvider(self._parents)]
 
1478
        self._providers = [_mod_graph.DictParentsProvider(self._parents)]
1249
1479
 
1250
1480
    def plan_merge(self, ver_a, ver_b, base=None):
1251
1481
        """See VersionedFile.plan_merge"""
1258
1488
 
1259
1489
    def plan_lca_merge(self, ver_a, ver_b, base=None):
1260
1490
        from bzrlib.merge import _PlanLCAMerge
1261
 
        graph = Graph(self)
 
1491
        graph = _mod_graph.Graph(self)
1262
1492
        new_plan = _PlanLCAMerge(ver_a, ver_b, self, (self._file_id,), graph).plan_merge()
1263
1493
        if base is None:
1264
1494
            return new_plan
1316
1546
            result[revision.NULL_REVISION] = ()
1317
1547
        self._providers = self._providers[:1] + self.fallback_versionedfiles
1318
1548
        result.update(
1319
 
            _StackedParentsProvider(self._providers).get_parent_map(keys))
 
1549
            _mod_graph.StackedParentsProvider(
 
1550
                self._providers).get_parent_map(keys))
1320
1551
        for key, parents in result.iteritems():
1321
1552
            if parents == ():
1322
1553
                result[key] = (revision.NULL_REVISION,)
1325
1556
 
1326
1557
class PlanWeaveMerge(TextMerge):
1327
1558
    """Weave merge that takes a plan as its input.
1328
 
    
 
1559
 
1329
1560
    This exists so that VersionedFile.plan_merge is implementable.
1330
1561
    Most callers will want to use WeaveMerge instead.
1331
1562
    """
1333
1564
    def __init__(self, plan, a_marker=TextMerge.A_MARKER,
1334
1565
                 b_marker=TextMerge.B_MARKER):
1335
1566
        TextMerge.__init__(self, a_marker, b_marker)
1336
 
        self.plan = plan
 
1567
        self.plan = list(plan)
1337
1568
 
1338
1569
    def _merge_struct(self):
1339
1570
        lines_a = []
1352
1583
                yield(lines_a,)
1353
1584
            else:
1354
1585
                yield (lines_a, lines_b)
1355
 
       
 
1586
 
1356
1587
        # We previously considered either 'unchanged' or 'killed-both' lines
1357
1588
        # to be possible places to resynchronize.  However, assuming agreement
1358
1589
        # on killed-both lines may be too aggressive. -- mbp 20060324
1364
1595
                lines_a = []
1365
1596
                lines_b = []
1366
1597
                ch_a = ch_b = False
1367
 
                
 
1598
 
1368
1599
            if state == 'unchanged':
1369
1600
                if line:
1370
1601
                    yield ([line],)
1386
1617
            elif state == 'conflicted-b':
1387
1618
                ch_b = ch_a = True
1388
1619
                lines_b.append(line)
 
1620
            elif state == 'killed-both':
 
1621
                # This counts as a change, even though there is no associated
 
1622
                # line
 
1623
                ch_b = ch_a = True
1389
1624
            else:
1390
1625
                if state not in ('irrelevant', 'ghost-a', 'ghost-b',
1391
 
                        'killed-base', 'killed-both'):
 
1626
                        'killed-base'):
1392
1627
                    raise AssertionError(state)
1393
1628
        for struct in outstanding_struct():
1394
1629
            yield struct
1395
1630
 
 
1631
    def base_from_plan(self):
 
1632
        """Construct a BASE file from the plan text."""
 
1633
        base_lines = []
 
1634
        for state, line in self.plan:
 
1635
            if state in ('killed-a', 'killed-b', 'killed-both', 'unchanged'):
 
1636
                # If unchanged, then this line is straight from base. If a or b
 
1637
                # or both killed the line, then it *used* to be in base.
 
1638
                base_lines.append(line)
 
1639
            else:
 
1640
                if state not in ('killed-base', 'irrelevant',
 
1641
                                 'ghost-a', 'ghost-b',
 
1642
                                 'new-a', 'new-b',
 
1643
                                 'conflicted-a', 'conflicted-b'):
 
1644
                    # killed-base, irrelevant means it doesn't apply
 
1645
                    # ghost-a/ghost-b are harder to say for sure, but they
 
1646
                    # aren't in the 'inc_c' which means they aren't in the
 
1647
                    # shared base of a & b. So we don't include them.  And
 
1648
                    # obviously if the line is newly inserted, it isn't in base
 
1649
 
 
1650
                    # If 'conflicted-a' or b, then it is new vs one base, but
 
1651
                    # old versus another base. However, if we make it present
 
1652
                    # in the base, it will be deleted from the target, and it
 
1653
                    # seems better to get a line doubled in the merge result,
 
1654
                    # rather than have it deleted entirely.
 
1655
                    # Example, each node is the 'text' at that point:
 
1656
                    #           MN
 
1657
                    #          /   \
 
1658
                    #        MaN   MbN
 
1659
                    #         |  X  |
 
1660
                    #        MabN MbaN
 
1661
                    #          \   /
 
1662
                    #           ???
 
1663
                    # There was a criss-cross conflict merge. Both sides
 
1664
                    # include the other, but put themselves first.
 
1665
                    # Weave marks this as a 'clean' merge, picking OTHER over
 
1666
                    # THIS. (Though the details depend on order inserted into
 
1667
                    # weave, etc.)
 
1668
                    # LCA generates a plan:
 
1669
                    # [('unchanged', M),
 
1670
                    #  ('conflicted-b', b),
 
1671
                    #  ('unchanged', a),
 
1672
                    #  ('conflicted-a', b),
 
1673
                    #  ('unchanged', N)]
 
1674
                    # If you mark 'conflicted-*' as part of BASE, then a 3-way
 
1675
                    # merge tool will cleanly generate "MaN" (as BASE vs THIS
 
1676
                    # removes one 'b', and BASE vs OTHER removes the other)
 
1677
                    # If you include neither, 3-way creates a clean "MbabN" as
 
1678
                    # THIS adds one 'b', and OTHER does too.
 
1679
                    # It seems that having the line 2 times is better than
 
1680
                    # having it omitted. (Easier to manually delete than notice
 
1681
                    # it needs to be added.)
 
1682
                    raise AssertionError('Unknown state: %s' % (state,))
 
1683
        return base_lines
 
1684
 
1396
1685
 
1397
1686
class WeaveMerge(PlanWeaveMerge):
1398
1687
    """Weave merge that takes a VersionedFile and two versions as its input."""
1399
1688
 
1400
 
    def __init__(self, versionedfile, ver_a, ver_b, 
 
1689
    def __init__(self, versionedfile, ver_a, ver_b,
1401
1690
        a_marker=PlanWeaveMerge.A_MARKER, b_marker=PlanWeaveMerge.B_MARKER):
1402
1691
        plan = versionedfile.plan_merge(ver_a, ver_b)
1403
1692
        PlanWeaveMerge.__init__(self, plan, a_marker, b_marker)
1404
1693
 
1405
1694
 
1406
1695
class VirtualVersionedFiles(VersionedFiles):
1407
 
    """Dummy implementation for VersionedFiles that uses other functions for 
 
1696
    """Dummy implementation for VersionedFiles that uses other functions for
1408
1697
    obtaining fulltexts and parent maps.
1409
1698
 
1410
 
    This is always on the bottom of the stack and uses string keys 
 
1699
    This is always on the bottom of the stack and uses string keys
1411
1700
    (rather than tuples) internally.
1412
1701
    """
1413
1702
 
1415
1704
        """Create a VirtualVersionedFiles.
1416
1705
 
1417
1706
        :param get_parent_map: Same signature as Repository.get_parent_map.
1418
 
        :param get_lines: Should return lines for specified key or None if 
 
1707
        :param get_lines: Should return lines for specified key or None if
1419
1708
                          not available.
1420
1709
        """
1421
1710
        super(VirtualVersionedFiles, self).__init__()
1422
1711
        self._get_parent_map = get_parent_map
1423
1712
        self._get_lines = get_lines
1424
 
        
 
1713
 
1425
1714
    def check(self, progressbar=None):
1426
1715
        """See VersionedFiles.check.
1427
1716
 
1469
1758
        """See VersionedFile.iter_lines_added_or_present_in_versions()."""
1470
1759
        for i, (key,) in enumerate(keys):
1471
1760
            if pb is not None:
1472
 
                pb.update("iterating texts", i, len(keys))
 
1761
                pb.update("Finding changed lines", i, len(keys))
1473
1762
            for l in self._get_lines(key):
1474
1763
                yield (l, key)
 
1764
 
 
1765
 
 
1766
class NoDupeAddLinesDecorator(object):
 
1767
    """Decorator for a VersionedFiles that skips doing an add_lines if the key
 
1768
    is already present.
 
1769
    """
 
1770
 
 
1771
    def __init__(self, store):
 
1772
        self._store = store
 
1773
 
 
1774
    def add_lines(self, key, parents, lines, parent_texts=None,
 
1775
            left_matching_blocks=None, nostore_sha=None, random_id=False,
 
1776
            check_content=True):
 
1777
        """See VersionedFiles.add_lines.
 
1778
        
 
1779
        This implementation may return None as the third element of the return
 
1780
        value when the original store wouldn't.
 
1781
        """
 
1782
        if nostore_sha:
 
1783
            raise NotImplementedError(
 
1784
                "NoDupeAddLinesDecorator.add_lines does not implement the "
 
1785
                "nostore_sha behaviour.")
 
1786
        if key[-1] is None:
 
1787
            sha1 = osutils.sha_strings(lines)
 
1788
            key = ("sha1:" + sha1,)
 
1789
        else:
 
1790
            sha1 = None
 
1791
        if key in self._store.get_parent_map([key]):
 
1792
            # This key has already been inserted, so don't do it again.
 
1793
            if sha1 is None:
 
1794
                sha1 = osutils.sha_strings(lines)
 
1795
            return sha1, sum(map(len, lines)), None
 
1796
        return self._store.add_lines(key, parents, lines,
 
1797
                parent_texts=parent_texts,
 
1798
                left_matching_blocks=left_matching_blocks,
 
1799
                nostore_sha=nostore_sha, random_id=random_id,
 
1800
                check_content=check_content)
 
1801
 
 
1802
    def __getattr__(self, name):
 
1803
        return getattr(self._store, name)
 
1804
 
 
1805
 
 
1806
def network_bytes_to_kind_and_offset(network_bytes):
 
1807
    """Strip of a record kind from the front of network_bytes.
 
1808
 
 
1809
    :param network_bytes: The bytes of a record.
 
1810
    :return: A tuple (storage_kind, offset_of_remaining_bytes)
 
1811
    """
 
1812
    line_end = network_bytes.find('\n')
 
1813
    storage_kind = network_bytes[:line_end]
 
1814
    return storage_kind, line_end + 1
 
1815
 
 
1816
 
 
1817
class NetworkRecordStream(object):
 
1818
    """A record_stream which reconstitures a serialised stream."""
 
1819
 
 
1820
    def __init__(self, bytes_iterator):
 
1821
        """Create a NetworkRecordStream.
 
1822
 
 
1823
        :param bytes_iterator: An iterator of bytes. Each item in this
 
1824
            iterator should have been obtained from a record_streams'
 
1825
            record.get_bytes_as(record.storage_kind) call.
 
1826
        """
 
1827
        self._bytes_iterator = bytes_iterator
 
1828
        self._kind_factory = {
 
1829
            'fulltext': fulltext_network_to_record,
 
1830
            'groupcompress-block': groupcompress.network_block_to_records,
 
1831
            'knit-ft-gz': knit.knit_network_to_record,
 
1832
            'knit-delta-gz': knit.knit_network_to_record,
 
1833
            'knit-annotated-ft-gz': knit.knit_network_to_record,
 
1834
            'knit-annotated-delta-gz': knit.knit_network_to_record,
 
1835
            'knit-delta-closure': knit.knit_delta_closure_to_records,
 
1836
            }
 
1837
 
 
1838
    def read(self):
 
1839
        """Read the stream.
 
1840
 
 
1841
        :return: An iterator as per VersionedFiles.get_record_stream().
 
1842
        """
 
1843
        for bytes in self._bytes_iterator:
 
1844
            storage_kind, line_end = network_bytes_to_kind_and_offset(bytes)
 
1845
            for record in self._kind_factory[storage_kind](
 
1846
                storage_kind, bytes, line_end):
 
1847
                yield record
 
1848
 
 
1849
 
 
1850
def fulltext_network_to_record(kind, bytes, line_end):
 
1851
    """Convert a network fulltext record to record."""
 
1852
    meta_len, = struct.unpack('!L', bytes[line_end:line_end+4])
 
1853
    record_meta = bytes[line_end+4:line_end+4+meta_len]
 
1854
    key, parents = bencode.bdecode_as_tuple(record_meta)
 
1855
    if parents == 'nil':
 
1856
        parents = None
 
1857
    fulltext = bytes[line_end+4+meta_len:]
 
1858
    return [FulltextContentFactory(key, parents, None, fulltext)]
 
1859
 
 
1860
 
 
1861
def _length_prefix(bytes):
 
1862
    return struct.pack('!L', len(bytes))
 
1863
 
 
1864
 
 
1865
def record_to_fulltext_bytes(record):
 
1866
    if record.parents is None:
 
1867
        parents = 'nil'
 
1868
    else:
 
1869
        parents = record.parents
 
1870
    record_meta = bencode.bencode((record.key, parents))
 
1871
    record_content = record.get_bytes_as('fulltext')
 
1872
    return "fulltext\n%s%s%s" % (
 
1873
        _length_prefix(record_meta), record_meta, record_content)
 
1874
 
 
1875
 
 
1876
def sort_groupcompress(parent_map):
 
1877
    """Sort and group the keys in parent_map into groupcompress order.
 
1878
 
 
1879
    groupcompress is defined (currently) as reverse-topological order, grouped
 
1880
    by the key prefix.
 
1881
 
 
1882
    :return: A sorted-list of keys
 
1883
    """
 
1884
    # gc-optimal ordering is approximately reverse topological,
 
1885
    # properly grouped by file-id.
 
1886
    per_prefix_map = {}
 
1887
    for item in parent_map.iteritems():
 
1888
        key = item[0]
 
1889
        if isinstance(key, str) or len(key) == 1:
 
1890
            prefix = ''
 
1891
        else:
 
1892
            prefix = key[0]
 
1893
        try:
 
1894
            per_prefix_map[prefix].append(item)
 
1895
        except KeyError:
 
1896
            per_prefix_map[prefix] = [item]
 
1897
 
 
1898
    present_keys = []
 
1899
    for prefix in sorted(per_prefix_map):
 
1900
        present_keys.extend(reversed(tsort.topo_sort(per_prefix_map[prefix])))
 
1901
    return present_keys
 
1902
 
 
1903
 
 
1904
class _KeyRefs(object):
 
1905
 
 
1906
    def __init__(self, track_new_keys=False):
 
1907
        # dict mapping 'key' to 'set of keys referring to that key'
 
1908
        self.refs = {}
 
1909
        if track_new_keys:
 
1910
            # set remembering all new keys
 
1911
            self.new_keys = set()
 
1912
        else:
 
1913
            self.new_keys = None
 
1914
 
 
1915
    def clear(self):
 
1916
        if self.refs:
 
1917
            self.refs.clear()
 
1918
        if self.new_keys:
 
1919
            self.new_keys.clear()
 
1920
 
 
1921
    def add_references(self, key, refs):
 
1922
        # Record the new references
 
1923
        for referenced in refs:
 
1924
            try:
 
1925
                needed_by = self.refs[referenced]
 
1926
            except KeyError:
 
1927
                needed_by = self.refs[referenced] = set()
 
1928
            needed_by.add(key)
 
1929
        # Discard references satisfied by the new key
 
1930
        self.add_key(key)
 
1931
 
 
1932
    def get_new_keys(self):
 
1933
        return self.new_keys
 
1934
    
 
1935
    def get_unsatisfied_refs(self):
 
1936
        return self.refs.iterkeys()
 
1937
 
 
1938
    def _satisfy_refs_for_key(self, key):
 
1939
        try:
 
1940
            del self.refs[key]
 
1941
        except KeyError:
 
1942
            # No keys depended on this key.  That's ok.
 
1943
            pass
 
1944
 
 
1945
    def add_key(self, key):
 
1946
        # satisfy refs for key, and remember that we've seen this key.
 
1947
        self._satisfy_refs_for_key(key)
 
1948
        if self.new_keys is not None:
 
1949
            self.new_keys.add(key)
 
1950
 
 
1951
    def satisfy_refs_for_keys(self, keys):
 
1952
        for key in keys:
 
1953
            self._satisfy_refs_for_key(key)
 
1954
 
 
1955
    def get_referrers(self):
 
1956
        result = set()
 
1957
        for referrers in self.refs.itervalues():
 
1958
            result.update(referrers)
 
1959
        return result
 
1960
 
 
1961
 
 
1962