/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
1
# Copyright (C) 2005 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
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
16
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
17
from copy import deepcopy
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
18
from cStringIO import StringIO
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
19
from unittest import TestSuite
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
20
import xml.sax.saxutils
21
1185.65.10 by Robert Collins
Rename Controlfiles to LockableFiles.
22
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
23
import bzrlib.bzrdir as bzrdir
1534.4.28 by Robert Collins
first cut at merge from integration.
24
from bzrlib.decorators import needs_read_lock, needs_write_lock
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
25
import bzrlib.errors as errors
1534.4.28 by Robert Collins
first cut at merge from integration.
26
from bzrlib.errors import InvalidRevisionId
1185.66.3 by Aaron Bentley
Renamed ControlFiles to LockableFiles
27
from bzrlib.lockable_files import LockableFiles
1534.4.28 by Robert Collins
first cut at merge from integration.
28
from bzrlib.osutils import safe_unicode
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
29
from bzrlib.revision import NULL_REVISION
1185.65.14 by Robert Collins
Merge from aaron. Whee, we are synced. Yay. Begone the foul demons of merge conflicts.
30
from bzrlib.store import copy_all
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
31
from bzrlib.store.weave import WeaveStore
32
from bzrlib.store.text import TextStore
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
33
from bzrlib.symbol_versioning import *
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
34
from bzrlib.trace import mutter
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
35
from bzrlib.tree import RevisionTree
36
from bzrlib.testament import Testament
1534.4.28 by Robert Collins
first cut at merge from integration.
37
from bzrlib.tree import EmptyTree
38
import bzrlib.xml5
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
39
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
40
1185.66.5 by Aaron Bentley
Renamed RevisionStorage to Repository
41
class Repository(object):
1185.70.3 by Martin Pool
Various updates to make storage branch mergeable:
42
    """Repository holding history for one or more branches.
43
44
    The repository holds and retrieves historical information including
45
    revisions and file history.  It's normally accessed only by the Branch,
46
    which views a particular line of development through that history.
47
48
    The Repository builds on top of Stores and a Transport, which respectively 
49
    describe the disk data format and the way of accessing the (possibly 
50
    remote) disk.
51
    """
1185.65.17 by Robert Collins
Merge from integration, mode-changes are broken.
52
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
53
    @needs_read_lock
54
    def all_revision_ids(self):
55
        """Returns a list of all the revision ids in the repository.
56
57
        It would be nice to have this topologically sorted, but its not yet.
58
        """
59
        possible_ids = self.get_inventory_weave().names()
60
        result = []
61
        for id in possible_ids:
62
            if self.has_revision(id):
63
               result.append(id)
64
        return result
65
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
66
    @staticmethod
67
    def create(a_bzrdir):
68
        """Construct the current default format repository in a_bzrdir."""
69
        return RepositoryFormat.get_default_format().initialize(a_bzrdir)
70
71
    def __init__(self, transport, branch_format, _format=None, a_bzrdir=None):
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
72
        object.__init__(self)
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
73
        if transport is not None:
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
74
            warn("Repository.__init__(..., transport=XXX): The transport parameter is "
75
                 "deprecated and was never in a supported release. Please use "
76
                 "bzrdir.open_repository() or bzrdir.open_branch().repository.",
77
                 DeprecationWarning,
78
                 stacklevel=2)
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
79
            self.control_files = LockableFiles(transport.clone(bzrlib.BZRDIR), 'README')
80
        else: 
81
            # TODO: clone into repository if needed
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
82
            self.control_files = LockableFiles(a_bzrdir.get_repository_transport(None), 'README')
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
83
84
        dir_mode = self.control_files._dir_mode
85
        file_mode = self.control_files._file_mode
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
86
        self._format = _format
87
        self.bzrdir = a_bzrdir
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
88
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
89
        def get_weave(name, prefixed=False):
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
90
            if name:
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
91
                name = safe_unicode(name)
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
92
            else:
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
93
                name = ''
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
94
            relpath = self.control_files._escape(name)
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
95
            weave_transport = self.control_files._transport.clone(relpath)
1185.65.17 by Robert Collins
Merge from integration, mode-changes are broken.
96
            ws = WeaveStore(weave_transport, prefixed=prefixed,
97
                            dir_mode=dir_mode,
98
                            file_mode=file_mode)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
99
            if self.control_files._transport.should_cache():
100
                ws.enable_cache = True
