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

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2007-2011 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
"""WorkingTree3 format and implementation.
 
18
 
 
19
"""
 
20
 
 
21
from __future__ import absolute_import
 
22
 
 
23
import errno
 
24
 
 
25
from bzrlib import (
 
26
    bzrdir,
 
27
    errors,
 
28
    hashcache,
 
29
    inventory,
 
30
    revision as _mod_revision,
 
31
    trace,
 
32
    transform,
 
33
    )
 
34
from bzrlib.decorators import (
 
35
    needs_read_lock,
 
36
    )
 
37
from bzrlib.lockable_files import LockableFiles
 
38
from bzrlib.lockdir import LockDir
 
39
from bzrlib.transport.local import LocalTransport
 
40
from bzrlib.workingtree import (
 
41
    InventoryWorkingTree,
 
42
    WorkingTreeFormatMetaDir,
 
43
    )
 
44
 
 
45
 
 
46
class PreDirStateWorkingTree(InventoryWorkingTree):
 
47
 
 
48
    def __init__(self, basedir='.', *args, **kwargs):
 
49
        super(PreDirStateWorkingTree, self).__init__(basedir, *args, **kwargs)
 
50
        # update the whole cache up front and write to disk if anything changed;
 
51
        # in the future we might want to do this more selectively
 
52
        # two possible ways offer themselves : in self._unlock, write the cache
 
53
        # if needed, or, when the cache sees a change, append it to the hash
 
54
        # cache file, and have the parser take the most recent entry for a
 
55
        # given path only.
 
56
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
57
        cache_filename = wt_trans.local_abspath('stat-cache')
 
58
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
 
59
            self.bzrdir._get_file_mode(),
 
60
            self._content_filter_stack_provider())
 
61
        hc = self._hashcache
 
62
        hc.read()
 
63
        # is this scan needed ? it makes things kinda slow.
 
64
        #hc.scan()
 
65
 
 
66
        if hc.needs_write:
 
67
            trace.mutter("write hc")
 
68
            hc.write()
 
69
 
 
70
    def _write_hashcache_if_dirty(self):
 
71
        """Write out the hashcache if it is dirty."""
 
72
        if self._hashcache.needs_write:
 
73
            try:
 
74
                self._hashcache.write()
 
75
            except OSError, e:
 
76
                if e.errno not in (errno.EPERM, errno.EACCES):
 
77
                    raise
 
78
                # TODO: jam 20061219 Should this be a warning? A single line
 
79
                #       warning might be sufficient to let the user know what
 
80
                #       is going on.
 
81
                trace.mutter('Could not write hashcache for %s\nError: %s',
 
82
                              self._hashcache.cache_file_name(), e)
 
83
 
 
84
    @needs_read_lock
 
85
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
86
        if not path:
 
87
            path = self._inventory.id2path(file_id)
 
88
        return self._hashcache.get_sha1(path, stat_value)
 
89
 
 
90
 
 
91
class WorkingTree3(PreDirStateWorkingTree):
 
92
    """This is the Format 3 working tree.
 
93
 
 
94
    This differs from the base WorkingTree by:
 
95
     - having its own file lock
 
96
     - having its own last-revision property.
 
97
 
 
98
    This is new in bzr 0.8
 
99
    """
 
100
 
 
101
    @needs_read_lock
 
102
    def _last_revision(self):
 
103
        """See Mutable.last_revision."""
 
104
        try:
 
105
            return self._transport.get_bytes('last-revision')
 
106
        except errors.NoSuchFile:
 
107
            return _mod_revision.NULL_REVISION
 
108
 
 
109
    def _change_last_revision(self, revision_id):
 
110
        """See WorkingTree._change_last_revision."""
 
111
        if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
 
112
            try:
 
113
                self._transport.delete('last-revision')
 
114
            except errors.NoSuchFile:
 
115
                pass
 
116
            return False
 
117
        else:
 
118
            self._transport.put_bytes('last-revision', revision_id,
 
119
                mode=self.bzrdir._get_file_mode())
 
120
            return True
 
121
 
 
122
    def _get_check_refs(self):
 
123
        """Return the references needed to perform a check of this tree."""
 
124
        return [('trees', self.last_revision())]
 
125
 
 
126
    def unlock(self):
 
127
        if self._control_files._lock_count == 1:
 
128
           # do non-implementation specific cleanup
 
129
            self._cleanup()
 
130
            # _inventory_is_modified is always False during a read lock.
 
131
            if self._inventory_is_modified:
 
132
                self.flush()
 
133
            self._write_hashcache_if_dirty()
 
134
        # reverse order of locking.
 
135
        try:
 
136
            return self._control_files.unlock()
 
137
        finally:
 
138
            self.branch.unlock()
 
139
 
 
140
 
 
141
class WorkingTreeFormat3(WorkingTreeFormatMetaDir):
 
