/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

  • Committer: Andrew Bennetts
  • Date: 2007-10-05 02:18:53 UTC
  • mto: This revision was merged to the branch mainline in revision 2905.
  • Revision ID: andrew.bennetts@canonical.com-20071005021853-q410n3kcldnnp2ms
Revert bad change to bzrlib/progress.py

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
    debug,
 
21
    )
 
22
from bzrlib.store import revision
 
23
from bzrlib.store.revision.knit import KnitRevisionStore
 
24
""")
 
25
from bzrlib import (
 
26
    bzrdir,
 
27
    deprecated_graph,
 
28
    errors,
 
29
    knit,
 
30
    lockable_files,
 
31
    lockdir,
 
32
    osutils,
 
33
    transactions,
 
34
    xml5,
 
35
    xml7,
 
36
    )
 
37
 
 
38
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
39
from bzrlib.repository import (
 
40
    MetaDirRepository,
 
41
    MetaDirRepositoryFormat,
 
42
    RepositoryFormat,
 
43
    RootCommitBuilder,
 
44
    )
 
45
import bzrlib.revision as _mod_revision
 
46
from bzrlib.store.versioned import VersionedFileStore
 
47
from bzrlib.trace import mutter, mutter_callsite
 
48
from bzrlib.util import bencode
 
49
 
 
50
 
 
51
class _KnitParentsProvider(object):
 
52
 
 
53
    def __init__(self, knit):
 
54
        self._knit = knit
 
55
 
 
56
    def __repr__(self):
 
57
        return 'KnitParentsProvider(%r)' % self._knit
 
58
 
 
59
    def get_parents(self, revision_ids):
 
60
        parents_list = []
 
61
        for revision_id in revision_ids:
 
62
            if revision_id == _mod_revision.NULL_REVISION:
 
63
                parents = []
 
64
            else:
 
65
                try:
 
66
                    parents = self._knit.get_parents_with_ghosts(revision_id)
 
67
                except errors.RevisionNotPresent:
 
68
                    parents = None
 
69
                else:
 
70
                    if len(parents) == 0:
 
71
                        parents = [_mod_revision.NULL_REVISION]
 
72
            parents_list.append(parents)
 
73
        return parents_list
 
74
 
 
75
 
 
76
class KnitRepository(MetaDirRepository):
 
77
    """Knit format repository."""
 
78
 
 
79
    _serializer = xml5.serializer_v5
 
80
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
 
81
                 control_store, text_store):
 
82
        MetaDirRepository.__init__(self, _format, a_bzrdir, control_files,
 
83
                                   _revision_store, control_store, text_store)
 
84
        self._reconcile_fixes_text_parents = True
 
85
 
 
86
    def _warn_if_deprecated(self):
 
87
        # This class isn't deprecated
 
88
        pass
 
89
 
 
90
    def _inventory_add_lines(self, inv_vf, revid, parents, lines, check_content):
 
91
        return inv_vf.add_lines_with_ghosts(revid, parents, lines,
 
92
            check_content=check_content)[0]
 
93
 
 
94
    @needs_read_lock
 
95
    def _all_revision_ids(self):
 
96
        """See Repository.all_revision_ids()."""
 
97
        # Knits get the revision graph from the index of the revision knit, so
 
98
        # it's always possible even if they're on an unlistable transport.
 
99
        return self._revision_store.all_revision_ids(self.get_transaction())
 
100
 
 
101
    def fileid_involved_between_revs(self, from_revid, to_revid):
 
102
        """Find file_id(s) which are involved in the changes between revisions.
 
103
 
 
104
        This determines the set of revisions which are involved, and then
 
105
        finds all file ids affected by those revisions.
 
106
        """
 
107
        from_revid = osutils.safe_revision_id(from_revid)
 
108
        to_revid = osutils.safe_revision_id(to_revid)
 
109
        vf = self._get_revision_vf()
 
110
        from_set = set(vf.get_ancestry(from_revid))
 
111
        to_set = set(vf.get_ancestry(to_revid))
 
112
        changed = to_set.difference(from_set)
 
113
        return self._fileid_involved_by_set(changed)
 
114
 
 
115
    def fileid_involved(self, last_revid=None):
 
116
        """Find all file_ids modified in the ancestry of last_revid.
 
