/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/knitrepo.py

MergeĀ knitsĀ branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
from bzrlib.lazy_import import lazy_import
18
18
lazy_import(globals(), """
19
19
from bzrlib import (
 
20
        file_names,
 
21
        )
 
22
from bzrlib.index import InMemoryGraphIndex, GraphIndex, CombinedGraphIndex
 
23
from bzrlib.knit import KnitGraphIndex
 
24
from bzrlib.store import revision
 
25
""")
 
26
from bzrlib import (
20
27
    bzrdir,
 
28
    deprecated_graph,
21
29
    errors,
22
 
    knit as _mod_knit,
 
30
    knit,
23
31
    lockable_files,
24
32
    lockdir,
25
33
    osutils,
26
 
    revision as _mod_revision,
27
 
    trace,
28
34
    transactions,
29
 
    versionedfile,
30
35
    xml5,
31
 
    xml6,
32
36
    xml7,
33
37
    )
34
 
""")
35
 
from bzrlib import (
36
 
    symbol_versioning,
37
 
    )
 
38
 
38
39
from bzrlib.decorators import needs_read_lock, needs_write_lock
39
40
from bzrlib.repository import (
40
 
    CommitBuilder,
41
41
    MetaDirRepository,
42
42
    MetaDirRepositoryFormat,
43
43
    RepositoryFormat,
44
44
    RootCommitBuilder,
45
45
    )
 
46
import bzrlib.revision as _mod_revision
 
47
from bzrlib.store.revision.knit import KnitRevisionStore
 
48
from bzrlib.store.versioned import VersionedFileStore
 
49
from bzrlib.trace import mutter, note, warning
46
50
 
47
51
 
48
52
class _KnitParentsProvider(object):
53
57
    def __repr__(self):
54
58
        return 'KnitParentsProvider(%r)' % self._knit
55
59
 
56
 
    def get_parent_map(self, keys):
57
 
        """See graph.StackedParentsProvider.get_parent_map"""
58
 
        parent_map = {}
59
 
        for revision_id in keys:
60
 
            if revision_id is None:
61
 
                raise ValueError('get_parent_map(None) is not valid')
 
60
    def get_parents(self, revision_ids):
 
61
        parents_list = []
 
62
        for revision_id in revision_ids:
62
63
            if revision_id == _mod_revision.NULL_REVISION:
63
 
                parent_map[revision_id] = ()
 
64
                parents = []
64
65
            else:
65
66
                try:
66
 
                    parents = tuple(
67
 
                        self._knit.get_parents_with_ghosts(revision_id))
 
67
                    parents = self._knit.get_parents_with_ghosts(revision_id)
68
68
                except errors.RevisionNotPresent:
69
 
                    continue
 
69
                    parents = None
70
70
                else:
71
71
                    if len(parents) == 0:
72
 
                        parents = (_mod_revision.NULL_REVISION,)
73
 
                parent_map[revision_id] = parents
74
 
        return parent_map
75
 
 
76
 
 
77
 
class _KnitsParentsProvider(object):
78
 
 
79
 
    def __init__(self, knit, prefix=()):
80
 
        """Create a parent provider for string keys mapped to tuple keys."""
81
 
        self._knit = knit
82
 
        self._prefix = prefix
83
 
 
84
 
    def __repr__(self):
85
 
        return 'KnitsParentsProvider(%r)' % self._knit
86
 
 
87
 
    def get_parent_map(self, keys):
88
 
        """See graph.StackedParentsProvider.get_parent_map"""
89
 
        parent_map = self._knit.get_parent_map(
90
 
            [self._prefix + (key,) for key in keys])
91
 
        result = {}
92
 
        for key, parents in parent_map.items():
93
 
            revid = key[-1]
94
 
            if len(parents) == 0:
95
 
                parents = (_mod_revision.NULL_REVISION,)
96
 
            else:
97
 
                parents = tuple(parent[-1] for parent in parents)
98
 
            result[revid] = parents
99
 
        for revision_id in keys:
100
 
            if revision_id == _mod_revision.NULL_REVISION:
101
 
                result[revision_id] = ()
102
 
        return result
 
72
                        parents = [_mod_revision.NULL_REVISION]
 
73
            parents_list.append(parents)
 
74
        return parents_list
103
75
 
104
76
 
105
77
class KnitRepository(MetaDirRepository):
106
78
    """Knit format repository."""
107
79
 
108
 
    # These attributes are inherited from the Repository base class. Setting
109
 
    # them to None ensures that if the constructor is changed to not initialize
110
 
    # them, or a subclass fails to call the constructor, that an error will
111
 
    # occur rather than the system working but generating incorrect data.
112
 
    _commit_builder_class = None
113
 
    _serializer = None
114
 
 
115
 
    def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
116
 
        _serializer):
117
 
        MetaDirRepository.__init__(self, _format, a_bzrdir, control_files)
118
 
        self._commit_builder_class = _commit_builder_class
119
 
        self._serializer = _serializer
120
 
        self._reconcile_fixes_text_parents = True
 
80
    _serializer = xml5.serializer_v5
 
81
 
 
82
    def _warn_if_deprecated(self):
 
83
        # This class isn't deprecated
 
84
        pass
 
85
 
 
86
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
 
87
        inv_vf.add_lines_with_ghosts(revid, parents, lines)
121
88
 
122
89
    @needs_read_lock
123
90
    def _all_revision_ids(self):
124
91
        """See Repository.all_revision_ids()."""
125
 
        return [key[0] for key in self.revisions.keys()]
126
 
 
127
 
    def _activate_new_inventory(self):
128
 
        """Put a replacement inventory.new into use as inventories."""
129
 
        # Copy the content across
130
 
        t = self._transport
131
 
        t.copy('inventory.new.kndx', 'inventory.kndx')
132
 
        try:
133
 
            t.copy('inventory.new.knit', 'inventory.knit')
134
 
        except errors.NoSuchFile:
135
 
            # empty inventories knit
136
 
            t.delete('inventory.knit')
137
 
        # delete the temp inventory
138
 
        t.delete('inventory.new.kndx')
139
 
        try:
140
 
            t.delete('inventory.new.knit')
141
 
        except errors.NoSuchFile:
142
 
            # empty inventories knit
143
 
            pass
144
 
        # Force index reload (sanity check)
145
 
        self.inventories._index._reset_cache()
146
 
        self.inventories.keys()
147
 
 
148
 
    def _backup_inventory(self):
149
 
        t = self._transport
150
 
        t.copy('inventory.kndx', 'inventory.backup.kndx')
151
 
        t.copy('inventory.knit', 'inventory.backup.knit')
152
 
 
153
 
    def _move_file_id(self, from_id, to_id):
154
 
        t = self._transport.clone('knits')
155
 
        from_rel_url = self.texts._index._mapper.map((from_id, None))
156
 
        to_rel_url = self.texts._index._mapper.map((to_id, None))
157
 
        # We expect both files to always exist in this case.
158
 
        for suffix in ('.knit', '.kndx'):
159
 
            t.rename(from_rel_url + suffix, to_rel_url + suffix)
160
 
 
161
 
    def _remove_file_id(self, file_id):
162
 
        t = self._transport.clone('knits')
163
 
        rel_url = self.texts._index._mapper.map((file_id, None))
164
 
        for suffix in ('.kndx', '.knit'):
165
 
            try:
166
 
                t.delete(rel_url + suffix)
167
 
            except errors.NoSuchFile:
168
 
                pass
169
 
 
170
 
    def _temp_inventories(self):
171
 
        result = self._format._get_inventories(self._transport, self,
172
 
            'inventory.new')
173
 
        # Reconciling when the output has no revisions would result in no
174
 
        # writes - but we want to ensure there is an inventory for
175
 
        # compatibility with older clients that don't lazy-load.
176
 
        result.get_parent_map([('A',)])
177
 
        return result
 
92
        # Knits get the revision graph from the index of the revision knit, so
 
93
        # it's always possible even if they're on an unlistable transport.
 
94
        return self._revision_store.all_revision_ids(self.get_transaction())
178
95
 
179
96
    def fileid_involved_between_revs(self, from_revid, to_revid):
180
97
        """Find file_id(s) which are involved in the changes between revisions.
