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
287
373
# we don't destroy ourselves
289
375
self._weave[i:i] = ([('{', new_version)]
291
+ [('}', new_version)])
292
378
offset += 2 + (j2 - j1)
294
380
return new_version
382
def add_identical(self, old_rev_id, new_rev_id, parents):
383
"""Add an identical text to old_rev_id as new_rev_id."""
384
old_lines = self.get(self.lookup(old_rev_id))
385
self.add(new_rev_id, parents, old_lines)
297
387
def inclusions(self, versions):
298
388
"""Return set of all ancestors of given version(s)."""
299
389
i = set(versions)
304
# include all its parents
305
i.update(self._parents[v])
309
raise ValueError("version %d not present in weave" % v)
390
for v in xrange(max(versions), 0, -1):
392
# include all its parents
393
i.update(self._parents[v])
395
## except IndexError:
396
## raise ValueError("version %d not present in weave" % v)
399
def parents(self, version):
400
return self._parents[version]
312
403
def minimal_parents(self, version):
352
443
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):
446
def annotate(self, name_or_index):
447
return list(self.annotate_iter(name_or_index))
450
def annotate_iter(self, name_or_index):
360
451
"""Yield list of (index-id, line) pairs for the specified version.
362
453
The index indicates when the line originated in the weave."""
363
for origin, lineno, text in self._extract([version]):
454
incls = [self.maybe_lookup(name_or_index)]
455
for origin, lineno, text in self._extract(incls):
364
456
yield origin, text
464
def get_iter(self, version):
559
def get_iter(self, name_or_index):
465
560
"""Yield lines for the specified version."""
466
for origin, lineno, line in self._extract([version]):
561
incls = [self.maybe_lookup(name_or_index)]
562
for origin, lineno, line in self._extract(incls):
470
def get(self, index):
471
return list(self.get_iter(index))
566
def get_text(self, name_or_index):
567
return ''.join(self.get_iter(name_or_index))
568
assert isinstance(version, int)
571
def get_lines(self, name_or_index):
572
return list(self.get_iter(name_or_index))
474
578
def mash_iter(self, included):
475
579
"""Return composed version of multiple included versions."""
580
included = map(self.maybe_lookup, included)
476
581
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):
778
"""Show the weave's table-of-contents"""
779
print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
780
for i in (6, 50, 10, 10):
679
783
for i in range(w.numversions()):
680
784
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
786
parent_str = ' '.join(map(str, w._parents[i]))
787
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
791
def weave_stats(weave_file, pb):
687
792
from bzrlib.weavefile import read_weave
691
794
wf = file(weave_file, 'rb')
692
795
w = read_weave(wf)
693
796
# FIXME: doesn't work on pipes
731
837
Display composite of all selected versions.
732
838
weave merge WEAVEFILE VERSION1 VERSION2 > OUT
733
839
Auto-merge two versions and display conflicts.
840
weave diff WEAVEFILE VERSION1 VERSION2
841
Show differences between two versions.
737
845
% weave init foo.weave
739
% weave add foo.weave < foo.txt
847
% weave add foo.weave ver0 < foo.txt
742
850
(create updated version)
744
852
% weave get foo.weave 0 | diff -u - foo.txt
745
% weave add foo.weave 0 < foo.txt
853
% weave add foo.weave ver1 0 < foo.txt
748
856
% weave get foo.weave 0 > foo.txt (create forked version)
750
% weave add foo.weave 0 < foo.txt
858
% weave add foo.weave ver2 0 < foo.txt
753
861
% weave merge foo.weave 1 2 > foo.txt (merge them)
754
862
% vi foo.txt (resolve conflicts)
755
% weave add foo.weave 1 2 < foo.txt (commit merged version)
863
% weave add foo.weave merged 1 2 < foo.txt (commit merged version)
777
898
elif cmd == 'add':
779
900
# at the moment, based on everything in the file
780
parents = map(int, argv[3:])
902
parents = map(int, argv[4:])
781
903
lines = sys.stdin.readlines()
782
ver = w.add(parents, lines)
904
ver = w.add(name, parents, lines)
783
905
write_weave(w, file(argv[2], 'wb'))
784
print 'added version %d' % ver
906
print 'added version %r %d' % (name, ver)
785
907
elif cmd == 'init':
787
909
if os.path.exists(fn):
865
999
raise ValueError('unknown command %r' % cmd)
1003
def profile_main(argv):
1004
import tempfile, hotshot, hotshot.stats
1006
prof_f = tempfile.NamedTemporaryFile()
1008
prof = hotshot.Profile(prof_f.name)
1010
ret = prof.runcall(main, argv)
1013
stats = hotshot.stats.load(prof_f.name)
1015
stats.sort_stats('cumulative')
1016
## XXX: Might like to write to stderr or the trace file instead but
1017
## print_stats seems hardcoded to stdout
1018
stats.print_stats(20)
868
1023
if __name__ == '__main__':
870
sys.exit(main(sys.argv))
1025
if '--profile' in sys.argv:
1027
args.remove('--profile')
1028
sys.exit(profile_main(args))
1030
sys.exit(main(sys.argv))