/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.115.12 by John Arbash Meinel
Add a bunch of direct tests for the _TreeShim interface.
1
# Copyright (C) 2008, 2009 Canonical Ltd
0.64.5 by Ian Clatworthy
first cut at generic processing method
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
0.64.334 by Jelmer Vernooij
Remove old FSF address. Thanks Dan Callaghan.
14
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
0.64.5 by Ian Clatworthy
first cut at generic processing method
15
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
16
"""An abstraction of a repository providing just the bits importing needs."""
0.64.5 by Ian Clatworthy
first cut at generic processing method
17
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
18
from __future__ import absolute_import
19
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
20
import cStringIO
0.64.5 by Ian Clatworthy
first cut at generic processing method
21
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
22
from ... import (
0.116.1 by John Arbash Meinel
Use the new KnownGraph.add_node() functionality.
23
    errors,
24
    graph as _mod_graph,
25
    lru_cache,
26
    osutils,
27
    revision as _mod_revision,
28
    trace,
29
    )
6670.4.1 by Jelmer Vernooij
Update imports.
30
from ...bzr import (
31
    knit,
32
    inventory,
33
    )
0.64.6 by Ian Clatworthy
generic processing method working for one revision in one branch
34
35
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
36
class _TreeShim(object):
37
    """Fake a Tree implementation.
38
39
    This implements just enough of the tree api to make commit builder happy.
40
    """
41
0.115.7 by John Arbash Meinel
Fall back to the repository for cases where the content is not present in the stream yet.
42
    def __init__(self, repo, basis_inv, inv_delta, content_provider):
43
        self._repo = repo
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
44
        self._content_provider = content_provider
45
        self._basis_inv = basis_inv
46
        self._inv_delta = inv_delta
47
        self._new_info_by_id = dict([(file_id, (new_path, ie))
48
                                    for _, new_path, file_id, ie in inv_delta])
49
50
    def id2path(self, file_id):
51
        if file_id in self._new_info_by_id:
52
            new_path = self._new_info_by_id[file_id][0]
53
            if new_path is None:
0.115.12 by John Arbash Meinel
Add a bunch of direct tests for the _TreeShim interface.
54
                raise errors.NoSuchId(self, file_id)
55
            return new_path
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
56
        return self._basis_inv.id2path(file_id)
57
58
    def path2id(self, path):
0.115.12 by John Arbash Meinel
Add a bunch of direct tests for the _TreeShim interface.
59
        # CommitBuilder currently only requires access to the root id. We don't
60
        # build a map of renamed files, etc. One possibility if we ever *do*
61
        # need more than just root, is to defer to basis_inv.path2id() and then
62
        # check if the file_id is in our _new_info_by_id dict. And in that
63
        # case, return _new_info_by_id[file_id][0]
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
64
        if path != '':
65
            raise NotImplementedError(_TreeShim.path2id)
66
        # TODO: Handle root renames?
67
        return self._basis_inv.root.file_id
68
69
    def get_file_with_stat(self, file_id, path=None):
0.123.16 by Jelmer Vernooij
Support get_file_text in _TreeShim.
70
        content = self.get_file_text(file_id, path)
71
        sio = cStringIO.StringIO(content)
72
        return sio, None
73
74
    def get_file_text(self, file_id, path=None):
0.115.7 by John Arbash Meinel
Fall back to the repository for cases where the content is not present in the stream yet.
75
        try:
0.123.16 by Jelmer Vernooij
Support get_file_text in _TreeShim.
76
            return self._content_provider(file_id)
0.115.7 by John Arbash Meinel
Fall back to the repository for cases where the content is not present in the stream yet.
77
        except KeyError:
78
            # The content wasn't shown as 'new'. Just validate this fact
79
            assert file_id not in self._new_info_by_id
80
            old_ie = self._basis_inv[file_id]
81
            old_text_key = (file_id, old_ie.revision)
82
            stream = self._repo.texts.get_record_stream([old_text_key],
83
                                                        'unordered', True)
0.123.16 by Jelmer Vernooij
Support get_file_text in _TreeShim.
84
            return stream.next().get_bytes_as('fulltext')
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
85
86
    def get_symlink_target(self, file_id):
87
        if file_id in self._new_info_by_id:
88
            ie = self._new_info_by_id[file_id][1]
89
            return ie.symlink_target
90
        return self._basis_inv[file_id].symlink_target
91
92
    def get_reference_revision(self, file_id, path=None):
93
        raise NotImplementedError(_TreeShim.get_reference_revision)
94
95
    def _delta_to_iter_changes(self):
96
        """Convert the inv_delta into an iter_changes repr."""
97
        # iter_changes is:
98
        #   (file_id,
99
        #    (old_path, new_path),
100
        #    content_changed,
101
        #    (old_versioned, new_versioned),
102
        #    (old_parent_id, new_parent_id),
103
        #    (old_name, new_name),
104
        #    (old_kind, new_kind),
105
        #    (old_exec, new_exec),
106
        #   )
107
        basis_inv = self._basis_inv
108
        for old_path, new_path, file_id, ie in self._inv_delta:
0.115.12 by John Arbash Meinel
Add a bunch of direct tests for the _TreeShim interface.
109
            # Perf: Would this be faster if we did 'if file_id in basis_inv'?
110
            # Since the *very* common case is that the file already exists, it
111
            # probably is better to optimize for that
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
112
            try:
