/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.64.5 by Ian Clatworthy
first cut at generic processing method
1
# Copyright (C) 2008 Canonical Ltd
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
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
17
"""An abstraction of a repository providing just the bits importing needs."""
0.64.5 by Ian Clatworthy
first cut at generic processing method
18
19
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
20
from bzrlib import errors, inventory, knit, lru_cache, osutils
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
21
from bzrlib import revision as _mod_revision
0.64.6 by Ian Clatworthy
generic processing method working for one revision in one branch
22
23
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
24
class AbstractRevisionStore(object):
0.64.5 by Ian Clatworthy
first cut at generic processing method
25
0.64.48 by Ian Clatworthy
one revision loader instance
26
    def __init__(self, repo):
0.64.5 by Ian Clatworthy
first cut at generic processing method
27
        """An object responsible for loading revisions into a repository.
28
29
        NOTE: Repository locking is not managed by this class. Clients
30
        should take a write lock, call load() multiple times, then release
31
        the lock.
32
33
        :param repository: the target repository
0.64.48 by Ian Clatworthy
one revision loader instance
34
        """
35
        self.repo = repo
0.84.8 by Ian Clatworthy
ensure the chk stuff is only used on formats actually supporting it
36
        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
37
38
    def expects_rich_root(self):
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
39
        """Does this store expect inventories with rich roots?"""
0.81.3 by Ian Clatworthy
enhance RevisionLoader to try inventory deltas & decide on rich-roots
40
        return self.repo.supports_rich_root()
0.64.48 by Ian Clatworthy
one revision loader instance
41
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
42
    def init_inventory(self, revision_id):
43
        """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
44
        if self._supports_chks:
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
45
            inv = self._init_chk_inventory(revision_id, inventory.ROOT_ID)
46
        else:
47
            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
48
            if self.expects_rich_root():
49
                # The very first root needs to have the right revision
50
                inv.root.revision = revision_id
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
51
        return inv
52
53
    def _init_chk_inventory(self, revision_id, root_id):
54
        """Generate a CHKInventory for a parentless revision."""
55
        from bzrlib import chk_map
56
        # Get the creation parameters
0.84.8 by Ian Clatworthy
ensure the chk stuff is only used on formats actually supporting it
57
        chk_store = self.repo.chk_bytes
0.84.4 by Ian Clatworthy
improved-but-not-yet-working CHKInventory support
58
        serializer = self.repo._format._serializer
59
        search_key_name = serializer.search_key_name
60
        maximum_size = serializer.maximum_size
61
62
        # Maybe the rest of this ought to be part of the CHKInventory API?
63
        inv = inventory.CHKInventory(search_key_name)
64
        inv.revision_id = revision_id
65
        inv.root_id = root_id
66
        search_key_func = chk_map.search_key_registry.get(search_key_name)
67
        inv.id_to_entry = chk_map.CHKMap(chk_store, None, search_key_func)
68
        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
69
        inv.parent_id_basename_to_file_id = chk_map.CHKMap(chk_store,
70
            None, search_key_func)
71
        inv.parent_id_basename_to_file_id._root_node.set_maximum_size(
72
            maximum_size)
73
        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
74
        return inv
75
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
76
    def get_inventory(self, revision_id):
77
        """Get a stored inventory."""
78
        return self.repo.get_inventory(revision_id)
79
80
    def get_file_text(self, revision_id, file_id):
81
        """Get the text stored for a file in a given revision."""
82
        revtree = self.repo.revision_tree(revision_id)
83
        return revtree.get_file_text(file_id)
84
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
85
    def get_file_lines(self, revision_id, file_id):
86
        """Get the lines stored for a file in a given revision."""
0.64.156 by Ian Clatworthy
minor revision_store clean-ups
87
        revtree = self.repo.revision_tree(revision_id)
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
88
        return osutils.split_lines(revtree.get_file_text(file_id))
89
0.85.2 by Ian Clatworthy
improve per-file graph generation
90
    def start_new_revision(self, revision, parents, parent_invs):
91
        """Init the metadata needed for get_parents_and_revision_for_entry().
92
93
        :param revision: a Revision object
