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?
91
from cStringIO import StringIO
93
from bzrlib.osutils import sha_strings
81
96
class WeaveError(Exception):
160
177
each version; the parent's parents are implied.
163
List of hex SHA-1 of each version, or None if not recorded.
180
List of hex SHA-1 of each version.
183
List of symbolic names for each version. Each should be unique.
186
For each name, the version number.
189
Descriptive name of this weave; typically the filename if known.
166
__slots__ = ['_weave', '_parents', '_sha1s']
193
__slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
196
def __init__(self, weave_name=None):
170
198
self._parents = []
202
self._weave_name = weave_name
174
205
def __eq__(self, other):
175
206
if not isinstance(other, Weave):
177
208
return self._parents == other._parents \
178
and self._weave == other._weave
209
and self._weave == other._weave \
210
and self._sha1s == other._sha1s
181
213
def __ne__(self, other):
182
214
return not self.__eq__(other)
185
def add(self, parents, text):
217
def lookup(self, name):
219
return self._name_map[name]
221
raise WeaveError("name %s not present in weave %s" %
222
(name, self._weave_name))
225
def idx_to_name(self, version):
226
return self._names[version]
229
def _check_repeated_add(self, name, parents, text):
230
"""Check that a duplicated add is OK.
232
If it is, return the (old) index; otherwise raise an exception.
234
idx = self.lookup(name)
235
if sorted(self._parents[idx]) != sorted(parents):
236
raise WeaveError("name \"%s\" already present in weave "
237
"with different parents" % name)
238
new_sha1 = sha_strings(text)
239
if new_sha1 != self._sha1s[idx]:
240
raise WeaveError("name \"%s\" already present in weave "
241
"with different text" % name)
246
def add(self, name, parents, text):
186
247
"""Add a single text on top of the weave.
188
249
Returns the index number of the newly added version.
252
Symbolic name for this version.
253
(Typically the revision-id of the revision that added it.)
191
256
List or set of direct parent version numbers.
194
259
Sequence of lines to be added in the new version."""
261
assert isinstance(name, basestring)
262
if name in self._name_map:
263
return self._check_repeated_add(name, parents, text)
196
265
self._check_versions(parents)
197
266
## self._check_lines(text)
198
267
new_version = len(self._parents)
269
sha1 = sha_strings(text)
206
# if we abort after here the weave will be corrupt
207
self._parents.append(frozenset(parents))
271
# if we abort after here the (in-memory) weave will be corrupt because only
272
# some fields are updated
273
self._parents.append(parents[:])
208
274
self._sha1s.append(sha1)
275
self._names.append(name)
276
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):
756
"""Show the weave's table-of-contents"""
757
print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
758
for i in (6, 50, 10, 10):
679
761
for i in range(w.numversions()):
680
762
sha1 = w._sha1s[i]
681
print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
764
parent_str = ' '.join(map(str, w._parents[i]))
765
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
737
824
% weave init foo.weave
739
% weave add foo.weave < foo.txt
826
% weave add foo.weave ver0 < foo.txt
742
829
(create updated version)
744
831
% weave get foo.weave 0 | diff -u - foo.txt
745
% weave add foo.weave 0 < foo.txt
832
% weave add foo.weave ver1 0 < foo.txt
748
835
% weave get foo.weave 0 > foo.txt (create forked version)
750
% weave add foo.weave 0 < foo.txt
837
% weave add foo.weave ver2 0 < foo.txt
753
840
% weave merge foo.weave 1 2 > foo.txt (merge them)
754
841
% vi foo.txt (resolve conflicts)
755
% weave add foo.weave 1 2 < foo.txt (commit merged version)
842
% weave add foo.weave merged 1 2 < foo.txt (commit merged version)
777
871
elif cmd == 'add':
779
873
# at the moment, based on everything in the file
780
parents = map(int, argv[3:])
875
parents = map(int, argv[4:])
781
876
lines = sys.stdin.readlines()
782
ver = w.add(parents, lines)
877
ver = w.add(name, parents, lines)
783
878
write_weave(w, file(argv[2], 'wb'))
784
print 'added version %d' % ver
879
print 'added version %r %d' % (name, ver)
785
880
elif cmd == 'init':
787
882
if os.path.exists(fn):
865
960
raise ValueError('unknown command %r' % cmd)
964
def profile_main(argv):
965
import tempfile, hotshot, hotshot.stats
967
prof_f = tempfile.NamedTemporaryFile()
969
prof = hotshot.Profile(prof_f.name)
971
ret = prof.runcall(main, argv)
974
stats = hotshot.stats.load(prof_f.name)
976
stats.sort_stats('cumulative')
977
## XXX: Might like to write to stderr or the trace file instead but
978
## print_stats seems hardcoded to stdout
979
stats.print_stats(20)
868
984
if __name__ == '__main__':
870
sys.exit(main(sys.argv))
986
if '--profile' in sys.argv:
988
args.remove('--profile')
989
sys.exit(profile_main(args))
991
sys.exit(main(sys.argv))