113
                old_ie = basis_inv[file_id]
114
            except errors.NoSuchId:
115
                old_ie = None
0.115.6 by John Arbash Meinel
We need to handle when the object has been deleted.
116
                if ie is None:
117
                    raise AssertionError('How is both old and new None?')
118
                    change = (file_id,
119
                        (old_path, new_path),
120
                        False,
121
                        (False, False),
122
                        (None, None),
123
                        (None, None),
124
                        (None, None),
125
                        (None, None),
126
                        )
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
127
                change = (file_id,
128
                    (old_path, new_path),
129
                    True,
130
                    (False, True),
131
                    (None, ie.parent_id),
132
                    (None, ie.name),
133
                    (None, ie.kind),
134
                    (None, ie.executable),
135
                    )
136
            else:
0.115.6 by John Arbash Meinel
We need to handle when the object has been deleted.
137
                if ie is None:
138
                    change = (file_id,
139
                        (old_path, new_path),
140
                        True,
141
                        (True, False),
142
                        (old_ie.parent_id, None),
143
                        (old_ie.name, None),
144
                        (old_ie.kind, None),
145
                        (old_ie.executable, None),
146
                        )
147
                else:
148
                    content_modified = (ie.text_sha1 != old_ie.text_sha1
149
                                        or ie.text_size != old_ie.text_size)
0.115.7 by John Arbash Meinel
Fall back to the repository for cases where the content is not present in the stream yet.
150
                    # TODO: ie.kind != old_ie.kind
0.115.12 by John Arbash Meinel
Add a bunch of direct tests for the _TreeShim interface.
151
                    # TODO: symlinks changing targets, content_modified?
0.115.6 by John Arbash Meinel
We need to handle when the object has been deleted.
152
                    change = (file_id,
153
                        (old_path, new_path),
154
                        content_modified,
155
                        (True, True),
156
                        (old_ie.parent_id, ie.parent_id),
157
                        (old_ie.name, ie.name),
158
                        (old_ie.kind, ie.kind),
159
                        (old_ie.executable, ie.executable),
160
                        )
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
161
            yield change
162
163
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
164
class AbstractRevisionStore(object):
0.64.5 by Ian Clatworthy
first cut at generic processing method
165
0.64.48 by Ian Clatworthy
one revision loader instance
166
    def __init__(self, repo):
0.64.5 by Ian Clatworthy
first cut at generic processing method
167
        """An object responsible for loading revisions into a repository.
168
169
        NOTE: Repository locking is not managed by this class. Clients
170
        should take a write lock, call load() multiple times, then release
171
        the lock.
172
173
        :param repository: the target repository
0.64.48 by Ian Clatworthy
one revision loader instance
174
        """
175
        self.repo = repo
0.116.1 by John Arbash Meinel
Use the new KnownGraph.add_node() functionality.
176
        self._graph = None
177
        self._use_known_graph = True
0.84.8 by Ian Clatworthy
ensure the chk stuff is only used on formats actually supporting it
178
        self._supports_chks = getattr(repo._format, 'supports_chks', False)
0.81.3 by Ian Clatworthy
enhance RevisionLoader to try inventory deltas & decide on rich-roots
179
180
    def expects_rich_root(self):
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
181
        """Does this store expect inventories with rich roots?"""
0.81.3 by Ian Clatworthy
enhance RevisionLoader to try inventory deltas & decide on rich-roots
182
        return self.repo.supports_rich_root()
0.64.48 by Ian Clatworthy
one revision loader instance
183
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
184
    def init_inventory(self, revision_id):
185
        """Generate an inventory for a parentless revision."""
0.84.8 by Ian Clatworthy
ensure the chk stuff is only used on formats actually supporting it
186
        if self._supports_chks:
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
187
            inv = self._init_chk_inventory(revision_id, inventory.ROOT_ID)
188
        else:
189
            inv = inventory.Inventory(revision_id=revision_id)
0.84.6 by Ian Clatworthy
set maximum_size & key_width for initial parent_id_basename_to_file_id map
190
            if self.expects_rich_root():
191
                # The very first root needs to have the right revision
192
                inv.root.revision = revision_id
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
193
        return inv
194
195
    def _init_chk_inventory(self, revision_id, root_id):
196
        """Generate a CHKInventory for a parentless revision."""
6670.4.3 by Jelmer Vernooij
Fix more imports.
197
        from ...bzr import chk_map
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
198
        # Get the creation parameters
0.84.8 by Ian Clatworthy
ensure the chk stuff is only used on formats actually supporting it
199
        chk_store = self.repo.chk_bytes
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
200
        serializer = self.repo._format._serializer
201
        search_key_name = serializer.search_key_name
202
        maximum_size = serializer.maximum_size
203
204
        # Maybe the rest of this ought to be part of the CHKInventory API?
205
        inv = inventory.CHKInventory(search_key_name)
206
        inv.revision_id = revision_id
207
        inv.root_id = root_id
208
        search_key_func = chk_map.search_key_registry.get(search_key_name)
209
        inv.id_to_entry = chk_map.CHKMap(chk_store, None, search_key_func)
210
        inv.id_to_entry._root_node.set_maximum_size(maximum_size)
0.64.151 by Ian Clatworthy
parent_id_to_basename_index is no longer a serializer attribute - always required now
211
        inv.parent_id_basename_to_file_id = chk_map.CHKMap(chk_store,
212
            None, search_key_func)
