~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugins/weave_fmt/workingtree.py

Merge clean-up-lazy-hooks branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2010 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
"""Weave-era working tree objects."""
 
18
 
 
19
from cStringIO import StringIO
 
20
 
 
21
from bzrlib import (
 
22
    conflicts as _mod_conflicts,
 
23
    errors,
 
24
    inventory,
 
25
    osutils,
 
26
    revision as _mod_revision,
 
27
    transform,
 
28
    xml5,
 
29
    )
 
30
from bzrlib.decorators import needs_read_lock
 
31
from bzrlib.transport.local import LocalTransport
 
32
from bzrlib.workingtree import (
 
33
    WorkingTreeFormat,
 
34
    WorkingTree,
 
35
    )
 
36
 
 
37
 
 
38
def get_conflicted_stem(path):
 
39
    for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
 
40
        if path.endswith(suffix):
 
41
            return path[:-len(suffix)]
 
42
 
 
43
 
 
44
class WorkingTreeFormat2(WorkingTreeFormat):
 
45
    """The second working tree format.
 
46
 
 
47
    This format modified the hash cache from the format 1 hash cache.
 
48
    """
 
49
 
 
50
    upgrade_recommended = True
 
51
 
 
52
    requires_normalized_unicode_filenames = True
 
53
 
 
54
    case_sensitive_filename = "Branch-FoRMaT"
 
55
 
 
56
    missing_parent_conflicts = False
 
57
 
 
58
    def get_format_description(self):
 
59
        """See WorkingTreeFormat.get_format_description()."""
 
60
        return "Working tree format 2"
 
61
 
 
62
    def _stub_initialize_on_transport(self, transport, file_mode):
 
63
        """Workaround: create control files for a remote working tree.
 
64
 
 
65
        This ensures that it can later be updated and dealt with locally,
 
66
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
 
67
        no working tree.  (See bug #43064).
 
68
        """
 
69
        sio = StringIO()
 
70
        inv = inventory.Inventory()
 
71
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
 
72
        sio.seek(0)
 
73
        transport.put_file('inventory', sio, file_mode)
 
74
        transport.put_bytes('pending-merges', '', file_mode)
 
75
 
 
76
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
77
                   accelerator_tree=None, hardlink=False):
 
78
        """See WorkingTreeFormat.initialize()."""
 
79
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
80
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
81
        if from_branch is not None:
 
82
            branch = from_branch
 
83
        else:
 
84
            branch = a_bzrdir.open_branch()
 
85
        if revision_id is None:
 
86
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
87
        branch.lock_write()
 
88
        try:
 
89
            branch.generate_revision_history(revision_id)
 
90
        finally:
 
91
            branch.unlock()
 
92
        inv = inventory.Inventory()
 
93
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
 
94
                         branch,
 
95
                         inv,
 
96
                         _internal=True,
 
97
                         _format=self,
 
98
                         _bzrdir=a_bzrdir,
 
99
                         _control_files=branch.control_files)
 
100
        basis_tree = branch.repository.revision_tree(revision_id)
 
101
        if basis_tree.inventory.root is not None:
 
102
            wt.set_root_id(basis_tree.get_root_id())
 
103
        # set the parent list and cache the basis tree.
 
104
        if _mod_revision.is_null(revision_id):
 
105
            parent_trees = []
 
106
        else:
 
107
            parent_trees = [(revision_id, basis_tree)]
 
108
        wt.set_parent_trees(parent_trees)
 
109
        transform.build_tree(basis_tree, wt)
 
110
        return wt
 
111
 
 
112
    def __init__(self):
 
113
        super(WorkingTreeFormat2, self).__init__()
 
114
        from bzrlib.plugins.weave_fmt.bzrdir import BzrDirFormat6
 
115
        self._matchingbzrdir = BzrDirFormat6()
 
116
 
 
117
    def open(self, a_bzrdir, _found=False):
 
118
        """Return the WorkingTree object for a_bzrdir
 
119
 
 
120
        _found is a private parameter, do not use it. It is used to indicate
 
121
               if format probing has already been done.
 
122
        """
 
