23
from __future__ import absolute_import
27
from cStringIO import StringIO
29
from bzrlib.lazy_import import lazy_import
30
lazy_import(globals(), """
23
from StringIO import StringIO
32
revision as _mod_revision,
53
37
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
38
from bzrlib.repository import (
56
RepositoryFormatMetaDir,
40
MetaDirVersionedFileRepository,
41
MetaDirRepositoryFormat,
58
45
from bzrlib.store.text import TextStore
59
from bzrlib.versionedfile import (
61
FulltextContentFactory,
64
from bzrlib.vf_repository import (
65
InterSameDataRepository,
66
VersionedFileCommitBuilder,
67
VersionedFileRepository,
68
VersionedFileRepositoryFormat,
69
MetaDirVersionedFileRepository,
70
MetaDirVersionedFileRepositoryFormat,
73
from bzrlib.plugins.weave_fmt import bzrdir as weave_bzrdir
76
class AllInOneRepository(VersionedFileRepository):
46
from bzrlib.trace import mutter
49
class AllInOneRepository(Repository):
77
50
"""Legacy support - the repository behaviour for all-in-one branches."""
80
def _serializer(self):
81
return xml5.serializer_v5
83
def _escape(self, file_or_path):
84
if not isinstance(file_or_path, basestring):
85
file_or_path = '/'.join(file_or_path)
86
if file_or_path == '':
88
return urlutils.escape(osutils.safe_unicode(file_or_path))
90
def __init__(self, _format, a_bzrdir):
52
_serializer = xml5.serializer_v5
54
def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
91
55
# we reuse one control files instance.
92
dir_mode = a_bzrdir._get_dir_mode()
93
file_mode = a_bzrdir._get_file_mode()
56
dir_mode = a_bzrdir._control_files._dir_mode
57
file_mode = a_bzrdir._control_files._file_mode
95
59
def get_store(name, compressed=True, prefixed=False):
96
60
# FIXME: This approach of assuming stores are all entirely compressed
97
# or entirely uncompressed is tidy, but breaks upgrade from
98
# some existing branches where there's a mixture; we probably
61
# or entirely uncompressed is tidy, but breaks upgrade from
62
# some existing branches where there's a mixture; we probably
99
63
# still want the option to look for both.
100
relpath = self._escape(name)
101
store = TextStore(a_bzrdir.transport.clone(relpath),
64
relpath = a_bzrdir._control_files._escape(name)
65
store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
102
66
prefixed=prefixed, compressed=compressed,
103
67
dir_mode=dir_mode,
104
68
file_mode=file_mode)
107
71
# not broken out yet because the controlweaves|inventory_store
108
# and texts bits are still different.
72
# and text_store | weave_store bits are still different.
109
73
if isinstance(_format, RepositoryFormat4):
110
# cannot remove these - there is still no consistent api
74
# cannot remove these - there is still no consistent api
111
75
# which allows access to this old info.
112
76
self.inventory_store = get_store('inventory-store')
113
self._text_store = get_store('text-store')
114
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files)
77
text_store = get_store('text-store')
78
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
79
if control_store is not None:
80
control_store.get_scope = self.get_transaction
81
text_store.get_scope = self.get_transaction
117
84
def _all_possible_ids(self):
118
85
"""Return all the possible revisions that we could find."""
119
86
if 'evil' in debug.debug_flags:
120
trace.mutter_callsite(
121
3, "_all_possible_ids scales with size of history.")
122
return [key[-1] for key in self.inventories.keys()]
87
mutter_callsite(3, "_all_possible_ids scales with size of history.")
88
return self.get_inventory_weave().versions()
125
91
def _all_revision_ids(self):
126
"""Returns a list of all the revision ids in the repository.
92
"""Returns a list of all the revision ids in the repository.
128
These are in as much topological order as the underlying store can
94
These are in as much topological order as the underlying store can
129
95
present: for weaves ghosts may lead to a lack of correctness until
130
96
the reweave updates the parents list.
132
return [key[-1] for key in self.revisions.keys()]
134
def _activate_new_inventory(self):
135
"""Put a replacement inventory.new into use as inventories."""
136
# Copy the content across
137
t = self.bzrdir._control_files._transport
138
t.copy('inventory.new.weave', 'inventory.weave')
139
# delete the temp inventory
140
t.delete('inventory.new.weave')
141
# Check we can parse the new weave properly as a sanity check
142
self.inventories.keys()
144
def _backup_inventory(self):
145
t = self.bzrdir._control_files._transport
146
t.copy('inventory.weave', 'inventory.backup.weave')
148
def _temp_inventories(self):
149
t = self.bzrdir._control_files._transport
150
return self._format._get_inventories(t, self, 'inventory.new')
98
if self._revision_store.text_store.listable():
99
return self._revision_store.all_revision_ids(self.get_transaction())
100
result = self._all_possible_ids()
101
# TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
102
# ids. (It should, since _revision_store's API should change to
103
# return utf8 revision_ids)
104
return self._eliminate_revisions_not_present(result)
106
def _check_revision_parents(self, revision, inventory):
107
"""Private to Repository and Fetch.
109
This checks the parentage of revision in an inventory weave for
110
consistency and is only applicable to inventory-weave-for-ancestry
111
using repository formats & fetchers.
113
weave_parents = inventory.get_parent_map(
114
[revision.revision_id])[revision.revision_id]
115
parent_map = inventory.get_parent_map(revision.parent_ids)
116
for parent_id in revision.parent_ids:
117
if parent_id in parent_map:
118
# this parent must not be a ghost.
119
if not parent_id in weave_parents:
121
raise errors.CorruptRepository(self)
152
123
def get_commit_builder(self, branch, parents, config, timestamp=None,
153
124
timezone=None, committer=None, revprops=None,
154
revision_id=None, lossy=False):
155
126
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
156
result = VersionedFileCommitBuilder(self, parents, config, timestamp,
157
timezone, committer, revprops, revision_id, lossy=lossy)
127
result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
128
committer, revprops, revision_id)
158
129
self.start_write_group()
162
133
def get_revisions(self, revision_ids):
163
134
revs = self._get_revisions(revision_ids)
135
# weave corruption can lead to absent revision markers that should be
137
# the following test is reasonably cheap (it needs a single weave read)
138
# and the weave is cached in read transactions. In write transactions
139
# it is not cached but typically we only read a small number of
140
# revisions. For knits when they are introduced we will probably want
141
# to ensure that caching write transactions are in use.
142
inv = self.get_inventory_weave()
144
self._check_revision_parents(rev, inv)
166
def _inventory_add_lines(self, revision_id, parents, lines,
168
"""Store lines in inv_vf and return the sha1 of the inventory."""
169
present_parents = self.get_graph().get_parent_map(parents)
171
for parent in parents:
172
if parent in present_parents:
173
final_parents.append((parent,))
174
return self.inventories.add_lines((revision_id,), final_parents, lines,
175
check_content=check_content)[0]
147
def has_revisions(self, revision_ids):
148
"""See Repository.has_revisions()."""
150
transaction = self.get_transaction()
151
for revision_id in revision_ids:
152
if self._revision_store.has_revision_id(revision_id, transaction):
153
result.add(revision_id)
177
157
def is_shared(self):
178
158
"""AllInOne repositories cannot be shared."""
188
168
:param new_value: True to restore the default, False to disable making
191
raise errors.RepositoryUpgradeRequired(self.user_url)
171
raise errors.RepositoryUpgradeRequired(self.bzrdir.root_transport.base)
193
173
def make_working_trees(self):
194
174
"""Returns the policy for making working trees on new branches."""
177
def revision_graph_can_have_wrong_parents(self):
178
# XXX: This is an old format that we don't support full checking on, so
179
# just claim that checking for this inconsistency is not required.
198
183
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
199
184
"""A subclass of MetaDirRepository to set weave specific policy."""
201
def __init__(self, _format, a_bzrdir, control_files):
202
super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
203
self._serializer = _format._serializer
186
_serializer = xml5.serializer_v5
206
189
def _all_possible_ids(self):
207
190
"""Return all the possible revisions that we could find."""
208
191
if 'evil' in debug.debug_flags:
209
trace.mutter_callsite(
210
3, "_all_possible_ids scales with size of history.")
211
return [key[-1] for key in self.inventories.keys()]
192
mutter_callsite(3, "_all_possible_ids scales with size of history.")
193
return self.get_inventory_weave().versions()
214
196
def _all_revision_ids(self):
215
"""Returns a list of all the revision ids in the repository.
197
"""Returns a list of all the revision ids in the repository.
217
These are in as much topological order as the underlying store can
199
These are in as much topological order as the underlying store can
218
200
present: for weaves ghosts may lead to a lack of correctness until
219
201
the reweave updates the parents list.
221
return [key[-1] for key in self.revisions.keys()]
223
def _activate_new_inventory(self):
224
"""Put a replacement inventory.new into use as inventories."""
225
# Copy the content across
227
t.copy('inventory.new.weave', 'inventory.weave')
228
# delete the temp inventory
229
t.delete('inventory.new.weave')
230
# Check we can parse the new weave properly as a sanity check
231
self.inventories.keys()
233
def _backup_inventory(self):
235
t.copy('inventory.weave', 'inventory.backup.weave')
237
def _temp_inventories(self):
239
return self._format._get_inventories(t, self, 'inventory.new')
203
if self._revision_store.text_store.listable():
204
return self._revision_store.all_revision_ids(self.get_transaction())
205
result = self._all_possible_ids()
206
# TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
207
# ids. (It should, since _revision_store's API should change to
208
# return utf8 revision_ids)
209
return self._eliminate_revisions_not_present(result)
211
def _check_revision_parents(self, revision, inventory):
212
"""Private to Repository and Fetch.
214
This checks the parentage of revision in an inventory weave for
215
consistency and is only applicable to inventory-weave-for-ancestry
216
using repository formats & fetchers.
218
weave_parents = inventory.get_parent_map(
219
[revision.revision_id])[revision.revision_id]
220
parent_map = inventory.get_parent_map(revision.parent_ids)
221
for parent_id in revision.parent_ids:
222
if parent_id in parent_map:
223
# this parent must not be a ghost.
224
if not parent_id in weave_parents:
226
raise errors.CorruptRepository(self)
241
228
def get_commit_builder(self, branch, parents, config, timestamp=None,
242
229
timezone=None, committer=None, revprops=None,
243
revision_id=None, lossy=False):
244
231
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
245
result = VersionedFileCommitBuilder(self, parents, config, timestamp,
246
timezone, committer, revprops, revision_id, lossy=lossy)
232
result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
233
committer, revprops, revision_id)
247
234
self.start_write_group()
251
238
def get_revision(self, revision_id):
252
239
"""Return the Revision object for a named revision"""
240
# TODO: jam 20070210 get_revision_reconcile should do this for us
253
241
r = self.get_revision_reconcile(revision_id)
242
# weave corruption can lead to absent revision markers that should be
244
# the following test is reasonably cheap (it needs a single weave read)
245
# and the weave is cached in read transactions. In write transactions
246
# it is not cached but typically we only read a small number of
247
# revisions. For knits when they are introduced we will probably want
248
# to ensure that caching write transactions are in use.
249
inv = self.get_inventory_weave()
250
self._check_revision_parents(r, inv)
256
def _inventory_add_lines(self, revision_id, parents, lines,
258
"""Store lines in inv_vf and return the sha1 of the inventory."""
259
present_parents = self.get_graph().get_parent_map(parents)
261
for parent in parents:
262
if parent in present_parents:
263
final_parents.append((parent,))
264
return self.inventories.add_lines((revision_id,), final_parents, lines,
265
check_content=check_content)[0]
268
class PreSplitOutRepositoryFormat(VersionedFileRepositoryFormat):
253
def has_revisions(self, revision_ids):
254
"""See Repository.has_revisions()."""
256
transaction = self.get_transaction()
257
for revision_id in revision_ids:
258
if self._revision_store.has_revision_id(revision_id, transaction):
259
result.add(revision_id)
262
def revision_graph_can_have_wrong_parents(self):
263
# XXX: This is an old format that we don't support full checking on, so
264
# just claim that checking for this inconsistency is not required.
268
class PreSplitOutRepositoryFormat(RepositoryFormat):
269
269
"""Base class for the pre split out repository formats."""
271
271
rich_root_data = False
272
272
supports_tree_reference = False
273
273
supports_ghosts = False
274
274
supports_external_lookups = False
275
supports_chks = False
276
supports_nesting_repositories = True
277
_fetch_order = 'topological'
278
_fetch_reconcile = True
280
supports_leaving_lock = False
281
# XXX: This is an old format that we don't support full checking on, so
282
# just claim that checking for this inconsistency is not required.
283
revision_graph_can_have_wrong_parents = False
285
276
def initialize(self, a_bzrdir, shared=False, _internal=False):
286
277
"""Create a weave repository."""
546
514
weavefile.write_weave_v5(weave.Weave(), sio)
547
515
empty_weave = sio.getvalue()
549
trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
517
mutter('creating repository in %s.', a_bzrdir.transport.base)
550
518
dirs = ['revision-store', 'weaves']
551
files = [('inventory.weave', StringIO(empty_weave)),
519
files = [('inventory.weave', StringIO(empty_weave)),
553
521
utf8_files = [('format', self.get_format_string())]
555
523
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
556
524
return self.open(a_bzrdir=a_bzrdir, _found=True)
558
526
def open(self, a_bzrdir, _found=False, _override_transport=None):
559
527
"""See RepositoryFormat.open().
561
529
:param _override_transport: INTERNAL USE ONLY. Allows opening the
562
530
repository at a slightly different url
563
531
than normal. I.e. during 'upgrade'.
566
format = RepositoryFormatMetaDir.find_format(a_bzrdir)
534
format = RepositoryFormat.find_format(a_bzrdir)
567
535
if _override_transport is not None:
568
536
repo_transport = _override_transport
570
538
repo_transport = a_bzrdir.get_repository_transport(None)
571
539
control_files = lockable_files.LockableFiles(repo_transport,
572
540
'lock', lockdir.LockDir)
573
result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
574
control_files=control_files)
575
result.revisions = self._get_revisions(repo_transport, result)
576
result.signatures = self._get_signatures(repo_transport, result)
577
result.inventories = self._get_inventories(repo_transport, result)
578
result.texts = self._get_texts(repo_transport, result)
579
result.chk_bytes = None
580
result._transport = repo_transport
583
def is_deprecated(self):
587
class TextVersionedFiles(VersionedFiles):
588
"""Just-a-bunch-of-files based VersionedFile stores."""
590
def __init__(self, transport, compressed, mapper, is_locked, can_write):
591
self._compressed = compressed
592
self._transport = transport
593
self._mapper = mapper
598
self._is_locked = is_locked
599
self._can_write = can_write
601
def add_lines(self, key, parents, lines):
602
"""Add a revision to the store."""
603
if not self._is_locked():
604
raise errors.ObjectNotLocked(self)
605
if not self._can_write():
606
raise errors.ReadOnlyError(self)
608
raise ValueError('bad idea to put / in %r' % (key,))
609
text = ''.join(lines)
611
text = tuned_gzip.bytes_to_gzip(text)
612
path = self._map(key)
613
self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
615
def insert_record_stream(self, stream):
617
for record in stream:
618
# Raise an error when a record is missing.
619
if record.storage_kind == 'absent':
620
raise errors.RevisionNotPresent([record.key[0]], self)
621
# adapt to non-tuple interface
622
if record.storage_kind == 'fulltext':
623
self.add_lines(record.key, None,
624
osutils.split_lines(record.get_bytes_as('fulltext')))
626
adapter_key = record.storage_kind, 'fulltext'
628
adapter = adapters[adapter_key]
630
adapter_factory = adapter_registry.get(adapter_key)
631
adapter = adapter_factory(self)
632
adapters[adapter_key] = adapter
633
lines = osutils.split_lines(adapter.get_bytes(
634
record, record.get_bytes_as(record.storage_kind)))
636
self.add_lines(record.key, None, lines)
637
except errors.RevisionAlreadyPresent:
640
def _load_text(self, key):
641
if not self._is_locked():
642
raise errors.ObjectNotLocked(self)
643
path = self._map(key)
645
text = self._transport.get_bytes(path)
646
compressed = self._compressed
647
except errors.NoSuchFile:
649
# try without the .gz
652
text = self._transport.get_bytes(path)
654
except errors.NoSuchFile:
659
text = gzip.GzipFile(mode='rb', fileobj=StringIO(text)).read()
663
return self._mapper.map(key) + self._ext
666
class RevisionTextStore(TextVersionedFiles):
667
"""Legacy thunk for format 4 repositories."""
669
def __init__(self, transport, serializer, compressed, mapper, is_locked,
671
"""Create a RevisionTextStore at transport with serializer."""
672
TextVersionedFiles.__init__(self, transport, compressed, mapper,
673
is_locked, can_write)
674
self._serializer = serializer
676
def _load_text_parents(self, key):
677
text = self._load_text(key)
680
parents = self._serializer.read_revision_from_string(text).parent_ids
681
return text, tuple((parent,) for parent in parents)
683
def get_parent_map(self, keys):
686
parents = self._load_text_parents(key)[1]
689
result[key] = parents
692
def get_known_graph_ancestry(self, keys):
693
"""Get a KnownGraph instance with the ancestry of keys."""
695
parent_map = self.get_parent_map(keys)
696
kg = _mod_graph.KnownGraph(parent_map)
699
def get_record_stream(self, keys, sort_order, include_delta_closure):
701
text, parents = self._load_text_parents(key)
703
yield AbsentContentFactory(key)
705
yield FulltextContentFactory(key, parents, None, text)
708
if not self._is_locked():
709
raise errors.ObjectNotLocked(self)
711
for quoted_relpath in self._transport.iter_files_recursive():
712
relpath = urlutils.unquote(quoted_relpath)
713
path, ext = os.path.splitext(relpath)
716
if not relpath.endswith('.sig'):
717
relpaths.add(relpath)
718
paths = list(relpaths)
719
return set([self._mapper.unmap(path) for path in paths])
722
class SignatureTextStore(TextVersionedFiles):
723
"""Legacy thunk for format 4-7 repositories."""
725
def __init__(self, transport, compressed, mapper, is_locked, can_write):
726
TextVersionedFiles.__init__(self, transport, compressed, mapper,
727
is_locked, can_write)
728
self._ext = '.sig' + self._ext
730
def get_parent_map(self, keys):
733
text = self._load_text(key)
739
def get_record_stream(self, keys, sort_order, include_delta_closure):
741
text = self._load_text(key)
743
yield AbsentContentFactory(key)
745
yield FulltextContentFactory(key, None, None, text)
748
if not self._is_locked():
749
raise errors.ObjectNotLocked(self)
751
for quoted_relpath in self._transport.iter_files_recursive():
752
relpath = urlutils.unquote(quoted_relpath)
753
path, ext = os.path.splitext(relpath)
756
if not relpath.endswith('.sig'):
758
relpaths.add(relpath[:-4])
759
paths = list(relpaths)
760
return set([self._mapper.unmap(path) for path in paths])
763
class InterWeaveRepo(InterSameDataRepository):
764
"""Optimised code paths between Weave based repositories.
768
def _get_repo_format_to_test(self):
769
return RepositoryFormat7()
772
def is_compatible(source, target):
773
"""Be compatible with known Weave formats.
775
We don't test for the stores being of specific types because that
776
could lead to confusing results, and there is no need to be
780
return (isinstance(source._format, (RepositoryFormat5,
782
RepositoryFormat7)) and
783
isinstance(target._format, (RepositoryFormat5,
786
except AttributeError:
790
def copy_content(self, revision_id=None):
791
"""See InterRepository.copy_content()."""
792
# weave specific optimised path:
794
self.target.set_make_working_trees(self.source.make_working_trees())
795
except (errors.RepositoryUpgradeRequired, NotImplemented):
798
if self.source._transport.listable():
799
pb = ui.ui_factory.nested_progress_bar()
801
self.target.texts.insert_record_stream(
802
self.source.texts.get_record_stream(
803
self.source.texts.keys(), 'topological', False))
804
pb.update('Copying inventory', 0, 1)
805
self.target.inventories.insert_record_stream(
806
self.source.inventories.get_record_stream(
807
self.source.inventories.keys(), 'topological', False))
808
self.target.signatures.insert_record_stream(
809
self.source.signatures.get_record_stream(
810
self.source.signatures.keys(),
812
self.target.revisions.insert_record_stream(
813
self.source.revisions.get_record_stream(
814
self.source.revisions.keys(),
815
'topological', True))
819
self.target.fetch(self.source, revision_id=revision_id)
822
def search_missing_revision_ids(self,
823
revision_id=symbol_versioning.DEPRECATED_PARAMETER,
824
find_ghosts=True, revision_ids=None, if_present_ids=None,
826
"""See InterRepository.search_missing_revision_ids()."""
827
# we want all revisions to satisfy revision_id in source.
828
# but we don't want to stat every file here and there.
829
# we want then, all revisions other needs to satisfy revision_id
830
# checked, but not those that we have locally.
831
# so the first thing is to get a subset of the revisions to
832
# satisfy revision_id in source, and then eliminate those that
833
# we do already have.
834
# this is slow on high latency connection to self, but as this
835
# disk format scales terribly for push anyway due to rewriting
836
# inventory.weave, this is considered acceptable.
838
if symbol_versioning.deprecated_passed(revision_id):
839
symbol_versioning.warn(
840
'search_missing_revision_ids(revision_id=...) was '
841
'deprecated in 2.4. Use revision_ids=[...] instead.',
842
DeprecationWarning, stacklevel=2)
843
if revision_ids is not None:
844
raise AssertionError(
845
'revision_ids is mutually exclusive with revision_id')
846
if revision_id is not None:
847
revision_ids = [revision_id]
849
source_ids_set = self._present_source_revisions_for(
850
revision_ids, if_present_ids)
851
# source_ids is the worst possible case we may need to pull.
852
# now we want to filter source_ids against what we actually
853
# have in target, but don't try to check for existence where we know
854
# we do not have a revision as that would be pointless.
855
target_ids = set(self.target._all_possible_ids())
856
possibly_present_revisions = target_ids.intersection(source_ids_set)
857
actually_present_revisions = set(
858
self.target._eliminate_revisions_not_present(possibly_present_revisions))
859
required_revisions = source_ids_set.difference(actually_present_revisions)
860
if revision_ids is not None:
861
# we used get_ancestry to determine source_ids then we are assured all
862
# revisions referenced are present as they are installed in topological order.
863
# and the tip revision was validated by get_ancestry.
864
result_set = required_revisions
866
# if we just grabbed the possibly available ids, then
867
# we only have an estimate of whats available and need to validate
868
# that against the revision records.
870
self.source._eliminate_revisions_not_present(required_revisions))
871
if limit is not None:
872
topo_ordered = self.get_graph().iter_topo_order(result_set)
873
result_set = set(itertools.islice(topo_ordered, limit))
874
return self.source.revision_ids_to_search_result(result_set)
877
InterRepository.register_optimiser(InterWeaveRepo)
880
def get_extra_interrepo_test_combinations():
881
from bzrlib.repofmt import knitrepo
882
return [(InterRepository, RepositoryFormat5(),
883
knitrepo.RepositoryFormatKnit3())]
541
text_store = self._get_text_store(repo_transport, control_files)
542
control_store = self._get_control_store(repo_transport, control_files)
543
_revision_store = self._get_revision_store(repo_transport, control_files)
544
return WeaveMetaDirRepository(_format=self,
546
control_files=control_files,
547
_revision_store=_revision_store,
548
control_store=control_store,
549
text_store=text_store)
552
class WeaveCommitBuilder(CommitBuilder):
553
"""A builder for weave based repos that don't support ghosts."""
555
def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
556
versionedfile = self.repository.weave_store.get_weave_or_empty(
557
file_id, self.repository.get_transaction())
558
result = versionedfile.add_lines(
559
self._new_revision_id, parents, new_lines,
560
nostore_sha=nostore_sha)[0:2]
564
_legacy_formats = [RepositoryFormat4(),