~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/memorytree.py

Turn completion assertions into separate methods.

Many common assertions used to be expressed as arguments to the complete
method.  This makes the checks more explicit, and the code easier to read.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""MemoryTree object.
18
 
 
19
 
See MemoryTree for more details.
20
 
"""
21
 
 
22
 
 
23
 
import os
24
 
 
25
 
from bzrlib import (
26
 
    errors,
27
 
    mutabletree,
28
 
    revision as _mod_revision,
29
 
    )
30
 
from bzrlib.decorators import needs_read_lock
31
 
from bzrlib.inventory import Inventory
32
 
from bzrlib.osutils import sha_file
33
 
from bzrlib.mutabletree import needs_tree_write_lock
34
 
from bzrlib.transport.memory import MemoryTransport
35
 
 
36
 
 
37
 
class MemoryTree(mutabletree.MutableInventoryTree):
38
 
    """A MemoryTree is a specialisation of MutableTree.
39
 
 
40
 
    It maintains nearly no state outside of read_lock and write_lock
41
 
    transactions. (it keeps a reference to the branch, and its last-revision
42
 
    only).
43
 
    """
44
 
 
45
 
    def __init__(self, branch, revision_id):
46
 
        """Construct a MemoryTree for branch using revision_id."""
47
 
        self.branch = branch
48
 
        self.bzrdir = branch.bzrdir
49
 
        self._branch_revision_id = revision_id
50
 
        self._locks = 0
51
 
        self._lock_mode = None
52
 
 
53
 
    def is_control_filename(self, filename):
54
 
        # Memory tree doesn't have any control filenames
55
 
        return False
56
 
 
57
 
    @needs_tree_write_lock
58
 
    def _add(self, files, ids, kinds):
59
 
        """See MutableTree._add."""
60
 
        for f, file_id, kind in zip(files, ids, kinds):
61
 
            if kind is None:
62
 
                kind = 'file'
63
 
            if file_id is None:
64
 
                self._inventory.add_path(f, kind=kind)
65
 
            else:
66
 
                self._inventory.add_path(f, kind=kind, file_id=file_id)
67
 
 
68
 
    def basis_tree(self):
69
 
        """See Tree.basis_tree()."""
70
 
        return self._basis_tree
71
 
 
72
 
    @staticmethod
73
 
    def create_on_branch(branch):
74
 
        """Create a MemoryTree for branch, using the last-revision of branch."""
75
 
        revision_id = _mod_revision.ensure_null(branch.last_revision())
76
 
        return MemoryTree(branch, revision_id)
77
 
 
78
 
    def _gather_kinds(self, files, kinds):
79
 
        """See MutableTree._gather_kinds.
80
 
 
81
 
        This implementation does not care about the file kind of
82
 
        missing files, so is a no-op.
83
 
        """
84
 
 
85
 
    def get_file(self, file_id, path=None):
86
 
        """See Tree.get_file."""
87
 
        if path is None:
88
 
            path = self.id2path(file_id)
89
 
        return self._file_transport.get(path)
90
 
 
91
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
92
 
        """See Tree.get_file_sha1()."""
93
 
        if path is None:
94
 
            path = self.id2path(file_id)
95
 
        stream = self._file_transport.get(path)
96
 
        return sha_file(stream)
97
 
 
98
 
    def get_root_id(self):
99
 
        return self.path2id('')
100
 
 
101
 
    def _comparison_data(self, entry, path):
102
 
        """See Tree._comparison_data."""
103
 
        if entry is None:
104
 
            return None, False, None
105
 
        return entry.kind, entry.executable, None
106
 
 
107
 
    @needs_tree_write_lock
108
 
    def rename_one(self, from_rel, to_rel):
109
 
        file_id = self.path2id(from_rel)
110
 
        to_dir, to_tail = os.path.split(to_rel)
111
 
        to_parent_id = self.path2id(to_dir)
112
 
        self._file_transport.move(from_rel, to_rel)
113
 
        self._inventory.rename(file_id, to_parent_id, to_tail)
114
 
 
115
 
    def path_content_summary(self, path):
116
 
        """See Tree.path_content_summary."""
117
 
        id = self.path2id(path)
118
 
        if id is None:
119
 
            return 'missing', None, None, None
120
 
        kind = self.kind(id)
121
 
        if kind == 'file':
122
 
            bytes = self._file_transport.get_bytes(path)
123
 
            size = len(bytes)
124
 
            executable = self._inventory[id].executable
125
 
            sha1 = None # no stat cache
126
 
            return (kind, size, executable, sha1)
127
 
        elif kind == 'directory':
128
 
            # memory tree does not support nested trees yet.
129
 
            return kind, None, None, None
130
 
        elif kind == 'symlink':
131
 
            raise NotImplementedError('symlink support')
132
 
        else:
133
 
            raise NotImplementedError('unknown kind')
134
 
 
135
 
    def _file_size(self, entry, stat_value):
136
 
        """See Tree._file_size."""
137
 
        if entry is None:
138
 
            return 0
139
 
        return entry.text_size
140
 
 
141
 
    @needs_read_lock
142
 
    def get_parent_ids(self):
143
 
        """See Tree.get_parent_ids.