182
99
        This determines the set of revisions which are involved, and then
183
100
        finds all file ids affected by those revisions.
184
101
        """
 
102
        from_revid = osutils.safe_revision_id(from_revid)
 
103
        to_revid = osutils.safe_revision_id(to_revid)
185
104
        vf = self._get_revision_vf()
186
105
        from_set = set(vf.get_ancestry(from_revid))
187
106
        to_set = set(vf.get_ancestry(to_revid))
202
121
        return self._fileid_involved_by_set(changed)
203
122
 
204
123
    @needs_read_lock
 
124
    def get_ancestry(self, revision_id, topo_sorted=True):
 
125
        """Return a list of revision-ids integrated by a revision.
 
126
        
 
127
        This is topologically sorted, unless 'topo_sorted' is specified as
 
128
        False.
 
129
        """
 
130
        if _mod_revision.is_null(revision_id):
 
131
            return [None]
 
132
        revision_id = osutils.safe_revision_id(revision_id)
 
133
        vf = self._get_revision_vf()
 
134
        try:
 
135
            return [None] + vf.get_ancestry(revision_id, topo_sorted)
 
136
        except errors.RevisionNotPresent:
 
137
            raise errors.NoSuchRevision(self, revision_id)
 
138
 
 
139
    @needs_read_lock
205
140
    def get_revision(self, revision_id):
206
141
        """Return the Revision object for a named revision"""
207
142
        revision_id = osutils.safe_revision_id(revision_id)
208
143
        return self.get_revision_reconcile(revision_id)
209
144
 
210
 
    def _refresh_data(self):
211
 
        if not self.is_locked():
212
 
            return
213
 
        # Create a new transaction to force all knits to see the scope change.
214
 
        # This is safe because we're outside a write group.
215
 
        self.control_files._finish_transaction()
216
 
        if self.is_write_locked():
217
 
            self.control_files._set_write_transaction()
218
 
        else:
219
 
            self.control_files._set_read_transaction()
 
145
    @needs_read_lock
 
146
    def get_revision_graph(self, revision_id=None):
 
147
        """Return a dictionary containing the revision graph.
 
148
 
 
149
        :param revision_id: The revision_id to get a graph from. If None, then
 
150
        the entire revision graph is returned. This is a deprecated mode of
 
151
        operation and will be removed in the future.
 
152
        :return: a dictionary of revision_id->revision_parents_list.
 
153
        """
 
154
        # special case NULL_REVISION
 
155
        if revision_id == _mod_revision.NULL_REVISION:
 
156
            return {}
 
157
        revision_id = osutils.safe_revision_id(revision_id)
 
158
        a_weave = self._get_revision_vf()
 
159
        if revision_id is None:
 
160
            return a_weave.get_graph()
 
161
        if revision_id not in a_weave:
 
162
            raise errors.NoSuchRevision(self, revision_id)
 
163
        else:
 
164
            return a_weave.get_graph([revision_id])
 
165
 
 
166
    @needs_read_lock
 
167
    def get_revision_graph_with_ghosts(self, revision_ids=None):
 
168
        """Return a graph of the revisions with ghosts marked as applicable.
 
