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

  • Committer: John Arbash Meinel
  • Date: 2010-01-12 22:51:31 UTC
  • mto: This revision was merged to the branch mainline in revision 4955.
  • Revision ID: john@arbash-meinel.com-20100112225131-he8h411p6aeeb947
Delay grabbing an output stream until we actually go to show a diff.

This makes the test suite happy, but it also seems to be reasonable.
If we aren't going to write anything, we don't need to hold an
output stream open.

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