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))
185
def add(self, parents, text):
222
def add(self, name, parents, text):
186
223
"""Add a single text on top of the weave.
188
225
Returns the index number of the newly added version.
228
Symbolic name for this version.
229
(Typically the revision-id of the revision that added it.)
191
232
List or set of direct parent version numbers.
194
235
Sequence of lines to be added in the new version."""
237
assert isinstance(name, basestring)
238
if name in self._name_map:
239
raise WeaveError("name %r already present in weave" % name)
196
241
self._check_versions(parents)
197
242
## self._check_lines(text)
198
243
new_version = len(self._parents)
202
246
map(s.update, text)
203
247
sha1 = s.hexdigest()
206
# if we abort after here the weave will be corrupt
207
self._parents.append(frozenset(parents))
250
# if we abort after here the (in-memory) weave will be corrupt because only
251
# some fields are updated
252
self._parents.append(parents[:])
208
253
self._sha1s.append(sha1)
254
self._names.append(name)
255
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):
731
"""Show the weave's table-of-contents"""
732
print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
733
for i in (6, 50, 10, 10):
679
736
for i in range(w.numversions()):
680
737
sha1 = w._sha1s[i]
681
print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
739
parent_str = ' '.join(map(str, w._parents[i]))
740
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
721
783
Write out specified version.
722
784
weave check WEAVEFILE
723
785
Check consistency of all versions.
725
787
Display table of contents.
726
weave add WEAVEFILE [BASE...] < NEWTEXT
788
weave add WEAVEFILE NAME [BASE...] < NEWTEXT
727
789
Add NEWTEXT, with specified parent versions.
728
790
weave annotate WEAVEFILE VERSION
729
791
Display origin of each line.
737
799
% weave init foo.weave
739
% weave add foo.weave < foo.txt
801
% weave add foo.weave ver0 < foo.txt
742
804
(create updated version)
744
806
% weave get foo.weave 0 | diff -u - foo.txt
745
% weave add foo.weave 0 < foo.txt
807
% weave add foo.weave ver1 0 < foo.txt
748
810
% weave get foo.weave 0 > foo.txt (create forked version)
750
% weave add foo.weave 0 < foo.txt
812
% weave add foo.weave ver2 0 < foo.txt
753
815
% weave merge foo.weave 1 2 > foo.txt (merge them)
754
816
% vi foo.txt (resolve conflicts)
755
% weave add foo.weave 1 2 < foo.txt (commit merged version)
817
% weave add foo.weave merged 1 2 < foo.txt (commit merged version)
777
846
elif cmd == 'add':
779
848
# at the moment, based on everything in the file
780
parents = map(int, argv[3:])
850
parents = map(int, argv[4:])
781
851
lines = sys.stdin.readlines()
782
ver = w.add(parents, lines)
852
ver = w.add(name, parents, lines)
783
853
write_weave(w, file(argv[2], 'wb'))
784
print 'added version %d' % ver
854
print 'added version %r %d' % (name, ver)
785
855
elif cmd == 'init':
787
857
if os.path.exists(fn):
865
935
raise ValueError('unknown command %r' % cmd)
939
def profile_main(argv):
940
import tempfile, hotshot, hotshot.stats
942
prof_f = tempfile.NamedTemporaryFile()
944
prof = hotshot.Profile(prof_f.name)
946
ret = prof.runcall(main, argv)
949
stats = hotshot.stats.load(prof_f.name)
951
stats.sort_stats('cumulative')
952
## XXX: Might like to write to stderr or the trace file instead but
953
## print_stats seems hardcoded to stdout
954
stats.print_stats(20)
868
959
if __name__ == '__main__':
870
sys.exit(main(sys.argv))
961
if '--profile' in sys.argv:
963
args.remove('--profile')
964
sys.exit(profile_main(args))
966
sys.exit(main(sys.argv))