213
        inv.parent_id_basename_to_file_id._root_node.set_maximum_size(
214
            maximum_size)
215
        inv.parent_id_basename_to_file_id._root_node._key_width = 2
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
216
        return inv
217
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
218
    def get_inventory(self, revision_id):
219
        """Get a stored inventory."""
220
        return self.repo.get_inventory(revision_id)
221
222
    def get_file_text(self, revision_id, file_id):
223
        """Get the text stored for a file in a given revision."""
224
        revtree = self.repo.revision_tree(revision_id)
225
        return revtree.get_file_text(file_id)
226
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
227
    def get_file_lines(self, revision_id, file_id):
228
        """Get the lines stored for a file in a given revision."""
0.64.156 by Ian Clatworthy
minor revision_store clean-ups
229
        revtree = self.repo.revision_tree(revision_id)
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
230
        return osutils.split_lines(revtree.get_file_text(file_id))
231
0.85.2 by Ian Clatworthy
improve per-file graph generation
232
    def start_new_revision(self, revision, parents, parent_invs):
233
        """Init the metadata needed for get_parents_and_revision_for_entry().
234
235
        :param revision: a Revision object
236
        """
237
        self._current_rev_id = revision.revision_id
238
        self._rev_parents = parents
239
        self._rev_parent_invs = parent_invs
240
        # We don't know what the branch will be so there's no real BranchConfig.
241
        # That means we won't be triggering any hooks and that's a good thing.
242
        # Without a config though, we must pass in the committer below so that
243
        # the commit builder doesn't try to look up the config.
244
        config = None
245
        # We can't use self.repo.get_commit_builder() here because it starts a
246
        # new write group. We want one write group around a batch of imports
247
        # where the default batch size is currently 10000. IGC 20090312
248
        self._commit_builder = self.repo._commit_builder_class(self.repo,
249
            parents, config, timestamp=revision.timestamp,
250
            timezone=revision.timezone, committer=revision.committer,
251
            revprops=revision.properties, revision_id=revision.revision_id)
252
253
    def get_parents_and_revision_for_entry(self, ie):
254
        """Get the parents and revision for an inventory entry.
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
255
 
0.85.2 by Ian Clatworthy
improve per-file graph generation
256
        :param ie: the inventory entry
257
        :return parents, revision_id where
0.64.160 by Ian Clatworthy
make per-file parents tuples and fix text loading in chk formats
258
            parents is the tuple of parent revision_ids for the per-file graph
0.85.2 by Ian Clatworthy
improve per-file graph generation
259
            revision_id is the revision_id to use for this entry
260
        """
261
        # Check for correct API usage
262
        if self._current_rev_id is None:
263
            raise AssertionError("start_new_revision() must be called"
264
                " before get_parents_and_revision_for_entry()")
265
        if ie.revision != self._current_rev_id:
266
            raise AssertionError("start_new_revision() registered a different"
267
                " revision (%s) to that in the inventory entry (%s)" %
268
                (self._current_rev_id, ie.revision))
269
270
        # Find the heads. This code is lifted from
271
        # repository.CommitBuilder.record_entry_contents().
272
        parent_candidate_entries = ie.parent_candidates(self._rev_parent_invs)
273
        head_set = self._commit_builder._heads(ie.file_id,
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
274
            list(parent_candidate_entries))
0.85.2 by Ian Clatworthy
improve per-file graph generation
275
        heads = []
276
        for inv in self._rev_parent_invs:
0.64.323 by Jelmer Vernooij
Avoid deprecated Inventory.__contains__.
277
            if inv.has_id(ie.file_id):
0.85.2 by Ian Clatworthy
improve per-file graph generation
278
                old_rev = inv[ie.file_id].revision
279
                if old_rev in head_set:
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
280
                    rev_id = inv[ie.file_id].revision
281
                    heads.append(rev_id)
282
                    head_set.remove(rev_id)
0.85.2 by Ian Clatworthy
improve per-file graph generation
283
284
        # Find the revision to use. If the content has not changed
285
        # since the parent, record the parent's revision.
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
286
        if len(heads) == 0:
287
            return (), ie.revision
0.85.2 by Ian Clatworthy
improve per-file graph generation
288
        parent_entry = parent_candidate_entries[heads[0]]
289
        changed = False
290
        if len(heads) > 1:
291
            changed = True
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
292
        elif (parent_entry.name != ie.name or parent_entry.kind != ie.kind or
0.85.2 by Ian Clatworthy
improve per-file graph generation
293
            parent_entry.parent_id != ie.parent_id): 
294
            changed = True
295
        elif ie.kind == 'file':
296
            if (parent_entry.text_sha1 != ie.text_sha1 or
297
                parent_entry.executable != ie.executable):
298
                changed = True
299
        elif ie.kind == 'symlink':
300
            if parent_entry.symlink_target != ie.symlink_target:
301
                changed = True
302
        if changed:
303
            rev_id = ie.revision
304
        else:
305
            rev_id = parent_entry.revision
0.64.160 by Ian Clatworthy
make per-file parents tuples and fix text loading in chk formats
306
        return tuple(heads), rev_id
