~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Martin Pool
  • Date: 2005-07-22 19:44:09 UTC
  • Revision ID: mbp@sourcefrog.net-20050722194409-9f99ffe3d62cf8c0
- tidy up imports to use full module names

Show diffs side-by-side

added added

removed removed

Lines of Context:
45
45
 
46
46
# with delta folded in and mutation of the list, 36.13s
47
47
 
48
 
# with all this and simplification of add code, 33s
49
 
 
50
 
 
51
 
 
 
48
# with all this and simplification of add code, 33s 
52
49
 
53
50
 
54
51
# TODO: Perhaps have copy method for Weave instances?
62
59
# binaries, perhaps by naively splitting on \n or perhaps using
63
60
# something like a rolling checksum.
64
61
 
 
62
# TODO: Track version names as well as indexes. 
 
63
 
65
64
# TODO: End marker for each version so we can stop reading?
66
65
 
67
66
# TODO: Check that no insertion occurs inside a deletion that was
76
75
# description of which revisions include it.  Nice for checking all
77
76
# shas in parallel.
78
77
 
79
 
# TODO: Using a single _extract routine and then processing the output
80
 
# is probably inefficient.  It's simple enough that we can afford to
81
 
# have slight specializations for different ways its used: annotate,
82
 
# basis for add, get, etc.
83
 
 
84
 
# TODO: Perhaps the API should work only in names to hide the integer
85
 
# indexes from the user?
86
 
 
87
 
 
88
 
 
89
 
import sha
90
78
 
91
79
 
92
80
 
113
101
 
114
102
    * a nonnegative index number.
115
103
 
116
 
    * a version-id string. (not implemented yet)
 
104
    * a version-id string.
117
105
 
118
106
    Typically the index number will be valid only inside this weave and
119
107
    the version-id is used to reference it in the larger world.
130
118
    The instruction can be '{' or '}' for an insertion block, and '['
131
119
    and ']' for a deletion block respectively.  The version is the
132
120
    integer version index.  There is no replace operator, only deletes
133
 
    and inserts.  For '}', the end of an insertion, there is no
134
 
    version parameter because it always closes the most recently
135
 
    opened insertion.
 
121
    and inserts.
136
122
 
137
123
    Constraints/notes:
138
124
 
174
160
        each version; the parent's parents are implied.
175
161
 
176
162
    _sha1s
177
 
        List of hex SHA-1 of each version.
178
 
 
179
 
    _names
180
 
        List of symbolic names for each version.  Each should be unique.
181
 
 
182
 
    _name_map
183
 
        For each name, the version number.
 
163
        List of hex SHA-1 of each version, or None if not recorded.
184
164
    """
185
165
 
186
 
    __slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map']
 
166
    __slots__ = ['_weave', '_parents', '_sha1s']
187
167
    
188
168
    def __init__(self):
189
169
        self._weave = []
190
170
        self._parents = []
191
171
        self._sha1s = []
192
 
        self._names = []
193
 
        self._name_map = {}
194
172
 
195
173
 
196
174
    def __eq__(self, other):
197
175
        if not isinstance(other, Weave):
198
176
            return False
199
177
        return self._parents == other._parents \
200
 
               and self._weave == other._weave \
201
 
               and self._sha1s == other._sha1s 
 
178
               and self._weave == other._weave
 
179
    
202
180
 
203
 
    
204
181
    def __ne__(self, other):
205
182
        return not self.__eq__(other)
206
183
 
207
 
 
208
 
    def lookup(self, name):
209
 
        try:
210
 
            return self._name_map[name]
211
 
        except KeyError:
212
 
            raise WeaveError("name %s not present in weave" % name)
213
 
 
214
184
        
215
 
    def add(self, name, parents, text):
 
185
    def add(self, parents, text):
216
186
        """Add a single text on top of the weave.
217
187
  
218
188
        Returns the index number of the newly added version.
219
189
 
220
 
        name
221
 
            Symbolic name for this version.
222
 
            (Typically the revision-id of the revision that added it.)
223
 
 
224
190
        parents
225
191
            List or set of direct parent version numbers.
226
192
            
227
193
        text
