/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Martin Pool
  • Date: 2005-09-12 09:45:18 UTC
  • Revision ID: mbp@sourcefrog.net-20050912094518-46a8e0d6b02dda98
- hashcache should be written out if it can't be read
  
  otherwise it never gets created if it doesn't exist

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
 
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
 
90
from cStringIO import StringIO
79
91
 
80
92
 
81
93
class WeaveError(Exception):
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.
 
184
 
 
185
    _weave_name
 
186
        Descriptive name of this weave; typically the filename if known.
 
187
        Set by read_weave.
164
188
    """
165
189
 
166
 
    __slots__ = ['_weave', '_parents', '_sha1s']
 
190
    __slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
 
191
                 '_weave_name']
167
192
    
168
 
    def __init__(self):
 
193
    def __init__(self, weave_name=None):
169
194
        self._weave = []
170
195
        self._parents = []
171
196
        self._sha1s = []
 
197
        self._names = []
 
198
        self._name_map = {}
 
199
        self._weave_name = weave_name
172
200
 
173
201
 
174
202
    def __eq__(self, other):
175
203
        if not isinstance(other, Weave):
176
204
            return False
177
205
        return self._parents == other._parents \
178
 
               and self._weave == other._weave
 
206
               and self._weave == other._weave \
 
207
               and self._sha1s == other._sha1s 
 
208
 
179
209
    
180
 
 
181
210
    def __ne__(self, other):
182
211
        return not self.__eq__(other)
183
212
 
 
213
 
 
214
    def lookup(self, name):
 
215
        try:
 
216
            return self._name_map[name]
 
217
        except KeyError:
 
218
            raise WeaveError("name %s not present in weave %s" %
 
219
                             (name, self._weave_name))
 
220
 
184
221
        
185
 
    def add(self, parents, text):
 
222
    def add(self, name, parents, text):
186
223
        """Add a single text on top of the weave.
187
224
  
188
225
        Returns the index number of the newly added version.
189
226
 
 
227
        name
 
228
            Symbolic name for this version.
 
229
            (Typically the revision-id of the revision that added it.)
 
230
 
190
231
        parents
191
232
            List or set of direct parent version numbers.
192
233
            
193
234
        text
194
235
            Sequence of lines to be added in the new version."""
195
236
 
 
237
        assert isinstance(name, basestring)
 
238
        if name in self._name_map:
 
239
            raise WeaveError("name %r already present in weave" % name)
 
240
        
196
241
        self._check_versions(parents)
197
242
        ## self._check_lines(text)
198
243
        new_version = len(self._parents)
199
244
 
200
 
        import sha
201
245
        s = sha.new()
202
246
        map(s.update, text)
203
247
        sha1 = s.hexdigest()
204
248
        del s
205
249
 
206
 
        # if we abort after here the weave will be corrupt
207
 
        self._parents.append(frozenset(parents))
 
250
        # if we abort after here the (in-memory) weave will be corrupt because only
 
251
        # some fields are updated
 
252
        self._parents.append(parents[:])
208
253
        self._sha1s.append(sha1)
 
254
        self._names.append(name)
 
255
        self._name_map[name] = new_version
209
256
 
210
257
            
211
258
        if not parents:
216
263
            if text:
217
264
                self._weave.append(('{', new_version))
218
265
                self._weave.extend(text)
219
 
                self._weave.append(('}', new_version))
 
266
                self._weave.append(('}', None))
220
267
        
221
268
            return new_version
222
269
 
238
285
            basis_lineno.append(lineno)
239
286
            basis_lines.append(line)
240
287
 
241
 
        # another small special case: a merge, producing the same text as auto-merge
 
288
        # another small special case: a merge, producing the same text
 
289
        # as auto-merge
242
290
        if text == basis_lines:
243
291
            return new_version            
244
292
 
287
335
                # we don't destroy ourselves
288
336
                i = i2 + offset
289
337
                self._weave[i:i] = ([('{', new_version)] 
290
 
                                + text[j1:j2] 
291
 
                                + [('}', new_version)])
 
338
                                    + text[j1:j2] 
 
339
                                    + [('}', None)])
292
340
                offset += 2 + (j2 - j1)
293
341
 
294
342
        return new_version
384
432
                if c == '{':
385
433
                    istack.append(v)
386
434
                elif c == '}':
387
 
                    oldv = istack.pop()
 
435
                    istack.pop()
388
436
                elif c == '[':
389
437
                    assert v not in dset
390
438
                    dset.add(v)
410
458
 
411
459
        The set typically but not necessarily corresponds to a version.
412
460
        """
 
461
        for i in versions:
 
462
            if not isinstance(i, int):
 
463
                raise ValueError(i)
 
464
            
413
465
        included = self.inclusions(versions)
414
466
 
415
467
        istack = []
431
483
                    assert v not in istack
432
484
                    istack.append(v)
433
485
                elif c == '}':
434
 
                    oldv = istack.pop()
435
 
                    assert oldv == v
 
486
                    istack.pop()
436
487
                elif c == '[':
437
488
                    if v in included:
438
489
                        assert v not in dset
467
518
            yield line
468
519
 
469
520
 
 
521
    def get_text(self, version):
 
522
        assert isinstance(version, int)
 
523
        s = StringIO()
 
524
        s.writelines(self.get_iter(version))
 
525
        return s.getvalue()
 
526
 
 
527
 
470
528
    def get(self, index):
471
529
        return list(self.get_iter(index))
472
530
 
508
566
 
509
567
        # try extracting all versions; this is a bit slow and parallel
510
568
        # extraction could be used
511
 
        import sha