169
 
 
170
        :param revision_ids: an iterable of revisions to graph or None for all.
 
171
        :return: a Graph object with the graph reachable from revision_ids.
 
172
        """
 
173
        result = deprecated_graph.Graph()
 
174
        vf = self._get_revision_vf()
 
175
        versions = set(vf.versions())
 
176
        if not revision_ids:
 
177
            pending = set(self.all_revision_ids())
 
178
            required = set([])
 
179
        else:
 
180
            pending = set(osutils.safe_revision_id(r) for r in revision_ids)
 
181
            # special case NULL_REVISION
 
182
            if _mod_revision.NULL_REVISION in pending:
 
183
                pending.remove(_mod_revision.NULL_REVISION)
 
184
            required = set(pending)
 
185
        done = set([])
 
186
        while len(pending):
 
187
            revision_id = pending.pop()
 
188
            if not revision_id in versions:
 
189
                if revision_id in required:
 
190
                    raise errors.NoSuchRevision(self, revision_id)
 
191
                # a ghost
 
192
                result.add_ghost(revision_id)
 
193
                # mark it as done so we don't try for it again.
 
194
                done.add(revision_id)
 
195
                continue
 
196
            parent_ids = vf.get_parents_with_ghosts(revision_id)
 
197
            for parent_id in parent_ids:
 
198
                # is this queued or done ?
 
199
                if (parent_id not in pending and
 
200
                    parent_id not in done):
 
201
                    # no, queue it.
 
202
                    pending.add(parent_id)
 
203
            result.add_node(revision_id, parent_ids)
 
204
            done.add(revision_id)
 
205
        return result
 
206
 
 
207
    def _get_revision_vf(self):
 
208
        """:return: a versioned file containing the revisions."""
 
209
        vf = self._revision_store.get_revision_file(self.get_transaction())
 
210
        return vf
 
211
 
 
212
    def _get_history_vf(self):
 
213
        """Get a versionedfile whose history graph reflects all revisions.
 
214
 
 
215
        For knit repositories, this is the revision knit.
 
216
        """
 
217
        return self._get_revision_vf()
220
218
 
221
219
    @needs_write_lock
222
220
    def reconcile(self, other=None, thorough=False):
225
223
        reconciler = KnitReconciler(self, thorough=thorough)
226
224
        reconciler.reconcile()
227
225
        return reconciler
 
226
    
 
227
    def revision_parents(self, revision_id):
 
228
        revision_id = osutils.safe_revision_id(revision_id)
 
229
        return self._get_revision_vf().get_parents(revision_id)
228
230
 
229
231
    def _make_parents_provider(self):
230
 
        return _KnitsParentsProvider(self.revisions)
231
 
 
232
 
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
233
 
        """Find revisions with different parent lists in the revision object
234
 
        and in the index graph.
235
 
 
236
 
        :param revisions_iterator: None, or an iterator of (revid,
237
 
            Revision-or-None). This iterator controls the revisions checked.
238
 
        :returns: an iterator yielding tuples of (revison-id, parents-in-index,
239
 
            parents-in-revision).
240
 
        """
241
 
        if not self.is_locked():
242
 
            raise AssertionError()
243
 
        vf = self.revisions
244
 
        if revisions_iterator is None:
245
 
            revisions_iterator = self._iter_revisions(None)
246
 
        for revid, revision in revisions_iterator:
247
 
            if revision is None:
248
 
                pass
249
 
            parent_map = vf.get_parent_map([(revid,)])
250
 
            parents_according_to_index = tuple(parent[-1] for parent in
251
 
                parent_map[(revid,)])
252
 
            parents_according_to_revision = tuple(revision.parent_ids)
253
 
            if parents_according_to_index != parents_according_to_revision:
254
 
                yield (revid, parents_according_to_index,
255
 
                    parents_according_to_revision)
256
 
 
257
 
    def _check_for_inconsistent_revision_parents(self):
258
 
        inconsistencies = list(self._find_inconsistent_revision_parents())
259
 
        if inconsistencies:
260
 
            raise errors.BzrCheckError(
261
 
                "Revision knit has inconsistent parents.")
262
 
 
263
 
    def revision_graph_can_have_wrong_parents(self):
264
 
        # The revision.kndx could potentially claim a revision has a different
265
 
        # parent to the revision text.
266
 
        return True
 
232
        return _KnitParentsProvider(self._get_revision_vf())
 
233
 
 
234
 
 
235
class KnitRepository3(KnitRepository):
 
236
 
 
237
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
 
238
                 control_store, text_store):
 
239
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
 
240
                              _revision_store, control_store, text_store)
 
241
        self._serializer = xml7.serializer_v7
 
242
 
 
243
    def deserialise_inventory(self, revision_id, xml):
 
244
        """Transform the xml into an inventory object. 
 
245
 
 
246
        :param revision_id: The expected revision id of the inventory.
 
247
        :param xml: A serialised inventory.
 
248
        """
 
249
        result = self._serializer.read_inventory_from_string(xml)
 
250
        assert result.root.revision is not None
 
251
        return result
 
252
 
 
253
    def serialise_inventory(self, inv):
 
254
        """Transform the inventory object into XML text.
 
255
 
 
256
        :param revision_id: The expected revision id of the inventory.
 
257
        :param xml: A serialised inventory.
 
