~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: John Arbash Meinel
  • Date: 2008-10-30 00:55:00 UTC
  • mto: (3815.2.5 prepare-1.9)
  • mto: This revision was merged to the branch mainline in revision 3811.
  • Revision ID: john@arbash-meinel.com-20081030005500-r5cej1cxflqhs3io
Switch so that we are using a simple timestamp as the first action.

Show diffs side-by-side

added added

removed removed

Lines of Context:
71
71
from copy import copy
72
72
from cStringIO import StringIO
73
73
import os
74
 
import sha
75
74
import time
76
75
import warnings
77
76
 
 
77
from bzrlib.lazy_import import lazy_import
 
78
lazy_import(globals(), """
 
79
from bzrlib import tsort
 
80
""")
78
81
from bzrlib import (
79
82
    progress,
80
83
    )
86
89
        WeaveRevisionNotPresent,
87
90
        )
88
91
import bzrlib.errors as errors
89
 
from bzrlib.osutils import sha_strings, split_lines
 
92
from bzrlib.osutils import dirname, sha, sha_strings, split_lines
90
93
import bzrlib.patiencediff
 
94
from bzrlib.revision import NULL_REVISION
91
95
from bzrlib.symbol_versioning import *
92
96
from bzrlib.trace import mutter
93
 
from bzrlib.tsort import topo_sort
94
97
from bzrlib.versionedfile import (
95
98
    AbsentContentFactory,
96
99
    adapter_registry,
97
100
    ContentFactory,
98
 
    InterVersionedFile,
99
101
    VersionedFile,
100
102
    )
101
103
from bzrlib.weavefile import _read_weave_v5, write_weave_v5
110
112
    def __init__(self, version, weave):
111
113
        """Create a WeaveContentFactory for version from weave."""
112
114
        ContentFactory.__init__(self)
113
 
        self.sha1 = weave.get_sha1s([version])[0]
 
115
        self.sha1 = weave.get_sha1s([version])[version]
114
116
        self.key = (version,)
115
117
        parents = weave.get_parent_map([version])[version]
116
118
        self.parents = tuple((parent,) for parent in parents)
119
121
 
120
122
    def get_bytes_as(self, storage_kind):
121
123
        if storage_kind == 'fulltext':
122
 
            return self._weave.get_text(self.key[0])
 
124
            return self._weave.get_text(self.key[-1])
123
125
        else:
124
126
            raise UnavailableRepresentation(self.key, storage_kind, 'fulltext')
125
127
 
214
216
    """
215
217
 
216
218
    __slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
217
 
                 '_weave_name', '_matcher']
218
 
    
219
 
    def __init__(self, weave_name=None, access_mode='w', matcher=None, get_scope=None):
 
219
                 '_weave_name', '_matcher', '_allow_reserved']
 
220
 
 
221
    def __init__(self, weave_name=None, access_mode='w', matcher=None,
 
222
                 get_scope=None, allow_reserved=False):
220
223
        """Create a weave.
221
224
 
222
225
        :param get_scope: A callable that returns an opaque object to be used
223
226
            for detecting when this weave goes out of scope (should stop
224
227
            answering requests or allowing mutation).
225
228
        """
226
 
        super(Weave, self).__init__(access_mode)
 
229
        super(Weave, self).__init__()
227
230
        self._weave = []
228
231
        self._parents = []
229
232
        self._sha1s = []
239
242
        self._get_scope = get_scope
240
243
        self._scope = get_scope()
241
244
        self._access_mode = access_mode
 
245
        self._allow_reserved = allow_reserved
242
246
 
243
247
    def __repr__(self):
244
248
        return "Weave(%r)" % self._weave_name
278
282
 
279
283
    def _lookup(self, name):
280
284
        """Convert symbolic version name to index."""
281
 
        self.check_not_reserved_id(name)
 
285
        if not self._allow_reserved:
 
286
            self.check_not_reserved_id(name)
282
287
        try:
283
288
            return self._name_map[name]
284
289
        except KeyError:
307
312
        :return: An iterator of ContentFactory objects, each of which is only
308
313
            valid until the iterator is advanced.
309
314
        """
 
