~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 22:37:53 UTC
  • Revision ID: mbp@sourcefrog.net-20050722223753-7dced4e32d3ce21d
- add the start of a test for inventory file-id matching

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
 
from cStringIO import StringIO
 
78
 
91
79
 
92
80
 
93
81
class WeaveError(Exception):
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)
451
410
 
452
411
        The set typically but not necessarily corresponds to a version.
453
412
        """
454
 
        for i in versions:
455
 
            if not isinstance(i, int):
456
 
                raise ValueError(i)
457
 
            
458
413
        included = self.inclusions(versions)
459
414
 
460
415
        istack = []
476
431
                    assert v not in istack
477
432
                    istack.append(v)
478
433
                elif c == '}':
479
 
                    istack.pop()
 
434
                    oldv = istack.pop()
 
435
                    assert oldv == v
480
436
                elif c == '[':
481
437
                    if v in included:
482
438
                        assert v not in dset
511
467
            yield line
512
468
 
513
469
 
514
 
    def get_text(self, version):
515
 
        assert isinstance(version, int)
516
 
        s = StringIO()
517
 
        s.writelines(self.get_iter(version))
518
 
        return s.getvalue()
519
 
 
520
 
 
521
470
    def get(self, index):
522
471
        return list(self.get_iter(index))
523
472
 
559
508
 
560
509
        # try extracting all versions; this is a bit slow and parallel
561
510
        # extraction could be used
 
511
        import sha
562
512
        nv = self.numversions()
563
513
        for version in range(nv):
564
514
            if progress_bar:
720
670
 
721
671
 
722
672
 
723
 
def weave_toc(w):
724
 
    """Show the weave's table-of-contents"""
725
 
    print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
726
 
    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):
727
677
        print '-' * i,
728
678
    print
729
679
    for i in range(w.numversions()):
730
680
        sha1 = w._sha1s[i]
731
 
        name = w._names[i]
732
 
        parent_str = ' '.join(map(str, w._parents[i]))
733
 
        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])))
734
682
 
735
683
 
736
684
 
758
706
    print 'weave file        %9d bytes' % weave_size
759
707
    print 'total contents    %9d bytes' % total
760
708
    print 'compression ratio %9.2fx' % (float(total) / float(weave_size))
761
 
    if vers:
762
 
        avg = total/vers
763
 
        print 'average size      %9d bytes' % avg
764
 
        print 'relative size     %9.2fx' % (float(weave_size) / float(avg))
 
709
 
765
710
 
766
711
 
767
712
def usage():
776
721
        Write out specified version.
777
722
    weave check WEAVEFILE
778
723
        Check consistency of all versions.
779
 
    weave toc WEAVEFILE
 
724
    weave info WEAVEFILE
780
725
        Display table of contents.
781
 
    weave add WEAVEFILE NAME [BASE...] < NEWTEXT
 
726
    weave add WEAVEFILE [BASE...] < NEWTEXT
782
727
        Add NEWTEXT, with specified parent versions.
783
728
    weave annotate WEAVEFILE VERSION
784
729
        Display origin of each line.
791
736
 
792
737
    % weave init foo.weave
793
738
    % vi foo.txt
794
 
    % weave add foo.weave ver0 < foo.txt
 
739
    % weave add foo.weave < foo.txt
795
740
    added version 0
796
741
 
797
742
    (create updated version)
798
743
    % vi foo.txt
799
744
    % weave get foo.weave 0 | diff -u - foo.txt
800
 
    % weave add foo.weave ver1 0 < foo.txt
 
745
    % weave add foo.weave 0 < foo.txt
801
746
    added version 1
802
747
 
803
748
    % weave get foo.weave 0 > foo.txt       (create forked version)
804
749
    % vi foo.txt
805
 
    % weave add foo.weave ver2 0 < foo.txt
 
750
    % weave add foo.weave 0 < foo.txt
806
751
    added version 2
807
752
 
808
753
    % weave merge foo.weave 1 2 > foo.txt   (merge them)
809
754
    % vi foo.txt                            (resolve conflicts)
810
 
    % weave add foo.weave merged 1 2 < foo.txt     (commit merged version)     
 
755
    % weave add foo.weave 1 2 < foo.txt     (commit merged version)     
811
756
    
812
757
"""
813
758
    
819
764
    from weavefile import write_weave, read_weave
820
765
    from bzrlib.progress import ProgressBar
821
766
 
822
 
    try:
823
 
        import psyco
824
 
        psyco.full()
825
 
    except ImportError:
826
 
        pass
827
 
 
828
 
    if len(argv) < 2:
829
 
        usage()
830
 
        return 0
 
767
    #import psyco
 
768
    #psyco.full()
831
769
 
832
770
    cmd = argv[1]
833
771
 
839
777
    elif cmd == 'add':
840
778
        w = readit()
841
779
        # at the moment, based on everything in the file
842
 
        name = argv[3]
843
 
        parents = map(int, argv[4:])
 
780
        parents = map(int, argv[3:])
844
781
        lines = sys.stdin.readlines()
845
 
        ver = w.add(name, parents, lines)
 
782
        ver = w.add(parents, lines)
846
783
        write_weave(w, file(argv[2], 'wb'))
847
 
        print 'added version %r %d' % (name, ver)
 
784
        print 'added version %d' % ver
848
785
    elif cmd == 'init':
849
786
        fn = argv[2]
850
787
        if os.path.exists(fn):
872
809
                print '%5d | %s' % (origin, text)
873
810
                lasto = origin
874
811
                
875
 
    elif cmd == 'toc':
876
 
        weave_toc(readit())
 
812
    elif cmd == 'info':
 
813
        weave_info(readit())
877
814
 
878
815
    elif cmd == 'stats':
879
816
        weave_stats(argv[2])
928
865
        raise ValueError('unknown command %r' % cmd)
929
866
    
930
867
 
931
 
 
932
 
def profile_main(argv): 
933
 
    import tempfile, hotshot, hotshot.stats
934
 
 
935
 
    prof_f = tempfile.NamedTemporaryFile()
936
 
 
937
 
    prof = hotshot.Profile(prof_f.name)
938
 
 
939
 
    ret = prof.runcall(main, argv)
940
 
    prof.close()
941
 
 
942
 
    stats = hotshot.stats.load(prof_f.name)
943
 
    #stats.strip_dirs()
944
 
    stats.sort_stats('cumulative')
945
 
    ## XXX: Might like to write to stderr or the trace file instead but
946
 
    ## print_stats seems hardcoded to stdout
947
 
    stats.print_stats(20)
948
 
            
949
 
    return ret
950
 
 
951
 
 
952
868
if __name__ == '__main__':
953
869
    import sys
954
 
    if '--profile' in sys.argv:
955
 
        args = sys.argv[:]
956
 
        args.remove('--profile')
957
 
        sys.exit(profile_main(args))
958
 
    else:
959
 
        sys.exit(main(sys.argv))
960
 
 
 
870
    sys.exit(main(sys.argv))