160
180
each version; the parent's parents are implied.
163
List of hex SHA-1 of each version, or None if not recorded.
183
List of hex SHA-1 of each version.
186
List of symbolic names for each version. Each should be unique.
189
For each name, the version number.
192
Descriptive name of this weave; typically the filename if known.
166
__slots__ = ['_weave', '_parents', '_sha1s']
196
__slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
199
def __init__(self, weave_name=None):
170
201
self._parents = []
205
self._weave_name = weave_name
174
208
def __eq__(self, other):
175
209
if not isinstance(other, Weave):
177
211
return self._parents == other._parents \
178
and self._weave == other._weave
212
and self._weave == other._weave \
213
and self._sha1s == other._sha1s
181
216
def __ne__(self, other):
182
217
return not self.__eq__(other)
185
def add(self, parents, text):
220
def maybe_lookup(self, name_or_index):
221
"""Convert possible symbolic name to index, or pass through indexes."""
222
if isinstance(name_or_index, (int, long)):
225
return self.lookup(name_or_index)
228
def lookup(self, name):
229
"""Convert symbolic version name to index."""
231
return self._name_map[name]
233
raise WeaveError("name %r not present in weave %r" %
234
(name, self._weave_name))
237
def idx_to_name(self, version):
238
return self._names[version]
241
def _check_repeated_add(self, name, parents, text, sha1):
242
"""Check that a duplicated add is OK.
244
If it is, return the (old) index; otherwise raise an exception.
246
idx = self.lookup(name)
247
if sorted(self._parents[idx]) != sorted(parents):
248
raise WeaveError("name \"%s\" already present in weave "
249
"with different parents" % name)
250
if sha1 != self._sha1s[idx]:
251
raise WeaveError("name \"%s\" already present in weave "
252
"with different text" % name)
257
def add(self, name, parents, text, sha1=None):
186
258
"""Add a single text on top of the weave.
188
260
Returns the index number of the newly added version.
263
Symbolic name for this version.
264
(Typically the revision-id of the revision that added it.)
191
267
List or set of direct parent version numbers.
194
Sequence of lines to be added in the new version."""
270
Sequence of lines to be added in the new version.
272
sha -- SHA-1 of the file, if known. This is trusted to be
275
from bzrlib.osutils import sha_strings
277
assert isinstance(name, basestring)
279
sha1 = sha_strings(text)
280
if name in self._name_map:
281
return self._check_repeated_add(name, parents, text, sha1)
283
parents = map(self.maybe_lookup, parents)
196
284
self._check_versions(parents)
197
285
## self._check_lines(text)
198
286
new_version = len(self._parents)
206
# if we abort after here the weave will be corrupt
207
self._parents.append(frozenset(parents))
289
# if we abort after here the (in-memory) weave will be corrupt because only
290
# some fields are updated
291
self._parents.append(parents[:])
208
292
self._sha1s.append(sha1)
293
self._names.append(name)
294
self._name_map[name] = new_version
297
383
def inclusions(self, versions):
298
384
"""Return set of all ancestors of given version(s)."""
299
385
i = set(versions)
304
# include all its parents
305
i.update(self._parents[v])
309
raise ValueError("version %d not present in weave" % v)
386
for v in xrange(max(versions), 0, -1):
388
# include all its parents
389
i.update(self._parents[v])
391
## except IndexError:
392
## raise ValueError("version %d not present in weave" % v)
395
def parents(self, version):
396
return self._parents[version]
312
399
def minimal_parents(self, version):
352
439
raise IndexError("invalid version number %r" % i)
355
def annotate(self, index):
356
return list(self.annotate_iter(index))
359
def annotate_iter(self, version):
442
def annotate(self, name_or_index):
443
return list(self.annotate_iter(name_or_index))
446
def annotate_iter(self, name_or_index):
360
447
"""Yield list of (index-id, line) pairs for the specified version.
362
449
The index indicates when the line originated in the weave."""
363
for origin, lineno, text in self._extract([version]):
450
incls = [self.maybe_lookup(name_or_index)]
451
for origin, lineno, text in self._extract(incls):
364
452
yield origin, text
464
def get_iter(self, version):
555
def get_iter(self, name_or_index):
465
556
"""Yield lines for the specified version."""
466
for origin, lineno, line in self._extract([version]):
557
incls = [self.maybe_lookup(name_or_index)]
558
for origin, lineno, line in self._extract(incls):
470
def get(self, index):
471
return list(self.get_iter(index))
562
def get_text(self, name_or_index):
563
return ''.join(self.get_iter(name_or_index))
564
assert isinstance(version, int)
567
def get_lines(self, name_or_index):
568
return list(self.get_iter(name_or_index))
474
574
def mash_iter(self, included):
475
575
"""Return composed version of multiple included versions."""
576
included = map(self.maybe_lookup, included)
476
577
for origin, lineno, text in self._extract(included):
674
"""Show some text information about the weave."""
675
print '%6s %40s %20s' % ('ver', 'sha1', 'parents')
676
for i in (6, 40, 20):
774
"""Show the weave's table-of-contents"""
775
print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
776
for i in (6, 50, 10, 10):
679
779
for i in range(w.numversions()):
680
780
sha1 = w._sha1s[i]
681
print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
685
def weave_stats(weave_file):
686
from bzrlib.progress import ProgressBar
782
parent_str = ' '.join(map(str, w._parents[i]))
783
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
787
def weave_stats(weave_file, pb):
687
788
from bzrlib.weavefile import read_weave
691
790
wf = file(weave_file, 'rb')
692
791
w = read_weave(wf)
693
792
# FIXME: doesn't work on pipes
731
833
Display composite of all selected versions.
732
834
weave merge WEAVEFILE VERSION1 VERSION2 > OUT
733
835
Auto-merge two versions and display conflicts.
836
weave diff WEAVEFILE VERSION1 VERSION2
837
Show differences between two versions.
737
841
% weave init foo.weave
739
% weave add foo.weave < foo.txt
843
% weave add foo.weave ver0 < foo.txt
742
846
(create updated version)
744
848
% weave get foo.weave 0 | diff -u - foo.txt
745
% weave add foo.weave 0 < foo.txt
849
% weave add foo.weave ver1 0 < foo.txt
748
852
% weave get foo.weave 0 > foo.txt (create forked version)
750
% weave add foo.weave 0 < foo.txt
854
% weave add foo.weave ver2 0 < foo.txt
753
857
% weave merge foo.weave 1 2 > foo.txt (merge them)
754
858
% vi foo.txt (resolve conflicts)
755
% weave add foo.weave 1 2 < foo.txt (commit merged version)
859
% weave add foo.weave merged 1 2 < foo.txt (commit merged version)
777
894
elif cmd == 'add':
779
896
# at the moment, based on everything in the file
780
parents = map(int, argv[3:])
898
parents = map(int, argv[4:])
781
899
lines = sys.stdin.readlines()
782
ver = w.add(parents, lines)
900
ver = w.add(name, parents, lines)
783
901
write_weave(w, file(argv[2], 'wb'))
784
print 'added version %d' % ver
902
print 'added version %r %d' % (name, ver)
785
903
elif cmd == 'init':
787
905
if os.path.exists(fn):
865
995
raise ValueError('unknown command %r' % cmd)
999
def profile_main(argv):
1000
import tempfile, hotshot, hotshot.stats
1002
prof_f = tempfile.NamedTemporaryFile()
1004
prof = hotshot.Profile(prof_f.name)
1006
ret = prof.runcall(main, argv)
1009
stats = hotshot.stats.load(prof_f.name)
1011
stats.sort_stats('cumulative')
1012
## XXX: Might like to write to stderr or the trace file instead but
1013
## print_stats seems hardcoded to stdout
1014
stats.print_stats(20)
868
1019
if __name__ == '__main__':
870
sys.exit(main(sys.argv))
1021
if '--profile' in sys.argv:
1023
args.remove('--profile')
1024
sys.exit(profile_main(args))
1026
sys.exit(main(sys.argv))