228
194
            Sequence of lines to be added in the new version."""
229
195
 
230
 
        assert isinstance(name, basestring)
231
 
        if name in self._name_map:
232
 
            raise WeaveError("name %r already present in weave" % name)
233
 
        
234
196
        self._check_versions(parents)
235
197
        ## self._check_lines(text)
236
198
        new_version = len(self._parents)
237
199
 
 
200
        import sha
238
201
        s = sha.new()
239
202
        map(s.update, text)
240
203
        sha1 = s.hexdigest()
241
204
        del s
242
205
 
243
 
        # if we abort after here the (in-memory) weave will be corrupt because only
244
 
        # some fields are updated
245
 
        self._parents.append(parents[:])
 
206
        # if we abort after here the weave will be corrupt
 
207
        self._parents.append(frozenset(parents))
246
208
        self._sha1s.append(sha1)
247
 
        self._names.append(name)
248
 
        self._name_map[name] = new_version
249
209
 
250
210
            
251
211
        if not parents:
256
216
            if text:
257
217
                self._weave.append(('{', new_version))
258
218
                self._weave.extend(text)
259
 
                self._weave.append(('}', None))
 
219
                self._weave.append(('}', new_version))
260
220
        
261
221
            return new_version
262
222
 
278
238
            basis_lineno.append(lineno)
279
239
            basis_lines.append(line)
280
240
 
281
 
        # another small special case: a merge, producing the same text
282
 
        # as auto-merge
 
241
        # another small special case: a merge, producing the same text as auto-merge
283
242
        if text == basis_lines:
284
243
            return new_version            
285
244
 
328
287
                # we don't destroy ourselves
329
288
                i = i2 + offset
330
289
                self._weave[i:i] = ([('{', new_version)] 
331
 
                                    + text[j1:j2] 
332
 
                                    + [('}', None)])
 
290
                                + text[j1:j2] 
 
291
                                + [('}', new_version)])
333
292
                offset += 2 + (j2 - j1)
334
293
 
335
294
        return new_version
425
384
                if c == '{':
426
385
                    istack.append(v)
427
386
                elif c == '}':
428
 
                    istack.pop()
 
387
                    oldv = istack.pop()
429
388
                elif c == '[':
430
389
                    assert v not in dset
431
390
                    dset.add(v)
472
431
                    assert v not in istack
473
432
                    istack.append(v)
474
433
                elif c == '}':
475
 
                    istack.pop()
 
434
                    oldv = istack.pop()
 
435
                    assert oldv == v
476
436
                elif c == '[':
477
437
                    if v in included:
478
438
                        assert v not in dset
548
508
 
549
509
        # try extracting all versions; this is a bit slow and parallel
550
510
        # extraction could be used
 
511
        import sha
551
512
        nv = self.numversions()
552
513
        for version in range(nv):
553
514
            if progress_bar:
709
670
 
710
671
 
711
672
 
712
 
def weave_toc(w):
713
 
    """Show the weave's table-of-contents"""
714
 
    print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
715
 
    for i in (6, 50, 10, 10):
 
673
def weave_info(w):
 
674
    """Show some text information about the weave."""
 
675
    print '%6s %40s %20s' % ('ver', 'sha1', 'parents')
 
676
    for i in (6, 40, 20):
716
677
        print '-' * i,
717
678
    print
718
679
    for i in range(w.numversions()):
719
680
        sha1 = w._sha1s[i]
720
 
        name = w._names[i]
721
 
        parent_str = ' '.join(map(str, w._parents[i]))
722
 
        print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
 
681
        print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
723
682
 
724
683
 
725
684
 
747
706
    print 'weave file        %9d bytes' % weave_size
748
707
    print 'total contents    %9d bytes' % total
749
708
    print 'compression ratio %9.2fx' % (float(total) / float(weave_size))
750
 
    if vers:
751
 
        avg = total/vers
752
 
        print 'average size      %9d bytes' % avg
753
 
        print 'relative size     %9.2fx' % (float(weave_size) / float(avg))
 
709
 
754
710
 
755
711
 
756
712
def usage():
765
721
        Write out specified version.
766
722
    weave check WEAVEFILE
767
723
        Check consistency of all versions.
768
 
    weave toc WEAVEFILE
 
724
    weave info WEAVEFILE
769
725
        Display table of contents.
