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

Refactored out ControlFiles and RevisionStore from _Branch

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