0.85.2 by Ian Clatworthy
improve per-file graph generation
307
308
    def load(self, rev, inv, signature, text_provider, parents_provider,
0.64.48 by Ian Clatworthy
one revision loader instance
309
        inventories_provider=None):
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
310
        """Load a revision.
0.64.48 by Ian Clatworthy
one revision loader instance
311
312
        :param rev: the Revision
313
        :param inv: the inventory
314
        :param signature: signing information
315
        :param text_provider: a callable expecting a file_id parameter
316
            that returns the text for that file-id
0.85.2 by Ian Clatworthy
improve per-file graph generation
317
        :param parents_provider: a callable expecting a file_id parameter
318
            that return the list of parent-ids for that file-id
0.64.5 by Ian Clatworthy
first cut at generic processing method
319
        :param inventories_provider: a callable expecting a repository and
320
            a list of revision-ids, that returns:
321
              * the list of revision-ids present in the repository
322
              * the list of inventories for the revision-id's,
323
                including an empty inventory for the missing revisions
324
            If None, a default implementation is provided.
325
        """
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
326
        # NOTE: This is breezy.repository._install_revision refactored to
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
327
        # to provide more flexibility in how previous revisions are cached,
328
        # data is feed in, etc.
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
329
330
        # Get the non-ghost parents and their inventories
331
        if inventories_provider is None:
332
            inventories_provider = self._default_inventories_provider
333
        present_parents, parent_invs = inventories_provider(rev.parent_ids)
334
335
        # Load the inventory
336
        try:
337
            rev.inventory_sha1 = self._add_inventory(rev.revision_id,
338
                inv, present_parents, parent_invs)
339
        except errors.RevisionAlreadyPresent:
340
            pass
341
342
        # Load the texts, signature and revision
343
        entries = self._non_root_entries_iter(inv, rev.revision_id)
0.85.2 by Ian Clatworthy
improve per-file graph generation
344
        self._load_texts(rev.revision_id, entries, text_provider,
345
            parents_provider)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
346
        if signature is not None:
347
            self.repo.add_signature_text(rev.revision_id, signature)
348
        self._add_revision(rev, inv)
349
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
350
    def load_using_delta(self, rev, basis_inv, inv_delta, signature,
0.85.2 by Ian Clatworthy
improve per-file graph generation
351
        text_provider, parents_provider, inventories_provider=None):
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
352
        """Load a revision by applying a delta to a (CHK)Inventory.
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
353
354
        :param rev: the Revision
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
355
        :param basis_inv: the basis Inventory or CHKInventory
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
356
        :param inv_delta: the inventory delta
357
        :param signature: signing information
358
        :param text_provider: a callable expecting a file_id parameter
359
            that returns the text for that file-id
0.85.2 by Ian Clatworthy
improve per-file graph generation
360
        :param parents_provider: a callable expecting a file_id parameter
361
            that return the list of parent-ids for that file-id
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
362
        :param inventories_provider: a callable expecting a repository and
363
            a list of revision-ids, that returns:
364
              * the list of revision-ids present in the repository
365
              * the list of inventories for the revision-id's,
366
                including an empty inventory for the missing revisions
367
            If None, a default implementation is provided.
368
        """
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
369
        # TODO: set revision_id = rev.revision_id
370
        builder = self.repo._commit_builder_class(self.repo,
371
            parents=rev.parent_ids, config=None, timestamp=rev.timestamp,
372
            timezone=rev.timezone, committer=rev.committer,
373
            revprops=rev.properties, revision_id=rev.revision_id)
0.116.1 by John Arbash Meinel
Use the new KnownGraph.add_node() functionality.
374
        if self._graph is None and self._use_known_graph:
0.64.290 by Jelmer Vernooij
Avoid use of Repository.revisions, which may not be set.
375
            if (getattr(_mod_graph, 'GraphThunkIdsToKeys', None) and
376
                getattr(_mod_graph.GraphThunkIdsToKeys, "add_node", None) and
377
                getattr(self.repo, "get_known_graph_ancestry", None)):
378
                self._graph = self.repo.get_known_graph_ancestry(
379
                    rev.parent_ids)
380
            else:
0.116.1 by John Arbash Meinel
Use the new KnownGraph.add_node() functionality.
381
                self._use_known_graph = False
382
        if self._graph is not None:
0.116.2 by John Arbash Meinel
Some debugging code. It looks like the main bugs involve files that are deleted and restored.
383
            orig_heads = builder._heads
0.116.1 by John Arbash Meinel
Use the new KnownGraph.add_node() functionality.
384
            def thunked_heads(file_id, revision_ids):
385
                # self._graph thinks in terms of keys, not ids, so translate
386
                # them
0.116.2 by John Arbash Meinel
Some debugging code. It looks like the main bugs involve files that are deleted and restored.
387
                # old_res = orig_heads(file_id, revision_ids)
0.116.1 by John Arbash Meinel
Use the new KnownGraph.add_node() functionality.
388
                if len(revision_ids) < 2:
0.116.2 by John Arbash Meinel
Some debugging code. It looks like the main bugs involve files that are deleted and restored.
389
                    res = set(revision_ids)
390
                else:
0.64.290 by Jelmer Vernooij
Avoid use of Repository.revisions, which may not be set.
391
                    res = set(self._graph.heads(revision_ids))
0.116.2 by John Arbash Meinel
Some debugging code. It looks like the main bugs involve files that are deleted and restored.
392
                # if old_res != res:
393
                #     import pdb; pdb.set_trace()
394
                return res