101
            return ws
102
1534.4.28 by Robert Collins
first cut at merge from integration.
103
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
104
        def get_store(name, compressed=True, prefixed=False):
105
            # FIXME: This approach of assuming stores are all entirely compressed
106
            # or entirely uncompressed is tidy, but breaks upgrade from 
107
            # some existing branches where there's a mixture; we probably 
108
            # still want the option to look for both.
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
109
            if name:
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
110
                name = safe_unicode(name)
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
111
            else:
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
112
                name = ''
1185.69.2 by John Arbash Meinel
Changed LockableFiles to take the root directory directly. Moved mode information into LockableFiles instead of Branch
113
            relpath = self.control_files._escape(name)
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
114
            store = TextStore(self.control_files._transport.clone(relpath),
1185.65.17 by Robert Collins
Merge from integration, mode-changes are broken.
115
                              prefixed=prefixed, compressed=compressed,
116
                              dir_mode=dir_mode,
117
                              file_mode=file_mode)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
118
            #if self._transport.should_cache():
119
            #    cache_path = os.path.join(self.cache_root, name)
120
            #    os.mkdir(cache_path)
121
            #    store = bzrlib.store.CachedStore(store, cache_path)
122
            return store
123
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
124
        if branch_format is not None:
125
            # circular dependencies:
126
            from bzrlib.branch import (BzrBranchFormat4,
127
                                       BzrBranchFormat5,
128
                                       BzrBranchFormat6,
129
                                       )
130
            if isinstance(branch_format, BzrBranchFormat4):
131
                self._format = RepositoryFormat4()
132
            elif isinstance(branch_format, BzrBranchFormat5):
133
                self._format = RepositoryFormat5()
134
            elif isinstance(branch_format, BzrBranchFormat6):
135
                self._format = RepositoryFormat6()
136
            
1534.4.28 by Robert Collins
first cut at merge from integration.
137
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
138
        if isinstance(self._format, RepositoryFormat4):
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
139
            self.inventory_store = get_store('inventory-store')
140
            self.text_store = get_store('text-store')
141
            self.revision_store = get_store('revision-store')
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
142
        elif isinstance(self._format, RepositoryFormat5):
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
143
            self.control_weaves = get_weave('')
144
            self.weave_store = get_weave('weaves')
145
            self.revision_store = get_store('revision-store', compressed=False)
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
146
        elif isinstance(self._format, RepositoryFormat6):
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
147
            self.control_weaves = get_weave('')
148
            self.weave_store = get_weave('weaves', prefixed=True)
149
            self.revision_store = get_store('revision-store', compressed=False,
150
                                            prefixed=True)
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
151
        elif isinstance(self._format, RepositoryFormat7):
152
            self.control_weaves = get_weave('')
153
            self.weave_store = get_weave('weaves', prefixed=True)
154
            self.revision_store = get_store('revision-store', compressed=False,
155
                                            prefixed=True)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
156
        self.revision_store.register_suffix('sig')
157
158
    def lock_write(self):
159
        self.control_files.lock_write()
160
161
    def lock_read(self):
162
        self.control_files.lock_read()
163
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
164
    @staticmethod
165
    def open(base):
166
        """Open the repository rooted at base.
167
168
        For instance, if the repository is at URL/.bzr/repository,
169
        Repository.open(URL) -> a Repository instance.
170
        """
171
        control = bzrdir.BzrDir.open(base)
172
        return control.open_repository()
173
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
174
    def push_stores(self, to, revision=NULL_REVISION):
175
        """FIXME: document and find a consistent name with other classes."""
176
        if (not isinstance(self._format, RepositoryFormat4) or
177
            self._format != to._format):
178
            from bzrlib.fetch import RepoFetcher
179
            mutter("Using fetch logic to push between %s(%s) and %s(%s)",
180
                   self, self._format, to, to._format)
181
            RepoFetcher(to_repository=to, from_repository=self,
182
                         last_revision=revision)
183
            return
184
185
        # format 4 to format 4 logic only.
186
        store_pairs = ((self.text_store,      to.text_store),
187
                       (self.inventory_store, to.inventory_store),
188
                       (self.revision_store,  to.revision_store))
189
        try:
190
            for from_store, to_store in store_pairs: 
191
                copy_all(from_store, to_store)
192
        except UnlistableStore:
193
            raise UnlistableBranch(from_store)
