~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
WorkingTree.open(dir).
30
30
"""
31
31
 
32
 
from __future__ import absolute_import
33
32
 
34
33
from cStringIO import StringIO
35
34
import os
47
46
 
48
47
from bzrlib import (
49
48
    branch,
 
49
    bzrdir,
50
50
    conflicts as _mod_conflicts,
51
51
    controldir,
52
52
    errors,
60
60
    revision as _mod_revision,
61
61
    revisiontree,
62
62
    rio as _mod_rio,
63
 
    shelf,
64
63
    transform,
65
64
    transport,
66
65
    ui,
70
69
    )
71
70
""")
72
71
 
73
 
# Explicitly import bzrlib.bzrdir so that the BzrProber
74
 
# is guaranteed to be registered.
75
 
from bzrlib import (
76
 
    bzrdir,
77
 
    symbol_versioning,
78
 
    )
79
 
 
 
72
from bzrlib import symbol_versioning
80
73
from bzrlib.decorators import needs_read_lock, needs_write_lock
81
74
from bzrlib.i18n import gettext
82
75
from bzrlib.lock import LogicalLockResult
91
84
    realpath,
92
85
    safe_unicode,
93
86
    splitpath,
 
87
    supports_executable,
94
88
    )
95
89
from bzrlib.trace import mutter, note
96
90
from bzrlib.revision import CURRENT_REVISION
178
172
 
179
173
    def __init__(self, basedir='.',
180
174
                 branch=DEPRECATED_PARAMETER,
 
175
                 _control_files=None,
181
176
                 _internal=False,
182
 
                 _transport=None,
183
177
                 _format=None,
184
178
                 _bzrdir=None):
185
179
        """Construct a WorkingTree instance. This is not a public API.
198
192
        else:
199
193
            self._branch = self.bzrdir.open_branch()
200
194
        self.basedir = realpath(basedir)
201
 
        self._transport = _transport
 
195
        self._control_files = _control_files
 
196
        self._transport = self._control_files._transport
202
197
        self._rules_searcher = None
203
198
        self.views = self._make_views()
204
199
 
234
229
        """See `Tree.has_versioned_directories`."""
235
230
        return self._format.supports_versioned_directories
236
231
 
237
 
    def _supports_executable(self):
238
 
        if sys.platform == 'win32':
239
 
            return False
240
 
        # FIXME: Ideally this should check the file system
241
 
        return True
242
 
 
243
232
    def break_lock(self):
244
233
        """Break a lock if one is present from another instance.
245
234
 
248
237
 
249
238
        This will probe the repository for its lock as well.
250
239
        """
251
 
        raise NotImplementedError(self.break_lock)
 
240
        self._control_files.break_lock()
 
241
        self.branch.break_lock()
252
242
 
253
243
    def requires_rich_root(self):
254
244
        return self._format.requires_rich_root
262
252
    def supports_views(self):
263
253
        return self.views.supports_views()
264
254
 
265
 
    def get_config_stack(self):
266
 
        """Retrieve the config stack for this tree.
267
 
 
268
 
        :return: A ``bzrlib.config.Stack``
269
 
        """
270
 
        # For the moment, just provide the branch config stack.
271
 
        return self.branch.get_config_stack()
272
 
 
273
255
    @staticmethod
274
256
    def open(path=None, _unsupported=False):
275
257
        """Open an existing working tree at path.
277
259
        """
278
260
        if path is None:
279
261
            path = osutils.getcwd()
280
 
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
281
 
        return control.open_workingtree(unsupported=_unsupported)
 
262
        control = bzrdir.BzrDir.open(path, _unsupported)
 
263
        return control.open_workingtree(_unsupported)
282
264
 
283
265
    @staticmethod
284
266
    def open_containing(path=None):
295
277
        """
296
278
        if path is None:
297
279
            path = osutils.getcwd()
298
 
        control, relpath = controldir.ControlDir.open_containing(path)
 
280
        control, relpath = bzrdir.BzrDir.open_containing(path)
299
281
        return control.open_workingtree(), relpath
300
282
 
301
283
    @staticmethod
384
366
            else:
385
367
                return True, tree
386
368
        t = transport.get_transport(location)
387
 
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
 
369
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
388
370
                                              list_current=list_current)
389
371
        return [tr for tr in iterator if tr is not None]
390
372
 
 
373
    def all_file_ids(self):
 
374
        """See Tree.iter_all_file_ids"""
 
375
        raise NotImplementedError(self.all_file_ids)
 
376
 
391
377
    def __repr__(self):
392
378
        return "<%s of %s>" % (self.__class__.__name__,
393
379
                               getattr(self, 'basedir', None))
508
494
        raise NotImplementedError(self.get_root_id)
509
495
 
510
496
    @needs_read_lock
511
 
    def clone(self, to_controldir, revision_id=None):
 
497
    def clone(self, to_bzrdir, revision_id=None):
512
498
        """Duplicate this working tree into to_bzr, including all state.
513
499
 
514
500
        Specifically modified files are kept as modified, but
515
501
        ignored and unknown files are discarded.
516
502
 
517
 
        If you want to make a new line of development, see ControlDir.sprout()
 
503
        If you want to make a new line of development, see bzrdir.sprout()
518
504
 
519
505
        revision
520
506
            If not None, the cloned tree will have its last revision set to
522
508
            and this one merged in.
523
509
        """