512
569
        nv = self.numversions()
513
570
        for version in range(nv):
514
571
            if progress_bar:
670
727
 
671
728
 
672
729
 
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):
 
730
def weave_toc(w):
 
731
    """Show the weave's table-of-contents"""
 
732
    print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
 
733
    for i in (6, 50, 10, 10):
677
734
        print '-' * i,
678
735
    print
679
736
    for i in range(w.numversions()):
680
737
        sha1 = w._sha1s[i]
681
 
        print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
 
738
        name = w._names[i]
 
739
        parent_str = ' '.join(map(str, w._parents[i]))
 
740
        print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
682
741
 
683
742
 
684
743
 
706
765
    print 'weave file        %9d bytes' % weave_size
707
766
    print 'total contents    %9d bytes' % total
708
767
    print 'compression ratio %9.2fx' % (float(total) / float(weave_size))
709
 
 
 
768
    if vers:
 
769
        avg = total/vers
 
770
        print 'average size      %9d bytes' % avg
 
771
        print 'relative size     %9.2fx' % (float(weave_size) / float(avg))
710
772
 
711
773
 
712
774
def usage():
721
783
        Write out specified version.
722
784
    weave check WEAVEFILE
723
785
        Check consistency of all versions.
724
 
    weave info WEAVEFILE
 
786
    weave toc WEAVEFILE
725
787
        Display table of contents.
726
 
    weave add WEAVEFILE [BASE...] < NEWTEXT
 
788
    weave add WEAVEFILE NAME [BASE...] < NEWTEXT
727
789
        Add NEWTEXT, with specified parent versions.
728
790
    weave annotate WEAVEFILE VERSION
729
791
        Display origin of each line.
736
798
 
737
799
    % weave init foo.weave
738
800
    % vi foo.txt
739
 
    % weave add foo.weave < foo.txt
 
801
    % weave add foo.weave ver0 < foo.txt
740
802
    added version 0
741
803
 
742
804
    (create updated version)
743
805
    % vi foo.txt
744
806
    % weave get foo.weave 0 | diff -u - foo.txt
745
 
    % weave add foo.weave 0 < foo.txt
 
807
    % weave add foo.weave ver1 0 < foo.txt
746
808
    added version 1
747
809
 
748
810
    % weave get foo.weave 0 > foo.txt       (create forked version)
749
811
    % vi foo.txt
750
 
    % weave add foo.weave 0 < foo.txt
 
812
    % weave add foo.weave ver2 0 < foo.txt
751
813
    added version 2
752
814
 
753
815
    % weave merge foo.weave 1 2 > foo.txt   (merge them)
754
816
    % vi foo.txt                            (resolve conflicts)
755
 
    % weave add foo.weave 1 2 < foo.txt     (commit merged version)     
 
817
    % weave add foo.weave merged 1 2 < foo.txt     (commit merged version)     
756
818
    
757
819
"""
758
820
    
764
826
    from weavefile import write_weave, read_weave
765
827
    from bzrlib.progress import ProgressBar
766
828
 
767
 
    #import psyco
768
 
    #psyco.full()
 
829
    try:
 
830
        import psyco
 
831
        psyco.full()
 
832
    except ImportError:
 
833
        pass
 
834
 
 
835
    if len(argv) < 2:
 
836
        usage()
 
837
        return 0
769
838
 
770
839
    cmd = argv[1]
771
840
 
777
846
    elif cmd == 'add':
778
847
        w = readit()
779
848
        # at the moment, based on everything in the file
780
 
        parents = map(int, argv[3:])
 
849
        name = argv[3]
 
850
        parents = map(int, argv[4:])
781
851
        lines = sys.stdin.readlines()
782
 
        ver = w.add(parents, lines)
 
852
        ver = w.add(name, parents, lines)
783
853
        write_weave(w, file(argv[2], 'wb'))
784
 
        print 'added version %d' % ver
 
854
        print 'added version %r %d' % (name, ver)
785
855
    elif cmd == 'init':
786
856
        fn = argv[2]
787
857
        if os.path.exists(fn):
809
879
                print '%5d | %s' % (origin, text)
810
880
                lasto = origin
811
881
                
812
 
    elif cmd == 'info':
813
 
        weave_info(readit())
 
882
    elif cmd == 'toc':
 
883
        weave_toc(readit())
814
884
 
815
885
    elif cmd == 'stats':
816
886
        weave_stats(argv[2])
865
935
        raise ValueError('unknown command %r' % cmd)
866
936
    
867
937
 
 
938
 
 
939
def profile_main(argv): 
 
940
    import tempfile, hotshot, hotshot.stats
 
941
 
 
942
    prof_f = tempfile.NamedTemporaryFile()
 
943
 
 
944
    prof = hotshot.Profile(prof_f.name)
 
945
 
 
946
    ret = prof.runcall(main, argv)
 
947
    prof.close()
 
948
 
 
949
    stats = hotshot.stats.load(prof_f.name)
 
950
    #stats.strip_dirs()
 
951
    stats.sort_stats('cumulative')
 
952
    ## XXX: Might like to write to stderr or the trace file instead but
 
953
    ## print_stats seems hardcoded to stdout
 
954
    stats.print_stats(20)
 
955
            
 
956
    return ret
 
957
 
 
958
 
868
959
if __name__ == '__main__':
869
960
    import sys
870
 
    sys.exit(main(sys.argv))
 
961
    if '--profile' in sys.argv:
 
962
        args = sys.argv[:]
 
963
        args.remove('--profile')
 
964
        sys.exit(profile_main(args))
 
965
    else:
 
966
        sys.exit(main(sys.argv))
 
967