142
    """The second working tree format updated to record a format marker.
 
143
 
 
144
    This format:
 
145
        - exists within a metadir controlling .bzr
 
146
        - includes an explicit version marker for the workingtree control
 
147
          files, separate from the ControlDir format
 
148
        - modifies the hash cache format
 
149
        - is new in bzr 0.8
 
150
        - uses a LockDir to guard access for writes.
 
151
    """
 
152
 
 
153
    upgrade_recommended = True
 
154
 
 
155
    missing_parent_conflicts = True
 
156
 
 
157
    supports_versioned_directories = True
 
158
 
 
159
    @classmethod
 
160
    def get_format_string(cls):
 
161
        """See WorkingTreeFormat.get_format_string()."""
 
162
        return "Bazaar-NG Working Tree format 3"
 
163
 
 
164
    def get_format_description(self):
 
165
        """See WorkingTreeFormat.get_format_description()."""
 
166
        return "Working tree format 3"
 
167
 
 
168
    _tree_class = WorkingTree3
 
169
 
 
170
    def __get_matchingbzrdir(self):
 
171
        return bzrdir.BzrDirMetaFormat1()
 
172
 
 
173
    _matchingbzrdir = property(__get_matchingbzrdir)
 
174
 
 
175
    def _open_control_files(self, a_bzrdir):
 
176
        transport = a_bzrdir.get_workingtree_transport(None)
 
177
        return LockableFiles(transport, 'lock', LockDir)
 
178
 
 
179
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
180
                   accelerator_tree=None, hardlink=False):
 
181
        """See WorkingTreeFormat.initialize().
 
182
 
 
183
        :param revision_id: if supplied, create a working tree at a different
 
184
            revision than the branch is at.
 
185
        :param accelerator_tree: A tree which can be used for retrieving file
 
186
            contents more quickly than the revision tree, i.e. a workingtree.
 
187
            The revision tree will be used for cases where accelerator_tree's
 
188
            content is different.
 
189
        :param hardlink: If true, hard-link files from accelerator_tree,
 
190
            where possible.
 
191
        """
 
192
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
193
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
194
        transport = a_bzrdir.get_workingtree_transport(self)
 
195
        control_files = self._open_control_files(a_bzrdir)
 
196
        control_files.create_lock()
 
197
        control_files.lock_write()
 
198
        transport.put_bytes('format', self.as_string(),
 
199
            mode=a_bzrdir._get_file_mode())
 
200
        if from_branch is not None:
 
201
            branch = from_branch
 
202
        else:
 
203
            branch = a_bzrdir.open_branch()
 
204
        if revision_id is None:
 
205
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
206
        # WorkingTree3 can handle an inventory which has a unique root id.
 
207
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
 
208
        # those trees. And because there isn't a format bump inbetween, we
 
209
        # are maintaining compatibility with older clients.
 
210
        # inv = Inventory(root_id=gen_root_id())
 
211
        inv = self._initial_inventory()
 
212
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
213
                         branch,
 
214
                         inv,
 
215
                         _internal=True,
 
216
                         _format=self,
 
217
                         _bzrdir=a_bzrdir,
 
218
                         _control_files=control_files)
 
219
        wt.lock_tree_write()
 
220
        try:
 
221
            basis_tree = branch.repository.revision_tree(revision_id)
 
222
            # only set an explicit root id if there is one to set.
 
223
            if basis_tree.inventory.root is not None:
 
224
                wt.set_root_id(basis_tree.get_root_id())
 
225
            if revision_id == _mod_revision.NULL_REVISION:
 
226
                wt.set_parent_trees([])
 
227
            else:
 
228
                wt.set_parent_trees([(revision_id, basis_tree)])
 
229
            transform.build_tree(basis_tree, wt)
 
230
        finally:
 
231
            # Unlock in this order so that the unlock-triggers-flush in
 
232
            # WorkingTree is given a chance to fire.
 
233
            control_files.unlock()
 
234
            wt.unlock()
 
235
        return wt
 
236
 
 
237
    def _initial_inventory(self):
 
238
        return inventory.Inventory()
 
239
 
 
240
    def open(self, a_bzrdir, _found=False):
 
241
        """Return the WorkingTree object for a_bzrdir
 
242
 
 
243
        _found is a private parameter, do not use it. It is used to indicate
 
244
               if format probing has already been done.
 
245
        """
 
246
        if not _found:
 
247
            # we are being called directly and must probe.
 
248
            raise NotImplementedError
 
249
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
250
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
251
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
 
252
        return wt
 
253
 
 
254
    def _open(self, a_bzrdir, control_files):
 
255
        """Open the tree itself.
 
256
 
 
257
        :param a_bzrdir: the dir for the tree.
 
258
        :param control_files: the control files for the tree.
 
259
        """
 
260
        return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
 
261
                                _internal=True,
 
262
                                _format=self,
 
263
                                _bzrdir=a_bzrdir,
 
264
                                _control_files=control_files)