524
510
        # assumes the target bzr dir format is compatible.
525
 
        result = to_controldir.create_workingtree()
 
511
        result = to_bzrdir.create_workingtree()
526
512
        self.copy_content_into(result, revision_id)
527
513
        return result
528
514
 
535
521
        else:
536
522
            # TODO now merge from tree.last_revision to revision (to preserve
537
523
            # user local changes)
538
 
            try:
539
 
                other_tree = self.revision_tree(revision_id)
540
 
            except errors.NoSuchRevision:
541
 
                other_tree = self.branch.repository.revision_tree(revision_id)
542
 
 
543
 
            merge.transform_tree(tree, other_tree)
544
 
            if revision_id == _mod_revision.NULL_REVISION:
545
 
                new_parents = []
546
 
            else:
547
 
                new_parents = [revision_id]
548
 
            tree.set_parent_ids(new_parents)
 
524
            merge.transform_tree(tree, self)
 
525
            tree.set_parent_ids([revision_id])
549
526
 
550
527
    def id2abspath(self, file_id):
551
528
        return self.abspath(self.id2path(file_id))
760
737
 
761
738
    @needs_tree_write_lock
762
739
    def set_merge_modified(self, modified_hashes):
763
 
        """Set the merge modified hashes."""
764
 
        raise NotImplementedError(self.set_merge_modified)
 
740
        def iter_stanzas():
 
741
            for file_id, hash in modified_hashes.iteritems():
 
742
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
 
743
                    hash=hash)
 
744
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
765
745
 
766
746
    def _sha_from_stat(self, path, stat_result):
767
747
        """Get a sha digest from the tree's stat cache.
773
753
        """
774
754
        return None
775
755
 
 
756
    def _put_rio(self, filename, stanzas, header):
 
757
        self._must_be_locked()
 
758
        my_file = _mod_rio.rio_file(stanzas, header)
 
759
        self._transport.put_file(filename, my_file,
 
760
            mode=self.bzrdir._get_file_mode())
 
761
 
776
762
    @needs_write_lock # because merge pulls data into the branch.
777
763
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
778
764
                          merge_type=None, force=False):
1018
1004
                                show_base=show_base)
1019
1005
                    basis_root_id = basis_tree.get_root_id()
1020
1006
                    new_root_id = new_basis_tree.get_root_id()
1021
 
                    if new_root_id is not None and basis_root_id != new_root_id:
 
1007
                    if basis_root_id != new_root_id:
1022
1008
                        self.set_root_id(new_root_id)
1023
1009
                finally:
1024
1010
                    basis_tree.unlock()
1025
1011
                # TODO - dedup parents list with things merged by pull ?
1026
1012
                # reuse the revisiontree we merged against to set the new
1027
1013
                # tree data.
1028
 
                parent_trees = []
1029
 
                if self.branch.last_revision() != _mod_revision.NULL_REVISION:
1030
 
                    parent_trees.append(
1031
 
                        (self.branch.last_revision(), new_basis_tree))
 
1014
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1032
1015
                # we have to pull the merge trees out again, because
1033
1016
                # merge_inner has set the ids. - this corner is not yet
1034
1017
                # layered well enough to prevent double handling.
1133
1116
        else:
1134
1117
            mode = stat_value.st_mode
1135
1118
            kind = osutils.file_kind_from_stat_mode(mode)
1136
 
            if not self._supports_executable():
 
1119
            if not supports_executable():
1137
1120
                executable = entry is not None and entry.executable
1138
1121
            else:
1139
1122
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1158
1141
        return _mod_revision.ensure_null(self.branch.last_revision())
1159
1142
 
1160
1143
    def is_locked(self):
1161
 
        """Check if this tree is locked."""
1162
 
        raise NotImplementedError(self.is_locked)
 
1144
        return self._control_files.is_locked()
 
1145
 
 
1146
    def _must_be_locked(self):
 
1147
        if not self.is_locked():
 
1148
            raise errors.ObjectNotLocked(self)
1163
1149
 
1164
1150
    def lock_read(self):
1165
1151
        """Lock the tree for reading.
1168
1154
 
1169
1155
        :return: A bzrlib.lock.LogicalLockResult.
1170
1156
        """
1171
 
        raise NotImplementedError(self.lock_read)
 
1157
        if not self.is_locked():
 
1158
            self._reset_data()
 
1159
        self.branch.lock_read()
 
1160
        try:
 
1161
            self._control_files.lock_read()
 
1162
            return LogicalLockResult(self.unlock)
 
1163
        except:
 
1164
            self.branch.unlock()
 
1165
            raise
1172
1166
 
1173
1167
    def lock_tree_write(self):
1174
1168
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1175
1169
 
1176
1170
        :return: A bzrlib.lock.LogicalLockResult.
1177
1171
        """
1178
 
        raise NotImplementedError(self.lock_tree_write)
 
1172
        if not self.is_locked():
 
1173
            self._reset_data()
 
1174
        self.branch.lock_read()
 
1175
        try:
 
1176
            self._control_files.lock_write()
 
1177
            return LogicalLockResult(self.unlock)
 
1178
        except:
 
1179
            self.branch.unlock()
 
1180
            raise
1179
1181
 
1180
1182
    def lock_write(self):
1181
1183
        """See MutableTree.lock_write, and WorkingTree.unlock.
1182
1184
 
