/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

  • Committer: Aaron Bentley
  • Date: 2005-12-25 23:06:24 UTC
  • mto: (1185.67.11 bzr.revision-storage)
  • mto: This revision was merged to the branch mainline in revision 1550.
  • Revision ID: aaron.bentley@utoronto.ca-20051225230624-f61a1538912a578a
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile

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
    def __init__(self, transport, branch_format):
 
55
        object.__init__(self)
 
56
        self.control_files = LockableFiles(transport, 'storage-lock')
 
57
        def get_weave(name, prefixed=False):
 
58
            relpath = self.control_files._rel_controlfilename(unicode(name))
 
59
            weave_transport = self.control_files.make_transport(relpath)
 
60
            ws = WeaveStore(weave_transport, prefixed=prefixed)
 
61
            if self.control_files._transport.should_cache():
 
62
                ws.enable_cache = True
 
63
            return ws
 
64
 
 
65
        def get_store(name, compressed=True, prefixed=False):
 
66
            # FIXME: This approach of assuming stores are all entirely compressed
 
67
            # or entirely uncompressed is tidy, but breaks upgrade from 
 
68
            # some existing branches where there's a mixture; we probably 
 
69
            # still want the option to look for both.
 
70
            name = unicode(name)
 
71
            relpath = self.control_files._rel_controlfilename(name)
 
72
            store = TextStore(self.control_files.make_transport(relpath),
 
73
                              prefixed=prefixed, compressed=compressed)
 
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 copy(self, destination):
 
105
        destination.control_weaves.copy_multi(self.control_weaves, 
 
106
                ['inventory'])
 
107
        copy_all(self.weave_store, destination.weave_store)
 
108
        copy_all(self.revision_store, destination.revision_store)
 
109
 
 
110
    def has_revision(self, revision_id):
 
111
        """True if this branch has a copy of the revision.
 
112
 
 
113
        This does not necessarily imply the revision is merge
 
114
        or on the mainline."""
 
115
        return (revision_id is None
 
116
                or self.revision_store.has_id(revision_id))
 
117
 
 
118
    @needs_read_lock
 
119
    def get_revision_xml_file(self, revision_id):
 
120
        """Return XML file object for revision object."""
 
121
        if not revision_id or not isinstance(revision_id, basestring):
 
122
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
 
123
        try:
 
124
            return self.revision_store.get(revision_id)
 
125
        except (IndexError, KeyError):
 
126
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
 
127
 
 
128
    def get_revision_xml(self, revision_id):
 
129
        return self.get_revision_xml_file(revision_id).read()
 
130
 
 
131
    def get_revision(self, revision_id):
 
132
        """Return the Revision object for a named revision"""
 
133
        xml_file = self.get_revision_xml_file(revision_id)
 
134
 
 
135
        try:
 
136
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
 
137
        except SyntaxError, e:
 
138
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
 
139
                                         [revision_id,
 
140
                                          str(e)])
 
141
            
 
142
        assert r.revision_id == revision_id
 
143
        return r
 
144
 
 
145
    def get_revision_sha1(self, revision_id):
 
146
        """Hash the stored value of a revision, and return it."""
 
147
        # In the future, revision entries will be signed. At that
 
148
        # point, it is probably best *not* to include the signature
 
149
        # in the revision hash. Because that lets you re-sign
 
150
        # the revision, (add signatures/remove signatures) and still
 
151
        # have all hash pointers stay consistent.
 
152
        # But for now, just hash the contents.
 
153
        return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
 
154
 
 
155
    @needs_write_lock
 
156
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
 
157
        self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)), 
 
158
                                revision_id, "sig")
 
159
 
 
160
    def get_inventory_weave(self):
 
161
        return self.control_weaves.get_weave('inventory',
 
162
            self.get_transaction())
 
163
 
 
164
    def get_inventory(self, revision_id):
 
165
        """Get Inventory object by hash."""
 
166
        xml = self.get_inventory_xml(revision_id)
 
167
        return bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
 
168
 
 
169
    def get_inventory_xml(self, revision_id):
 
170
        """Get inventory XML as a file object."""
 
171
        try:
 
172
            assert isinstance(revision_id, basestring), type(revision_id)
 
173
            iw = self.get_inventory_weave()
 
174
            return iw.get_text(iw.lookup(revision_id))
 
175
        except IndexError:
 
176
            raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
 
177
 
 
178
    def get_inventory_sha1(self, revision_id):
 
179
        """Return the sha1 hash of the inventory entry
 
180
        """
 
181
        return self.get_revision(revision_id).inventory_sha1
 
182
 
 
183
    def get_revision_inventory(self, revision_id):
 
184
        """Return inventory of a past revision."""
 
185
        # TODO: Unify this with get_inventory()
 
186
        # bzr 0.0.6 and later imposes the constraint that the inventory_id
 
187
        # must be the same as its revision, so this is trivial.
 
188
        if revision_id == None:
 
189
            # This does not make sense: if there is no revision,
 
190
            # then it is the current tree inventory surely ?!
 
191
            # and thus get_root_id() is something that looks at the last
 
192
            # commit on the branch, and the get_root_id is an inventory check.
 
193
            raise NotImplementedError
 
194
            # return Inventory(self.get_root_id())
 
195
        else:
 
196
            return self.get_inventory(revision_id)
 
197
 
 
198
    def revision_tree(self, revision_id):
 
199
        """Return Tree for a revision on this branch.
 
200
 
 
201
        `revision_id` may be None for the null revision, in which case
 
202
        an `EmptyTree` is returned."""
 
203
        # TODO: refactor this to use an existing revision object
 
204
        # so we don't need to read it in twice.
 
205
        if revision_id == None or revision_id == NULL_REVISION:
 
206
            return EmptyTree()
 
207
        else:
 
208
            inv = self.get_revision_inventory(revision_id)
 
209
            return RevisionTree(self.weave_store, inv, revision_id)
 
210
 
 
211
    def get_ancestry(self, revision_id):
 
212
        """Return a list of revision-ids integrated by a revision.
 
213
        
 
214
        This is topologically sorted.
 
215
        """
 
216
        if revision_id is None:
 
217
            return [None]
 
218
        w = self.get_inventory_weave()
 
219
        return [None] + map(w.idx_to_name,
 
220
                            w.inclusions([w.lookup(revision_id)]))
 
221
 
 
222
    @needs_read_lock
 
223
    def print_file(self, file, revision_id):
 
224
        """Print `file` to stdout."""
 
225
        tree = self.revision_tree(revision_id)
 
226
        # use inventory as it was in that revision
 
227
        file_id = tree.inventory.path2id(file)
 
228
        if not file_id:
 
229
            raise BzrError("%r is not present in revision %s" % (file, revno))
 
230
            try:
 
231
                revno = self.revision_id_to_revno(revision_id)
 
232
            except errors.NoSuchRevision:
 
233
                # TODO: This should not be BzrError,
 
234
                # but NoSuchFile doesn't fit either
 
235
                raise BzrError('%r is not present in revision %s' 
 
236
                                % (file, revision_id))
 
237
            else:
 
238
                raise BzrError('%r is not present in revision %s'
 
239
                                % (file, revno))
 
240
        tree.print_file(file_id)
 
241
 
 
242
    def get_transaction(self):
 
243
        return self.control_files.get_transaction()
 
244
 
 
245
    def sign_revision(self, revision_id, gpg_strategy):
 
246
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
 
247
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)