258
        """
 
259
        assert inv.revision_id is not None
 
260
        assert inv.root.revision is not None
 
261
        return KnitRepository.serialise_inventory(self, inv)
 
262
 
 
263
    def get_commit_builder(self, branch, parents, config, timestamp=None,
 
264
                           timezone=None, committer=None, revprops=None,
 
265
                           revision_id=None):
 
266
        """Obtain a CommitBuilder for this repository.
 
267
        
 
268
        :param branch: Branch to commit to.
 
269
        :param parents: Revision ids of the parents of the new revision.
 
270
        :param config: Configuration to use.
 
271
        :param timestamp: Optional timestamp recorded for commit.
 
272
        :param timezone: Optional timezone for timestamp.
 
273
        :param committer: Optional committer to set for commit.
 
274
        :param revprops: Optional dictionary of revision properties.
 
275
        :param revision_id: Optional revision id.
 
276
        """
 
277
        revision_id = osutils.safe_revision_id(revision_id)
 
278
        result = RootCommitBuilder(self, parents, config, timestamp, timezone,
 
279
                                 committer, revprops, revision_id)
 
280
        self.start_write_group()
 
281
        return result
 
282
 
 
283
 
 
284
class GraphKnitRevisionStore(KnitRevisionStore):
 
285
    """An object to adapt access from RevisionStore's to use GraphKnits.
 
286
 
 
287
    This should not live through to production: by production time we should
 
288
    have fully integrated the new indexing and have new data for the
 
289
    repository classes; also we may choose not to do a Knit1 compatible
 
290
    new repository, just a Knit3 one. If neither of these happen, this 
 
291
    should definately be cleaned up before merging.
 
292
 
 
293
    This class works by replacing the original RevisionStore.
 
294
    We need to do this because the GraphKnitRevisionStore is less
 
295
    isolated in its layering - it uses services from the repo.
 
296
 
 
297
    DEFECTS:
 
298
     - unlock writes an index even on error. This is fine while we are writing
 
299
       data to knits, but we really should not use unlock to trigger writes,
 
300
       rather operations should finish explicitly.
 
301
    """
 
302
 
 
303
    def __init__(self, repo, revisionstore):
 
304
        """Create a GraphKnitRevisionStore on repo with revisionstore.
 
305
 
 
306
        This will store its state in the Repository, use the
 
307
        revision-indices FileNames to provide a KnitGraphIndex,
 
308
        and at the end of transactions write new indices.
 