1183
1185
        :return: A bzrlib.lock.LogicalLockResult.
1184
1186
        """
1185
 
        raise NotImplementedError(self.lock_write)
 
1187
        if not self.is_locked():
 
1188
            self._reset_data()
 
1189
        self.branch.lock_write()
 
1190
        try:
 
1191
            self._control_files.lock_write()
 
1192
            return LogicalLockResult(self.unlock)
 
1193
        except:
 
1194
            self.branch.unlock()
 
1195
            raise
1186
1196
 
1187
1197
    def get_physical_lock_status(self):
1188
 
        raise NotImplementedError(self.get_physical_lock_status)
 
1198
        return self._control_files.get_physical_lock_status()
 
1199
 
 
1200
    def _reset_data(self):
 
1201
        """Reset transient data that cannot be revalidated."""
 
1202
        raise NotImplementedError(self._reset_data)
1189
1203
 
1190
1204
    def set_last_revision(self, new_revision):
1191
1205
        """Change the last revision in the working tree."""
1357
1371
                basis_tree.unlock()
1358
1372
        return conflicts
1359
1373
 
1360
 
    @needs_write_lock
1361
 
    def store_uncommitted(self):
1362
 
        """Store uncommitted changes from the tree in the branch."""
1363
 
        target_tree = self.basis_tree()
1364
 
        shelf_creator = shelf.ShelfCreator(self, target_tree)
1365
 
        try:
1366
 
            if not shelf_creator.shelve_all():
1367
 
                return
1368
 
            self.branch.store_uncommitted(shelf_creator)
1369
 
            shelf_creator.transform()
1370
 
        finally:
1371
 
            shelf_creator.finalize()
1372
 
        note('Uncommitted changes stored in branch "%s".', self.branch.nick)
1373
 
 
1374
 
    @needs_write_lock
1375
 
    def restore_uncommitted(self):
1376
 
        """Restore uncommitted changes from the branch into the tree."""
1377
 
        unshelver = self.branch.get_unshelver(self)
1378
 
        if unshelver is None:
1379
 
            return
1380
 
        try:
1381
 
            merger = unshelver.make_merger()
1382
 
            merger.ignore_zero = True
1383
 
            merger.do_merge()
1384
 
            self.branch.store_uncommitted(None)
1385
 
        finally:
1386
 
            unshelver.finalize()
1387
 
 
1388
1374
    def revision_tree(self, revision_id):
1389
1375
        """See Tree.revision_tree.
1390
1376
 
1795
1781
        :param branch: A branch to override probing for the branch.
1796
1782
        """
1797
1783
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1798
 
            branch=branch, _transport=_control_files._transport,
1799
 
            _internal=_internal, _format=_format, _bzrdir=_bzrdir)
 
1784
            branch=branch, _control_files=_control_files, _internal=_internal,
 
1785
            _format=_format, _bzrdir=_bzrdir)
1800
1786
 
1801
 
        self._control_files = _control_files
1802
1787
        self._detect_case_handling()
1803
1788
 
1804
1789
        if _inventory is None:
1843
1828
    def _deserialize(selt, in_file):
1844
1829
        return xml5.serializer_v5.read_inventory(in_file)
1845
1830
 
1846
 
    def break_lock(self):
1847
 
        """Break a lock if one is present from another instance.
1848
 
 
1849
 
        Uses the ui factory to ask for confirmation if the lock may be from
1850
 
        an active process.
1851
 
 
1852
 
        This will probe the repository for its lock as well.
1853
 
        """
1854
 
        self._control_files.break_lock()
1855
 
        self.branch.break_lock()
1856
 
 
1857
 
    def is_locked(self):
1858
 
        return self._control_files.is_locked()
1859
 
 
1860
 
    def _must_be_locked(self):
1861
 
        if not self.is_locked():
1862
 
            raise errors.ObjectNotLocked(self)
1863
 
 
1864
 
    def lock_read(self):
1865
 
        """Lock the tree for reading.
1866
 
 
1867
 
        This also locks the branch, and can be unlocked via self.unlock().
1868
 
 
1869
 
        :return: A bzrlib.lock.LogicalLockResult.
1870
 
        """
1871
 
        if not self.is_locked():
1872
 
            self._reset_data()
1873
 
        self.branch.lock_read()
1874
 
        try:
1875
 
            self._control_files.lock_read()
1876
 
            return LogicalLockResult(self.unlock)
1877
 
        except:
1878
 
            self.branch.unlock()
1879
 
            raise
1880
 
 
1881
 
    def lock_tree_write(self):
1882
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1883
 
 
1884
 
        :return: A bzrlib.lock.LogicalLockResult.
1885
 
        """
1886
 
        if not self.is_locked():
1887
 
            self._reset_data()
1888
 
        self.branch.lock_read()
1889
 
        try:
1890
 
            self._control_files.lock_write()
1891
 
            return LogicalLockResult(self.unlock)
1892
 
        except:
1893
 
            self.branch.unlock()
1894
 
            raise
1895
 
 
1896
 
    def lock_write(self):
1897
 
        """See MutableTree.lock_write, and WorkingTree.unlock.
1898
 
 
1899
 
        :return: A bzrlib.lock.LogicalLockResult.
1900
 
        """
1901
 
        if not self.is_locked():
1902
 
            self._reset_data()
1903
 
        self.branch.lock_write()