117
 
 
118
        :param last_revid: If None, last_revision() will be used.
 
119
        """
 
120
        if not last_revid:
 
121
            changed = set(self.all_revision_ids())
 
122
        else:
 
123
            changed = set(self.get_ancestry(last_revid))
 
124
        if None in changed:
 
125
            changed.remove(None)
 
126
        return self._fileid_involved_by_set(changed)
 
127
 
 
128
    @needs_read_lock
 
129
    def get_ancestry(self, revision_id, topo_sorted=True):
 
130
        """Return a list of revision-ids integrated by a revision.
 
131
        
 
132
        This is topologically sorted, unless 'topo_sorted' is specified as
 
133
        False.
 
134
        """
 
135
        if _mod_revision.is_null(revision_id):
 
136
            return [None]
 
137
        revision_id = osutils.safe_revision_id(revision_id)
 
138
        vf = self._get_revision_vf()
 
139
        try:
 
140
            return [None] + vf.get_ancestry(revision_id, topo_sorted)
 
141
        except errors.RevisionNotPresent:
 
142
            raise errors.NoSuchRevision(self, revision_id)
 
143
 
 
144
    @needs_read_lock
 
145
    def get_revision_graph(self, revision_id=None):
 
146
        """Return a dictionary containing the revision graph.
 
147
 
 
148
        :param revision_id: The revision_id to get a graph from. If None, then
 
149
        the entire revision graph is returned. This is a deprecated mode of
 
150
        operation and will be removed in the future.
 
151
        :return: a dictionary of revision_id->revision_parents_list.
 
152
        """
 
153
        if 'evil' in debug.debug_flags:
 
154
            mutter_callsite(3,
 
155
                "get_revision_graph scales with size of history.")
 
156
        # special case NULL_REVISION
 
157
        if revision_id == _mod_revision.NULL_REVISION:
 
158
            return {}
 
159
        revision_id = osutils.safe_revision_id(revision_id)
 
160
        a_weave = self._get_revision_vf()
 
161
        if revision_id is None:
 
162
            return a_weave.get_graph()
 
163
        if revision_id not in a_weave:
 
164
            raise errors.NoSuchRevision(self, revision_id)
 
165
        else:
 
166
            # add what can be reached from revision_id
 
167
            return a_weave.get_graph([revision_id])
 
168
 
 
169
    @needs_read_lock
 
170
    def get_revision_graph_with_ghosts(self, revision_ids=None):
 
171
        """Return a graph of the revisions with ghosts marked as applicable.
 
172
 
 
173
        :param revision_ids: an iterable of revisions to graph or None for all.
 
174
        :return: a Graph object with the graph reachable from revision_ids.
 
175
        """
 
176
        if 'evil' in debug.debug_flags:
 
177
            mutter_callsite(3,
 
178
                "get_revision_graph_with_ghosts scales with size of history.")
 
179
        result = deprecated_graph.Graph()
 
180
        vf = self._get_revision_vf()
 
181
        versions = set(vf.versions())
 
182
        if not revision_ids:
 
183
            pending = set(self.all_revision_ids())
 
184
            required = set([])
 
185
        else:
 
186
            pending = set(osutils.safe_revision_id(r) for r in revision_ids)
 
187
            # special case NULL_REVISION
 
188
            if _mod_revision.NULL_REVISION in pending:
 
189
                pending.remove(_mod_revision.NULL_REVISION)
 
190
            required = set(pending)
 
191
        done = set([])
 
192
        while len(pending):
 
193
            revision_id = pending.pop()
 
194
            if not revision_id in versions:
 
195
                if revision_id in required:
 
196
                    raise errors.NoSuchRevision(self, revision_id)
 
197
                # a ghost
 
198
                result.add_ghost(revision_id)
 
199
                # mark it as done so we don't try for it again.
 
200
                done.add(revision_id)
 
201
                continue
 
202
            parent_ids = vf.get_parents_with_ghosts(revision_id)
 
203
            for parent_id in parent_ids:
 
204
                # is this queued or done ?
 
205
                if (parent_id not in pending and
 
206
                    parent_id not in done):
 
207
                    # no, queue it.
 
208
                    pending.add(parent_id)
 
209
            result.add_node(revision_id, parent_ids)
 
210
            done.add(revision_id)
 
211
        return result
 
212
 
 
213
    def _get_revision_vf(self):
 
214
        """:return: a versioned file containing the revisions."""
 
215
        vf = self._revision_store.get_revision_file(self.get_transaction())
 
216
        return vf
 
217
 
 
218
    def _get_history_vf(self):
 
219
        """Get a versionedfile whose history graph reflects all revisions.
 