309
        """
 
310
        KnitRevisionStore.__init__(self, revisionstore.versioned_file_store)
 
311
        self.repo = repo
 
312
        self._serializer = revisionstore._serializer
 
313
 
 
314
    def _ensure_names_loaded(self):
 
315
        if self.repo._revision_indices is None:
 
316
            index_transport = self.get_indices_transport()
 
317
            self.repo._revision_indices = file_names.FileNames(
 
318
                index_transport, 'index')
 
319
            self.repo._revision_indices.load()
 
320
 
 
321
    def get_indices_transport(self):
 
322
        return self.versioned_file_store._transport.clone('indices')
 
323
 
 
324
    def get_revision_file(self, transaction):
 
325
        """Get the revision versioned file object."""
 
326
        if getattr(self.repo, '_revision_knit', None) is not None:
 
327
            return self.repo._revision_knit
 
328
        index_transport = self.get_indices_transport()
 
329
        indices = []
 
330
        self._ensure_names_loaded()
 
331
        def _cmp(x, y): return cmp(int(x), int(y))
 
332
        for name in sorted(self.repo._revision_indices.names(), cmp=_cmp, reverse=True):
 
333
            # TODO: maybe this should expose size to us  to allow
 
334
            # sorting of the indices for better performance ?
 
335
            index_name = self.name_to_revision_index_name(name)
 
336
            indices.append(GraphIndex(index_transport, index_name))
 
337
        if self.repo.is_in_write_group():
 
338
            # allow writing: queue writes to a new index
 
339
            indices.append(self.repo._revision_write_index)
 
340
            add_callback = self.repo._revision_write_index.add_nodes
 
341
        else:
 
342
            add_callback = None # no data-adding permitted.
 
343
        self.repo._revision_all_indices = CombinedGraphIndex(indices)
 
344
        knit_index = KnitGraphIndex(self.repo._revision_all_indices,
 
345
            add_callback=add_callback)
 
346
        self.repo._revision_knit = knit.KnitVersionedFile(
 
347
            'revisions', index_transport.clone('..'),
 
348
            self.repo.control_files._file_mode,
 
349
            create=False, access_mode=self.repo.control_files._lock_mode,
 
350
            index=knit_index, delta=False, factory=knit.KnitPlainFactory())
 
351
        return self.repo._revision_knit
 
352
 
 
353
    def get_signature_file(self, transaction):
 
354
        """Get the signature versioned file object."""
 
355
        if getattr(self.repo, '_signature_knit', None) is not None:
 
356
            return self.repo._signature_knit
 
357
        index_transport = self.get_indices_transport()
 
358
        indices = []
 
359
        self._ensure_names_loaded()
 
360
        def _cmp(x, y): return cmp(int(x), int(y))
 
361
        for name in sorted(self.repo._revision_indices.names(), cmp=_cmp, reverse=True):
 
362
            # TODO: maybe this should expose size to us  to allow
 
363
            # sorting of the indices for better performance ?
 
364
            index_name = self.name_to_signature_index_name(name)
 
365
            indices.append(GraphIndex(index_transport, index_name))
 
366
        if self.repo.is_in_write_group():
 
367
            # allow writing: queue writes to a new index
 
368
            indices.append(self.repo._signature_write_index)
 
369
            add_callback = self.repo._signature_write_index.add_nodes
 
370
        else:
 
371
            add_callback = None # no data-adding permitted.
 
372
        self.repo._signature_all_indices = CombinedGraphIndex(indices)
 
373
        knit_index = KnitGraphIndex(self.repo._signature_all_indices,
 
374
            add_callback=add_callback, parents=False)
 
375
        self.repo._signature_knit = knit.KnitVersionedFile(
 
376
            'signatures', index_transport.clone('..'),
 
377
            self.repo.control_files._file_mode,
 
378
            create=False, access_mode=self.repo.control_files._lock_mode,
 
379
            index=knit_index, delta=False, factory=knit.KnitPlainFactory())
 
380
        return self.repo._signature_knit
 
381
 
 
382
    def flush(self):
 
383
        """Write out pending indices."""
 
384
        # if any work has been done, allocate a new name
 
385
        if (getattr(self.repo, '_revision_knit', None) is not None or
 
386
            getattr(self.repo, '_signature_knit', None) is not None):
 
387
            new_name = self.repo._revision_indices.allocate()
 
388
            self.repo._revision_indices.save()
 
389
        else:
 
390
            # no knits actually accessed
 
391
            return
 
392
        index_transport = self.get_indices_transport()
 
393
        # write a revision index (might be empty)
 
394
        new_index_name = self.name_to_revision_index_name(new_name)
 
395
        index_transport.put_file(new_index_name,
 
396
            self.repo._revision_write_index.finish())
 
397
        self.repo._revision_write_index = None
 
398
        if self.repo._revision_all_indices is not None:
 
399
            # revisions 'knit' accessed : update it.
 
400
            self.repo._revision_all_indices.insert_index(0,
 
401
                GraphIndex(index_transport, new_index_name))
 
402
            # remove the write buffering index. XXX: API break
 
403
            # - clearly we need a remove_index call too.
 
404
            del self.repo._revision_all_indices._indices[-1]
 
405
        # write a signatures index (might be empty)
 
406
        new_index_name = self.name_to_signature_index_name(new_name)
 
407
        index_transport.put_file(new_index_name,
 
408
            self.repo._signature_write_index.finish())
 
409
        self.repo._signature_write_index = None
 
410
        if self.repo._signature_all_indices is not None:
 
411
            # sigatures 'knit' accessed : update it.
 
412
            self.repo._signature_all_indices.insert_index(0,
 
413
                GraphIndex(index_transport, new_index_name))
 
414
            # remove the write buffering index. XXX: API break
 
415
            # - clearly we need a remove_index call too.
 
416
            del self.repo._signature_all_indices._indices[-1]
 
417
 
 
418
    def name_to_revision_index_name(self, name):
 
419
        """The revision index is the name + .rix."""
 
420
        return name + '.rix'
 
421
 
 
422
    def name_to_signature_index_name(self, name):
 
423
        """The signature index is the name + .six."""
 
424
        return name + '.six'
 
425
 
 
426
    def reset(self):
 
427
        """Clear all cached data."""
 
428
        # the packs that exist
 
429
        self.repo._revision_indices = None
 
430
        # cached revision data
 
431
        self.repo._revision_knit = None
 
432
        self.repo._revision_write_index = None
 
433
        self.repo._revision_all_indices = None
 
434
        # cached signature data
 
435
        self.repo._signature_knit = None
 
436
        self.repo._signature_write_index = None
 
437
        self.repo._signature_all_indices = None
 
438
 
 
439
    def setup(self):
 
440
        # setup in-memory indices to accumulate data.
 
441
        if self.repo.control_files._lock_mode != 'w':
 
442
            raise errors.NotWriteLocked(self)
 
443
        self.repo._revision_write_index = InMemoryGraphIndex(1)
 
444
        self.repo._signature_write_index = InMemoryGraphIndex(0)
 
445
        # if knit indices have been handed out, add a mutable
 
446
        # index to them
 
447
        if self.repo._revision_knit is not None:
 
448
            self.repo._revision_all_indices.insert_index(0, self.repo._revision_write_index)
 
449
            self.repo._revision_knit._index._add_callback = self.repo._revision_write_index.add_nodes
 
450
        if self.repo._signature_knit is not None:
 
451
            self.repo._signature_all_indices.insert_index(0, self.repo._signature_write_index)
 
452
            self.repo._signature_knit._index._add_callback = self.repo._signature_write_index.add_nodes
 
453
 
 
454
 
 
455
class GraphKnitRepository1(KnitRepository):
 
456
    """Experimental graph-knit using repository."""
 
457
 
 
458
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
 
459
                 control_store, text_store):
 
460
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
 
461
                              _revision_store, control_store, text_store)
 
462
        self._revision_store = GraphKnitRevisionStore(self, self._revision_store)
 
463
 
 
464
    def _abort_write_group(self):
 
465
        # FIXME: just drop the transient index.
 
466
        self._revision_store.reset()
 
467
 
 
468
    def _refresh_data(self):
 
469
        if self.control_files._lock_count==1:
 
470
            self._revision_store.reset()
 
471
 
 
472
    def _start_write_group(self):
 
473
        self._revision_store.setup()
 
474
 
 
475
    def _commit_write_group(self):
 
476
        self._revision_store.flush()
 
477
        self._revision_store.reset()
 
478
 
 
479
 
 
480
class GraphKnitRepository3(KnitRepository3):
 
481
    """Experimental graph-knit using subtrees repository."""
 
482
 
 
483
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
 
484
                 control_store, text_store):
 
485
        KnitRepository3.__init__(self, _format, a_bzrdir, control_files,
 
486
                              _revision_store, control_store, text_store)
 
487
        self._revision_store = GraphKnitRevisionStore(self, self._revision_store)
 
488
 
 
489
    def _abort_write_group(self):
 
490
        # FIXME: just drop the transient index.
 
491
        self._revision_store.reset()
 
492
 
 
493
    def _refresh_data(self):
 
494
        if self.control_files._lock_count==1:
 
495
            self._revision_store.reset()
 
496
 
 
497
    def _start_write_group(self):
 
498
        self._revision_store.setup()
 
499
 
 
500
    def _commit_write_group(self):
 
501
        self._revision_store.flush()
 
502
        self._revision_store.reset()
267
503
 
268
504
 
269
505
class RepositoryFormatKnit(MetaDirRepositoryFormat):
270
 
    """Bzr repository knit format (generalized).
 