1904
 
        try:
1905
 
            self._control_files.lock_write()
1906
 
            return LogicalLockResult(self.unlock)
1907
 
        except:
1908
 
            self.branch.unlock()
1909
 
            raise
1910
 
 
1911
 
    def get_physical_lock_status(self):
1912
 
        return self._control_files.get_physical_lock_status()
1913
 
 
1914
1831
    @needs_tree_write_lock
1915
1832
    def _write_inventory(self, inv):
1916
1833
        """Write inventory as the current inventory."""
1984
1901
            if entry.parent_id == orig_root_id:
1985
1902
                entry.parent_id = inv.root.file_id
1986
1903
 
 
1904
    def all_file_ids(self):
 
1905
        """See Tree.iter_all_file_ids"""
 
1906
        return set(self.inventory)
 
1907
 
1987
1908
    @needs_tree_write_lock
1988
1909
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1989
1910
        """See MutableTree.set_parent_trees."""
2008
1929
                # parent tree from the repository.
2009
1930
                self._cache_basis_inventory(leftmost_parent_id)
2010
1931
            else:
2011
 
                inv = leftmost_parent_tree.root_inventory
 
1932
                inv = leftmost_parent_tree.inventory
2012
1933
                xml = self._create_basis_xml_from_inventory(
2013
1934
                                        leftmost_parent_id, inv)
2014
1935
                self._write_basis_inventory(xml)
2111
2032
 
2112
2033
    def has_id(self, file_id):
2113
2034
        # files that have been deleted are excluded
2114
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2115
 
        if not inv.has_id(inv_file_id):
 
2035
        inv = self.inventory
 
2036
        if not inv.has_id(file_id):
2116
2037
            return False
2117
 
        path = inv.id2path(inv_file_id)
 
2038
        path = inv.id2path(file_id)
2118
2039
        return osutils.lexists(self.abspath(path))
2119
2040
 
2120
2041
    def has_or_had_id(self, file_id):
2121
 
        if file_id == self.get_root_id():
 
2042
        if file_id == self.inventory.root.file_id:
2122
2043
            return True
2123
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2124
 
        return inv.has_id(inv_file_id)
 
2044
        return self.inventory.has_id(file_id)
2125
2045
 
2126
 
    def all_file_ids(self):
 
2046
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
2047
    def __iter__(self):
2127
2048
        """Iterate through file_ids for this tree.
2128
2049
 
2129
2050
        file_ids are in a WorkingTree if they are in the working inventory
2130
2051
        and the working file exists.
2131
2052
        """
2132
 
        ret = set()
2133
 
        for path, ie in self.iter_entries_by_dir():
2134
 
            ret.add(ie.file_id)
2135
 
        return ret
 
2053
        inv = self._inventory
 
2054
        for path, ie in inv.iter_entries():
 
2055
            if osutils.lexists(self.abspath(path)):
 
2056
                yield ie.file_id
2136
2057
 
2137
2058
    @needs_tree_write_lock
2138
2059
    def set_last_revision(self, new_revision):
2194
2115
                _mod_revision.NULL_REVISION)
2195
2116
        else:
2196
2117
            rt = self.branch.repository.revision_tree(revision_ids[0])
2197
 
        self._write_inventory(rt.root_inventory)
 
2118
        self._write_inventory(rt.inventory)
2198
2119
        self.set_parent_ids(revision_ids)
2199
2120
 
2200
2121
    def flush(self):
2212
2133
    def get_file_mtime(self, file_id, path=None):
2213
2134
        """See Tree.get_file_mtime."""
2214
2135
        if not path:
2215
 
            path = self.id2path(file_id)
 
2136
            path = self.inventory.id2path(file_id)
2216
2137
        try:
2217
2138
            return os.lstat(self.abspath(path)).st_mtime
2218
2139
        except OSError, e:
2221
2142
            raise
2222
2143
 
2223
2144
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2224
 
        inv, file_id = self._path2inv_file_id(path)
 
2145
        file_id = self.path2id(path)
2225
2146
        if file_id is None:
2226
2147
            # For unversioned files on win32, we just assume they are not
2227
2148
            # executable
2228
2149
            return False
2229
 
        return inv[file_id].executable
 
2150
        return self._inventory[file_id].executable
2230
2151
 
2231
2152
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2232
2153
        mode = stat_result.st_mode
2233
2154
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2234
2155
 
2235
 
    def is_executable(self, file_id, path=None):
2236
 
        if not self._supports_executable():
2237
 
            inv, inv_file_id = self._unpack_file_id(file_id)
2238
 
            return inv[inv_file_id].executable
2239
 
        else:
 
2156
    if not supports_executable():
 
2157
        def is_executable(self, file_id, path=None):
 
2158
            return self._inventory[file_id].executable
 
2159
 
 
2160
        _is_executable_from_path_and_stat = \
 
2161
            _is_executable_from_path_and_stat_from_basis
 
2162
    else:
 
2163
        def is_executable(self, file_id, path=None):
2240
2164
            if not path:
2241
2165
                path = self.id2path(file_id)
2242
2166
            mode = os.lstat(self.abspath(path)).st_mode
2243
2167
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2244
2168
 
2245
 
    def _is_executable_from_path_and_stat(self, path, stat_result):
2246
 
        if not self._supports_executable():
2247
 
            return self._is_executable_from_path_and_stat_from_basis(path, stat_result)