94
        """
95
        self._current_rev_id = revision.revision_id
96
        self._rev_parents = parents
97
        self._rev_parent_invs = parent_invs
98
        # We don't know what the branch will be so there's no real BranchConfig.
99
        # That means we won't be triggering any hooks and that's a good thing.
100
        # Without a config though, we must pass in the committer below so that
101
        # the commit builder doesn't try to look up the config.
102
        config = None
103
        # We can't use self.repo.get_commit_builder() here because it starts a
104
        # new write group. We want one write group around a batch of imports
105
        # where the default batch size is currently 10000. IGC 20090312
106
        self._commit_builder = self.repo._commit_builder_class(self.repo,
107
            parents, config, timestamp=revision.timestamp,
108
            timezone=revision.timezone, committer=revision.committer,
109
            revprops=revision.properties, revision_id=revision.revision_id)
110
111
    def get_parents_and_revision_for_entry(self, ie):
112
        """Get the parents and revision for an inventory entry.
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
113
 
0.85.2 by Ian Clatworthy
improve per-file graph generation
114
        :param ie: the inventory entry
115
        :return parents, revision_id where
0.64.160 by Ian Clatworthy
make per-file parents tuples and fix text loading in chk formats
116
            parents is the tuple of parent revision_ids for the per-file graph
0.85.2 by Ian Clatworthy
improve per-file graph generation
117
            revision_id is the revision_id to use for this entry
118
        """
119
        # Check for correct API usage
120
        if self._current_rev_id is None:
121
            raise AssertionError("start_new_revision() must be called"
122
                " before get_parents_and_revision_for_entry()")
123
        if ie.revision != self._current_rev_id:
124
            raise AssertionError("start_new_revision() registered a different"
125
                " revision (%s) to that in the inventory entry (%s)" %
126
                (self._current_rev_id, ie.revision))
127
128
        # Find the heads. This code is lifted from
129
        # repository.CommitBuilder.record_entry_contents().
130
        parent_candidate_entries = ie.parent_candidates(self._rev_parent_invs)
131
        head_set = self._commit_builder._heads(ie.file_id,
132
            parent_candidate_entries.keys())
133
        heads = []
134
        for inv in self._rev_parent_invs:
135
            if ie.file_id in inv:
136
                old_rev = inv[ie.file_id].revision
137
                if old_rev in head_set:
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
138
                    rev_id = inv[ie.file_id].revision
139
                    heads.append(rev_id)
140
                    head_set.remove(rev_id)
0.85.2 by Ian Clatworthy
improve per-file graph generation
141
142
        # Find the revision to use. If the content has not changed
143
        # since the parent, record the parent's revision.
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
144
        if len(heads) == 0:
145
            return (), ie.revision
0.85.2 by Ian Clatworthy
improve per-file graph generation
146
        parent_entry = parent_candidate_entries[heads[0]]
147
        changed = False
148
        if len(heads) > 1:
149
            changed = True
0.64.161 by Ian Clatworthy
fix per-graph parent handling for adds and renames
150
        elif (parent_entry.name != ie.name or parent_entry.kind != ie.kind or
0.85.2 by Ian Clatworthy
improve per-file graph generation
151
            parent_entry.parent_id != ie.parent_id): 
152
            changed = True
153
        elif ie.kind == 'file':
154
            if (parent_entry.text_sha1 != ie.text_sha1 or
155
                parent_entry.executable != ie.executable):
156
                changed = True
157
        elif ie.kind == 'symlink':
158
            if parent_entry.symlink_target != ie.symlink_target:
159
                changed = True
160
        if changed:
161
            rev_id = ie.revision
162
        else:
163
            rev_id = parent_entry.revision
0.64.160 by Ian Clatworthy
make per-file parents tuples and fix text loading in chk formats
164
        return tuple(heads), rev_id
0.85.2 by Ian Clatworthy
improve per-file graph generation
165
166
    def load(self, rev, inv, signature, text_provider, parents_provider,
0.64.48 by Ian Clatworthy
one revision loader instance
167
        inventories_provider=None):
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
168
        """Load a revision.
0.64.48 by Ian Clatworthy
one revision loader instance
169
170
        :param rev: the Revision
171
        :param inv: the inventory
172
        :param signature: signing information
173
        :param text_provider: a callable expecting a file_id parameter