506
    """Bzr repository knit format (generalized). 
271
507
 
272
508
    This repository format has:
273
509
     - knits for file texts and inventory
280
516
     - a LockDir lock
281
517
    """
282
518
 
283
 
    # Set this attribute in derived classes to control the repository class
284
 
    # created by open and initialize.
285
 
    repository_class = None
286
 
    # Set this attribute in derived classes to control the
287
 
    # _commit_builder_class that the repository objects will have passed to
288
 
    # their constructor.
289
 
    _commit_builder_class = None
290
 
    # Set this attribute in derived clases to control the _serializer that the
291
 
    # repository objects will have passed to their constructor.
292
 
    @property
293
 
    def _serializer(self):
294
 
        return xml5.serializer_v5
295
 
    # Knit based repositories handle ghosts reasonably well.
296
 
    supports_ghosts = True
297
 
    # External lookups are not supported in this format.
298
 
    supports_external_lookups = False
299
 
    # No CHK support.
300
 
    supports_chks = False
301
 
    _fetch_order = 'topological'
302
 
    _fetch_uses_deltas = True
303
 
    fast_deltas = False
304
 
 
305
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
306
 
        mapper = versionedfile.ConstantMapper(name)
307
 
        index = _mod_knit._KndxIndex(repo_transport, mapper,
308
 
            repo.get_transaction, repo.is_write_locked, repo.is_locked)
309
 
        access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
310
 
        return _mod_knit.KnitVersionedFiles(index, access, annotated=False)
311
 
 
312
 
    def _get_revisions(self, repo_transport, repo):
313
 
        mapper = versionedfile.ConstantMapper('revisions')
314
 
        index = _mod_knit._KndxIndex(repo_transport, mapper,
315
 
            repo.get_transaction, repo.is_write_locked, repo.is_locked)
316
 
        access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
317
 
        return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
318
 
            annotated=False)
319
 
 
320
 
    def _get_signatures(self, repo_transport, repo):
321
 
        mapper = versionedfile.ConstantMapper('signatures')
322
 
        index = _mod_knit._KndxIndex(repo_transport, mapper,
323
 
            repo.get_transaction, repo.is_write_locked, repo.is_locked)
324
 
        access = _mod_knit._KnitKeyAccess(repo_transport, mapper)
325
 
        return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=0,
326
 
            annotated=False)
327
 
 
328
 
    def _get_texts(self, repo_transport, repo):
329
 
        mapper = versionedfile.HashEscapedPrefixMapper()
330
 
        base_transport = repo_transport.clone('knits')
331
 
        index = _mod_knit._KndxIndex(base_transport, mapper,
332
 
            repo.get_transaction, repo.is_write_locked, repo.is_locked)
333
 
        access = _mod_knit._KnitKeyAccess(base_transport, mapper)
334
 
        return _mod_knit.KnitVersionedFiles(index, access, max_delta_chain=200,
335
 
            annotated=True)
 
519
    def _get_control_store(self, repo_transport, control_files):
 
520
        """Return the control store for this repository."""
 
521
        return VersionedFileStore(
 
522
            repo_transport,
 
523
            prefixed=False,
 
524
            file_mode=control_files._file_mode,
 
525
            versionedfile_class=knit.KnitVersionedFile,
 
526
            versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
 
527
            )
 
528
 
 
529
    def _get_revision_store(self, repo_transport, control_files):
 
530
        """See RepositoryFormat._get_revision_store()."""
 
531
        versioned_file_store = VersionedFileStore(
 
532
            repo_transport,
 
533
            file_mode=control_files._file_mode,
 
534
            prefixed=False,
 
535
            precious=True,
 
536
            versionedfile_class=knit.KnitVersionedFile,
 
537
            versionedfile_kwargs={'delta':False,
 
538
                                  'factory':knit.KnitPlainFactory(),
 
539
                                 },
 
540
            escaped=True,
 
541
            )
 
542
        return KnitRevisionStore(versioned_file_store)
 
543
 
 
544
    def _get_text_store(self, transport, control_files):
 
545
        """See RepositoryFormat._get_text_store()."""
 
546
        return self._get_versioned_file_store('knits',
 
547
                                  transport,
 
548
                                  control_files,
 
549
                                  versionedfile_class=knit.KnitVersionedFile,
 
550
                                  versionedfile_kwargs={
 
551
                                      'create_parent_dir':True,
 
552
                                      'delay_create':True,
 
553
                                      'dir_mode':control_files._dir_mode,
 
554
                                  },
 
555
                                  escaped=True)
336
556
 
337
557
    def initialize(self, a_bzrdir, shared=False):