2248
 
        else:
2249
 
            return self._is_executable_from_path_and_stat_from_stat(path, stat_result)
 
2169
        _is_executable_from_path_and_stat = \
 
2170
            _is_executable_from_path_and_stat_from_stat
2250
2171
 
2251
2172
    @needs_tree_write_lock
2252
2173
    def _add(self, files, ids, kinds):
2255
2176
        # should probably put it back with the previous ID.
2256
2177
        # the read and write working inventory should not occur in this
2257
2178
        # function - they should be part of lock_write and unlock.
2258
 
        # FIXME: nested trees
2259
 
        inv = self.root_inventory
 
2179
        inv = self.inventory
2260
2180
        for f, file_id, kind in zip(files, ids, kinds):
2261
2181
            if file_id is None:
2262
2182
                inv.add_path(f, kind=kind)
2303
2223
                parent_tree = self.branch.repository.revision_tree(parent_id)
2304
2224
            parent_tree.lock_read()
2305
2225
            try:
2306
 
                try:
2307
 
                    kind = parent_tree.kind(file_id)
2308
 
                except errors.NoSuchId:
 
2226
                if not parent_tree.has_id(file_id):
2309
2227
                    continue
2310
 
                if kind != 'file':
 
2228
                ie = parent_tree.inventory[file_id]
 
2229
                if ie.kind != 'file':
2311
2230
                    # Note: this is slightly unnecessary, because symlinks and
2312
2231
                    # directories have a "text" which is the empty text, and we
2313
2232
                    # know that won't mess up annotations. But it seems cleaner
2314
2233
                    continue
2315
 
                parent_text_key = (
2316
 
                    file_id, parent_tree.get_file_revision(file_id))
 
2234
                parent_text_key = (file_id, ie.revision)
2317
2235
                if parent_text_key not in maybe_file_parent_keys:
2318
2236
                    maybe_file_parent_keys.append(parent_text_key)
2319
2237
            finally:
2334
2252
                       for key, line in annotator.annotate_flat(this_key)]
2335
2253
        return annotations
2336
2254
 
2337
 
    def _put_rio(self, filename, stanzas, header):
2338
 
        self._must_be_locked()
2339
 
        my_file = _mod_rio.rio_file(stanzas, header)
2340
 
        self._transport.put_file(filename, my_file,
2341
 
            mode=self.bzrdir._get_file_mode())
2342
 
 
2343
 
    @needs_tree_write_lock
2344
 
    def set_merge_modified(self, modified_hashes):
2345
 
        def iter_stanzas():
2346
 
            for file_id, hash in modified_hashes.iteritems():
2347
 
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
2348
 
                    hash=hash)
2349
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
2350
 
 
2351
2255
    @needs_read_lock
2352
2256
    def merge_modified(self):