174
            that returns the text for that file-id
0.85.2 by Ian Clatworthy
improve per-file graph generation
175
        :param parents_provider: a callable expecting a file_id parameter
176
            that return the list of parent-ids for that file-id
0.64.5 by Ian Clatworthy
first cut at generic processing method
177
        :param inventories_provider: a callable expecting a repository and
178
            a list of revision-ids, that returns:
179
              * the list of revision-ids present in the repository
180
              * the list of inventories for the revision-id's,
181
                including an empty inventory for the missing revisions
182
            If None, a default implementation is provided.
183
        """
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
184
        # NOTE: This is bzrlib.repository._install_revision refactored to
185
        # to provide more flexibility in how previous revisions are cached,
186
        # data is feed in, etc.
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
187
188
        # Get the non-ghost parents and their inventories
189
        if inventories_provider is None:
190
            inventories_provider = self._default_inventories_provider
191
        present_parents, parent_invs = inventories_provider(rev.parent_ids)
192
193
        # Load the inventory
194
        try:
195
            rev.inventory_sha1 = self._add_inventory(rev.revision_id,
196
                inv, present_parents, parent_invs)
197
        except errors.RevisionAlreadyPresent:
198
            pass
199
200
        # Load the texts, signature and revision
201
        entries = self._non_root_entries_iter(inv, rev.revision_id)
0.85.2 by Ian Clatworthy
improve per-file graph generation
202
        self._load_texts(rev.revision_id, entries, text_provider,
203
            parents_provider)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
204
        if signature is not None:
205
            self.repo.add_signature_text(rev.revision_id, signature)
206
        self._add_revision(rev, inv)
207
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
208
    def load_using_delta(self, rev, basis_inv, inv_delta, signature,
0.85.2 by Ian Clatworthy
improve per-file graph generation
209
        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
210
        """Load a revision by applying a delta to a (CHK)Inventory.
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
211
212
        :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
213
        :param basis_inv: the basis Inventory or CHKInventory
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
214
        :param inv_delta: the inventory delta
215
        :param signature: signing information
216
        :param text_provider: a callable expecting a file_id parameter
217
            that returns the text for that file-id
0.85.2 by Ian Clatworthy
improve per-file graph generation
218
        :param parents_provider: a callable expecting a file_id parameter
219
            that return the list of parent-ids for that file-id
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
220
        :param inventories_provider: a callable expecting a repository and
221
            a list of revision-ids, that returns:
222
              * the list of revision-ids present in the repository
223
              * the list of inventories for the revision-id's,
224
                including an empty inventory for the missing revisions
225
            If None, a default implementation is provided.
226
        """
227
        # Get the non-ghost parents and their inventories
228
        if inventories_provider is None:
229
            inventories_provider = self._default_inventories_provider
230
        present_parents, parent_invs = inventories_provider(rev.parent_ids)
231
232
        # Load the inventory
233
        try:
234
            rev_id = rev.revision_id
235
            rev.inventory_sha1, inv = self._add_inventory_by_delta(
236
                rev_id, basis_inv, inv_delta, present_parents, parent_invs)
237
        except errors.RevisionAlreadyPresent:
238
            pass
239
240
        # Load the texts, signature and revision
0.64.155 by Ian Clatworthy
store empty texts for non-files for chk formats, not just other formats
241
        file_rev_ids_needing_texts = [(id, ie.revision)
0.64.160 by Ian Clatworthy
make per-file parents tuples and fix text loading in chk formats
242
            for _, n, id, ie in inv_delta
243
            if n is not None and ie.revision == rev_id]
0.64.155 by Ian Clatworthy
store empty texts for non-files for chk formats, not just other formats
244
        self._load_texts_for_file_rev_ids(file_rev_ids_needing_texts,
0.85.2 by Ian Clatworthy
improve per-file graph generation
245
            text_provider, parents_provider)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
246
        if signature is not None:
247
            self.repo.add_signature_text(rev_id, signature)
248
        self._add_revision(rev, inv)
249
        return inv
250
251
    def _non_root_entries_iter(self, inv, revision_id):
0.84.11 by Ian Clatworthy
use iter_non_root_entries if it exists
252
        if hasattr(inv, 'iter_non_root_entries'):
253
            entries = inv.iter_non_root_entries()
254
        else:
255
            path_entries = inv.iter_entries()
256
            # Backwards compatibility hack: skip the root id.
257
            if not self.repo.supports_rich_root():
258
                path, root = path_entries.next()
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
259
                if root.revision != revision_id:
0.84.11 by Ian Clatworthy
use iter_non_root_entries if it exists
260
                    raise errors.IncompatibleRevision(repr(self.repo))
261
            entries = iter([ie for path, ie in path_entries])
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
262
        return entries
0.81.10 by Ian Clatworthy
get DeltaCommitHandler passing all tests
263
0.85.2 by Ian Clatworthy
improve per-file graph generation
264
    def _load_texts(self, revision_id, entries, text_provider,
265
        parents_provider):
0.64.5 by Ian Clatworthy
first cut at generic processing method
266
        """Load texts to a repository for inventory entries.
