~bzr-pqm/bzr/bzr.dev

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