194
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
195
    def unlock(self):
196
        self.control_files.unlock()
197
1185.65.27 by Robert Collins
Tweak storage towards mergability.
198
    @needs_read_lock
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
199
    def clone(self, a_bzrdir):
200
        """Clone this repository into a_bzrdir using the current format.
201
202
        Currently no check is made that the format of this repository and
203
        the bzrdir format are compatible. FIXME RBC 20060201.
204
        """
205
        result = self._format.initialize(a_bzrdir)
206
        self.copy(result)
207
        return result
208
209
    @needs_read_lock
1185.66.8 by Aaron Bentley
Applied Jelmer's patch to make clone a branch operation
210
    def copy(self, destination):
1185.65.27 by Robert Collins
Tweak storage towards mergability.
211
        destination.lock_write()
212
        try:
213
            destination.control_weaves.copy_multi(self.control_weaves, 
1185.66.8 by Aaron Bentley
Applied Jelmer's patch to make clone a branch operation
214
                ['inventory'])
1185.65.27 by Robert Collins
Tweak storage towards mergability.
215
            copy_all(self.weave_store, destination.weave_store)
216
            copy_all(self.revision_store, destination.revision_store)
217
        finally:
218
            destination.unlock()
1185.66.8 by Aaron Bentley
Applied Jelmer's patch to make clone a branch operation
219
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
220
    def has_revision(self, revision_id):
221
        """True if this branch has a copy of the revision.
222
223
        This does not necessarily imply the revision is merge
224
        or on the mainline."""
225
        return (revision_id is None
226
                or self.revision_store.has_id(revision_id))
227
228
    @needs_read_lock
229
    def get_revision_xml_file(self, revision_id):
230
        """Return XML file object for revision object."""
231
        if not revision_id or not isinstance(revision_id, basestring):
232
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
233
        try:
234
            return self.revision_store.get(revision_id)
235
        except (IndexError, KeyError):
236
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
237
1185.65.27 by Robert Collins
Tweak storage towards mergability.
238
    @needs_read_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
239
    def get_revision_xml(self, revision_id):
240
        return self.get_revision_xml_file(revision_id).read()
241
1185.65.27 by Robert Collins
Tweak storage towards mergability.
242
    @needs_read_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
243
    def get_revision(self, revision_id):
244
        """Return the Revision object for a named revision"""
245
        xml_file = self.get_revision_xml_file(revision_id)
246
247
        try:
248
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
249
        except SyntaxError, e:
250
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
251
                                         [revision_id,
252
                                          str(e)])
253
            
254
        assert r.revision_id == revision_id
255
        return r
256
1185.65.27 by Robert Collins
Tweak storage towards mergability.
257
    @needs_read_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
258
    def get_revision_sha1(self, revision_id):
259
        """Hash the stored value of a revision, and return it."""
260
        # In the future, revision entries will be signed. At that
261
        # point, it is probably best *not* to include the signature
262
        # in the revision hash. Because that lets you re-sign
263
        # the revision, (add signatures/remove signatures) and still
264
        # have all hash pointers stay consistent.
265
        # But for now, just hash the contents.
266
        return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
267
268
    @needs_write_lock
269
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
270
        self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)), 
271
                                revision_id, "sig")
272
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
273
    def fileid_involved_between_revs(self, from_revid, to_revid):
274
        """Find file_id(s) which are involved in the changes between revisions.
275
276
        This determines the set of revisions which are involved, and then
277
        finds all file ids affected by those revisions.
278
        """
279
        # TODO: jam 20060119 This code assumes that w.inclusions will
280
        #       always be correct. But because of the presence of ghosts
281
        #       it is possible to be wrong.
282
        #       One specific example from Robert Collins:
283
        #       Two branches, with revisions ABC, and AD
284
        #       C is a ghost merge of D.
285
        #       Inclusions doesn't recognize D as an ancestor.
286
        #       If D is ever merged in the future, the weave
287
        #       won't be fixed, because AD never saw revision C
288
        #       to cause a conflict which would force a reweave.
289
        w = self.get_inventory_weave()
290
        from_set = set(w.inclusions([w.lookup(from_revid)]))
291
        to_set = set(w.inclusions([w.lookup(to_revid)]))
292
        included = to_set.difference(from_set)
293
        changed = map(w.idx_to_name, included)
294
        return self._fileid_involved_by_set(changed)
295
296
    def fileid_involved(self, last_revid=None):