267
        
268
        This method is provided for subclasses to use or override.
269
270
        :param revision_id: the revision identifier
271
        :param entries: iterator over the inventory entries
272
        :param text_provider: a callable expecting a file_id parameter
273
            that returns the text for that file-id
0.85.2 by Ian Clatworthy
improve per-file graph generation
274
        :param parents_provider: a callable expecting a file_id parameter
275
            that return the list of parent-ids for that file-id
0.64.5 by Ian Clatworthy
first cut at generic processing method
276
        """
0.64.79 by Ian Clatworthy
support new Repository API
277
        raise NotImplementedError(self._load_texts)
0.64.5 by Ian Clatworthy
first cut at generic processing method
278
0.81.3 by Ian Clatworthy
enhance RevisionLoader to try inventory deltas & decide on rich-roots
279
    def _add_inventory(self, revision_id, inv, parents, parent_invs):
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
280
        """Add the inventory inv to the repository as revision_id.
281
        
282
        :param parents: The revision ids of the parents that revision_id
283
                        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
284
        :param parent_invs: the parent inventories
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
285
286
        :returns: The validator(which is a sha1 digest, though what is sha'd is
287
            repository format specific) of the serialized inventory.
288
        """
0.64.156 by Ian Clatworthy
minor revision_store clean-ups
289
        return self.repo.add_inventory(revision_id, inv, parents)
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
290
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
291
    def _add_inventory_by_delta(self, revision_id, basis_inv, inv_delta,
292
        parents, parent_invs):
293
        """Add the inventory to the repository as revision_id.
294
        
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
295
        :param basis_inv: the basis Inventory or CHKInventory
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
296
        :param inv_delta: the inventory delta
297
        :param parents: The revision ids of the parents that revision_id
298
                        is known to have and are in the repository already.
299
        :param parent_invs: the parent inventories
300
0.64.156 by Ian Clatworthy
minor revision_store clean-ups
301
        :returns: (validator, inv) where validator is the validator
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
302
          (which is a sha1 digest, though what is sha'd is repository format
303
          specific) of the serialized inventory;
304
          inv is the generated inventory
305
        """
306
        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
307
            if self._supports_chks:
308
                validator, new_inv = self.repo.add_inventory_by_delta(parents[0],
309
                    inv_delta, revision_id, parents, basis_inv=basis_inv,
310
                    propagate_caches=False)
311
            else:
312
                validator, new_inv = self.repo.add_inventory_by_delta(parents[0],
313
                    inv_delta, revision_id, parents)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
314
        else:
0.64.171 by Ian Clatworthy
use inv deltas by default for all formats now: --classic to get old algorithm for packs
315
            if hasattr(basis_inv, 'create_by_apply_delta'):
316
                new_inv = basis_inv.create_by_apply_delta(inv_delta, revision_id)
317
            else:
318
                new_inv = inventory.Inventory(revision_id=revision_id)
319
                # This is set in the delta so remove it to prevent a duplicate
320
                del new_inv[inventory.ROOT_ID]
321
                new_inv.apply_delta(inv_delta)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
322
            validator = self.repo.add_inventory(revision_id, new_inv, parents)
323
        return validator, new_inv
324
0.64.79 by Ian Clatworthy
support new Repository API
325
    def _add_revision(self, rev, inv):
326
        """Add a revision and its inventory to a repository.