770
 
    weave add WEAVEFILE NAME [BASE...] < NEWTEXT
 
726
    weave add WEAVEFILE [BASE...] < NEWTEXT
771
727
        Add NEWTEXT, with specified parent versions.
772
728
    weave annotate WEAVEFILE VERSION
773
729
        Display origin of each line.
780
736
 
781
737
    % weave init foo.weave
782
738
    % vi foo.txt
783
 
    % weave add foo.weave ver0 < foo.txt
 
739
    % weave add foo.weave < foo.txt
784
740
    added version 0
785
741
 
786
742
    (create updated version)
787
743
    % vi foo.txt
788
744
    % weave get foo.weave 0 | diff -u - foo.txt
789
 
    % weave add foo.weave ver1 0 < foo.txt
 
745
    % weave add foo.weave 0 < foo.txt
790
746
    added version 1
791
747
 
792
748
    % weave get foo.weave 0 > foo.txt       (create forked version)
793
749
    % vi foo.txt
794
 
    % weave add foo.weave ver2 0 < foo.txt
 
750
    % weave add foo.weave 0 < foo.txt
795
751
    added version 2
796
752
 
797
753
    % weave merge foo.weave 1 2 > foo.txt   (merge them)
798
754
    % vi foo.txt                            (resolve conflicts)
799
 
    % weave add foo.weave merged 1 2 < foo.txt     (commit merged version)     
 
755
    % weave add foo.weave 1 2 < foo.txt     (commit merged version)     
800
756
    
801
757
"""
802
758
    
808
764
    from weavefile import write_weave, read_weave
809
765
    from bzrlib.progress import ProgressBar
810
766
 
811
 
    try:
812
 
        import psyco
813
 
        psyco.full()
814
 
    except ImportError:
815
 
        pass
816
 
 
817
 
    if len(argv) < 2:
818
 
        usage()
819
 
        return 0
 
767
    #import psyco
 
768
    #psyco.full()
820
769
 
821
770
    cmd = argv[1]
822
771
 
828
777
    elif cmd == 'add':
829
778
        w = readit()
830
779
        # at the moment, based on everything in the file
831
 
        name = argv[3]
832
 
        parents = map(int, argv[4:])
 
780
        parents = map(int, argv[3:])
833
781
        lines = sys.stdin.readlines()
834
 
        ver = w.add(name, parents, lines)
 
782
        ver = w.add(parents, lines)
835
783
        write_weave(w, file(argv[2], 'wb'))
836
 
        print 'added version %r %d' % (name, ver)
 
784
        print 'added version %d' % ver
837
785
    elif cmd == 'init':
838
786
        fn = argv[2]
839
787
        if os.path.exists(fn):
861
809
                print '%5d | %s' % (origin, text)
862
810
                lasto = origin
863
811
                
864
 
    elif cmd == 'toc':
865
 
        weave_toc(readit())
 
812
    elif cmd == 'info':
 
813
        weave_info(readit())
866
814
 
867
815
    elif cmd == 'stats':
868
816
        weave_stats(argv[2])
917
865
        raise ValueError('unknown command %r' % cmd)
918
866
    
919
867
 
920
 
 
921
 
def profile_main(argv): 
922
 
    import tempfile, hotshot, hotshot.stats
923
 
 
924
 
    prof_f = tempfile.NamedTemporaryFile()
925
 
 
926
 
    prof = hotshot.Profile(prof_f.name)
927
 
 
928
 
    ret = prof.runcall(main, argv)
929
 
    prof.close()
930
 
 
931
 
    stats = hotshot.stats.load(prof_f.name)
932
 
    #stats.strip_dirs()
933
 
    stats.sort_stats('cumulative')
934
 
    ## XXX: Might like to write to stderr or the trace file instead but
935
 
    ## print_stats seems hardcoded to stdout
936
 
    stats.print_stats(20)
937
 
            
938
 
    return ret
939
 
 
940
 
 
941
868
if __name__ == '__main__':
942
869
    import sys
943
 
    if '--profile' in sys.argv:
944
 
        args = sys.argv[:]
945
 
        args.remove('--profile')
946
 
        sys.exit(profile_main(args))
947
 
    else:
948
 
        sys.exit(main(sys.argv))
949
 
 
 
870
    sys.exit(main(sys.argv))