0.116.1 by John Arbash Meinel
Use the new KnownGraph.add_node() functionality.
395
            builder._heads = thunked_heads
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
396
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
397
        if rev.parent_ids:
398
            basis_rev_id = rev.parent_ids[0]
399
        else:
400
            basis_rev_id = _mod_revision.NULL_REVISION
0.115.7 by John Arbash Meinel
Fall back to the repository for cases where the content is not present in the stream yet.
401
        tree = _TreeShim(self.repo, basis_inv, inv_delta, text_provider)
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
402
        changes = tree._delta_to_iter_changes()
403
        for (file_id, path, fs_hash) in builder.record_iter_changes(
404
                tree, basis_rev_id, changes):
405
            # So far, we don't *do* anything with the result
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
406
            pass
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
407
        builder.finish_inventory()
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
408
        # TODO: This is working around a bug in the breezy code base.
0.115.5 by John Arbash Meinel
Found a bug in CommitBuilder.finish_inventory().
409
        # 'builder.finish_inventory()' ends up doing:
410
        # self.inv_sha1 = self.repository.add_inventory_by_delta(...)
411
        # However, add_inventory_by_delta returns (sha1, inv)
412
        # And we *want* to keep a handle on both of those objects
413
        if isinstance(builder.inv_sha1, tuple):
414
            builder.inv_sha1, builder.new_inventory = builder.inv_sha1
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
415
        # This is a duplicate of Builder.commit() since we already have the
416
        # Revision object, and we *don't* want to call commit_write_group()
417
        rev.inv_sha1 = builder.inv_sha1
6690.2.2 by Jelmer Vernooij
Drop support for bzr < 2.5.
418
        config = builder._config_stack
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
419
        builder.repository.add_revision(builder._new_revision_id, rev,
0.64.345 by Jelmer Vernooij
Fix compatibility with bzr 2.5.
420
            builder.new_inventory)
0.116.1 by John Arbash Meinel
Use the new KnownGraph.add_node() functionality.
421
        if self._graph is not None:
422
            # TODO: Use StaticTuple and .intern() for these things
0.64.290 by Jelmer Vernooij
Avoid use of Repository.revisions, which may not be set.
423
            self._graph.add_node(builder._new_revision_id, rev.parent_ids)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
424
425
        if signature is not None:
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
426
            raise AssertionError('signatures not guaranteed yet')
0.64.290 by Jelmer Vernooij
Avoid use of Repository.revisions, which may not be set.
427
            self.repo.add_signature_text(rev.revision_id, signature)
0.115.4 by John Arbash Meinel
(broken) Start working towards using CommitBuilder rather than using a custom implementation.
428
        # self._add_revision(rev, inv)
6643.1.1 by Jelmer Vernooij
Fix fastimport tests now that RevisionTree.inventory has been removed.
429
        return builder.revision_tree().root_inventory
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
430
431
    def _non_root_entries_iter(self, inv, revision_id):
0.84.11 by Ian Clatworthy
use iter_non_root_entries if it exists
432
        if hasattr(inv, 'iter_non_root_entries'):
433
            entries = inv.iter_non_root_entries()
434
        else:
435
            path_entries = inv.iter_entries()
436
            # Backwards compatibility hack: skip the root id.
437
            if not self.repo.supports_rich_root():
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
438
                path, root = next(path_entries)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
439
                if root.revision != revision_id:
0.84.11 by Ian Clatworthy
use iter_non_root_entries if it exists
440
                    raise errors.IncompatibleRevision(repr(self.repo))
441
            entries = iter([ie for path, ie in path_entries])
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
442
        return entries
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
443
0.85.2 by Ian Clatworthy
improve per-file graph generation
444
    def _load_texts(self, revision_id, entries, text_provider,
445
        parents_provider):
0.64.5 by Ian Clatworthy
first cut at generic processing method
446
        """Load texts to a repository for inventory entries.
447
        
448
        This method is provided for subclasses to use or override.
449
450
        :param revision_id: the revision identifier
451
        :param entries: iterator over the inventory entries
452
        :param text_provider: a callable expecting a file_id parameter
453
            that returns the text for that file-id
0.85.2 by Ian Clatworthy
improve per-file graph generation
454
        :param parents_provider: a callable expecting a file_id parameter
455
            that return the list of parent-ids for that file-id
0.64.5 by Ian Clatworthy
first cut at generic processing method
456
        """
0.64.79 by Ian Clatworthy
support new Repository API
457
        raise NotImplementedError(self._load_texts)
0.64.5 by Ian Clatworthy
first cut at generic processing method
458
0.81.3 by Ian Clatworthy
enhance RevisionLoader to try inventory deltas & decide on rich-roots
459
    def _add_inventory(self, revision_id, inv, parents, parent_invs):
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
460
        """Add the inventory inv to the repository as revision_id.
461
        
462
        :param parents: The revision ids of the parents that revision_id
463
                        is known to have and are in the repository already.
0.81.3 by Ian Clatworthy
enhance RevisionLoader to try inventory deltas & decide on rich-roots
464
        :param parent_invs: the parent inventories
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
465
466
        :returns: The validator(which is a sha1 digest, though what is sha'd is
467
            repository format specific) of the serialized inventory.
468
        """
0.64.156 by Ian Clatworthy
minor revision_store clean-ups
469
        return self.repo.add_inventory(revision_id, inv, parents)
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
470
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
471
    def _add_inventory_by_delta(self, revision_id, basis_inv, inv_delta,
472
        parents, parent_invs):
