/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: mernst at mit
  • Date: 2008-10-16 10:57:16 UTC
  • mto: This revision was merged to the branch mainline in revision 3799.
  • Revision ID: mernst@csail.mit.edu-20081016105716-v8x8n5t2pf7f6uds
Improved documentation of stacked and lightweight branches

These patches improve the User Guide's documentation of stacked and
lightweight branches.

Section "1.2.6 Putting the concepts together" should mention stacked
branches and the difference between them and lightweight branches.  It
should also contain links to further details of the common scenarios.

Section "5.3.4 Getting a lightweight checkout" should mention stacked
branches as an option, and should link to all the options, not just some of
them.  It should also clarify that lightweight only applies to checkouts,
not to arbitrary branches.

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