/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/repository.py

Merge from integration, mode-changes are broken.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
16
 
 
17
from cStringIO import StringIO
 
18
 
 
19
from bzrlib.lockable_files import LockableFiles
 
20
from bzrlib.tree import EmptyTree
 
21
from bzrlib.revision import NULL_REVISION
 
22
from bzrlib.store import copy_all
 
23
from bzrlib.store.weave import WeaveStore
 
24
from bzrlib.store.text import TextStore
 
25
import bzrlib.xml5
 
26
from bzrlib.tree import RevisionTree
 
27
from bzrlib.errors import InvalidRevisionId
 
28
from bzrlib.testament import Testament
 
29
 
 
30
 
 
31
def needs_read_lock(unbound):
 
32
    """Decorate unbound to take out and release a read lock."""
 
33
    def decorated(self, *args, **kwargs):
 
34
        self.control_files.lock_read()
 
35
        try:
 
36
            return unbound(self, *args, **kwargs)
 
37
        finally:
 
38
            self.control_files.unlock()
 
39
    return decorated
 
40
 
 
41
 
 
42
def needs_write_lock(unbound):
 
43
    """Decorate unbound to take out and release a write lock."""
 
44
    def decorated(self, *args, **kwargs):
 
45
        self.control_files.lock_write()
 
46
        try:
 
47
            return unbound(self, *args, **kwargs)
 
48
        finally:
 
49
            self.control_files.unlock()
 
50
    return decorated
 
51
 
 
52
 
 
53
class Repository(object):
 
54
 
 
55
    def __init__(self, transport, branch_format, 
 
56
                 dir_mode=None, file_mode=None):
 
57
        object.__init__(self)
 
58
        self.control_files = LockableFiles(transport, bzrlib.BZRDIR, 'README')
 
59
        def get_weave(name, prefixed=False):
 
60
            relpath = self.control_files._rel_controlfilename(unicode(name))
 
61
            weave_transport = transport.clone(relpath)
 
62
            ws = WeaveStore(weave_transport, prefixed=prefixed,
 
63
                            dir_mode=dir_mode,
 
64
                            file_mode=file_mode)
 
65
            if self.control_files._transport.should_cache():
 
66
                ws.enable_cache = True
 
67
            return ws
 
68
 
 
69
        def get_store(name, compressed=True, prefixed=False):
 
70
            # FIXME: This approach of assuming stores are all entirely compressed
 
71
            # or entirely uncompressed is tidy, but breaks upgrade from 
 
72
            # some existing branches where there's a mixture; we probably 
 
73
            # still want the option to look for both.
 
74
            name = unicode(name)
 
75
            relpath = self.control_files._rel_controlfilename(name)
 
76
            store = TextStore(transport.clone(relpath),
 
77
                              prefixed=prefixed, compressed=compressed,
 
78
                              dir_mode=dir_mode,
 
79
                              file_mode=file_mode)
 
80
            #if self._transport.should_cache():
 
81
            #    cache_path = os.path.join(self.cache_root, name)
 
82
            #    os.mkdir(cache_path)
 
83
            #    store = bzrlib.store.CachedStore(store, cache_path)
 
84
            return store
 
85
 
 
86
        if branch_format == 4:
 
87
            self.inventory_store = get_store('inventory-store')
 
88
            self.text_store = get_store('text-store')
 
89
            self.revision_store = get_store('revision-store')
 
90
        elif branch_format == 5:
 
91
            self.control_weaves = get_weave('')
 
92
            self.weave_store = get_weave('weaves')
 
93
            self.revision_store = get_store('revision-store', compressed=False)
 
94
        elif branch_format == 6:
 
95
            self.control_weaves = get_weave('')
 
96
            self.weave_store = get_weave('weaves', prefixed=True)
 
97
            self.revision_store = get_store('revision-store', compressed=False,
 
98
                                            prefixed=True)
 
99
        self.revision_store.register_suffix('sig')
 
100
 
 
101
    def lock_write(self):
 
102
        self.control_files.lock_write()
 
103
 
 
104
    def lock_read(self):
 
105
        self.control_files.lock_read()
 
106
 
 
107
    def unlock(self):
 
108
        self.control_files.unlock()
 
109
 
 
110
    def copy(self, destination):
 
111
        destination.control_weaves.copy_multi(self.control_weaves, 
 
112
                ['inventory'])
 
113
        copy_all(self.weave_store, destination.weave_store)
 
114
        copy_all(self.revision_store, destination.revision_store)
 
115
 
 
116
    def has_revision(self, revision_id):
 
117
        """True if this branch has a copy of the revision.
 
118
 
 
119
        This does not necessarily imply the revision is merge
 
120
        or on the mainline."""
 
121
        return (revision_id is None
 
122
                or self.revision_store.has_id(revision_id))
 
123
 
 
124
    @needs_read_lock
 
125
    def get_revision_xml_file(self, revision_id):
 
126
        """Return XML file object for revision object."""
 
127
        if not revision_id or not isinstance(revision_id, basestring):
 
128
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
 
129
        try:
 
130
            return self.revision_store.get(revision_id)
 