338
558
        """Create a knit format 1 repository.
342
562
        :param shared: If true the repository will be initialized as a shared
343
563
                       repository.
344
564
        """
345
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
346
 
        dirs = ['knits']
 
565
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
566
        dirs = ['revision-store', 'knits']
347
567
        files = []
348
568
        utf8_files = [('format', self.get_format_string())]
349
 
 
 
569
        
350
570
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
351
571
        repo_transport = a_bzrdir.get_repository_transport(None)
352
572
        control_files = lockable_files.LockableFiles(repo_transport,
353
573
                                'lock', lockdir.LockDir)
 
574
        control_store = self._get_control_store(repo_transport, control_files)
354
575
        transaction = transactions.WriteTransaction()
355
 
        result = self.open(a_bzrdir=a_bzrdir, _found=True)
356
 
        result.lock_write()
 
576
        # trigger a write of the inventory store.
 
577
        control_store.get_weave_or_empty('inventory', transaction)
 
578
        _revision_store = self._get_revision_store(repo_transport, control_files)
357
579
        # the revision id here is irrelevant: it will not be stored, and cannot
358
 
        # already exist, we do this to create files on disk for older clients.
359
 
        result.inventories.get_parent_map([('A',)])
360
 
        result.revisions.get_parent_map([('A',)])
361
 
        result.signatures.get_parent_map([('A',)])
362
 
        result.unlock()
363
 
        self._run_post_repo_init_hooks(result, a_bzrdir, shared)
364
 
        return result
 
580
        # already exist.
 
581
        _revision_store.has_revision_id('A', transaction)
 
582
        _revision_store.get_signature_file(transaction)
 
583
        return self.open(a_bzrdir=a_bzrdir, _found=True)
365
584
 
366
585
    def open(self, a_bzrdir, _found=False, _override_transport=None):
367
586
        """See RepositoryFormat.open().
368
 
 
 
587
        
369
588
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
370
589
                                    repository at a slightly different url
371
590
                                    than normal. I.e. during 'upgrade'.
372
591
        """
373
592
        if not _found:
374
593
            format = RepositoryFormat.find_format(a_bzrdir)
 
594
            assert format.__class__ ==  self.__class__
375
595
        if _override_transport is not None:
376
596
            repo_transport = _override_transport
377
597
        else:
378
598
            repo_transport = a_bzrdir.get_repository_transport(None)
379
599
        control_files = lockable_files.LockableFiles(repo_transport,
380
600
                                'lock', lockdir.LockDir)
381
 
        repo = self.repository_class(_format=self,
 
601
        text_store = self._get_text_store(repo_transport, control_files)
 
602
        control_store = self._get_control_store(repo_transport, control_files)
 
603
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
604
        return self.repository_class(_format=self,
382
605
                              a_bzrdir=a_bzrdir,
383
606
                              control_files=control_files,
384
 
                              _commit_builder_class=self._commit_builder_class,
385
 
                              _serializer=self._serializer)
386
 
        repo.revisions = self._get_revisions(repo_transport, repo)
387
 
        repo.signatures = self._get_signatures(repo_transport, repo)
388
 
        repo.inventories = self._get_inventories(repo_transport, repo)
389
 
        repo.texts = self._get_texts(repo_transport, repo)
390
 
        repo.chk_bytes = None
391
 
        repo._transport = repo_transport
392
 
        return repo
 
607
                              _revision_store=_revision_store,
 
608
                              control_store=control_store,
 
609
                              text_store=text_store)
393
610
 
394
611
 
395
612
class RepositoryFormatKnit1(RepositoryFormatKnit):
409
626
    """
410
627
 
411
628
    repository_class = KnitRepository
412
 
    _commit_builder_class = CommitBuilder
413
 
    @property
414
 
    def _serializer(self):
415
 
        return xml5.serializer_v5
416
629
 
417
630
    def __ne__(self, other):
418
631
        return self.__class__ is not other.__class__
425
638
        """See RepositoryFormat.get_format_description()."""
426
639
        return "Knit repository format 1"
427
640
 
 
641
    def check_conversion_target(self, target_format):
 
642
        pass
 
643
 
428
644
 
429
645
class RepositoryFormatKnit3(RepositoryFormatKnit):
430
 
    """Bzr repository knit format 3.
 
646
    """Bzr repository knit format 2.
431
647
 
432
648
    This repository format has:
433
649
     - knits for file texts and inventory
442
658
     - support for recording tree-references
443
659
    """
444
660
 
445
 
    repository_class = KnitRepository
446
 
    _commit_builder_class = RootCommitBuilder
 
661
    repository_class = KnitRepository3
447
662
    rich_root_data = True
448
 
    experimental = True
449
663
    supports_tree_reference = True
450
 
    @property
451
 
    def _serializer(self):
452
 
        return xml7.serializer_v7
453
664
 
454
665
    def _get_matching_bzrdir(self):
455
666
        return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
459
670
 
460
671
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
461
672
 
 
673
    def check_conversion_target(self, target_format):
 
674
        if not target_format.rich_root_data:
 
675
            raise errors.BadConversionTarget(
 
676
                'Does not support rich root data.', target_format)
 
677
        if not getattr(target_format, 'supports_tree_reference', False):
 
678
            raise errors.BadConversionTarget(
 
679
                'Does not support nested trees', target_format)
 
680
            
462
681
    def get_format_string(self):
463
682
        """See RepositoryFormat.get_format_string()."""
464
683
        return "Bazaar Knit Repository Format 3 (bzr 0.15)\n"
468
687
        return "Knit repository format 3"
469
688
 
470
689
 
471
 
