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