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