473
        """Add the inventory to the repository as revision_id.
474
        
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
475
        :param basis_inv: the basis Inventory or CHKInventory
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
476
        :param inv_delta: the inventory delta
477
        :param parents: The revision ids of the parents that revision_id
478
                        is known to have and are in the repository already.
479
        :param parent_invs: the parent inventories
480
0.64.156 by Ian Clatworthy
minor revision_store clean-ups
481
        :returns: (validator, inv) where validator is the validator
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
482
          (which is a sha1 digest, though what is sha'd is repository format
483
          specific) of the serialized inventory;
484
          inv is the generated inventory
485
        """
486
        if len(parents):
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
487
            if self._supports_chks:
0.99.3 by Ian Clatworthy
Improve diagnostics collection when inconsistent delta found
488
                try:
489
                    validator, new_inv = self.repo.add_inventory_by_delta(parents[0],
490
                        inv_delta, revision_id, parents, basis_inv=basis_inv,
491
                        propagate_caches=False)
492
                except errors.InconsistentDelta:
493
                    #print "BASIS INV IS\n%s\n" % "\n".join([str(i) for i in basis_inv.iter_entries_by_dir()])
494
                    trace.mutter("INCONSISTENT DELTA IS:\n%s\n" % "\n".join([str(i) for i in inv_delta]))
495
                    raise
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
496
            else:
497
                validator, new_inv = self.repo.add_inventory_by_delta(parents[0],
498
                    inv_delta, revision_id, parents)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
499
        else:
0.64.257 by Ian Clatworthy
Get fastimport working on non-chk repositories again for bzr versions after 2.0.0
500
            if isinstance(basis_inv, inventory.CHKInventory):
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
501
                new_inv = basis_inv.create_by_apply_delta(inv_delta, revision_id)
502
            else:
503
                new_inv = inventory.Inventory(revision_id=revision_id)
504
                # This is set in the delta so remove it to prevent a duplicate
505
                del new_inv[inventory.ROOT_ID]
506
                new_inv.apply_delta(inv_delta)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
507
            validator = self.repo.add_inventory(revision_id, new_inv, parents)
508
        return validator, new_inv
509
0.64.79 by Ian Clatworthy
support new Repository API
510
    def _add_revision(self, rev, inv):
511
        """Add a revision and its inventory to a repository.
512
513
        :param rev: the Revision
514
        :param inv: the inventory
515
        """
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
516
        self.repo.add_revision(rev.revision_id, rev, inv)
0.64.79 by Ian Clatworthy
support new Repository API
517
0.64.5 by Ian Clatworthy
first cut at generic processing method
518
    def _default_inventories_provider(self, revision_ids):
519
        """An inventories provider that queries the repository."""
520
        present = []
521
        inventories = []
522
        for revision_id in revision_ids:
523
            if self.repo.has_revision(revision_id):
524
                present.append(revision_id)
525
                rev_tree = self.repo.revision_tree(revision_id)
526
            else:
527
                rev_tree = self.repo.revision_tree(None)
6643.1.1 by Jelmer Vernooij
Fix fastimport tests now that RevisionTree.inventory has been removed.
528
            inventories.append(rev_tree.root_inventory)
0.64.5 by Ian Clatworthy
first cut at generic processing method
529
        return present, inventories
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
530
531
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
532
class RevisionStore1(AbstractRevisionStore):
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
533
    """A RevisionStore that uses the old breezy Repository API.
0.64.79 by Ian Clatworthy
support new Repository API
534
    
535
    The old API was present until bzr.dev rev 3510.
536
    """
537
0.85.2 by Ian Clatworthy
improve per-file graph generation
538
    def _load_texts(self, revision_id, entries, text_provider, parents_provider):
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
539
        """See RevisionStore._load_texts()."""
0.64.79 by Ian Clatworthy
support new Repository API
540
        # Add the texts that are not already present
541
        tx = self.repo.get_transaction()
0.84.11 by Ian Clatworthy
use iter_non_root_entries if it exists
542
        for ie in entries:
0.64.79 by Ian Clatworthy
support new Repository API
543
            # This test is *really* slow: over 50% of import time
544
            #w = self.repo.weave_store.get_weave_or_empty(ie.file_id, tx)
545
            #if ie.revision in w:
546
            #    continue
547
            # Try another way, realising that this assumes that the
548
            # version is not already there. In the general case,
549
            # a shared repository might already have the revision but
550
            # we arguably don't need that check when importing from
551
            # a foreign system.
552
            if ie.revision != revision_id:
553
                continue
0.85.2 by Ian Clatworthy
improve per-file graph generation
554
            file_id = ie.file_id
555
            text_parents = [(file_id, p) for p in parents_provider(file_id)]
556
            lines = text_provider(file_id)
557
            vfile = self.repo.weave_store.get_weave_or_empty(file_id,  tx)
0.64.79 by Ian Clatworthy
support new Repository API
558
            vfile.add_lines(revision_id, text_parents, lines)
559
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
560
    def get_file_lines(self, revision_id, file_id):
0.74.1 by John Arbash Meinel
Change the rename code to create a new text entry.
561
        tx = self.repo.get_transaction()
0.64.139 by Ian Clatworthy
fix ie reference in _get_file_lines()
562
        w = self.repo.weave_store.get_weave(file_id, tx)
