~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Aaron Bentley
  • Date: 2005-09-18 19:21:48 UTC
  • mto: (1185.1.29)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: aaron.bentley@utoronto.ca-20050918192148-3f9373ac85a83b02
Refactored and documented graph stuff

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
 
288
328
                # we don't destroy ourselves
289
329
                i = i2 + offset
290
330
                self._weave[i:i] = ([('{', new_version)] 
291
 
                                + text[j1:j2] 
292
 
                                + [('}', new_version)])
 
331
                                    + text[j1:j2] 
 
332
                                    + [('}', None)])
293
333
                offset += 2 + (j2 - j1)
294
334
 
295
335
        return new_version
385
425
                if c == '{':
386
426
                    istack.append(v)
387
427
                elif c == '}':
388
 
                    oldv = istack.pop()
 
428
                    istack.pop()
389
429
                elif c == '[':
390
430
                    assert v not in dset
391
431
                    dset.add(v)
432
472
                    assert v not in istack
433
473
                    istack.append(v)
434
474
                elif c == '}':
435
 
                    oldv = istack.pop()
436
 
                    assert oldv == v
 
475
                    istack.pop()
437
476
                elif c == '[':
438
477
                    if v in included:
439
478
                        assert v not in dset
509
548
 
510
549
        # try extracting all versions; this is a bit slow and parallel
511
550
        # extraction could be used
512
 
        import sha
513
551
        nv = self.numversions()
514
552
        for version in range(nv):
515
553
            if progress_bar:
671
709
 
672
710
 
673
711
 
674
 
def weave_info(w):
675
 
    """Show some text information about the weave."""
676
 
    print '%6s %40s %20s' % ('ver', 'sha1', 'parents')
677
 
    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):
678
716
        print '-' * i,
679
717
    print
680
718
    for i in range(w.numversions()):
681
719
        sha1 = w._sha1s[i]
682
 
        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)
683
723
 
684
724
 
685
725
 
725
765
        Write out specified version.
726
766
    weave check WEAVEFILE
727
767
        Check consistency of all versions.
728
 
    weave info WEAVEFILE
 
768
    weave toc WEAVEFILE
729
769
        Display table of contents.
730
 
    weave add WEAVEFILE [BASE...] < NEWTEXT
 
770
    weave add WEAVEFILE NAME [BASE...] < NEWTEXT
731
771
        Add NEWTEXT, with specified parent versions.
732
772
    weave annotate WEAVEFILE VERSION
733
773
        Display origin of each line.
740
780
 
741
781
    % weave init foo.weave
742
782
    % vi foo.txt
743
 
    % weave add foo.weave < foo.txt
 
783
    % weave add foo.weave ver0 < foo.txt
744
784
    added version 0
745
785
 
746
786
    (create updated version)
747
787
    % vi foo.txt
748
788
    % weave get foo.weave 0 | diff -u - foo.txt
749
 
    % weave add foo.weave 0 < foo.txt
 
789
    % weave add foo.weave ver1 0 < foo.txt
750
790
    added version 1
751
791
 
752
792
    % weave get foo.weave 0 > foo.txt       (create forked version)
753
793
    % vi foo.txt
754
 
    % weave add foo.weave 0 < foo.txt
 
794
    % weave add foo.weave ver2 0 < foo.txt
755
795
    added version 2
756
796
 
757
797
    % weave merge foo.weave 1 2 > foo.txt   (merge them)
758
798
    % vi foo.txt                            (resolve conflicts)
759
 
    % weave add foo.weave 1 2 < foo.txt     (commit merged version)     
 
799
    % weave add foo.weave merged 1 2 < foo.txt     (commit merged version)     
760
800
    
761
801
"""
762
802
    
768
808
    from weavefile import write_weave, read_weave
769
809
    from bzrlib.progress import ProgressBar
770
810
 
771
 
    #import psyco
772
 
    #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
773
820
 
774
821
    cmd = argv[1]
775
822
 
781
828
    elif cmd == 'add':
782
829
        w = readit()
783
830
        # at the moment, based on everything in the file
784
 
        parents = map(int, argv[3:])
 
831
        name = argv[3]
 
832
        parents = map(int, argv[4:])
785
833
        lines = sys.stdin.readlines()
786
 
        ver = w.add(parents, lines)
 
834
        ver = w.add(name, parents, lines)
787
835
        write_weave(w, file(argv[2], 'wb'))
788
 
        print 'added version %d' % ver
 
836
        print 'added version %r %d' % (name, ver)
789
837
    elif cmd == 'init':
790
838
        fn = argv[2]
791
839
        if os.path.exists(fn):
813
861
                print '%5d | %s' % (origin, text)
814
862
                lasto = origin
815
863
                
816
 
    elif cmd == 'info':
817
 
        weave_info(readit())
 
864
    elif cmd == 'toc':
 
865
        weave_toc(readit())
818
866
 
819
867
    elif cmd == 'stats':
820
868
        weave_stats(argv[2])
869
917
        raise ValueError('unknown command %r' % cmd)
870
918
    
871
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
 
872
941
if __name__ == '__main__':
873
942
    import sys
874
 
    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