2353
2257
        """Return a dictionary of files modified by a merge.
2373
2277
            for s in _mod_rio.RioReader(hashfile):
2374
2278
                # RioReader reads in Unicode, so convert file_ids back to utf8
2375
2279
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2376
 
                if not self.has_id(file_id):
 
2280
                if not self.inventory.has_id(file_id):
2377
2281
                    continue
2378
2282
                text_hash = s.get("hash")
2379
2283
                if text_hash == self.get_file_sha1(file_id):
2409
2313
        other_tree.lock_tree_write()
2410
2314
        try:
2411
2315
            new_parents = other_tree.get_parent_ids()
2412
 
            other_root = other_tree.root_inventory.root
 
2316
            other_root = other_tree.inventory.root
2413
2317
            other_root.parent_id = new_root_parent
2414
2318
            other_root.name = osutils.basename(other_tree_path)
2415
 
            self.root_inventory.add(other_root)
2416
 
            add_children(self.root_inventory, other_root)
2417
 
            self._write_inventory(self.root_inventory)
 
2319
            self.inventory.add(other_root)
 
2320
            add_children(self.inventory, other_root)
 
2321
            self._write_inventory(self.inventory)
2418
2322
            # normally we don't want to fetch whole repositories, but i think
2419
2323
            # here we really do want to consolidate the whole thing.
2420
2324
            for parent_id in other_tree.get_parent_ids():
2458
2362
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2459
2363
        if tree_transport.base != branch_transport.base:
2460
2364
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2461
 
            tree_bzrdir.set_branch_reference(new_branch)
 
2365
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
 
2366
                target_branch=new_branch)
2462
2367
        else:
2463
2368
            tree_bzrdir = branch_bzrdir
2464
2369
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2465
2370
        wt.set_parent_ids(self.get_parent_ids())
2466
 
        # FIXME: Support nested trees
2467
 
        my_inv = self.root_inventory
 
2371
        my_inv = self.inventory
2468
2372
        child_inv = inventory.Inventory(root_id=None)
2469
2373
        new_root = my_inv[file_id]
2470
2374
        my_inv.remove_recursive_id(file_id)
2490
2394
        if not self.is_locked():
2491
2395
            raise errors.ObjectNotLocked(self)
2492
2396
 
 
2397
        inv = self.inventory
2493
2398
        if from_dir is None and include_root is True:
2494
 
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
 
2399
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2495
2400
        # Convert these into local objects to save lookup times
2496
2401
        pathjoin = osutils.pathjoin
2497
2402
        file_kind = self._kind
2504
2409
 
2505
2410
        # directory file_id, relative path, absolute path, reverse sorted children
2506
2411
        if from_dir is not None:
2507
 
            inv, from_dir_id = self._path2inv_file_id(from_dir)
 
2412
            from_dir_id = inv.path2id(from_dir)
2508
2413
            if from_dir_id is None:
2509
2414
                # Directory not versioned
2510
2415
                return
2511
2416
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2512
2417
        else:
2513
 
            inv = self.root_inventory
2514
2418
            from_dir_id = inv.root.file_id
2515
2419
            from_dir_abspath = self.basedir
2516
2420
        children = os.listdir(from_dir_abspath)
2639
2543
        rename_entries = []
2640
2544
        rename_tuples = []
2641
2545
 
2642
 
        invs_to_write = set()
2643
 
 
2644
2546
        # check for deprecated use of signature
2645
2547
        if to_dir is None:
2646
2548
            raise TypeError('You must supply a target directory')
2647
2549
        # check destination directory
2648
2550
        if isinstance(from_paths, basestring):
2649
2551
            raise ValueError()
 
2552
        inv = self.inventory
2650
2553
        to_abs = self.abspath(to_dir)
2651
2554
        if not isdir(to_abs):
2652
2555
            raise errors.BzrMoveFailedError('',to_dir,
2654
2557
        if not self.has_filename(to_dir):
2655
2558
            raise errors.BzrMoveFailedError('',to_dir,
2656
2559
                errors.NotInWorkingDirectory(to_dir))
2657
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2560
        to_dir_id = inv.path2id(to_dir)
2658
2561
        if to_dir_id is None:
2659
2562
            raise errors.BzrMoveFailedError('',to_dir,
2660
2563
                errors.NotVersionedError(path=to_dir))
2661
2564
 
2662
 
        to_dir_ie = to_inv[to_dir_id]
 
2565
        to_dir_ie = inv[to_dir_id]
2663
2566
        if to_dir_ie.kind != 'directory':
2664
2567
            raise errors.BzrMoveFailedError('',to_dir,
2665
2568
                errors.NotADirectory(to_abs))
2667
2570
        # create rename entries and tuples
2668
2571
        for from_rel in from_paths:
2669
2572
            from_tail = splitpath(from_rel)[-1]
2670
 
            from_inv, from_id = self._path2inv_file_id(from_rel)
 
2573
            from_id = inv.path2id(from_rel)
2671
2574
            if from_id is None:
2672
2575
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2673
2576
                    errors.NotVersionedError(path=from_rel))
2674
2577
 
2675
 
            from_entry = from_inv[from_id]
 
2578
            from_entry = inv[from_id]
2676
2579
            from_parent_id = from_entry.parent_id
2677
2580
            to_rel = pathjoin(to_dir, from_tail)
2678
2581
            rename_entry = InventoryWorkingTree._RenameEntry(
2697
2600
            # restore the inventory on error
2698
2601
            self._inventory_is_modified = original_modified
2699
2602
            raise
2700
 
        #FIXME: Should potentially also write the from_invs
2701
 
        self._write_inventory(to_inv)
 
2603
        self._write_inventory(inv)
2702
2604
        return rename_tuples
2703
2605
 
2704
2606
    @needs_tree_write_lock
2724
2626
 
2725
2627
        Everything else results in an error.
2726
2628
        """
 
2629
        inv = self.inventory
2727
2630
        rename_entries = []
2728
2631
 
2729
2632
        # create rename entries and tuples
2730
2633
        from_tail = splitpath(from_rel)[-1]
2731
 
        from_inv, from_id = self._path2inv_file_id(from_rel)
 
2634
        from_id = inv.path2id(from_rel)
2732
2635
        if from_id is None:
2733
2636
            # if file is missing in the inventory maybe it's in the basis_tree
2734
2637
            basis_tree = self.branch.basis_tree()
2737
2640
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2738
2641
                    errors.NotVersionedError(path=from_rel))
2739
2642
            # put entry back in the inventory so we can rename it
2740
 
            from_entry = basis_tree.root_inventory[from_id].copy()
2741
 
            from_inv.add(from_entry)
 
2643
            from_entry = basis_tree.inventory[from_id].copy()
 
2644
            inv.add(from_entry)
2742
2645
        else:
2743
 
            from_inv, from_inv_id = self._unpack_file_id(from_id)
2744
 
            from_entry = from_inv[from_inv_id]
 
2646
            from_entry = inv[from_id]
2745
2647
        from_parent_id = from_entry.parent_id
2746
2648
        to_dir, to_tail = os.path.split(to_rel)
2747
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2649
        to_dir_id = inv.path2id(to_dir)
2748
2650
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2749
2651
                                     from_id=from_id,
2750
2652
                                     from_tail=from_tail,
2772
2674
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2773
2675
 
2774
2676
        self._move(rename_entries)
2775
 
        self._write_inventory(to_inv)
 
2677
        self._write_inventory(inv)
2776
2678
 
2777
2679
    class _RenameEntry(object):
2778
2680
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2794
2696
 
2795
2697
        Also does basic plausability tests.