0.74.1 by John Arbash Meinel
Change the rename code to create a new text entry.
563
        return w.get_lines(revision_id)
564
0.64.79 by Ian Clatworthy
support new Repository API
565
    def _add_revision(self, rev, inv):
566
        # There's no need to do everything repo.add_revision does and
567
        # doing so (since bzr.dev 3392) can be pretty slow for long
568
        # delta chains on inventories. Just do the essentials here ...
569
        _mod_revision.check_not_reserved_id(rev.revision_id)
570
        self.repo._revision_store.add_revision(rev, self.repo.get_transaction())
571
572
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
573
class RevisionStore2(AbstractRevisionStore):
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
574
    """A RevisionStore that uses the new breezy Repository API."""
0.64.79 by Ian Clatworthy
support new Repository API
575
0.85.2 by Ian Clatworthy
improve per-file graph generation
576
    def _load_texts(self, revision_id, entries, text_provider, parents_provider):
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
577
        """See RevisionStore._load_texts()."""
0.64.79 by Ian Clatworthy
support new Repository API
578
        text_keys = {}
0.84.11 by Ian Clatworthy
use iter_non_root_entries if it exists
579
        for ie in entries:
0.64.79 by Ian Clatworthy
support new Repository API
580
            text_keys[(ie.file_id, ie.revision)] = ie
581
        text_parent_map = self.repo.texts.get_parent_map(text_keys)
582
        missing_texts = set(text_keys) - set(text_parent_map)
0.85.2 by Ian Clatworthy
improve per-file graph generation
583
        self._load_texts_for_file_rev_ids(missing_texts, text_provider,
584
            parents_provider)
0.64.79 by Ian Clatworthy
support new Repository API
585
0.85.2 by Ian Clatworthy
improve per-file graph generation
586
    def _load_texts_for_file_rev_ids(self, file_rev_ids, text_provider,
587
        parents_provider):
0.64.155 by Ian Clatworthy
store empty texts for non-files for chk formats, not just other formats
588
        """Load texts to a repository for file-ids, revision-id tuples.
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
589
        
0.64.155 by Ian Clatworthy
store empty texts for non-files for chk formats, not just other formats
590
        :param file_rev_ids: iterator over the (file_id, revision_id) tuples
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
591
        :param text_provider: a callable expecting a file_id parameter
592
            that returns the text for that file-id
0.85.2 by Ian Clatworthy
improve per-file graph generation
593
        :param parents_provider: a callable expecting a file_id parameter
594
            that return the list of parent-ids for that file-id
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
595
        """
0.64.155 by Ian Clatworthy
store empty texts for non-files for chk formats, not just other formats
596
        for file_id, revision_id in file_rev_ids:
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
597
            text_key = (file_id, revision_id)
0.85.2 by Ian Clatworthy
improve per-file graph generation
598
            text_parents = [(file_id, p) for p in parents_provider(file_id)]
599
            lines = text_provider(file_id)
600
            #print "adding text for %s\n\tparents:%s" % (text_key,text_parents)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
601
            self.repo.texts.add_lines(text_key, text_parents, lines)
602
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
603
    def get_file_lines(self, revision_id, file_id):
6634.2.1 by Martin
Apply 2to3 next fixer and make compatible
604
        record = next(self.repo.texts.get_record_stream([(file_id, revision_id)],
605
            'unordered', True))
0.74.1 by John Arbash Meinel
Change the rename code to create a new text entry.
606
        if record.storage_kind == 'absent':
607
            raise errors.RevisionNotPresent(record.key, self.repo)
608
        return osutils.split_lines(record.get_bytes_as('fulltext'))
609
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
610
    # This is breaking imports into brisbane-core currently
611
    #def _add_revision(self, rev, inv):
612
    #    # There's no need to do everything repo.add_revision does and
613
    #    # doing so (since bzr.dev 3392) can be pretty slow for long
614
    #    # delta chains on inventories. Just do the essentials here ...
615
    #    _mod_revision.check_not_reserved_id(rev.revision_id)
616
    #    self.repo._add_revision(rev)
0.64.79 by Ian Clatworthy
support new Repository API
617
 
618
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
619
class ImportRevisionStore1(RevisionStore1):
620
    """A RevisionStore (old Repository API) optimised for importing.
0.64.79 by Ian Clatworthy
support new Repository API
621
622
    This implementation caches serialised inventory texts and provides
623
    fine-grained control over when inventories are stored as fulltexts.
624
    """
625
626
    def __init__(self, repo, parent_texts_to_cache=1, fulltext_when=None,
627
        random_ids=True):
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
628
        """See AbstractRevisionStore.__init__.
0.64.48 by Ian Clatworthy
one revision loader instance
629
630
        :param repository: the target repository
631
        :param parent_text_to_cache: the number of parent texts to cache
0.64.79 by Ian Clatworthy
support new Repository API
632
        :para fulltext_when: if non None, a function to call to decide
633
          whether to fulltext the inventory or not. The revision count
634
          is passed as a parameter and the result is treated as a boolean.
0.64.48 by Ian Clatworthy
one revision loader instance
635
        """
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
636
        RevisionStore1.__init__(self, repo)
0.64.48 by Ian Clatworthy
one revision loader instance
637
        self.inv_parent_texts = lru_cache.LRUCache(parent_texts_to_cache)