144
 
 
145
 
        This implementation returns the current cached value from
146
 
            self._parent_ids.
147
 
        """
148
 
        return list(self._parent_ids)
149
 
 
150
 
    def has_filename(self, filename):
151
 
        """See Tree.has_filename()."""
152
 
        return self._file_transport.has(filename)
153
 
 
154
 
    def is_executable(self, file_id, path=None):
155
 
        return self._inventory[file_id].executable
156
 
 
157
 
    def kind(self, file_id):
158
 
        return self._inventory[file_id].kind
159
 
 
160
 
    def mkdir(self, path, file_id=None):
161
 
        """See MutableTree.mkdir()."""
162
 
        self.add(path, file_id, 'directory')
163
 
        if file_id is None:
164
 
            file_id = self.path2id(path)
165
 
        self._file_transport.mkdir(path)
166
 
        return file_id
167
 
 
168
 
    @needs_read_lock
169
 
    def last_revision(self):
170
 
        """See MutableTree.last_revision."""
171
 
        return self._branch_revision_id
172
 
 
173
 
    def lock_read(self):
174
 
        """Lock the memory tree for reading.
175
 
 
176
 
        This triggers population of data from the branch for its revision.
177
 
        """
178
 
        self._locks += 1
179
 
        try:
180
 
            if self._locks == 1:
181
 
                self.branch.lock_read()
182
 
                self._lock_mode = "r"
183
 
                self._populate_from_branch()
184
 
        except:
185
 
            self._locks -= 1
186
 
            raise
187
 
 
188
 
    def lock_tree_write(self):
189
 
        """See MutableTree.lock_tree_write()."""
190
 
        self._locks += 1
191
 
        try:
192
 
            if self._locks == 1:
193
 
                self.branch.lock_read()
194
 
                self._lock_mode = "w"
195
 
                self._populate_from_branch()
196
 
            elif self._lock_mode == "r":
197
 
                raise errors.ReadOnlyError(self)
198
 
        except:
199
 
            self._locks -= 1
200
 
            raise
201
 
 
202
 
    def lock_write(self):
203
 
        """See MutableTree.lock_write()."""
204
 
        self._locks += 1
205
 
        try:
206
 
            if self._locks == 1:
207
 
                self.branch.lock_write()
208
 
                self._lock_mode = "w"
209
 
                self._populate_from_branch()
210
 
            elif self._lock_mode == "r":
211
 
                raise errors.ReadOnlyError(self)
212
 
        except:
213
 
            self._locks -= 1
214
 
            raise
215
 
 
216
 
    def _populate_from_branch(self):
217
 
        """Populate the in-tree state from the branch."""
218
 
        self._set_basis()
219
 
        if self._branch_revision_id == _mod_revision.NULL_REVISION:
220
 
            self._parent_ids = []
221
 
        else:
222
 
            self._parent_ids = [self._branch_revision_id]
223
 
        self._inventory = Inventory(None, self._basis_tree.get_revision_id())
224
 
        self._file_transport = MemoryTransport()
225
 
        # TODO copy the revision trees content, or do it lazy, or something.
226
 
        inventory_entries = self._basis_tree.iter_entries_by_dir()
227
 
        for path, entry in inventory_entries:
228
 
            self._inventory.add(entry.copy())
229
 
            if path == '':
230
 
                continue
231
 
            if entry.kind == 'directory':
232
 
                self._file_transport.mkdir(path)
233
 
            elif entry.kind == 'file':
234
 
                self._file_transport.put_file(path,
235
 
                    self._basis_tree.get_file(entry.file_id))
236
 
            else:
237
 
                raise NotImplementedError(self._populate_from_branch)
238
 
 
239
 
    def put_file_bytes_non_atomic(self, file_id, bytes):
240
 
        """See MutableTree.put_file_bytes_non_atomic."""
241
 
        self._file_transport.put_bytes(self.id2path(file_id), bytes)
242
 
 
243
 
    def unlock(self):
244
 
        """Release a lock.
