76
75
# description of which revisions include it. Nice for checking all
77
76
# 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
96
81
class WeaveError(Exception):
177
160
each version; the parent's parents are implied.
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.
163
List of hex SHA-1 of each version, or None if not recorded.
193
__slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
166
__slots__ = ['_weave', '_parents', '_sha1s']
196
def __init__(self, weave_name=None):
198
170
self._parents = []
202
self._weave_name = weave_name
205
174
def __eq__(self, other):
206
175
if not isinstance(other, Weave):
208
177
return self._parents == other._parents \
209
and self._weave == other._weave \
210
and self._sha1s == other._sha1s
178
and self._weave == other._weave
213
181
def __ne__(self, other):
214
182
return not self.__eq__(other)
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):
185
def add(self, parents, text):
247
186
"""Add a single text on top of the weave.
249
188
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.)
256
191
List or set of direct parent version numbers.
259
194
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)
265
196
self._check_versions(parents)
266
197
## self._check_lines(text)
267
198
new_version = len(self._parents)
269
sha1 = sha_strings(text)
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[:])
206
# if we abort after here the weave will be corrupt
207
self._parents.append(frozenset(parents))
274
208
self._sha1s.append(sha1)
275
self._names.append(name)
276
self._name_map[name] = new_version
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):
674
"""Show some text information about the weave."""
675
print '%6s %40s %20s' % ('ver', 'sha1', 'parents')
676
for i in (6, 40, 20):
761
679
for i in range(w.numversions()):
762
680
sha1 = w._sha1s[i]
764
parent_str = ' '.join(map(str, w._parents[i]))
765
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
681
print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
824
737
% weave init foo.weave
826
% weave add foo.weave ver0 < foo.txt
739
% weave add foo.weave < foo.txt
829
742
(create updated version)
831
744
% weave get foo.weave 0 | diff -u - foo.txt
832
% weave add foo.weave ver1 0 < foo.txt
745
% weave add foo.weave 0 < foo.txt
835
748
% weave get foo.weave 0 > foo.txt (create forked version)
837
% weave add foo.weave ver2 0 < foo.txt
750
% weave add foo.weave 0 < foo.txt
840
753
% weave merge foo.weave 1 2 > foo.txt (merge them)
841
754
% vi foo.txt (resolve conflicts)
842
% weave add foo.weave merged 1 2 < foo.txt (commit merged version)
755
% weave add foo.weave 1 2 < foo.txt (commit merged version)
871
777
elif cmd == 'add':
873
779
# at the moment, based on everything in the file
875
parents = map(int, argv[4:])
780
parents = map(int, argv[3:])
876
781
lines = sys.stdin.readlines()
877
ver = w.add(name, parents, lines)
782
ver = w.add(parents, lines)
878
783
write_weave(w, file(argv[2], 'wb'))
879
print 'added version %r %d' % (name, ver)
784
print 'added version %d' % ver
880
785
elif cmd == 'init':
882
787
if os.path.exists(fn):
960
865
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)
984
868
if __name__ == '__main__':
986
if '--profile' in sys.argv:
988
args.remove('--profile')
989
sys.exit(profile_main(args))
991
sys.exit(main(sys.argv))
870
sys.exit(main(sys.argv))