0.64.79 by Ian Clatworthy
support new Repository API
638
        self.fulltext_when = fulltext_when
0.64.49 by Ian Clatworthy
skip check re fulltext storage better than delta for inventories when in experimental mode
639
        self.random_ids = random_ids
0.64.79 by Ian Clatworthy
support new Repository API
640
        self.revision_count = 0
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
641
0.81.3 by Ian Clatworthy
enhance RevisionLoader to try inventory deltas & decide on rich-roots
642
    def _add_inventory(self, revision_id, inv, parents, parent_invs):
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
643
        """See RevisionStore._add_inventory."""
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
644
        # Code taken from breezy.repository.add_inventory
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
645
        assert self.repo.is_in_write_group()
646
        _mod_revision.check_not_reserved_id(revision_id)
647
        assert inv.revision_id is None or inv.revision_id == revision_id, \
648
            "Mismatch between inventory revision" \
649
            " id and insertion revid (%r, %r)" % (inv.revision_id, revision_id)
650
        assert inv.root is not None
651
        inv_lines = self.repo._serialise_inventory_to_lines(inv)
652
        inv_vf = self.repo.get_inventory_weave()
0.64.49 by Ian Clatworthy
skip check re fulltext storage better than delta for inventories when in experimental mode
653
        sha1, num_bytes, parent_text = self._inventory_add_lines(inv_vf,
654
            revision_id, parents, inv_lines, self.inv_parent_texts)
655
        self.inv_parent_texts[revision_id] = parent_text
656
        return sha1
657
658
    def _inventory_add_lines(self, inv_vf, version_id, parents, lines,
659
            parent_texts):
660
        """See Repository._inventory_add_lines()."""
661
        # setup parameters used in original code but not this API
662
        self.revision_count += 1
0.64.77 by Ian Clatworthy
add inv-fulltext option and improve speed
663
        if self.fulltext_when is not None:
664
            delta = not self.fulltext_when(self.revision_count)
0.64.49 by Ian Clatworthy
skip check re fulltext storage better than delta for inventories when in experimental mode
665
        else:
666
            delta = inv_vf.delta
667
        left_matching_blocks = None
668
        random_id = self.random_ids
669
        check_content = False
670
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
671
        # breezy.knit.add_lines() but error checking optimised
0.64.49 by Ian Clatworthy
skip check re fulltext storage better than delta for inventories when in experimental mode
672
        inv_vf._check_add(version_id, lines, random_id, check_content)
673
674
        ####################################################################
6628.1.2 by Jelmer Vernooij
Fix imports, move exporter.py, drop explorer metadata.
675
        # breezy.knit._add() but skip checking if fulltext better than delta
0.64.49 by Ian Clatworthy
skip check re fulltext storage better than delta for inventories when in experimental mode
676
        ####################################################################
677
678
        line_bytes = ''.join(lines)
679
        digest = osutils.sha_string(line_bytes)
680
        present_parents = []
681
        for parent in parents:
682
            if inv_vf.has_version(parent):
683
                present_parents.append(parent)
684
        if parent_texts is None:
685
            parent_texts = {}
686
687
        # can only compress against the left most present parent.
688
        if (delta and
689
            (len(present_parents) == 0 or
690
             present_parents[0] != parents[0])):
691
            delta = False
692
693
        text_length = len(line_bytes)
694
        options = []
695
        if lines:
696
            if lines[-1][-1] != '\n':
697
                # copy the contents of lines.
698
                lines = lines[:]
699
                options.append('no-eol')
700
                lines[-1] = lines[-1] + '\n'
701
                line_bytes += '\n'
702
703
        #if delta:
704
        #    # To speed the extract of texts the delta chain is limited
705
        #    # to a fixed number of deltas.  This should minimize both
706
        #    # I/O and the time spend applying deltas.
707
        #    delta = inv_vf._check_should_delta(present_parents)
708
709
        assert isinstance(version_id, str)
710
        content = inv_vf.factory.make(lines, version_id)
711
        if delta or (inv_vf.factory.annotated and len(present_parents) > 0):
712
            # Merge annotations from parent texts if needed.
713
            delta_hunks = inv_vf._merge_annotations(content, present_parents,
714
                parent_texts, delta, inv_vf.factory.annotated,
715
                left_matching_blocks)
716
717
        if delta:
718
            options.append('line-delta')
719
            store_lines = inv_vf.factory.lower_line_delta(delta_hunks)
720
            size, bytes = inv_vf._data._record_to_data(version_id, digest,
721
                store_lines)
722
        else:
723
            options.append('fulltext')
724
            # isinstance is slower and we have no hierarchy.
725
            if inv_vf.factory.__class__ == knit.KnitPlainFactory:
726
                # Use the already joined bytes saving iteration time in
727
                # _record_to_data.
728
                size, bytes = inv_vf._data._record_to_data(version_id, digest,
729
                    lines, [line_bytes])
730
            else:
731
                # get mixed annotation + content and feed it into the
732
                # serialiser.
733
                store_lines = inv_vf.factory.lower_fulltext(content)
734
                size, bytes = inv_vf._data._record_to_data(version_id, digest,
735
                    store_lines)
736
737
        access_memo = inv_vf._data.add_raw_records([size], bytes)[0]
738
        inv_vf._index.add_versions(
739
            ((version_id, options, access_memo, parents),),
740
            random_id=random_id)
741
        return digest, text_length, content