class RepositoryFormatKnit4(RepositoryFormatKnit):
472
 
    """Bzr repository knit format 4.
473
 
 
474
 
    This repository format has everything in format 3, except for
475
 
    tree-references:
476
 
     - knits for file texts and inventory
477
 
     - hash subdirectory based stores.
478
 
     - knits for revisions and signatures
 
690
class RepositoryFormatGraphKnit1(RepositoryFormatKnit):
 
691
    """Experimental repository with knit1 style data.
 
692
 
 
693
    This repository format has:
 
694
     - knits for file texts and inventory
 
695
     - hash subdirectory based stores.
 
696
     - knits for revisions and signatures
 
697
     - uses a GraphKnitIndex for revisions.knit.
 
698
     - TextStores for revisions and signatures.
 
699
     - a format marker of its own
 
700
     - an optional 'shared-storage' flag
 
701
     - an optional 'no-working-trees' flag
 
702
     - a LockDir lock
 
703
 
 
704
    This format was introduced in bzr.dev.
 
705
    """
 
706
 
 
707
    repository_class = GraphKnitRepository1
 
708
 
 
709
    def _get_matching_bzrdir(self):
 
710
        return bzrdir.format_registry.make_bzrdir('experimental')
 
711
 
 
712
    def _ignore_setting_bzrdir(self, format):
 
713
        pass
 
714
 
 
715
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
716
 
 
717
    def __ne__(self, other):
 
718
        return self.__class__ is not other.__class__
 
719
 
 
720
    def get_format_string(self):
 
721
        """See RepositoryFormat.get_format_string()."""
 
722
        return "Bazaar Experimental no-subtrees\n"
 
723
 
 
724
    def get_format_description(self):
 
725
        """See RepositoryFormat.get_format_description()."""
 
726
        return "Experimental no-subtrees"
 
727
 
 
728
    def check_conversion_target(self, target_format):
 
729
        pass
 
730
 
 
731
    def initialize(self, a_bzrdir, shared=False):
 
732
        """Create an experimental repository.
 
733
 
 
734
        :param a_bzrdir: bzrdir to contain the new repository; must already
 
735
            be initialized.
 
736
        :param shared: If true the repository will be initialized as a shared
 
737
                       repository.
 
738
        """
 
739
        # setup a basic Knit1 repository.
 
740
        result = RepositoryFormatKnit.initialize(self, a_bzrdir, shared)
 
741
        _knit_to_experimental(result, a_bzrdir)
 
742
        return result
 
743
 
 
744
 
 
745
def _knit_to_experimental(result, a_bzrdir):
 
746
    """Convert a knit1/3 repo to an experimental layout repo."""
 
747
    # and adapt it to a GraphKnit repo
 
748
    mutter('changing to GraphKnit1 repository in %s.', a_bzrdir.transport.base)
 
749
    repo_transport = a_bzrdir.get_repository_transport(None)
 
750
    repo_transport.mkdir('indices')
 
751
    names = file_names.FileNames(
 
752
        repo_transport.clone('indices'), 'index')
 
753
    names.initialise()
 
754
    names.save()
 
755
    repo_transport.delete('revisions.kndx')
 
756
    repo_transport.delete('signatures.kndx')
 
757
 
 
758
 
 
759
class RepositoryFormatGraphKnit3(RepositoryFormatKnit3):
 
760
    """Experimental repository with knit3 style data.
 
761
 
 
762
    This repository format has:
 
763
     - knits for file texts and inventory
 
764
     - hash subdirectory based stores.
 
765
     - knits for revisions and signatures
 
766
     - uses a GraphKnitIndex for revisions.knit.
479
767
     - TextStores for revisions and signatures.
480
768
     - a format marker of its own
481
769
     - an optional 'shared-storage' flag
482
770
     - an optional 'no-working-trees' flag
483
771
     - a LockDir lock
484
772
     - support for recording full info about the tree root
 
773
     - support for recording tree-references
485
774
    """
486
775
 
487
 
    repository_class = KnitRepository
488
 
    _commit_builder_class = RootCommitBuilder
489
 
    rich_root_data = True
490
 
    supports_tree_reference = False
491
 
    @property
492
 
    def _serializer(self):
493
 
        return xml6.serializer_v6
 
776
    repository_class = GraphKnitRepository3
494
777
 
495
778
    def _get_matching_bzrdir(self):
496
 
        return bzrdir.format_registry.make_bzrdir('rich-root')
 
779
        return bzrdir.format_registry.make_bzrdir('experimental-subtree')
497
780
 
498
781
    def _ignore_setting_bzrdir(self, format):
499
782
        pass
502
785
 
503
786
    def get_format_string(self):
504
787
        """See RepositoryFormat.get_format_string()."""
505
 
        return 'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
 
788
        return "Bazaar Experimental subtrees\n"
506
789
 
507
790
    def get_format_description(self):
508
791
        """See RepositoryFormat.get_format_description()."""
509
 
        return "Knit repository format 4"
 
792
        return "Experimental no-subtrees\n"
 
793
 
 
794
    def initialize(self, a_bzrdir, shared=False):
 
795
        """Create an experimental repository.
 
796
 
 
797
        :param a_bzrdir: bzrdir to contain the new repository; must already
 
798
            be initialized.
 
799
        :param shared: If true the repository will be initialized as a shared
 
800
                       repository.
 
801
        """
 
802
        # setup a basic Knit1 repository.
 
803
        result = RepositoryFormatKnit.initialize(self, a_bzrdir, shared)
 
804
        _knit_to_experimental(result, a_bzrdir)
 
805
        return result