/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) 2005, 2006, 2007 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
 
 
17
from bzrlib.lazy_import import lazy_import
 
18
lazy_import(globals(), """
 
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 (
 
27
    bzrdir,
 
28
    deprecated_graph,
 
29
    errors,
 
30
    knit,
 
31
    lockable_files,
 
32
    lockdir,
 
33
    osutils,
 
34
    transactions,
 
35
    xml5,
 
36
    xml7,
 
37
    )
 
38
 
 
39
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
40
from bzrlib.repository import (
 
41
    MetaDirRepository,
 
42
    MetaDirRepositoryFormat,
 
43
    RepositoryFormat,
 
44
    RootCommitBuilder,
 
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
 
50
 
 
51
 
 
52
class _KnitParentsProvider(object):
 
53
 
 
54
    def __init__(self, knit):
 
55
        self._knit = knit
 
56
 
 
57
    def __repr__(self):
 
58
        return 'KnitParentsProvider(%r)' % self._knit
 
59
 
 
60
    def get_parents(self, revision_ids):
 
61
        parents_list = []
 
62
        for revision_id in revision_ids:
 
63
            if revision_id == _mod_revision.NULL_REVISION:
 
64
                parents = []
 
65
            else:
 
66
                try:
 
67
                    parents = self._knit.get_parents_with_ghosts(revision_id)
 
68
                except errors.RevisionNotPresent:
 
69
                    parents = None
 
70
                else:
 
71
                    if len(parents) == 0:
 
72
                        parents = [_mod_revision.NULL_REVISION]
 
73
            parents_list.append(parents)
 
74
        return parents_list
 
75
 
 
76
 
 
77
class KnitRepository(MetaDirRepository):
 
78
    """Knit format repository."""
 
79
 
 
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)
 
88
 
 
89
    @needs_read_lock
 
90
    def _all_revision_ids(self):
 
91
        """See Repository.all_revision_ids()."""
 
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())
 
95
 
 
96
    def fileid_involved_between_revs(self, from_revid, to_revid):
 
97
        """Find file_id(s) which are involved in the changes between revisions.
 
98
 
 
99
        This determines the set of revisions which are involved, and then
 
100
        finds all file ids affected by those revisions.
 
101
        """
 
102
        from_revid = osutils.safe_revision_id(from_revid)
 
103
        to_revid = osutils.safe_revision_id(to_revid)
 
104
        vf = self._get_revision_vf()
 
105
        from_set = set(vf.get_ancestry(from_revid))
 
106
        to_set = set(vf.get_ancestry(to_revid))
 
107
        changed = to_set.difference(from_set)
 
108
        return self._fileid_involved_by_set(changed)
 
109
 
 
110
    def fileid_involved(self, last_revid=None):
 
111
        """Find all file_ids modified in the ancestry of last_revid.
 
112
 
 
113
        :param last_revid: If None, last_revision() will be used.
 
114
        """
 
115
        if not last_revid:
 
116
            changed = set(self.all_revision_ids())
 
117
        else:
 
118
            changed = set(self.get_ancestry(last_revid))
 
119
        if None in changed:
 
120
            changed.remove(None)
 
121
        return self._fileid_involved_by_set(changed)
 
122
 
 
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
 
140
    def get_revision(self, revision_id):
 
141
        """Return the Revision object for a named revision"""
 
142
        revision_id = osutils.safe_revision_id(revision_id)
 
143
        return self.get_revision_reconcile(revision_id)
 
144
 
 
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()
 
218
 
 
219
    @needs_write_lock
 
220
    def reconcile(self, other=None, thorough=False):
 
221
        """Reconcile this repository."""
 
222
        from bzrlib.reconcile import KnitReconciler
 
223
        reconciler = KnitReconciler(self, thorough=thorough)
 
224
        reconciler.reconcile()
 
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)
 
230
 
 
231
    def _make_parents_provider(self):
 
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()
 
503
 
 
504
 
 
505
class RepositoryFormatKnit(MetaDirRepositoryFormat):
 
506
    """Bzr repository knit format (generalized). 
 
507
 
 
508
    This repository format has:
 
509
     - knits for file texts and inventory
 
510
     - hash subdirectory based stores.
 
511
     - knits for revisions and signatures
 
512
     - TextStores for revisions and signatures.
 
513
     - a format marker of its own
 
514
     - an optional 'shared-storage' flag
 
515
     - an optional 'no-working-trees' flag
 
516
     - a LockDir lock
 
517
    """
 
518
 
 
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)
 
556
 
 
557
    def initialize(self, a_bzrdir, shared=False):
 
558
        """Create a knit format 1 repository.
 
559
 
 
560
        :param a_bzrdir: bzrdir to contain the new repository; must already
 
561
            be initialized.
 
562
        :param shared: If true the repository will be initialized as a shared
 
563
                       repository.
 
564
        """
 
565
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
566
        dirs = ['revision-store', 'knits']
 
567
        files = []
 
568
        utf8_files = [('format', self.get_format_string())]
 
569
        
 
570
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
 
571
        repo_transport = a_bzrdir.get_repository_transport(None)
 
572
        control_files = lockable_files.LockableFiles(repo_transport,
 
573
                                'lock', lockdir.LockDir)
 
574
        control_store = self._get_control_store(repo_transport, control_files)
 
575
        transaction = transactions.WriteTransaction()
 
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)
 
579
        # the revision id here is irrelevant: it will not be stored, and cannot
 
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)
 
584
 
 
585
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
586
        """See RepositoryFormat.open().
 
587
        
 
588
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
589
                                    repository at a slightly different url
 
590
                                    than normal. I.e. during 'upgrade'.
 
591
        """
 
592
        if not _found:
 
593
            format = RepositoryFormat.find_format(a_bzrdir)
 
594
            assert format.__class__ ==  self.__class__
 
595
        if _override_transport is not None:
 
596
            repo_transport = _override_transport
 
597
        else:
 
598
            repo_transport = a_bzrdir.get_repository_transport(None)
 
599
        control_files = lockable_files.LockableFiles(repo_transport,
 
600
                                'lock', lockdir.LockDir)
 
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,
 
605
                              a_bzrdir=a_bzrdir,
 
606
                              control_files=control_files,
 
607
                              _revision_store=_revision_store,
 
608
                              control_store=control_store,
 
609
                              text_store=text_store)
 
610
 
 
611
 
 
612
class RepositoryFormatKnit1(RepositoryFormatKnit):
 
613
    """Bzr repository knit format 1.
 
614
 
 
615
    This repository format has:
 
616
     - knits for file texts and inventory
 
617
     - hash subdirectory based stores.
 
618
     - knits for revisions and signatures
 
619
     - TextStores for revisions and signatures.
 
620
     - a format marker of its own
 
621
     - an optional 'shared-storage' flag
 
622
     - an optional 'no-working-trees' flag
 
623
     - a LockDir lock
 
624
 
 
625
    This format was introduced in bzr 0.8.
 
626
    """
 
627
 
 
628
    repository_class = KnitRepository
 
629
 
 
630
    def __ne__(self, other):
 
631
        return self.__class__ is not other.__class__
 
632
 
 
633
    def get_format_string(self):
 
634
        """See RepositoryFormat.get_format_string()."""
 
635
        return "Bazaar-NG Knit Repository Format 1"
 
636
 
 
637
    def get_format_description(self):
 
638
        """See RepositoryFormat.get_format_description()."""
 
639
        return "Knit repository format 1"
 
640
 
 
641
    def check_conversion_target(self, target_format):
 
642
        pass
 
643
 
 
644
 
 
645
class RepositoryFormatKnit3(RepositoryFormatKnit):
 
646
    """Bzr repository knit format 2.
 
647
 
 
648
    This repository format has:
 
649
     - knits for file texts and inventory
 
650
     - hash subdirectory based stores.
 
651
     - knits for revisions and signatures
 
652
     - TextStores for revisions and signatures.
 
653
     - a format marker of its own
 
654
     - an optional 'shared-storage' flag
 
655
     - an optional 'no-working-trees' flag
 
656
     - a LockDir lock
 
657
     - support for recording full info about the tree root
 
658
     - support for recording tree-references
 
659
    """
 
660
 
 
661
    repository_class = KnitRepository3
 
662
    rich_root_data = True
 
663
    supports_tree_reference = True
 
664
 
 
665
    def _get_matching_bzrdir(self):
 
666
        return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
 
667
 
 
668
    def _ignore_setting_bzrdir(self, format):
 
669
        pass
 
670
 
 
671
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
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
            
 
681
    def get_format_string(self):
 
682
        """See RepositoryFormat.get_format_string()."""
 
683
        return "Bazaar Knit Repository Format 3 (bzr 0.15)\n"
 
684
 
 
685
    def get_format_description(self):
 
686
        """See RepositoryFormat.get_format_description()."""
 
687
        return "Knit repository format 3"
 
688
 
 
689
 
 
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.
 
767
     - TextStores for revisions and signatures.
 
768
     - a format marker of its own
 
769
     - an optional 'shared-storage' flag
 
770
     - an optional 'no-working-trees' flag
 
771
     - a LockDir lock
 
772
     - support for recording full info about the tree root
 
773
     - support for recording tree-references
 
774
    """
 
775
 
 
776
    repository_class = GraphKnitRepository3
 
777
 
 
778
    def _get_matching_bzrdir(self):
 
779
        return bzrdir.format_registry.make_bzrdir('experimental-subtree')
 
780
 
 
781
    def _ignore_setting_bzrdir(self, format):
 
782
        pass
 
783
 
 
784
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
785
 
 
786
    def get_format_string(self):
 
787
        """See RepositoryFormat.get_format_string()."""
 
788
        return "Bazaar Experimental subtrees\n"
 
789
 
 
790
    def get_format_description(self):
 
791
        """See RepositoryFormat.get_format_description()."""
 
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