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.
186
Descriptive name of this weave; typically the filename if known.
166
__slots__ = ['_weave', '_parents', '_sha1s']
190
__slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
193
def __init__(self, weave_name=None):
170
195
self._parents = []
199
self._weave_name = weave_name
174
202
def __eq__(self, other):
175
203
if not isinstance(other, Weave):
177
205
return self._parents == other._parents \
178
and self._weave == other._weave
206
and self._weave == other._weave \
207
and self._sha1s == other._sha1s
181
210
def __ne__(self, other):
182
211
return not self.__eq__(other)
214
def lookup(self, name):
216
return self._name_map[name]
218
raise WeaveError("name %s not present in weave %s" %
219
(name, self._weave_name))
222
def idx_to_name(self, version):
223
return self._names[version]
185
def add(self, parents, text):
226
def add(self, name, parents, text):
186
227
"""Add a single text on top of the weave.
188
229
Returns the index number of the newly added version.
232
Symbolic name for this version.
233
(Typically the revision-id of the revision that added it.)
191
236
List or set of direct parent version numbers.
194
239
Sequence of lines to be added in the new version."""
241
assert isinstance(name, basestring)
242
if name in self._name_map:
243
raise WeaveError("name %r already present in weave" % name)
196
245
self._check_versions(parents)
197
246
## self._check_lines(text)
198
247
new_version = len(self._parents)
202
250
map(s.update, text)
203
251
sha1 = s.hexdigest()
206
# if we abort after here the weave will be corrupt
207
self._parents.append(frozenset(parents))
254
# if we abort after here the (in-memory) weave will be corrupt because only
255
# some fields are updated
256
self._parents.append(parents[:])
208
257
self._sha1s.append(sha1)
258
self._names.append(name)
259
self._name_map[name] = new_version
309
361
raise ValueError("version %d not present in weave" % v)
364
def parents(self, version):
365
return self._parents[version]
312
368
def minimal_parents(self, version):
313
369
"""Find the minimal set of parents for the version."""
314
370
included = self._parents[version]
674
"""Show some text information about the weave."""
675
print '%6s %40s %20s' % ('ver', 'sha1', 'parents')
676
for i in (6, 40, 20):
739
"""Show the weave's table-of-contents"""
740
print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
741
for i in (6, 50, 10, 10):
679
744
for i in range(w.numversions()):
680
745
sha1 = w._sha1s[i]
681
print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
747
parent_str = ' '.join(map(str, w._parents[i]))
748
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
721
791
Write out specified version.
722
792
weave check WEAVEFILE
723
793
Check consistency of all versions.
725
795
Display table of contents.
726
weave add WEAVEFILE [BASE...] < NEWTEXT
796
weave add WEAVEFILE NAME [BASE...] < NEWTEXT
727
797
Add NEWTEXT, with specified parent versions.
728
798
weave annotate WEAVEFILE VERSION
729
799
Display origin of each line.
737
807
% weave init foo.weave
739
% weave add foo.weave < foo.txt
809
% weave add foo.weave ver0 < foo.txt
742
812
(create updated version)
744
814
% weave get foo.weave 0 | diff -u - foo.txt
745
% weave add foo.weave 0 < foo.txt
815
% weave add foo.weave ver1 0 < foo.txt
748
818
% weave get foo.weave 0 > foo.txt (create forked version)
750
% weave add foo.weave 0 < foo.txt
820
% weave add foo.weave ver2 0 < foo.txt
753
823
% weave merge foo.weave 1 2 > foo.txt (merge them)
754
824
% vi foo.txt (resolve conflicts)
755
% weave add foo.weave 1 2 < foo.txt (commit merged version)
825
% weave add foo.weave merged 1 2 < foo.txt (commit merged version)
777
854
elif cmd == 'add':
779
856
# at the moment, based on everything in the file
780
parents = map(int, argv[3:])
858
parents = map(int, argv[4:])
781
859
lines = sys.stdin.readlines()
782
ver = w.add(parents, lines)
860
ver = w.add(name, parents, lines)
783
861
write_weave(w, file(argv[2], 'wb'))
784
print 'added version %d' % ver
862
print 'added version %r %d' % (name, ver)
785
863
elif cmd == 'init':
787
865
if os.path.exists(fn):
865
943
raise ValueError('unknown command %r' % cmd)
947
def profile_main(argv):
948
import tempfile, hotshot, hotshot.stats
950
prof_f = tempfile.NamedTemporaryFile()
952
prof = hotshot.Profile(prof_f.name)
954
ret = prof.runcall(main, argv)
957
stats = hotshot.stats.load(prof_f.name)
959
stats.sort_stats('cumulative')
960
## XXX: Might like to write to stderr or the trace file instead but
961
## print_stats seems hardcoded to stdout
962
stats.print_stats(20)
868
967
if __name__ == '__main__':
870
sys.exit(main(sys.argv))
969
if '--profile' in sys.argv:
971
args.remove('--profile')
972
sys.exit(profile_main(args))
974
sys.exit(main(sys.argv))