297
        """Find all file_ids modified in the ancestry of last_revid.
298
299
        :param last_revid: If None, last_revision() will be used.
300
        """
301
        w = self.get_inventory_weave()
302
        if not last_revid:
303
            changed = set(w._names)
304
        else:
305
            included = w.inclusions([w.lookup(last_revid)])
306
            changed = map(w.idx_to_name, included)
307
        return self._fileid_involved_by_set(changed)
308
309
    def fileid_involved_by_set(self, changes):
310
        """Find all file_ids modified by the set of revisions passed in.
311
312
        :param changes: A set() of revision ids
313
        """
314
        # TODO: jam 20060119 This line does *nothing*, remove it.
315
        #       or better yet, change _fileid_involved_by_set so
316
        #       that it takes the inventory weave, rather than
317
        #       pulling it out by itself.
318
        return self._fileid_involved_by_set(changes)
319
320
    def _fileid_involved_by_set(self, changes):
321
        """Find the set of file-ids affected by the set of revisions.
322
323
        :param changes: A set() of revision ids.
324
        :return: A set() of file ids.
325
        
326
        This peaks at the Weave, interpreting each line, looking to
327
        see if it mentions one of the revisions. And if so, includes
328
        the file id mentioned.
329
        This expects both the Weave format, and the serialization
330
        to have a single line per file/directory, and to have
331
        fileid="" and revision="" on that line.
332
        """
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
333
        assert isinstance(self._format, (RepositoryFormat5,
334
                                         RepositoryFormat6,
335
                                         RepositoryFormat7)), \
336
            "fileid_involved only supported for branches which store inventory as unnested xml"
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
337
338
        w = self.get_inventory_weave()
339
        file_ids = set()
340
        for line in w._weave:
341
342
            # it is ugly, but it is due to the weave structure
343
            if not isinstance(line, basestring): continue
344
345
            start = line.find('file_id="')+9
346
            if start < 9: continue
347
            end = line.find('"', start)
348
            assert end>= 0
349
            file_id = xml.sax.saxutils.unescape(line[start:end])
350
351
            # check if file_id is already present
352
            if file_id in file_ids: continue
353
354
            start = line.find('revision="')+10
355
            if start < 10: continue
356
            end = line.find('"', start)
357
            assert end>= 0
358
            revision_id = xml.sax.saxutils.unescape(line[start:end])
359
360
            if revision_id in changes:
361
                file_ids.add(file_id)
362
        return file_ids
363
1185.65.27 by Robert Collins
Tweak storage towards mergability.
364
    @needs_read_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
365
    def get_inventory_weave(self):
366
        return self.control_weaves.get_weave('inventory',
367
            self.get_transaction())
368
1185.65.27 by Robert Collins
Tweak storage towards mergability.
369
    @needs_read_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
370
    def get_inventory(self, revision_id):
371
        """Get Inventory object by hash."""
372
        xml = self.get_inventory_xml(revision_id)
373
        return bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
374
1185.65.27 by Robert Collins
Tweak storage towards mergability.
375
    @needs_read_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
376
    def get_inventory_xml(self, revision_id):
377
        """Get inventory XML as a file object."""
378
        try:
379
            assert isinstance(revision_id, basestring), type(revision_id)
380
            iw = self.get_inventory_weave()
381
            return iw.get_text(iw.lookup(revision_id))
382
        except IndexError:
383
            raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
384
1185.65.27 by Robert Collins
Tweak storage towards mergability.
385
    @needs_read_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
386
    def get_inventory_sha1(self, revision_id):
387
        """Return the sha1 hash of the inventory entry
388
        """
389
        return self.get_revision(revision_id).inventory_sha1
390
1185.65.27 by Robert Collins
Tweak storage towards mergability.
391
    @needs_read_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
392
    def get_revision_inventory(self, revision_id):
393
        """Return inventory of a past revision."""
394
        # TODO: Unify this with get_inventory()
395
        # bzr 0.0.6 and later imposes the constraint that the inventory_id
396
        # must be the same as its revision, so this is trivial.
1534.4.28 by Robert Collins
first cut at merge from integration.
397
        if revision_id is None:
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
398
            # This does not make sense: if there is no revision,
399
            # then it is the current tree inventory surely ?!
400
            # and thus get_root_id() is something that looks at the last
401
            # commit on the branch, and the get_root_id is an inventory check.