315
        versions = [version[-1] for version in versions]
310
316
        if ordering == 'topological':
311
317
            parents = self.get_parent_map(versions)
312
 
            new_versions = topo_sort(parents)
 
318
            new_versions = tsort.topo_sort(parents)
313
319
            new_versions.extend(set(versions).difference(set(parents)))
314
320
            versions = new_versions
315
321
        for version in versions:
322
328
        """See VersionedFile.get_parent_map."""
323
329
        result = {}
324
330
        for version_id in version_ids:
325
 
            try:
326
 
                result[version_id] = tuple(
327
 
                    map(self._idx_to_name, self._parents[self._lookup(version_id)]))
328
 
            except RevisionNotPresent:
329
 
                pass
 
331
            if version_id == NULL_REVISION:
 
332
                parents = ()
 
333
            else:
 
334
                try:
 
335
                    parents = tuple(
 
336
                        map(self._idx_to_name,
 
337
                            self._parents[self._lookup(version_id)]))
 
338
                except RevisionNotPresent:
 
339
                    continue
 
340
            result[version_id] = parents
330
341
        return result
331
342
 
332
343
    def get_parents_with_ghosts(self, version_id):
647
658
                # not in either revision
648
659
                yield 'irrelevant', line
649
660
 
650
 
        yield 'unchanged', ''           # terminator
651
 
 
652
661
    def _extract(self, versions):
653
662
        """Yield annotation of lines in included set.
654
663
 
759
768
 
760
769
    def get_sha1s(self, version_ids):
761
770
        """See VersionedFile.get_sha1s()."""
762
 
        return [self._sha1s[self._lookup(v)] for v in version_ids]
 
771
        result = {}
 
772
        for v in version_ids:
 
773
            result[v] = self._sha1s[self._lookup(v)]
 
774
        return result
763
775
 
764
776
    def num_versions(self):
765
777
        """How many versions are in this weave?"""
789
801
            # For creating the ancestry, IntSet is much faster (3.7s vs 0.17s)
790
802
            # The problem is that set membership is much more expensive
791
803
            name = self._idx_to_name(i)
792
 
            sha1s[name] = sha.new()
 
804
            sha1s[name] = sha()
793
805
            texts[name] = []
794
806
            new_inc = set([name])
795
807
            for p in self._parents[i]:
834
846
        # no lines outside of insertion blocks, that deletions are
835
847
        # properly paired, etc.
836
848
 
837
 
    def _join(self, other, pb, msg, version_ids, ignore_missing):
838
 
        """Worker routine for join()."""
839
 
        if not other.versions():
840
 
            return          # nothing to update, easy
841
 
 
842
 
        if not version_ids:
843
 
            # versions is never none, InterWeave checks this.
844
 
            return 0
845
 
 
846
 
        # two loops so that we do not change ourselves before verifying it
847
 
        # will be ok
848
 
        # work through in index order to make sure we get all dependencies
849
 
        names_to_join = []
850
 
        processed = 0
851
 
        # get the selected versions only that are in other.versions.
852
 
        version_ids = set(other.versions()).intersection(set(version_ids))
853
 
        # pull in the referenced graph.
854
 
        version_ids = other.get_ancestry(version_ids)
855
 
        pending_parents = other.get_parent_map(version_ids)
856
 
        pending_graph = pending_parents.items()
857
 
        if len(pending_graph) != len(version_ids):
858
 
            raise RevisionNotPresent(
859
 
                set(version_ids) - set(pending_parents.keys()), self)
860
 
        for name in topo_sort(pending_graph):
861
 
            other_idx = other._name_map[name]
862
 
            # returns True if we have it, False if we need it.
863
 
            if not self._check_version_consistent(other, other_idx, name):
864
 
                names_to_join.append((other_idx, name))
865
 
            processed += 1
866
 
 
867
 
        if pb and not msg:
868
 
            msg = 'weave join'
869
 
 
870
 
        merged = 0
871
 
        time0 = time.time()
872
 
        for other_idx, name in names_to_join:
873
 
            # TODO: If all the parents of the other version are already
874
 
            # present then we can avoid some work by just taking the delta
875
 
            # and adjusting the offsets.
876
 
            new_parents = self._imported_parents(other, other_idx)
877
 
            sha1 = other._sha1s[other_idx]
878
 
 
879
 
            merged += 1
880
 
 
881
 
            if pb:
882
 
                pb.update(msg, merged, len(names_to_join))
883
 
           
884
 
            lines = other.get_lines(other_idx)
885
 
            self._add(name, lines, new_parents, sha1)
886
 
 
887
 
        mutter("merged = %d, processed = %d, file_id=%s; deltat=%d"%(
888
 
                merged, processed, self._weave_name, time.time()-time0))
889
 
 
890
849
    def _imported_parents(self, other, other_idx):
891
850
        """Return list of parents in self corresponding to indexes in other."""
892
851
        new_parents = []
954
913
        
955
914
        :param create: If not True, only open an existing knit.
956
915
        """
