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):
218
assert isinstance(name, basestring), type(name)
220
return self._name_map[name]
222
raise WeaveError("name %r not present in weave %r" %
223
(name, self._weave_name))
226
def idx_to_name(self, version):
227
return self._names[version]
230
def _check_repeated_add(self, name, parents, text):
231
"""Check that a duplicated add is OK.
233
If it is, return the (old) index; otherwise raise an exception.
235
idx = self.lookup(name)
236
if sorted(self._parents[idx]) != sorted(parents):
237
raise WeaveError("name \"%s\" already present in weave "
238
"with different parents" % name)
239
new_sha1 = sha_strings(text)
240
if new_sha1 != self._sha1s[idx]:
241
raise WeaveError("name \"%s\" already present in weave "
242
"with different text" % name)
247
def add(self, name, parents, text):
186
248
"""Add a single text on top of the weave.
188
250
Returns the index number of the newly added version.
253
Symbolic name for this version.
254
(Typically the revision-id of the revision that added it.)
191
257
List or set of direct parent version numbers.
194
260
Sequence of lines to be added in the new version."""
262
assert isinstance(name, basestring)
263
if name in self._name_map:
264
return self._check_repeated_add(name, parents, text)
196
266
self._check_versions(parents)
197
267
## self._check_lines(text)
198
268
new_version = len(self._parents)
270
sha1 = sha_strings(text)
206
# if we abort after here the weave will be corrupt
207
self._parents.append(frozenset(parents))
272
# if we abort after here the (in-memory) weave will be corrupt because only
273
# some fields are updated
274
self._parents.append(parents[:])
208
275
self._sha1s.append(sha1)
276
self._names.append(name)
277
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):
757
"""Show the weave's table-of-contents"""
758
print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
759
for i in (6, 50, 10, 10):
679
762
for i in range(w.numversions()):
680
763
sha1 = w._sha1s[i]
681
print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
765
parent_str = ' '.join(map(str, w._parents[i]))
766
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
737
825
% weave init foo.weave
739
% weave add foo.weave < foo.txt
827
% weave add foo.weave ver0 < foo.txt
742
830
(create updated version)
744
832
% weave get foo.weave 0 | diff -u - foo.txt
745
% weave add foo.weave 0 < foo.txt
833
% weave add foo.weave ver1 0 < foo.txt
748
836
% weave get foo.weave 0 > foo.txt (create forked version)
750
% weave add foo.weave 0 < foo.txt
838
% weave add foo.weave ver2 0 < foo.txt
753
841
% weave merge foo.weave 1 2 > foo.txt (merge them)
754
842
% vi foo.txt (resolve conflicts)
755
% weave add foo.weave 1 2 < foo.txt (commit merged version)
843
% weave add foo.weave merged 1 2 < foo.txt (commit merged version)
777
872
elif cmd == 'add':
779
874
# at the moment, based on everything in the file
780
parents = map(int, argv[3:])
876
parents = map(int, argv[4:])
781
877
lines = sys.stdin.readlines()
782
ver = w.add(parents, lines)
878
ver = w.add(name, parents, lines)
783
879
write_weave(w, file(argv[2], 'wb'))
784
print 'added version %d' % ver
880
print 'added version %r %d' % (name, ver)
785
881
elif cmd == 'init':
787
883
if os.path.exists(fn):
865
961
raise ValueError('unknown command %r' % cmd)
965
def profile_main(argv):
966
import tempfile, hotshot, hotshot.stats
968
prof_f = tempfile.NamedTemporaryFile()
970
prof = hotshot.Profile(prof_f.name)
972
ret = prof.runcall(main, argv)
975
stats = hotshot.stats.load(prof_f.name)
977
stats.sort_stats('cumulative')
978
## XXX: Might like to write to stderr or the trace file instead but
979
## print_stats seems hardcoded to stdout
980
stats.print_stats(20)
868
985
if __name__ == '__main__':
870
sys.exit(main(sys.argv))
987
if '--profile' in sys.argv:
989
args.remove('--profile')
990
sys.exit(profile_main(args))
992
sys.exit(main(sys.argv))