327
328
        :param rev: the Revision
329
        :param inv: the inventory
330
        """
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
331
        self.repo.add_revision(rev.revision_id, rev, inv)
0.64.79 by Ian Clatworthy
support new Repository API
332
0.64.5 by Ian Clatworthy
first cut at generic processing method
333
    def _default_inventories_provider(self, revision_ids):
334
        """An inventories provider that queries the repository."""
335
        present = []
336
        inventories = []
337
        for revision_id in revision_ids:
338
            if self.repo.has_revision(revision_id):
339
                present.append(revision_id)
340
                rev_tree = self.repo.revision_tree(revision_id)
341
            else:
342
                rev_tree = self.repo.revision_tree(None)
343
            inventories.append(rev_tree.inventory)
344
        return present, inventories
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
345
346
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
347
class RevisionStore1(AbstractRevisionStore):
348
    """A RevisionStore that uses the old bzrlib Repository API.
0.64.79 by Ian Clatworthy
support new Repository API
349
    
350
    The old API was present until bzr.dev rev 3510.
351
    """
352
0.85.2 by Ian Clatworthy
improve per-file graph generation
353
    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
354
        """See RevisionStore._load_texts()."""
0.64.79 by Ian Clatworthy
support new Repository API
355
        # Add the texts that are not already present
356
        tx = self.repo.get_transaction()
0.84.11 by Ian Clatworthy
use iter_non_root_entries if it exists
357
        for ie in entries:
0.64.79 by Ian Clatworthy
support new Repository API
358
            # This test is *really* slow: over 50% of import time
359
            #w = self.repo.weave_store.get_weave_or_empty(ie.file_id, tx)
360
            #if ie.revision in w:
361
            #    continue
362
            # Try another way, realising that this assumes that the
363
            # version is not already there. In the general case,
364
            # a shared repository might already have the revision but
365
            # we arguably don't need that check when importing from
366
            # a foreign system.
367
            if ie.revision != revision_id:
368
                continue
0.85.2 by Ian Clatworthy
improve per-file graph generation
369
            file_id = ie.file_id
370
            text_parents = [(file_id, p) for p in parents_provider(file_id)]
371
            lines = text_provider(file_id)
372
            vfile = self.repo.weave_store.get_weave_or_empty(file_id,  tx)
0.64.79 by Ian Clatworthy
support new Repository API
373
            vfile.add_lines(revision_id, text_parents, lines)
374
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
375
    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.
376
        tx = self.repo.get_transaction()
0.64.139 by Ian Clatworthy
fix ie reference in _get_file_lines()
377
        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.
378
        return w.get_lines(revision_id)
379
0.64.79 by Ian Clatworthy
support new Repository API
380
    def _add_revision(self, rev, inv):
381
        # There's no need to do everything repo.add_revision does and
382
        # doing so (since bzr.dev 3392) can be pretty slow for long
383
        # delta chains on inventories. Just do the essentials here ...
384
        _mod_revision.check_not_reserved_id(rev.revision_id)
385
        self.repo._revision_store.add_revision(rev, self.repo.get_transaction())
386
387
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
388
class RevisionStore2(AbstractRevisionStore):
389
    """A RevisionStore that uses the new bzrlib Repository API."""
0.64.79 by Ian Clatworthy
support new Repository API
390
0.85.2 by Ian Clatworthy
improve per-file graph generation
391
    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
392
        """See RevisionStore._load_texts()."""
0.64.79 by Ian Clatworthy
support new Repository API
393
        text_keys = {}
0.84.11 by Ian Clatworthy
use iter_non_root_entries if it exists
394
        for ie in entries:
0.64.79 by Ian Clatworthy
support new Repository API
395
            text_keys[(ie.file_id, ie.revision)] = ie
396
        text_parent_map = self.repo.texts.get_parent_map(text_keys)
397
        missing_texts = set(text_keys) - set(text_parent_map)
0.85.2 by Ian Clatworthy
improve per-file graph generation
398
        self._load_texts_for_file_rev_ids(missing_texts, text_provider,
399
            parents_provider)
0.64.79 by Ian Clatworthy
support new Repository API
400
0.85.2 by Ian Clatworthy
improve per-file graph generation
401
    def _load_texts_for_file_rev_ids(self, file_rev_ids, text_provider,
402
        parents_provider):
0.64.155 by Ian Clatworthy
store empty texts for non-files for chk formats, not just other formats
403
        """Load texts to a repository for file-ids, revision-id tuples.
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
404
        
