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?
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
675
"""Show some text information about the weave."""
676
print '%6s %40s %20s' % ('ver', 'sha1', 'parents')
677
for i in (6, 40, 20):
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):
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])))
721
parent_str = ' '.join(map(str, w._parents[i]))
722
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
725
765
Write out specified version.
726
766
weave check WEAVEFILE
727
767
Check consistency of all versions.
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.
741
781
% weave init foo.weave
743
% weave add foo.weave < foo.txt
783
% weave add foo.weave ver0 < foo.txt
746
786
(create updated version)
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
752
792
% weave get foo.weave 0 > foo.txt (create forked version)
754
% weave add foo.weave 0 < foo.txt
794
% weave add foo.weave ver2 0 < foo.txt
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)
781
828
elif cmd == 'add':
783
830
# at the moment, based on everything in the file
784
parents = map(int, 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':
791
839
if os.path.exists(fn):
869
917
raise ValueError('unknown command %r' % cmd)
921
def profile_main(argv):
922
import tempfile, hotshot, hotshot.stats
924
prof_f = tempfile.NamedTemporaryFile()
926
prof = hotshot.Profile(prof_f.name)
928
ret = prof.runcall(main, argv)
931
stats = hotshot.stats.load(prof_f.name)
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)
872
941
if __name__ == '__main__':
874
sys.exit(main(sys.argv))
943
if '--profile' in sys.argv:
945
args.remove('--profile')
946
sys.exit(profile_main(args))
948
sys.exit(main(sys.argv))