402
            raise NotImplementedError
403
            # return Inventory(self.get_root_id())
404
        else:
405
            return self.get_inventory(revision_id)
406
1185.65.27 by Robert Collins
Tweak storage towards mergability.
407
    @needs_read_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
408
    def revision_tree(self, revision_id):
409
        """Return Tree for a revision on this branch.
410
411
        `revision_id` may be None for the null revision, in which case
412
        an `EmptyTree` is returned."""
413
        # TODO: refactor this to use an existing revision object
414
        # so we don't need to read it in twice.
1534.4.28 by Robert Collins
first cut at merge from integration.
415
        if revision_id is None or revision_id == NULL_REVISION:
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
416
            return EmptyTree()
417
        else:
418
            inv = self.get_revision_inventory(revision_id)
1185.65.17 by Robert Collins
Merge from integration, mode-changes are broken.
419
            return RevisionTree(self, inv, revision_id)
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
420
1185.65.27 by Robert Collins
Tweak storage towards mergability.
421
    @needs_read_lock
1185.66.2 by Aaron Bentley
Moved get_ancestry to RevisionStorage
422
    def get_ancestry(self, revision_id):
423
        """Return a list of revision-ids integrated by a revision.
424
        
425
        This is topologically sorted.
426
        """
427
        if revision_id is None:
428
            return [None]
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
429
        if not self.has_revision(revision_id):
430
            raise errors.NoSuchRevision(self, revision_id)
1185.66.2 by Aaron Bentley
Moved get_ancestry to RevisionStorage
431
        w = self.get_inventory_weave()
432
        return [None] + map(w.idx_to_name,
433
                            w.inclusions([w.lookup(revision_id)]))
434
1185.65.4 by Aaron Bentley
Fixed cat command
435
    @needs_read_lock
436
    def print_file(self, file, revision_id):
1185.65.29 by Robert Collins
Implement final review suggestions.
437
        """Print `file` to stdout.
438
        
439
        FIXME RBC 20060125 as John Meinel points out this is a bad api
440
        - it writes to stdout, it assumes that that is valid etc. Fix
441
        by creating a new more flexible convenience function.
442
        """
1185.65.4 by Aaron Bentley
Fixed cat command
443
        tree = self.revision_tree(revision_id)
444
        # use inventory as it was in that revision
445
        file_id = tree.inventory.path2id(file)
446
        if not file_id:
447
            raise BzrError("%r is not present in revision %s" % (file, revno))
1185.65.15 by Robert Collins
Merge from integration.
448
            try:
449
                revno = self.revision_id_to_revno(revision_id)
450
            except errors.NoSuchRevision:
451
                # TODO: This should not be BzrError,
452
                # but NoSuchFile doesn't fit either
453
                raise BzrError('%r is not present in revision %s' 
454
                                % (file, revision_id))
455
            else:
456
                raise BzrError('%r is not present in revision %s'
457
                                % (file, revno))
1185.65.4 by Aaron Bentley
Fixed cat command
458
        tree.print_file(file_id)
459
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
460
    def get_transaction(self):
461
        return self.control_files.get_transaction()
462
1185.65.27 by Robert Collins
Tweak storage towards mergability.
463
    @needs_write_lock
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
464
    def sign_revision(self, revision_id, gpg_strategy):
465
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
466
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
467
468
469
class RepositoryFormat(object):
470
    """A repository format.
471
472
    Formats provide three things:
473
     * An initialization routine to construct repository data on disk.
474
     * a format string which is used when the BzrDir supports versioned
475
       children.
476
     * an open routine which returns a Repository instance.
477
478
    Formats are placed in an dict by their format string for reference 
479
    during opening. These should be subclasses of RepositoryFormat
480
    for consistency.
481
482
    Once a format is deprecated, just deprecate the initialize and open
483
    methods on the format class. Do not deprecate the object, as the 
484
    object will be created every system load.
485
486
    Common instance attributes:
487
    _matchingbzrdir - the bzrdir format that the repository format was
488
    originally written to work with. This can be used if manually
489
    constructing a bzrdir and repository, or more commonly for test suite
490
    parameterisation.
491
    """
492
493
    _default_format = None
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
494
    """The default format used for new repositories."""
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
495
496
    _formats = {}
497
    """The known formats."""
498
499
    @classmethod
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
500
    def find_format(klass, a_bzrdir):
501
        """Return the format for the repository object in a_bzrdir."""