957
 
        super(WeaveFile, self).__init__(name, access_mode, get_scope=get_scope)
 
916
        super(WeaveFile, self).__init__(name, access_mode, get_scope=get_scope,
 
917
            allow_reserved=False)
958
918
        self._transport = transport
959
919
        self._filemode = filemode
960
920
        try:
989
949
        sio = StringIO()
990
950
        write_weave_v5(self, sio)
991
951
        sio.seek(0)
992
 
        self._transport.put_file(self._weave_name + WeaveFile.WEAVE_SUFFIX,
993
 
                                 sio,
994
 
                                 self._filemode)
 
952
        bytes = sio.getvalue()
 
953
        path = self._weave_name + WeaveFile.WEAVE_SUFFIX
 
954
        try:
 
955
            self._transport.put_bytes(path, bytes, self._filemode)
 
956
        except errors.NoSuchFile:
 
957
            self._transport.mkdir(dirname(path))
 
958
            self._transport.put_bytes(path, bytes, self._filemode)
995
959
 
996
960
    @staticmethod
997
961
    def get_suffixes():
1033
997
    # map from version name -> all parent names
1034
998
    combined_parents = _reweave_parent_graphs(wa, wb)
1035
999
    mutter("combined parents: %r", combined_parents)
1036
 
    order = topo_sort(combined_parents.iteritems())
 
1000
    order = tsort.topo_sort(combined_parents.iteritems())
1037
1001
    mutter("order to reweave: %r", order)
1038
1002
 
1039
1003
    if pb and not msg:
1272
1236
if __name__ == '__main__':
1273
1237
    import sys
1274
1238
    sys.exit(main(sys.argv))
1275
 
 
1276
 
 
1277
 
class InterWeave(InterVersionedFile):
1278
 
    """Optimised code paths for weave to weave operations."""
1279
 
    
1280
 
    _matching_file_from_factory = staticmethod(WeaveFile)
1281
 
    _matching_file_to_factory = staticmethod(WeaveFile)
1282
 
    
1283
 
    @staticmethod
1284
 
    def is_compatible(source, target):
1285
 
        """Be compatible with weaves."""
1286
 
        try:
1287
 
            return (isinstance(source, Weave) and
1288
 
                    isinstance(target, Weave))
1289
 
        except AttributeError:
1290
 
            return False
1291
 
 
1292
 
    def join(self, pb=None, msg=None, version_ids=None, ignore_missing=False):
1293
 
        """See InterVersionedFile.join."""
1294
 
        version_ids = self._get_source_version_ids(version_ids, ignore_missing)
1295
 
        if self.target.versions() == [] and version_ids is None:
1296
 
            self.target._copy_weave_content(self.source)
1297
 
            return
1298
 
        self.target._join(self.source, pb, msg, version_ids, ignore_missing)
1299
 
 
1300
 
 
1301
 
InterVersionedFile.register_optimiser(InterWeave)