~bzr-pqm/bzr/bzr.dev

5697.1.1 by Jelmer Vernooij
Move WorkingTreeFormat2 to a separate file.
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 (
5777.5.2 by Jelmer Vernooij
Split inventory-specific methods and WorkingTree interface method
33
    InventoryWorkingTree,
5697.1.1 by Jelmer Vernooij
Move WorkingTreeFormat2 to a separate file.
34
    WorkingTreeFormat,
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__()
5582.12.6 by Jelmer Vernooij
merge bzr.dev.
114
        from bzrlib.plugins.weave_fmt.bzrdir import BzrDirFormat6
5697.1.1 by Jelmer Vernooij
Move WorkingTreeFormat2 to a separate file.
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
5777.5.2 by Jelmer Vernooij
Split inventory-specific methods and WorkingTree interface method
136
class WorkingTree2(InventoryWorkingTree):
5697.1.1 by Jelmer Vernooij
Move WorkingTreeFormat2 to a separate file.
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