0.64.155 by Ian Clatworthy
store empty texts for non-files for chk formats, not just other formats
405
        :param file_rev_ids: iterator over the (file_id, revision_id) tuples
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
406
        :param text_provider: a callable expecting a file_id parameter
407
            that returns the text for that file-id
0.85.2 by Ian Clatworthy
improve per-file graph generation
408
        :param parents_provider: a callable expecting a file_id parameter
409
            that return the list of parent-ids for that file-id
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
410
        """
0.64.155 by Ian Clatworthy
store empty texts for non-files for chk formats, not just other formats
411
        for file_id, revision_id in file_rev_ids:
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
412
            text_key = (file_id, revision_id)
0.85.2 by Ian Clatworthy
improve per-file graph generation
413
            text_parents = [(file_id, p) for p in parents_provider(file_id)]
414
            lines = text_provider(file_id)
415
            #print "adding text for %s\n\tparents:%s" % (text_key,text_parents)
0.84.13 by Ian Clatworthy
smarter RevisionStore.chk_load()
416
            self.repo.texts.add_lines(text_key, text_parents, lines)
417
0.81.7 by Ian Clatworthy
merge import tests and tweaks to make them pass
418
    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.
419
        record = self.repo.texts.get_record_stream([(file_id, revision_id)],
420
            'unordered', True).next()
421
        if record.storage_kind == 'absent':
422
            raise errors.RevisionNotPresent(record.key, self.repo)
423
        return osutils.split_lines(record.get_bytes_as('fulltext'))
424
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
425
    # This is breaking imports into brisbane-core currently
426
    #def _add_revision(self, rev, inv):
427
    #    # There's no need to do everything repo.add_revision does and
428
    #    # doing so (since bzr.dev 3392) can be pretty slow for long
429
    #    # delta chains on inventories. Just do the essentials here ...
430
    #    _mod_revision.check_not_reserved_id(rev.revision_id)
431
    #    self.repo._add_revision(rev)
0.64.79 by Ian Clatworthy
support new Repository API
432
 
433
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
434
class ImportRevisionStore1(RevisionStore1):
435
    """A RevisionStore (old Repository API) optimised for importing.
0.64.79 by Ian Clatworthy
support new Repository API
436
437
    This implementation caches serialised inventory texts and provides
438
    fine-grained control over when inventories are stored as fulltexts.
439
    """
440
441
    def __init__(self, repo, parent_texts_to_cache=1, fulltext_when=None,
442
        random_ids=True):
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
443
        """See AbstractRevisionStore.__init__.
0.64.48 by Ian Clatworthy
one revision loader instance
444
445
        :param repository: the target repository
446
        :param parent_text_to_cache: the number of parent texts to cache
0.64.79 by Ian Clatworthy
support new Repository API
447
        :para fulltext_when: if non None, a function to call to decide
448
          whether to fulltext the inventory or not. The revision count
449
          is passed as a parameter and the result is treated as a boolean.