131
        except (IndexError, KeyError):
 
132
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
 
133
 
 
134
    def get_revision_xml(self, revision_id):
 
135
        return self.get_revision_xml_file(revision_id).read()
 
136
 
 
137
    def get_revision(self, revision_id):
 
138
        """Return the Revision object for a named revision"""
 
139
        xml_file = self.get_revision_xml_file(revision_id)
 
140
 
 
141
        try:
 
142
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
 
143
        except SyntaxError, e:
 
144
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
 
145
                                         [revision_id,
 
146
                                          str(e)])
 
147
            
 
148
        assert r.revision_id == revision_id
 
149
        return r
 
150
 
 
151
    def get_revision_sha1(self, revision_id):
 
152
        """Hash the stored value of a revision, and return it."""
 
153
        # In the future, revision entries will be signed. At that
 
154
        # point, it is probably best *not* to include the signature
 
155
        # in the revision hash. Because that lets you re-sign
 
156
        # the revision, (add signatures/remove signatures) and still
 
157
        # have all hash pointers stay consistent.
 
158
        # But for now, just hash the contents.
 
159
        return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
 
160
 
 
161
    @needs_write_lock
 
162
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
 
163
        self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)), 
 
164
                                revision_id, "sig")
 
165
 
 
166
    def get_inventory_weave(self):
 
167
        return self.control_weaves.get_weave('inventory',
 
168
            self.get_transaction())
 
169
 
 
170
    def get_inventory(self, revision_id):
 
171
        """Get Inventory object by hash."""
 
172
        xml = self.get_inventory_xml(revision_id)
 
173
        return bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
 
174
 
 
175
    def get_inventory_xml(self, revision_id):
 
176
        """Get inventory XML as a file object."""
 
177
        try:
 
178
            assert isinstance(revision_id, basestring), type(revision_id)
 
179
            iw = self.get_inventory_weave()
 
180
            return iw.get_text(iw.lookup(revision_id))
 
181
        except IndexError:
 
182
            raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
 
183
 
 
184
    def get_inventory_sha1(self, revision_id):
 
185
        """Return the sha1 hash of the inventory entry
 
186
        """
 
187
        return self.get_revision(revision_id).inventory_sha1
 
188
 
 
189
    def get_revision_inventory(self, revision_id):
 
190
        """Return inventory of a past revision."""
 
191
        # TODO: Unify this with get_inventory()
 
192
        # bzr 0.0.6 and later imposes the constraint that the inventory_id
 
193
        # must be the same as its revision, so this is trivial.
 
194
        if revision_id == None:
 
195
            # This does not make sense: if there is no revision,
 
196
            # then it is the current tree inventory surely ?!
 
197
            # and thus get_root_id() is something that looks at the last
 
198
            # commit on the branch, and the get_root_id is an inventory check.
 
199
            raise NotImplementedError
 
200
            # return Inventory(self.get_root_id())
 
201
        else:
 
202
            return self.get_inventory(revision_id)
 
203
 
 
204
    def revision_tree(self, revision_id):
 
205
        """Return Tree for a revision on this branch.
 
206
 
 
207
        `revision_id` may be None for the null revision, in which case
 
208
        an `EmptyTree` is returned."""
 
209
        # TODO: refactor this to use an existing revision object
 
210
        # so we don't need to read it in twice.
 
211
        if revision_id == None or revision_id == NULL_REVISION:
 
212
            return EmptyTree()
 
213
        else:
 
214
            inv = self.get_revision_inventory(revision_id)
 
215
            return RevisionTree(self, inv, revision_id)
 
216
 
 
217
    def get_ancestry(self, revision_id):
 
218
        """Return a list of revision-ids integrated by a revision.
 
219
        
 
220
        This is topologically sorted.
 
221
        """
 
222
        if revision_id is None:
 
223
            return [None]
 
224
        w = self.get_inventory_weave()
 
225
        return [None] + map(w.idx_to_name,
 
226
                            w.inclusions([w.lookup(revision_id)]))
 
227
 
 
228
    @needs_read_lock
 
229
    def print_file(self, file, revision_id):
 
230
        """Print `file` to stdout."""
 
231
        tree = self.revision_tree(revision_id)
 
232
        # use inventory as it was in that revision
 
233
        file_id = tree.inventory.path2id(file)
 
234
        if not file_id:
 
235
            raise BzrError("%r is not present in revision %s" % (file, revno))
 
236
            try:
 
237
                revno = self.revision_id_to_revno(revision_id)
 
238
            except errors.NoSuchRevision:
 
239
                # TODO: This should not be BzrError,
 
240
                # but NoSuchFile doesn't fit either
 
241
                raise BzrError('%r is not present in revision %s' 
 
242
                                % (file, revision_id))
 
243
            else:
 
244
                raise BzrError('%r is not present in revision %s'
 
245
                                % (file, revno))
 
246
        tree.print_file(file_id)
 
247
 
 
248
    def get_transaction(self):
 
249
        return self.control_files.get_transaction()
 
250
 
 
251
    def sign_revision(self, revision_id, gpg_strategy):
 
252
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
 
253
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)