220
 
 
221
        For knit repositories, this is the revision knit.
 
222
        """
 
223
        return self._get_revision_vf()
 
224
 
 
225
    @needs_write_lock
 
226
    def reconcile(self, other=None, thorough=False):
 
227
        """Reconcile this repository."""
 
228
        from bzrlib.reconcile import KnitReconciler
 
229
        reconciler = KnitReconciler(self, thorough=thorough)
 
230
        reconciler.reconcile()
 
231
        return reconciler
 
232
    
 
233
    def revision_parents(self, revision_id):
 
234
        revision_id = osutils.safe_revision_id(revision_id)
 
235
        return self._get_revision_vf().get_parents(revision_id)
 
236
 
 
237
    def _make_parents_provider(self):
 
238
        return _KnitParentsProvider(self._get_revision_vf())
 
239
 
 
240
    def _find_inconsistent_revision_parents(self):
 
241
        """Find revisions with different parent lists in the revision object
 
242
        and in the index graph.
 
243
 
 
244
        :returns: an iterator yielding tuples of (revison-id, parents-in-index,
 
245
            parents-in-revision).
 
246
        """
 
247
        vf = self._get_revision_vf()
 
248
        index_versions = vf.versions()
 
249
        for index_version in index_versions:
 
250
            parents_according_to_index = vf._index.get_parents_with_ghosts(
 
251
                index_version)
 
252
            revision = self._revision_store.get_revision(index_version,
 
253
                self.get_transaction())
 
254
            parents_according_to_revision = revision.parent_ids
 
255
            if parents_according_to_index != parents_according_to_revision:
 
256
                yield (index_version, parents_according_to_index,
 
257
                    parents_according_to_revision)
 
258
 
 
259
    def _check_for_inconsistent_revision_parents(self):
 
260
        inconsistencies = list(self._find_inconsistent_revision_parents())
 
261
        if inconsistencies:
 
262
            raise errors.BzrCheckError(
 
263
                "Revision knit has inconsistent parents.")
 
264
 
 
265
    def revision_graph_can_have_wrong_parents(self):
 
266
        # The revision.kndx could potentially claim a revision has a different
 
267
        # parent to the revision text.
 
268
        return True
 
269
 
 
270
 
 
271
class KnitRepository3(KnitRepository):
 
272
 
 
273
    # knit3 repositories need a RootCommitBuilder
 
274
    _commit_builder_class = RootCommitBuilder
 
275
 
 
276
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
 
277
                 control_store, text_store):
 
278
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
 
279
                              _revision_store, control_store, text_store)
 
280
        self._serializer = xml7.serializer_v7
 
281
 
 
282
    def deserialise_inventory(self, revision_id, xml):
 
283
        """Transform the xml into an inventory object. 
 
284
 
 
285
        :param revision_id: The expected revision id of the inventory.
 
286
        :param xml: A serialised inventory.
 
287
        """
 
288
        result = self._serializer.read_inventory_from_string(xml)
 
289
        assert result.root.revision is not None
 
290
        return result
 
291
 
 
292
    def serialise_inventory(self, inv):
 
293
        """Transform the inventory object into XML text.
 
294
 
 
295
        :param revision_id: The expected revision id of the inventory.
 
296
        :param xml: A serialised inventory.
 
297
        """
 
298
        assert inv.revision_id is not None
 
299
        assert inv.root.revision is not None
 
300
        return KnitRepository.serialise_inventory(self, inv)
 
301
 
 
302
 
 
303
class RepositoryFormatKnit(MetaDirRepositoryFormat):
 
304
    """Bzr repository knit format (generalized). 
 
305
 
 
306
    This repository format has:
 