0.64.48 by Ian Clatworthy
one revision loader instance
450
        """
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
451
        RevisionStore1.__init__(self, repo)
0.64.48 by Ian Clatworthy
one revision loader instance
452
        self.inv_parent_texts = lru_cache.LRUCache(parent_texts_to_cache)
0.64.79 by Ian Clatworthy
support new Repository API
453
        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
454
        self.random_ids = random_ids
0.64.79 by Ian Clatworthy
support new Repository API
455
        self.revision_count = 0
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
456
0.81.3 by Ian Clatworthy
enhance RevisionLoader to try inventory deltas & decide on rich-roots
457
    def _add_inventory(self, revision_id, inv, parents, parent_invs):
0.81.4 by Ian Clatworthy
generalise RevisionLoader to RevisionStore as a repo abstraction
458
        """See RevisionStore._add_inventory."""
0.64.44 by Ian Clatworthy
smart caching of serialised inventories
459
        # Code taken from bzrlib.repository.add_inventory
460
        assert self.repo.is_in_write_group()
461
        _mod_revision.check_not_reserved_id(revision_id)
462
        assert inv.revision_id is None or inv.revision_id == revision_id, \
463
            "Mismatch between inventory revision" \
464
            " id and insertion revid (%r, %r)" % (inv.revision_id, revision_id)
465
        assert inv.root is not None
466
        inv_lines = self.repo._serialise_inventory_to_lines(inv)
467
        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
468
        sha1, num_bytes, parent_text = self._inventory_add_lines(inv_vf,
469
            revision_id, parents, inv_lines, self.inv_parent_texts)
470
        self.inv_parent_texts[revision_id] = parent_text
471
        return sha1
472
473
    def _inventory_add_lines(self, inv_vf, version_id, parents, lines,
474
            parent_texts):
475
        """See Repository._inventory_add_lines()."""
476
        # setup parameters used in original code but not this API
477
        self.revision_count += 1
0.64.77 by Ian Clatworthy
add inv-fulltext option and improve speed
478
        if self.fulltext_when is not None:
479
            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
480
        else:
481
            delta = inv_vf.delta
482
        left_matching_blocks = None
483
        random_id = self.random_ids
484
        check_content = False
485
486
        # bzrlib.knit.add_lines() but error checking optimised
487
        inv_vf._check_add(version_id, lines, random_id, check_content)
488
489
        ####################################################################
490
        # bzrlib.knit._add() but skip checking if fulltext better than delta
491
        ####################################################################
492
493
        line_bytes = ''.join(lines)
494
        digest = osutils.sha_string(line_bytes)
495
        present_parents = []
496
        for parent in parents:
497
            if inv_vf.has_version(parent):
498
                present_parents.append(parent)
499
        if parent_texts is None:
500
            parent_texts = {}
501
502
        # can only compress against the left most present parent.
503
        if (delta and
504
            (len(present_parents) == 0 or
505
             present_parents[0] != parents[0])):
506
            delta = False
507
508
        text_length = len(line_bytes)
509
        options = []
510
        if lines:
511
            if lines[-1][-1] != '\n':
512
                # copy the contents of lines.
513
                lines = lines[:]
514
                options.append('no-eol')
515
                lines[-1] = lines[-1] + '\n'
516
                line_bytes += '\n'
517
518
        #if delta:
519
        #    # To speed the extract of texts the delta chain is limited
520
        #    # to a fixed number of deltas.  This should minimize both
521
        #    # I/O and the time spend applying deltas.
522
        #    delta = inv_vf._check_should_delta(present_parents)
523
524
        assert isinstance(version_id, str)
525
        content = inv_vf.factory.make(lines, version_id)
526
        if delta or (inv_vf.factory.annotated and len(present_parents) > 0):
527
            # Merge annotations from parent texts if needed.
528
            delta_hunks = inv_vf._merge_annotations(content, present_parents,
529
                parent_texts, delta, inv_vf.factory.annotated,
530
                left_matching_blocks)
531
532
        if delta:
533
            options.append('line-delta')
534
            store_lines = inv_vf.factory.lower_line_delta(delta_hunks)
535
            size, bytes = inv_vf._data._record_to_data(version_id, digest,
536
                store_lines)
537
        else:
538
            options.append('fulltext')
539
            # isinstance is slower and we have no hierarchy.
540
            if inv_vf.factory.__class__ == knit.KnitPlainFactory:
541
                # Use the already joined bytes saving iteration time in
542
                # _record_to_data.
543
                size, bytes = inv_vf._data._record_to_data(version_id, digest,
544
                    lines, [line_bytes])
545
            else:
546
                # get mixed annotation + content and feed it into the
547
                # serialiser.
548
                store_lines = inv_vf.factory.lower_fulltext(content)
549
                size, bytes = inv_vf._data._record_to_data(version_id, digest,
550
                    store_lines)
551
552
        access_memo = inv_vf._data.add_raw_records([size], bytes)[0]
553
        inv_vf._index.add_versions(
554
            ((version_id, options, access_memo, parents),),
555
            random_id=random_id)
556
        return digest, text_length, content