502
        try:
503
            transport = a_bzrdir.get_repository_transport(None)
504
            format_string = transport.get("format").read()
505
            return klass._formats[format_string]
506
        except errors.NoSuchFile:
507
            raise errors.NoRepositoryPresent(a_bzrdir)
508
        except KeyError:
509
            raise errors.UnknownFormatError(format_string)
510
511
    @classmethod
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
512
    def get_default_format(klass):
513
        """Return the current default format."""
514
        return klass._default_format
515
516
    def get_format_string(self):
517
        """Return the ASCII format string that identifies this format.
518
        
519
        Note that in pre format ?? repositories the format string is 
520
        not permitted nor written to disk.
521
        """
522
        raise NotImplementedError(self.get_format_string)
523
524
    def initialize(self, a_bzrdir):
525
        """Create a weave repository.
526
        
527
        TODO: when creating split out bzr branch formats, move this to a common
528
        base for Format5, Format6. or something like that.
529
        """
530
        from bzrlib.weavefile import write_weave_v5
531
        from bzrlib.weave import Weave
532
        
533
        # Create an empty weave
534
        sio = StringIO()
535
        bzrlib.weavefile.write_weave_v5(Weave(), sio)
536
        empty_weave = sio.getvalue()
537
538
        mutter('creating repository in %s.', a_bzrdir.transport.base)
539
        dirs = ['revision-store', 'weaves']
540
        lock_file = 'branch-lock'
541
        files = [('inventory.weave', StringIO(empty_weave)), 
542
                 ]
543
        
544
        # FIXME: RBC 20060125 dont peek under the covers
545
        # NB: no need to escape relative paths that are url safe.
546
        control_files = LockableFiles(a_bzrdir.transport, 'branch-lock')
547
        control_files.lock_write()
548
        control_files._transport.mkdir_multi(dirs,
549
                mode=control_files._dir_mode)
550
        try:
551
            for file, content in files:
552
                control_files.put(file, content)
553
        finally:
554
            control_files.unlock()
555
        return Repository(None, branch_format=None, _format=self, a_bzrdir=a_bzrdir)
556
557
    def is_supported(self):
558
        """Is this format supported?
559
560
        Supported formats must be initializable and openable.
561
        Unsupported formats may not support initialization or committing or 
562
        some other features depending on the reason for not being supported.
563
        """
564
        return True
565
566
    def open(self, a_bzrdir, _found=False):
567
        """Return an instance of this format for the bzrdir a_bzrdir.
568
        
569
        _found is a private parameter, do not use it.
570
        """
571
        if not _found:
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
572
            # we are being called directly and must probe.
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
573
            raise NotImplementedError
574
        return Repository(None, branch_format=None, _format=self, a_bzrdir=a_bzrdir)
575
576
    @classmethod
577
    def register_format(klass, format):
578
        klass._formats[format.get_format_string()] = format
579
580
    @classmethod
581
    def set_default_format(klass, format):
582
        klass._default_format = format
583
584
    @classmethod
585
    def unregister_format(klass, format):
586
        assert klass._formats[format.get_format_string()] is format
587
        del klass._formats[format.get_format_string()]
588
589
590
class RepositoryFormat4(RepositoryFormat):
591
    """Bzr repository format 4.
592
593
    This repository format has:
594
     - flat stores
595
     - TextStores for texts, inventories,revisions.
596
597
    This format is deprecated: it indexes texts using a text id which is
598
    removed in format 5; initializationa and write support for this format
599
    has been removed.
600
    """
601
602
    def __init__(self):
603
        super(RepositoryFormat4, self).__init__()
604
        self._matchingbzrdir = bzrdir.BzrDirFormat4()
605
606
    def initialize(self, url):
607
        """Format 4 branches cannot be created."""
608
        raise errors.UninitializableFormat(self)
609
610
    def is_supported(self):
611
        """Format 4 is not supported.
612
613
        It is not supported because the model changed from 4 to 5 and the
614
        conversion logic is expensive - so doing it on the fly was not 
615
        feasible.
616
        """
617
        return False
618
619
620
class RepositoryFormat5(RepositoryFormat):
621
    """Bzr control format 5.
622
623
    This repository format has:
624
     - weaves for file texts and inventory
625
     - flat stores
626
     - TextStores for revisions and signatures.
627
    """
628
629
    def __init__(self):