307
     - knits for file texts and inventory
 
308
     - hash subdirectory based stores.
 
309
     - knits for revisions and signatures
 
310
     - TextStores for revisions and signatures.
 
311
     - a format marker of its own
 
312
     - an optional 'shared-storage' flag
 
313
     - an optional 'no-working-trees' flag
 
314
     - a LockDir lock
 
315
    """
 
316
 
 
317
    # Set this attribute in derived classes to control the repository class
 
318
    # created by open and initialize.
 
319
    repository_class = None
 
320
 
 
321
    def _get_control_store(self, repo_transport, control_files):
 
322
        """Return the control store for this repository."""
 
323
        return VersionedFileStore(
 
324
            repo_transport,
 
325
            prefixed=False,
 
326
            file_mode=control_files._file_mode,
 
327
            versionedfile_class=knit.KnitVersionedFile,
 
328
            versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
 
329
            )
 
330
 
 
331
    def _get_revision_store(self, repo_transport, control_files):
 
332
        """See RepositoryFormat._get_revision_store()."""
 
333
        versioned_file_store = VersionedFileStore(
 
334
            repo_transport,
 
335
            file_mode=control_files._file_mode,
 
336
            prefixed=False,
 
337
            precious=True,
 
338
            versionedfile_class=knit.KnitVersionedFile,
 
339
            versionedfile_kwargs={'delta':False,
 
340
                                  'factory':knit.KnitPlainFactory(),
 
341
                                 },
 
342
            escaped=True,
 
343
            )
 
344
        return KnitRevisionStore(versioned_file_store)
 
345
 
 
346
    def _get_text_store(self, transport, control_files):
 
347
        """See RepositoryFormat._get_text_store()."""
 
348
        return self._get_versioned_file_store('knits',
 
349
                                  transport,
 
350
                                  control_files,
 
351
                                  versionedfile_class=knit.KnitVersionedFile,
 
352
                                  versionedfile_kwargs={
 
353
                                      'create_parent_dir':True,
 
354
                                      'delay_create':True,
 
355
                                      'dir_mode':control_files._dir_mode,
 
356
                                  },
 
357
                                  escaped=True)
 
358
 
 
359
    def initialize(self, a_bzrdir, shared=False):
 
360
        """Create a knit format 1 repository.
 
361
 
 
362
        :param a_bzrdir: bzrdir to contain the new repository; must already
 
363
            be initialized.
 
364
        :param shared: If true the repository will be initialized as a shared
 
365
                       repository.
 
366
        """
 
367
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
368
        dirs = ['knits']
 
369
        files = []
 
370
        utf8_files = [('format', self.get_format_string())]
 
371
        
 
372
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
 
373
        repo_transport = a_bzrdir.get_repository_transport(None)
 
374
        control_files = lockable_files.LockableFiles(repo_transport,
 
375
                                'lock', lockdir.LockDir)
 
376
        control_store = self._get_control_store(repo_transport, control_files)
 
377
        transaction = transactions.WriteTransaction()
 
378
        # trigger a write of the inventory store.
 
379
        control_store.get_weave_or_empty('inventory', transaction)
 
380
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
381
        # the revision id here is irrelevant: it will not be stored, and cannot
 
382
        # already exist.
 
383
        _revision_store.has_revision_id('A', transaction)
 
384
        _revision_store.get_signature_file(transaction)
 
385
        return self.open(a_bzrdir=a_bzrdir, _found=True)
 
386
 
 
387
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
388
        """See RepositoryFormat.open().
 
389
        
 
390
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
391
                                    repository at a slightly different url
 
392
                                    than normal. I.e. during 'upgrade'.
 
393
        """
 
394
        if not _found:
 
395
            format = RepositoryFormat.find_format(a_bzrdir)
 
396
            assert format.__class__ ==  self.__class__
 
397
        if _override_transport is not None:
 
398
            repo_transport = _override_transport
 
399
        else:
 
400
            repo_transport = a_bzrdir.get_repository_transport(None)
 
401
        control_files = lockable_files.LockableFiles(repo_transport,
 
402
                                'lock', lockdir.LockDir)
 
403
        text_store = self._get_text_store(repo_transport, control_files)
 
404
        control_store = self._get_control_store(repo_transport, control_files)
 
405
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
406
        return self.repository_class(_format=self,
 
407
                              a_bzrdir=a_bzrdir,
 
408
                              control_files=control_files,
 
409
                              _revision_store=_revision_store,
 
410
                              control_store=control_store,
 
411
                              text_store=text_store)
 
412
 
 
413
 
 
414
class RepositoryFormatKnit1(RepositoryFormatKnit):
 
415
    """Bzr repository knit format 1.
 
