46
46
# with delta folded in and mutation of the list, 36.13s
48
# with all this and simplification of add code, 33s
48
# with all this and simplification of add code, 33s
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.
62
# TODO: Track version names as well as indexes.
65
64
# TODO: End marker for each version so we can stop reading?
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.
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.
84
# TODO: Perhaps the API should work only in names to hide the integer
85
# indexes from the user?
90
from cStringIO import StringIO
93
81
class WeaveError(Exception):
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
137
123
Constraints/notes:
174
160
each version; the parent's parents are implied.
177
List of hex SHA-1 of each version.
180
List of symbolic names for each version. Each should be unique.
183
For each name, the version number.
163
List of hex SHA-1 of each version, or None if not recorded.
186
__slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map']
166
__slots__ = ['_weave', '_parents', '_sha1s']
188
168
def __init__(self):
190
170
self._parents = []
196
174
def __eq__(self, other):
197
175
if not isinstance(other, Weave):
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
204
181
def __ne__(self, other):
205
182
return not self.__eq__(other)
208
def lookup(self, name):
210
return self._name_map[name]
212
raise WeaveError("name %s not present in weave" % name)
215
def add(self, name, parents, text):
185
def add(self, parents, text):
216
186
"""Add a single text on top of the weave.
218
188
Returns the index number of the newly added version.
221
Symbolic name for this version.
222
(Typically the revision-id of the revision that added it.)
225
191
List or set of direct parent version numbers.
228
194
Sequence of lines to be added in the new version."""
230
assert isinstance(name, basestring)
231
if name in self._name_map:
232
raise WeaveError("name %r already present in weave" % name)
234
196
self._check_versions(parents)
235
197
## self._check_lines(text)
236
198
new_version = len(self._parents)
239
202
map(s.update, text)
240
203
sha1 = s.hexdigest()
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
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):
674
"""Show some text information about the weave."""
675
print '%6s %40s %20s' % ('ver', 'sha1', 'parents')
676
for i in (6, 40, 20):
729
679
for i in range(w.numversions()):
730
680
sha1 = w._sha1s[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])))
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))
763
print 'average size %9d bytes' % avg
764
print 'relative size %9.2fx' % (float(weave_size) / float(avg))
776
721
Write out specified version.
777
722
weave check WEAVEFILE
778
723
Check consistency of all versions.
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.
792
737
% weave init foo.weave
794
% weave add foo.weave ver0 < foo.txt
739
% weave add foo.weave < foo.txt
797
742
(create updated version)
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
803
748
% weave get foo.weave 0 > foo.txt (create forked version)
805
% weave add foo.weave ver2 0 < foo.txt
750
% weave add foo.weave 0 < foo.txt
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)
839
777
elif cmd == 'add':
841
779
# at the moment, based on everything in the file
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':
850
787
if os.path.exists(fn):
928
865
raise ValueError('unknown command %r' % cmd)
932
def profile_main(argv):
933
import tempfile, hotshot, hotshot.stats
935
prof_f = tempfile.NamedTemporaryFile()
937
prof = hotshot.Profile(prof_f.name)
939
ret = prof.runcall(main, argv)
942
stats = hotshot.stats.load(prof_f.name)
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)
952
868
if __name__ == '__main__':
954
if '--profile' in sys.argv:
956
args.remove('--profile')
957
sys.exit(profile_main(args))
959
sys.exit(main(sys.argv))
870
sys.exit(main(sys.argv))