~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Martin Pool
  • Date: 2005-09-06 07:39:09 UTC
  • Revision ID: mbp@sourcefrog.net-20050906073909-234eb078dfff15d9
- improved check for branch version

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