123
        if not _found:
 
124
            # we are being called directly and must probe.
 
125
            raise NotImplementedError
 
126
        if not isinstance(a_bzrdir.transport, LocalTransport):
 
127
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
 
128
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
 
129
                           _internal=True,
 
130
                           _format=self,
 
131
                           _bzrdir=a_bzrdir,
 
132
                           _control_files=a_bzrdir.open_branch().control_files)
 
133
        return wt
 
134
 
 
135
 
 
136
class WorkingTree2(WorkingTree):
 
137
    """This is the Format 2 working tree.
 
138
 
 
139
    This was the first weave based working tree.
 
140
     - uses os locks for locking.
 
141
     - uses the branch last-revision.
 
142
    """
 
143
 
 
144
    def __init__(self, *args, **kwargs):
 
145
        super(WorkingTree2, self).__init__(*args, **kwargs)
 
146
        # WorkingTree2 has more of a constraint that self._inventory must
 
147
        # exist. Because this is an older format, we don't mind the overhead
 
148
        # caused by the extra computation here.
 
149
 
 
150
        # Newer WorkingTree's should only have self._inventory set when they
 
151
        # have a read lock.
 
152
        if self._inventory is None:
 
153
            self.read_working_inventory()
 
154
 
 
155
    def _get_check_refs(self):
 
156
        """Return the references needed to perform a check of this tree."""
 
157
        return [('trees', self.last_revision())]
 
158
 
 
159
    def lock_tree_write(self):
 
160
        """See WorkingTree.lock_tree_write().
 
161
 
 
162
        In Format2 WorkingTrees we have a single lock for the branch and tree
 
163
        so lock_tree_write() degrades to lock_write().
 
164
 
 
165
        :return: An object with an unlock method which will release the lock
 
166
            obtained.
 
167
        """
 
168
        self.branch.lock_write()
 
169
        try:
 
170
            self._control_files.lock_write()
 
171
            return self
 
172
        except:
 
173
            self.branch.unlock()
 
174
            raise
 
175
 
 
176
    def unlock(self):
 
177
        # do non-implementation specific cleanup
 
178
        self._cleanup()
 
179
 
 
180
        # we share control files:
 
181
        if self._control_files._lock_count == 3:
 
182
            # _inventory_is_modified is always False during a read lock.
 
183
            if self._inventory_is_modified:
 
184
                self.flush()
 
185
            self._write_hashcache_if_dirty()
 
186
 
 
187
        # reverse order of locking.
 
188
        try:
 
189
            return self._control_files.unlock()
 
190
        finally:
 
191
            self.branch.unlock()
 
192
 
 
193
    def _iter_conflicts(self):
 
194
        conflicted = set()
 
195
        for info in self.list_files():
 
196
            path = info[0]
 
197
            stem = get_conflicted_stem(path)
 
198
            if stem is None:
 
199
                continue
 
200
            if stem not in conflicted:
 
201
                conflicted.add(stem)
 
202
                yield stem
 
203
 
 
204
    @needs_read_lock
 
205
    def conflicts(self):
 
206
        conflicts = _mod_conflicts.ConflictList()
 
207
        for conflicted in self._iter_conflicts():
 
208
            text = True
 
209
            try:
 
210
                if osutils.file_kind(self.abspath(conflicted)) != "file":
 
211
                    text = False
 
212
            except errors.NoSuchFile:
 
213
                text = False
 
214
            if text is True:
 
215
                for suffix in ('.THIS', '.OTHER'):
 
216
                    try:
 
217
                        kind = osutils.file_kind(self.abspath(conflicted+suffix))
 
218
                        if kind != "file":
 
219
                            text = False
 
220
                    except errors.NoSuchFile:
 
221
                        text = False
 
222
                    if text == False:
 
223
                        break
 
224
            ctype = {True: 'text conflict', False: 'contents conflict'}[text]
 
225
            conflicts.append(_mod_conflicts.Conflict.factory(ctype,
 
226
                             path=conflicted,
 
227
                             file_id=self.path2id(conflicted)))
 
228
        return conflicts
 
229
 
 
230