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
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.
62
# TODO: Track version names as well as indexes.
64
65
# TODO: End marker for each version so we can stop reading?
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.
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
81
93
class WeaveError(Exception):
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
133
and inserts. For '}', the end of an insertion, there is no
134
version parameter because it always closes the most recently
123
137
Constraints/notes:
160
174
each version; the parent's parents are implied.
163
List of hex SHA-1 of each version, or None if not recorded.
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.
166
__slots__ = ['_weave', '_parents', '_sha1s']
186
__slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map']
168
188
def __init__(self):
170
190
self._parents = []
174
196
def __eq__(self, other):
175
197
if not isinstance(other, Weave):
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
181
204
def __ne__(self, other):
182
205
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)
185
def add(self, parents, text):
215
def add(self, name, parents, text):
186
216
"""Add a single text on top of the weave.
188
218
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.)
191
225
List or set of direct parent version numbers.
194
228
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)
196
234
self._check_versions(parents)
197
235
## self._check_lines(text)
198
236
new_version = len(self._parents)
202
239
map(s.update, text)
203
240
sha1 = s.hexdigest()
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
674
"""Show some text information about the weave."""
675
print '%6s %40s %20s' % ('ver', 'sha1', 'parents')
676
for i in (6, 40, 20):
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):
679
729
for i in range(w.numversions()):
680
730
sha1 = w._sha1s[i]
681
print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
732
parent_str = ' '.join(map(str, w._parents[i]))
733
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
706
758
print 'weave file %9d bytes' % weave_size
707
759
print 'total contents %9d bytes' % total
708
760
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))
721
776
Write out specified version.
722
777
weave check WEAVEFILE
723
778
Check consistency of all versions.
725
780
Display table of contents.
726
weave add WEAVEFILE [BASE...] < NEWTEXT
781
weave add WEAVEFILE NAME [BASE...] < NEWTEXT
727
782
Add NEWTEXT, with specified parent versions.
728
783
weave annotate WEAVEFILE VERSION
729
784
Display origin of each line.
737
792
% weave init foo.weave
739
% weave add foo.weave < foo.txt
794
% weave add foo.weave ver0 < foo.txt
742
797
(create updated version)
744
799
% weave get foo.weave 0 | diff -u - foo.txt
745
% weave add foo.weave 0 < foo.txt
800
% weave add foo.weave ver1 0 < foo.txt
748
803
% weave get foo.weave 0 > foo.txt (create forked version)
750
% weave add foo.weave 0 < foo.txt
805
% weave add foo.weave ver2 0 < foo.txt
753
808
% weave merge foo.weave 1 2 > foo.txt (merge them)
754
809
% vi foo.txt (resolve conflicts)
755
% weave add foo.weave 1 2 < foo.txt (commit merged version)
810
% weave add foo.weave merged 1 2 < foo.txt (commit merged version)
777
839
elif cmd == 'add':
779
841
# at the moment, based on everything in the file
780
parents = map(int, argv[3:])
843
parents = map(int, argv[4:])
781
844
lines = sys.stdin.readlines()
782
ver = w.add(parents, lines)
845
ver = w.add(name, parents, lines)
783
846
write_weave(w, file(argv[2], 'wb'))
784
print 'added version %d' % ver
847
print 'added version %r %d' % (name, ver)
785
848
elif cmd == 'init':
787
850
if os.path.exists(fn):
865
928
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)
868
952
if __name__ == '__main__':
870
sys.exit(main(sys.argv))
954
if '--profile' in sys.argv:
956
args.remove('--profile')
957
sys.exit(profile_main(args))
959
sys.exit(main(sys.argv))