72
71
# properly nested, that there is no text outside of an insertion, that
73
72
# insertions or deletions are not repeated, etc.
74
# TODO: Make the info command just show info, not extract everything:
75
# it can be much faster.
77
# TODO: Perhaps use long integers as sets instead of set objects; may
75
80
# TODO: Parallel-extract that passes back each line along with a
76
81
# description of which revisions include it. Nice for checking all
77
82
# 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?
130
124
The instruction can be '{' or '}' for an insertion block, and '['
131
125
and ']' for a deletion block respectively. The version is the
132
126
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
137
129
Constraints/notes:
174
166
each version; the parent's parents are implied.
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.
169
List of hex SHA-1 of each version, or None if not recorded.
186
__slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map']
172
__slots__ = ['_weave', '_parents', '_sha1s']
188
174
def __init__(self):
190
176
self._parents = []
196
180
def __eq__(self, other):
197
181
if not isinstance(other, Weave):
199
183
return self._parents == other._parents \
200
and self._weave == other._weave \
201
and self._sha1s == other._sha1s
184
and self._weave == other._weave
204
187
def __ne__(self, other):
205
188
return not self.__eq__(other)
208
def lookup(self, name):
210
return self._name_map[name]
212
raise WeaveError("name %s not present in weave" % name)
215
def add(self, name, parents, text):
191
def add(self, parents, text):
216
192
"""Add a single text on top of the weave.
218
194
Returns the index number of the newly added version.
221
Symbolic name for this version.
222
(Typically the revision-id of the revision that added it.)
225
197
List or set of direct parent version numbers.
228
200
Sequence of lines to be added in the new version."""
230
assert isinstance(name, basestring)
231
if name in self._name_map:
232
raise WeaveError("name %r already present in weave" % name)
234
202
self._check_versions(parents)
235
203
## self._check_lines(text)
236
204
new_version = len(self._parents)
239
208
map(s.update, text)
240
209
sha1 = s.hexdigest()
243
# if we abort after here the (in-memory) weave will be corrupt because only
244
# some fields are updated
245
self._parents.append(parents[:])
212
# if we abort after here the weave will be corrupt
213
self._parents.append(frozenset(parents))
246
214
self._sha1s.append(sha1)
247
self._names.append(name)
248
self._name_map[name] = new_version
713
"""Show the weave's table-of-contents"""
714
print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
715
for i in (6, 50, 10, 10):
718
for i in range(w.numversions()):
721
parent_str = ' '.join(map(str, w._parents[i]))
722
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
726
def weave_stats(weave_file):
727
from bzrlib.progress import ProgressBar
728
from bzrlib.weavefile import read_weave
732
wf = file(weave_file, 'rb')
675
def weave_info(filename, out):
676
"""Show some text information about the weave."""
677
from weavefile import read_weave
678
wf = file(filename, 'rb')
733
679
w = read_weave(wf)
734
680
# FIXME: doesn't work on pipes
735
681
weave_size = wf.tell()
682
print >>out, "weave file size %d bytes" % weave_size
683
print >>out, "weave contains %d versions" % len(w._parents)
739
for i in range(vers):
740
pb.update('checking sizes', i, vers)
741
for line in w.get_iter(i):
746
print 'versions %9d' % vers
747
print 'weave file %9d bytes' % weave_size
748
print 'total contents %9d bytes' % total
749
print 'compression ratio %9.2fx' % (float(total) / float(weave_size))
752
print 'average size %9d bytes' % avg
753
print 'relative size %9.2fx' % (float(weave_size) / float(avg))
686
print '%6s %6s %8s %40s %20s' % ('ver', 'lines', 'bytes', 'sha1', 'parents')
687
for i in (6, 6, 8, 40, 20):
690
for i in range(len(w._parents)):
693
bytes = sum((len(a) for a in text))
695
print '%6d %6d %8d %40s' % (i, lines, bytes, sha1),
696
for pv in w._parents[i]:
701
print >>out, "versions total %d bytes" % total
702
print >>out, "compression ratio %.3f" % (float(total)/float(weave_size))
765
714
Write out specified version.
766
715
weave check WEAVEFILE
767
716
Check consistency of all versions.
769
718
Display table of contents.
770
weave add WEAVEFILE NAME [BASE...] < NEWTEXT
719
weave add WEAVEFILE [BASE...] < NEWTEXT
771
720
Add NEWTEXT, with specified parent versions.
772
721
weave annotate WEAVEFILE VERSION
773
722
Display origin of each line.
781
730
% weave init foo.weave
783
% weave add foo.weave ver0 < foo.txt
732
% weave add foo.weave < foo.txt
786
735
(create updated version)
788
737
% weave get foo.weave 0 | diff -u - foo.txt
789
% weave add foo.weave ver1 0 < foo.txt
738
% weave add foo.weave 0 < foo.txt
792
741
% weave get foo.weave 0 > foo.txt (create forked version)
794
% weave add foo.weave ver2 0 < foo.txt
743
% weave add foo.weave 0 < foo.txt
797
746
% weave merge foo.weave 1 2 > foo.txt (merge them)
798
747
% vi foo.txt (resolve conflicts)
799
% weave add foo.weave merged 1 2 < foo.txt (commit merged version)
748
% weave add foo.weave 1 2 < foo.txt (commit merged version)
828
770
elif cmd == 'add':
830
772
# at the moment, based on everything in the file
832
parents = map(int, argv[4:])
773
parents = map(int, argv[3:])
833
774
lines = sys.stdin.readlines()
834
ver = w.add(name, parents, lines)
775
ver = w.add(parents, lines)
835
776
write_weave(w, file(argv[2], 'wb'))
836
print 'added version %r %d' % (name, ver)
777
print 'added version %d' % ver
837
778
elif cmd == 'init':
839
780
if os.path.exists(fn):
917
855
raise ValueError('unknown command %r' % cmd)
921
def profile_main(argv):
922
import tempfile, hotshot, hotshot.stats
924
prof_f = tempfile.NamedTemporaryFile()
926
prof = hotshot.Profile(prof_f.name)
928
ret = prof.runcall(main, argv)
931
stats = hotshot.stats.load(prof_f.name)
933
stats.sort_stats('cumulative')
934
## XXX: Might like to write to stderr or the trace file instead but
935
## print_stats seems hardcoded to stdout
936
stats.print_stats(20)
941
858
if __name__ == '__main__':
943
if '--profile' in sys.argv:
945
args.remove('--profile')
946
sys.exit(profile_main(args))
948
sys.exit(main(sys.argv))
860
sys.exit(main(sys.argv))