630
        super(RepositoryFormat5, self).__init__()
631
        self._matchingbzrdir = bzrdir.BzrDirFormat5()
632
633
634
class RepositoryFormat6(RepositoryFormat):
635
    """Bzr control format 6.
636
637
    This repository format has:
638
     - weaves for file texts and inventory
639
     - hash subdirectory based stores.
640
     - TextStores for revisions and signatures.
641
    """
642
643
    def __init__(self):
644
        super(RepositoryFormat6, self).__init__()
645
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
646
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
647
648
class RepositoryFormat7(RepositoryFormat):
649
    """Bzr repository 7.
650
651
    This repository format has:
652
     - weaves for file texts and inventory
653
     - hash subdirectory based stores.
654
     - TextStores for revisions and signatures.
655
     - a format marker of its own
656
    """
657
658
    def get_format_string(self):
659
        """See RepositoryFormat.get_format_string()."""
660
        return "Bazaar-NG Repository format 7"
661
662
    def initialize(self, a_bzrdir):
663
        """Create a weave repository.
664
        """
665
        from bzrlib.weavefile import write_weave_v5
666
        from bzrlib.weave import Weave
667
668
        # Create an empty weave
669
        sio = StringIO()
670
        bzrlib.weavefile.write_weave_v5(Weave(), sio)
671
        empty_weave = sio.getvalue()
672
673
        mutter('creating repository in %s.', a_bzrdir.transport.base)
674
        dirs = ['revision-store', 'weaves']
675
        files = [('inventory.weave', StringIO(empty_weave)), 
676
                 ]
677
        utf8_files = [('format', self.get_format_string())]
678
        
679
        # FIXME: RBC 20060125 dont peek under the covers
680
        # NB: no need to escape relative paths that are url safe.
681
        lock_file = 'lock'
682
        repository_transport = a_bzrdir.get_repository_transport(self)
683
        repository_transport.put(lock_file, StringIO()) # TODO get the file mode from the bzrdir lock files., mode=file_mode)
684
        control_files = LockableFiles(repository_transport, 'lock')
685
        control_files.lock_write()
686
        control_files._transport.mkdir_multi(dirs,
687
                mode=control_files._dir_mode)
688
        try:
689
            for file, content in files:
690
                control_files.put(file, content)
691
            for file, content in utf8_files:
692
                control_files.put_utf8(file, content)
693
        finally:
694
            control_files.unlock()
695
        return Repository(None, branch_format=None, _format=self, a_bzrdir=a_bzrdir)
696
697
    def __init__(self):
698
        super(RepositoryFormat7, self).__init__()
699
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
700
701
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
702
# formats which have no format string are not discoverable
703
# and not independently creatable, so are not registered.
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
704
__default_format = RepositoryFormat7()
705
RepositoryFormat.register_format(__default_format)
706
RepositoryFormat.set_default_format(__default_format)
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
707
_legacy_formats = [RepositoryFormat4(),
708
                   RepositoryFormat5(),
709
                   RepositoryFormat6()]
710
711
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
712
# TODO: jam 20060108 Create a new branch format, and as part of upgrade
713
#       make sure that ancestry.weave is deleted (it is never used, but
714
#       used to be created)
715
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
716
class RepositoryTestProviderAdapter(object):
717
    """A tool to generate a suite testing multiple repository formats at once.
718
719
    This is done by copying the test once for each transport and injecting
720
    the transport_server, transport_readonly_server, and bzrdir_format and
721
    repository_format classes into each copy. Each copy is also given a new id()
722
    to make it easy to identify.
723
    """
724
725
    def __init__(self, transport_server, transport_readonly_server, formats):
726
        self._transport_server = transport_server
727
        self._transport_readonly_server = transport_readonly_server
728
        self._formats = formats
729
    
730
    def adapt(self, test):
731
        result = TestSuite()
732
        for repository_format, bzrdir_format in self._formats:
733
            new_test = deepcopy(test)
734
            new_test.transport_server = self._transport_server
735
            new_test.transport_readonly_server = self._transport_readonly_server
736
            new_test.bzrdir_format = bzrdir_format
737
            new_test.repository_format = repository_format
738
            def make_new_test_id():
739
                new_id = "%s(%s)" % (new_test.id(), repository_format.__class__.__name__)
740
                return lambda: new_id
741
            new_test.id = make_new_test_id()
742
            result.addTest(new_test)
743
        return result