416
 
 
417
    This repository format has:
 
418
     - knits for file texts and inventory
 
419
     - hash subdirectory based stores.
 
420
     - knits for revisions and signatures
 
421
     - TextStores for revisions and signatures.
 
422
     - a format marker of its own
 
423
     - an optional 'shared-storage' flag
 
424
     - an optional 'no-working-trees' flag
 
425
     - a LockDir lock
 
426
 
 
427
    This format was introduced in bzr 0.8.
 
428
    """
 
429
 
 
430
    repository_class = KnitRepository
 
431
 
 
432
    def __ne__(self, other):
 
433
        return self.__class__ is not other.__class__
 
434
 
 
435
    def get_format_string(self):
 
436
        """See RepositoryFormat.get_format_string()."""
 
437
        return "Bazaar-NG Knit Repository Format 1"
 
438
 
 
439
    def get_format_description(self):
 
440
        """See RepositoryFormat.get_format_description()."""
 
441
        return "Knit repository format 1"
 
442
 
 
443
    def check_conversion_target(self, target_format):
 
444
        pass
 
445
 
 
446
 
 
447
class RepositoryFormatKnit3(RepositoryFormatKnit):
 
448
    """Bzr repository knit format 2.
 
449
 
 
450
    This repository format has:
 
451
     - knits for file texts and inventory
 
452
     - hash subdirectory based stores.
 
453
     - knits for revisions and signatures
 
454
     - TextStores for revisions and signatures.
 
455
     - a format marker of its own
 
456
     - an optional 'shared-storage' flag
 
457
     - an optional 'no-working-trees' flag
 
458
     - a LockDir lock
 
459
     - support for recording full info about the tree root
 
460
     - support for recording tree-references
 
461
    """
 
462
 
 
463
    repository_class = KnitRepository3
 
464
    rich_root_data = True
 
465
    supports_tree_reference = True
 
466
 
 
467
    def _get_matching_bzrdir(self):
 
468
        return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
 
469
 
 
470
    def _ignore_setting_bzrdir(self, format):
 
471
        pass
 
472
 
 
473
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
474
 
 
475
    def check_conversion_target(self, target_format):
 
476
        if not target_format.rich_root_data:
 
477
            raise errors.BadConversionTarget(
 
478
                'Does not support rich root data.', target_format)
 
479
        if not getattr(target_format, 'supports_tree_reference', False):
 
480
            raise errors.BadConversionTarget(
 
481
                'Does not support nested trees', target_format)
 
482
            
 
483
    def get_format_string(self):
 
484
        """See RepositoryFormat.get_format_string()."""
 
485
        return "Bazaar Knit Repository Format 3 (bzr 0.15)\n"
 
486
 
 
487
    def get_format_description(self):
 
488
        """See RepositoryFormat.get_format_description()."""
 
489
        return "Knit repository format 3"
 
490
 
 
491
 
 
492
def _get_stream_as_bytes(knit, required_versions):
 
493
    """Generate a serialised data stream.
 
494
 
 
495
    The format is a bencoding of a list.  The first element of the list is a
 
496
    string of the format signature, then each subsequent element is a list
 
497
    corresponding to a record.  Those lists contain:
 
498
 
 
499
      * a version id
 
500
      * a list of options
 
501
      * a list of parents
 
502
      * the bytes
 
503
 
 
504
    :returns: a bencoded list.
 
505
    """
 
506
    knit_stream = knit.get_data_stream(required_versions)
 
507
    format_signature, data_list, callable = knit_stream
 
508
    data = []
 
509
    data.append(format_signature)
 
510
    for version, options, length, parents in data_list:
 
511
        data.append([version, options, parents, callable(length)])
 
512
    return bencode.bencode(data)