245
 
 
246
 
        This frees all cached state when the last lock context for the tree is
247
 
        left.
248
 
        """
249
 
        if self._locks == 1:
250
 
            self._basis_tree = None
251
 
            self._parent_ids = []
252
 
            self._inventory = None
253
 
            try:
254
 
                self.branch.unlock()
255
 
            finally:
256
 
                self._locks = 0
257
 
                self._lock_mode = None
258
 
        else:
259
 
            self._locks -= 1
260
 
 
261
 
    @needs_tree_write_lock
262
 
    def unversion(self, file_ids):
263
 
        """Remove the file ids in file_ids from the current versioned set.
264
 
 
265
 
        When a file_id is unversioned, all of its children are automatically
266
 
        unversioned.
267
 
 
268
 
        :param file_ids: The file ids to stop versioning.
269
 
        :raises: NoSuchId if any fileid is not currently versioned.
270
 
        """
271
 
        # XXX: This should be in mutabletree, but the inventory-save action
272
 
        # is not relevant to memory tree. Until that is done in unlock by
273
 
        # working tree, we cannot share the implementation.
274
 
        for file_id in file_ids:
275
 
            if self._inventory.has_id(file_id):
276
 
                self._inventory.remove_recursive_id(file_id)
277
 
            else:
278
 
                raise errors.NoSuchId(self, file_id)
279
 
 
280
 
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
281
 
        """See MutableTree.set_parent_trees()."""
282
 
        for revision_id in revision_ids:
283
 
            _mod_revision.check_not_reserved_id(revision_id)
284
 
        if len(revision_ids) == 0:
285
 
            self._parent_ids = []
286
 
            self._branch_revision_id = _mod_revision.NULL_REVISION
287
 
        else:
288
 
            self._parent_ids = revision_ids
289
 
            self._branch_revision_id = revision_ids[0]
290
 
        self._allow_leftmost_as_ghost = allow_leftmost_as_ghost
291
 
        self._set_basis()
292
 
    
293
 
    def _set_basis(self):
294
 
        try:
295
 
            self._basis_tree = self.branch.repository.revision_tree(
296
 
                self._branch_revision_id)
297
 
        except errors.NoSuchRevision:
298
 
            if self._allow_leftmost_as_ghost:
299
 
                self._basis_tree = self.branch.repository.revision_tree(
300
 
                    _mod_revision.NULL_REVISION)
301
 
            else:
302
 
                raise
303
 
 
304
 
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
305
 
        """See MutableTree.set_parent_trees()."""
306
 
        if len(parents_list) == 0:
307
 
            self._parent_ids = []
308
 
            self._basis_tree = self.branch.repository.revision_tree(
309
 
                                   _mod_revision.NULL_REVISION)
310
 
        else:
311
 
            if parents_list[0][1] is None and not allow_leftmost_as_ghost:
312
 
                # a ghost in the left most parent
313
 
                raise errors.GhostRevisionUnusableHere(parents_list[0][0])
314
 
            self._parent_ids = [parent_id for parent_id, tree in parents_list]
315
 
            if parents_list[0][1] is None or parents_list[0][1] == 'null:':
316
 
                self._basis_tree = self.branch.repository.revision_tree(
317
 
                                       _mod_revision.NULL_REVISION)
318
 
            else:
319
 
                self._basis_tree = parents_list[0][1]
320
 
            self._branch_revision_id = parents_list[0][0]