~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/knitrepo.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-03-12 17:30:53 UTC
  • mfrom: (2338.3.1 hide-nested)
  • Revision ID: pqm@pqm.ubuntu.com-20070312173053-4cdb4cd14190d29e
Hide nested-tree commands and improve their docs

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