2796
2698
        """
2797
 
        # FIXME: Handling of nested trees
2798
 
        inv = self.root_inventory
 
2699
        inv = self.inventory
2799
2700
 
2800
2701
        for rename_entry in rename_entries:
2801
2702
            # store to local variables for easier reference
2846
2747
                # something is wrong, so lets determine what exactly
2847
2748
                if not self.has_filename(from_rel) and \
2848
2749
                   not self.has_filename(to_rel):
2849
 
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
2850
 
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
 
2750
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
 
2751
                        errors.PathsDoNotExist(paths=(str(from_rel),
 
2752
                        str(to_rel))))
2851
2753
                else:
2852
2754
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2853
2755
            rename_entry.only_change_inv = only_change_inv
2859
2761
        Depending on the value of the flag 'only_change_inv', the
2860
2762
        file will be moved on the file system or not.
2861
2763
        """
 
2764
        inv = self.inventory
2862
2765
        moved = []
2863
2766
 
2864
2767
        for entry in rename_entries:
2871
2774
 
2872
2775
    def _rollback_move(self, moved):
2873
2776
        """Try to rollback a previous move in case of an filesystem error."""
 
2777
        inv = self.inventory
2874
2778
        for entry in moved:
2875
2779
            try:
2876
2780
                self._move_entry(WorkingTree._RenameEntry(
2885
2789
                        " Error message is: %s" % e)
2886
2790
 
2887
2791
    def _move_entry(self, entry):
2888
 
        inv = self.root_inventory
 
2792
        inv = self.inventory
2889
2793
        from_rel_abs = self.abspath(entry.from_rel)
2890
2794
        to_rel_abs = self.abspath(entry.to_rel)
2891
2795
        if from_rel_abs == to_rel_abs:
2932
2836
 
2933
2837
    def stored_kind(self, file_id):
2934
2838
        """See Tree.stored_kind"""
2935
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2936
 
        return inv[inv_file_id].kind
 
2839
        return self.inventory[file_id].kind
2937
2840
 
2938
2841
    def extras(self):
2939
2842
        """Yield all unversioned files in this WorkingTree.
2946
2849
        This is the same order used by 'osutils.walkdirs'.
2947
2850
        """
2948
2851
        ## TODO: Work from given directory downwards
2949
 
        for path, dir_entry in self.iter_entries_by_dir():
2950
 
            if dir_entry.kind != 'directory':
2951
 
                continue
 
2852
        for path, dir_entry in self.inventory.directories():
2952
2853
            # mutter("search for unknowns in %r", path)
2953
2854
            dirabs = self.abspath(path)
2954
2855
            if not isdir(dirabs):
2991
2892
        """
2992
2893
        _directory = 'directory'
2993
2894
        # get the root in the inventory
2994
 
        inv, top_id = self._path2inv_file_id(prefix)
 
2895
        inv = self.inventory
 
2896
        top_id = inv.path2id(prefix)
2995
2897
        if top_id is None:
2996
2898
            pending = []
2997
2899
        else:
3018
2920
                if dir[2] == _directory:
3019
2921
                    pending.append(dir)
3020
2922
 
3021
 
    @needs_write_lock
3022
 
    def update_feature_flags(self, updated_flags):
3023
 
        """Update the feature flags for this branch.
3024
 
 
3025
 
        :param updated_flags: Dictionary mapping feature names to necessities
3026
 
            A necessity can be None to indicate the feature should be removed
3027
 
        """
3028
 
        self._format._update_feature_flags(updated_flags)
3029
 
        self.control_transport.put_bytes('format', self._format.as_string())
3030
 
 
3031
2923
 
3032
2924
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
3033
2925
    """Registry for working tree formats."""
3089
2981
 
3090
2982
    supports_versioned_directories = None
3091
2983
 
3092
 
    def initialize(self, controldir, revision_id=None, from_branch=None,
 
2984
    @classmethod
 
2985
    def find_format_string(klass, a_bzrdir):
 
2986
        """Return format name for the working tree object in a_bzrdir."""
 
2987
        try:
 
2988
            transport = a_bzrdir.get_workingtree_transport(None)
 
2989
            return transport.get_bytes("format")
 
2990
        except errors.NoSuchFile:
 
2991
            raise errors.NoWorkingTree(base=transport.base)
 
2992
 
 
2993
    @classmethod
 
2994
    def find_format(klass, a_bzrdir):
 
2995
        """Return the format for the working tree object in a_bzrdir."""
 
2996
        try:
 
2997
            format_string = klass.find_format_string(a_bzrdir)
 
2998
            return format_registry.get(format_string)
 
2999
        except KeyError:
 
3000
            raise errors.UnknownFormatError(format=format_string,
 
3001
                                            kind="working tree")
 
3002
 
 
3003
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
3093
3004
                   accelerator_tree=None, hardlink=False):
3094
 
        """Initialize a new working tree in controldir.
 
3005
        """Initialize a new working tree in a_bzrdir.
3095
3006
 
3096
 
        :param controldir: ControlDir to initialize the working tree in.
 
3007
        :param a_bzrdir: BzrDir to initialize the working tree in.
3097
3008
        :param revision_id: allows creating a working tree at a different
3098
3009
            revision than the branch is at.
3099
3010
        :param from_branch: Branch to checkout
3112
3023
    def __ne__(self, other):
3113
3024
        return not (self == other)
3114
3025
 
 
3026
    @classmethod
 
3027
    @symbol_versioning.deprecated_method(
 
3028
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3029
    def get_default_format(klass):
 
3030
        """Return the current default format."""
 
3031
        return format_registry.get_default()
 
3032
 
 
3033
    def get_format_string(self):
 
3034
        """Return the ASCII format string that identifies this format."""
 
3035
        raise NotImplementedError(self.get_format_string)
 
3036
 
3115
3037
    def get_format_description(self):
3116
3038
        """Return the short description for this format."""
3117
3039
        raise NotImplementedError(self.get_format_description)
3133
3055
        """True if this format supports stored views."""
3134
3056
        return False
3135
3057
 
3136
 
    def get_controldir_for_branch(self):
3137
 
        """Get the control directory format for creating branches.
3138
 
 
3139
 
        This is to support testing of working tree formats that can not exist
3140
 
        in the same control directory as a branch.
3141
 
        """
3142
 
        return self._matchingbzrdir
3143
 
 
3144
 
 
3145
 
class WorkingTreeFormatMetaDir(bzrdir.BzrFormat, WorkingTreeFormat):
3146
 
    """Base class for working trees that live in bzr meta directories."""
3147
 
 
3148
 
    def __init__(self):
3149
 
        WorkingTreeFormat.__init__(self)
3150
 
        bzrdir.BzrFormat.__init__(self)
3151
 
 
3152
 
    @classmethod
3153
 
    def find_format_string(klass, controldir):
3154
 
        """Return format name for the working tree object in controldir."""
3155
 
        try:
3156
 
            transport = controldir.get_workingtree_transport(None)
3157
 
            return transport.get_bytes("format")
3158
 
        except errors.NoSuchFile:
3159
 
            raise errors.NoWorkingTree(base=transport.base)
3160
 
 
3161
 
    @classmethod
3162
 
    def find_format(klass, controldir):
3163
 
        """Return the format for the working tree object in controldir."""
3164
 
        format_string = klass.find_format_string(controldir)
3165
 
        return klass._find_format(format_registry, 'working tree',
3166
 
                format_string)
3167
 
 
3168
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
3169
 
            basedir=None):
3170
 
        WorkingTreeFormat.check_support_status(self,
3171
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
3172
 
            basedir=basedir)
3173
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
3174
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
3175
 
 
3176
 
    def get_controldir_for_branch(self):
3177
 
        """Get the control directory format for creating branches.
3178
 
 
3179
 
        This is to support testing of working tree formats that can not exist
3180
 
        in the same control directory as a branch.
3181
 
        """
3182
 
        return self._matchingbzrdir
3183
 
 
3184
 
 
3185
 
class WorkingTreeFormatMetaDir(bzrdir.BzrFormat, WorkingTreeFormat):
3186
 
    """Base class for working trees that live in bzr meta directories."""
3187
 
 
3188
 
    def __init__(self):
3189
 
        WorkingTreeFormat.__init__(self)
3190
 
        bzrdir.BzrFormat.__init__(self)
3191
 
 
3192
 
    @classmethod
3193
 
    def find_format_string(klass, controldir):
3194
 
        """Return format name for the working tree object in controldir."""
3195
 
        try:
3196
 
            transport = controldir.get_workingtree_transport(None)
3197
 
            return transport.get_bytes("format")
3198
 
        except errors.NoSuchFile:
3199
 
            raise errors.NoWorkingTree(base=transport.base)
3200
 
 
3201
 
    @classmethod
3202
 
    def find_format(klass, controldir):
3203
 
        """Return the format for the working tree object in controldir."""
3204
 
        format_string = klass.find_format_string(controldir)
3205
 
        return klass._find_format(format_registry, 'working tree',
3206
 
                format_string)
3207
 
 
3208
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
3209
 
            basedir=None):
3210
 
        WorkingTreeFormat.check_support_status(self,
3211
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
3212
 
            basedir=basedir)
3213
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
3214
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
 
3058
    @classmethod
 
3059
    @symbol_versioning.deprecated_method(
 
3060
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3061
    def register_format(klass, format):
 
3062
        format_registry.register(format)
 
3063
 
 
3064
    @classmethod
 
3065
    @symbol_versioning.deprecated_method(
 
3066
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3067
    def register_extra_format(klass, format):
 
3068
        format_registry.register_extra(format)
 
3069
 
 
3070
    @classmethod
 
3071
    @symbol_versioning.deprecated_method(
 
3072
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3073
    def unregister_extra_format(klass, format):
 
3074
        format_registry.unregister_extra(format)
 
3075
 
 
3076
    @classmethod
 
3077
    @symbol_versioning.deprecated_method(
 
3078
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3079
    def get_formats(klass):
 
3080
        return format_registry._get_all()
 
3081
 
 
3082
    @classmethod
 
3083
    @symbol_versioning.deprecated_method(
 
3084
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3085
    def set_default_format(klass, format):
 
3086
        format_registry.set_default(format)
 
3087
 
 
3088
    @classmethod
 
3089
    @symbol_versioning.deprecated_method(
 
3090
        symbol_versioning.deprecated_in((2, 4, 0)))
 
3091
    def unregister_format(klass, format):
 
3092
        format_registry.remove(format)
 
3093
 
 
3094
    def get_controldir_for_branch(self):
 
3095
        """Get the control directory format for creating branches.
 
3096
 
 
3097
        This is to support testing of working tree formats that can not exist
 
3098
        in the same control directory as a branch.
 
3099
        """
 
3100
        return self._matchingbzrdir
3215
3101
 
3216
3102
 
3217
3103
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",