~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Shannon Weyrick
  • Date: 2011-11-16 20:59:10 UTC
  • mto: This revision was merged to the branch mainline in revision 6268.
  • Revision ID: weyrick@mozek.us-20111116205910-1xbpqbimbw3fvfqw
A version of the patch, based on suggestions from John Meinel, which detects an empty page differently to avoid false positives.

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,
69
69
    )
70
70
""")
71
71
 
72
 
# Explicitly import bzrlib.bzrdir so that the BzrProber
73
 
# is guaranteed to be registered.
74
 
from bzrlib import (
75
 
    bzrdir,
76
 
    symbol_versioning,
77
 
    )
78
 
 
 
72
from bzrlib import symbol_versioning
79
73
from bzrlib.decorators import needs_read_lock, needs_write_lock
80
74
from bzrlib.i18n import gettext
81
75
from bzrlib.lock import LogicalLockResult
90
84
    realpath,
91
85
    safe_unicode,
92
86
    splitpath,
 
87
    supports_executable,
93
88
    )
94
89
from bzrlib.trace import mutter, note
95
90
from bzrlib.revision import CURRENT_REVISION
177
172
 
178
173
    def __init__(self, basedir='.',
179
174
                 branch=DEPRECATED_PARAMETER,
 
175
                 _control_files=None,
180
176
                 _internal=False,
181
 
                 _transport=None,
182
177
                 _format=None,
183
178
                 _bzrdir=None):
184
179
        """Construct a WorkingTree instance. This is not a public API.
197
192
        else:
198
193
            self._branch = self.bzrdir.open_branch()
199
194
        self.basedir = realpath(basedir)
200
 
        self._transport = _transport
 
195
        self._control_files = _control_files
 
196
        self._transport = self._control_files._transport
201
197
        self._rules_searcher = None
202
198
        self.views = self._make_views()
203
199
 
233
229
        """See `Tree.has_versioned_directories`."""
234
230
        return self._format.supports_versioned_directories
235
231
 
236
 
    def _supports_executable(self):
237
 
        if sys.platform == 'win32':
238
 
            return False
239
 
        # FIXME: Ideally this should check the file system
240
 
        return True
241
 
 
242
232
    def break_lock(self):
243
233
        """Break a lock if one is present from another instance.
244
234
 
247
237
 
248
238
        This will probe the repository for its lock as well.
249
239
        """
250
 
        raise NotImplementedError(self.break_lock)
 
240
        self._control_files.break_lock()
 
241
        self.branch.break_lock()
251
242
 
252
243
    def requires_rich_root(self):
253
244
        return self._format.requires_rich_root
261
252
    def supports_views(self):
262
253
        return self.views.supports_views()
263
254
 
264
 
    def get_config_stack(self):
265
 
        """Retrieve the config stack for this tree.
266
 
 
267
 
        :return: A ``bzrlib.config.Stack``
268
 
        """
269
 
        # For the moment, just provide the branch config stack.
270
 
        return self.branch.get_config_stack()
271
 
 
272
255
    @staticmethod
273
256
    def open(path=None, _unsupported=False):
274
257
        """Open an existing working tree at path.
276
259
        """
277
260
        if path is None:
278
261
            path = osutils.getcwd()
279
 
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
280
 
        return control.open_workingtree(unsupported=_unsupported)
 
262
        control = controldir.ControlDir.open(path, _unsupported)
 
263
        return control.open_workingtree(_unsupported)
281
264
 
282
265
    @staticmethod
283
266
    def open_containing(path=None):
387
370
                                              list_current=list_current)
388
371
        return [tr for tr in iterator if tr is not None]
389
372
 
 
373
    def all_file_ids(self):
 
374
        """See Tree.iter_all_file_ids"""
 
375
        raise NotImplementedError(self.all_file_ids)
 
376
 
390
377
    def __repr__(self):
391
378
        return "<%s of %s>" % (self.__class__.__name__,
392
379
                               getattr(self, 'basedir', None))
754
741
 
755
742
    @needs_tree_write_lock
756
743
    def set_merge_modified(self, modified_hashes):
757
 
        """Set the merge modified hashes."""
758
 
        raise NotImplementedError(self.set_merge_modified)
 
744
        def iter_stanzas():
 
745
            for file_id, hash in modified_hashes.iteritems():
 
746
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
 
747
                    hash=hash)
 
748
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
759
749
 
760
750
    def _sha_from_stat(self, path, stat_result):
761
751
        """Get a sha digest from the tree's stat cache.
767
757
        """
768
758
        return None
769
759
 
 
760
    def _put_rio(self, filename, stanzas, header):
 
761
        self._must_be_locked()
 
762
        my_file = _mod_rio.rio_file(stanzas, header)
 
763
        self._transport.put_file(filename, my_file,
 
764
            mode=self.bzrdir._get_file_mode())
 
765
 
770
766
    @needs_write_lock # because merge pulls data into the branch.
771
767
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
772
768
                          merge_type=None, force=False):
1127
1123
        else:
1128
1124
            mode = stat_value.st_mode
1129
1125
            kind = osutils.file_kind_from_stat_mode(mode)
1130
 
            if not self._supports_executable():
 
1126
            if not supports_executable():
1131
1127
                executable = entry is not None and entry.executable
1132
1128
            else:
1133
1129
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1152
1148
        return _mod_revision.ensure_null(self.branch.last_revision())
1153
1149
 
1154
1150
    def is_locked(self):
1155
 
        """Check if this tree is locked."""
1156
 
        raise NotImplementedError(self.is_locked)
 
1151
        return self._control_files.is_locked()
 
1152
 
 
1153
    def _must_be_locked(self):
 
1154
        if not self.is_locked():
 
1155
            raise errors.ObjectNotLocked(self)
1157
1156
 
1158
1157
    def lock_read(self):
1159
1158
        """Lock the tree for reading.
1162
1161
 
1163
1162
        :return: A bzrlib.lock.LogicalLockResult.
1164
1163
        """
1165
 
        raise NotImplementedError(self.lock_read)
 
1164
        if not self.is_locked():
 
1165
            self._reset_data()
 
1166
        self.branch.lock_read()
 
1167
        try:
 
1168
            self._control_files.lock_read()
 
1169
            return LogicalLockResult(self.unlock)
 
1170
        except:
 
1171
            self.branch.unlock()
 
1172
            raise
1166
1173
 
1167
1174
    def lock_tree_write(self):
1168
1175
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1169
1176
 
1170
1177
        :return: A bzrlib.lock.LogicalLockResult.
1171
1178
        """
1172
 
        raise NotImplementedError(self.lock_tree_write)
 
1179
        if not self.is_locked():
 
1180
            self._reset_data()
 
1181
        self.branch.lock_read()
 
1182
        try:
 
1183
            self._control_files.lock_write()
 
1184
            return LogicalLockResult(self.unlock)
 
1185
        except:
 
1186
            self.branch.unlock()
 
1187
            raise
1173
1188
 
1174
1189
    def lock_write(self):
1175
1190
        """See MutableTree.lock_write, and WorkingTree.unlock.
1176
1191
 
1177
1192
        :return: A bzrlib.lock.LogicalLockResult.
1178
1193
        """
1179
 
        raise NotImplementedError(self.lock_write)
 
1194
        if not self.is_locked():
 
1195
            self._reset_data()
 
1196
        self.branch.lock_write()
 
1197
        try:
 
1198
            self._control_files.lock_write()
 
1199
            return LogicalLockResult(self.unlock)
 
1200
        except:
 
1201
            self.branch.unlock()
 
1202
            raise
1180
1203
 
1181
1204
    def get_physical_lock_status(self):
1182
 
        raise NotImplementedError(self.get_physical_lock_status)
 
1205
        return self._control_files.get_physical_lock_status()
 
1206
 
 
1207
    def _reset_data(self):
 
1208
        """Reset transient data that cannot be revalidated."""
 
1209
        raise NotImplementedError(self._reset_data)
1183
1210
 
1184
1211
    def set_last_revision(self, new_revision):
1185
1212
        """Change the last revision in the working tree."""
1761
1788
        :param branch: A branch to override probing for the branch.
1762
1789
        """
1763
1790
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1764
 
            branch=branch, _transport=_control_files._transport,
1765
 
            _internal=_internal, _format=_format, _bzrdir=_bzrdir)
 
1791
            branch=branch, _control_files=_control_files, _internal=_internal,
 
1792
            _format=_format, _bzrdir=_bzrdir)
1766
1793
 
1767
 
        self._control_files = _control_files
1768
1794
        self._detect_case_handling()
1769
1795
 
1770
1796
        if _inventory is None:
1809
1835
    def _deserialize(selt, in_file):
1810
1836
        return xml5.serializer_v5.read_inventory(in_file)
1811
1837
 
1812
 
    def break_lock(self):
1813
 
        """Break a lock if one is present from another instance.
1814
 
 
1815
 
        Uses the ui factory to ask for confirmation if the lock may be from
1816
 
        an active process.
1817
 
 
1818
 
        This will probe the repository for its lock as well.
1819
 
        """
1820
 
        self._control_files.break_lock()
1821
 
        self.branch.break_lock()
1822
 
 
1823
 
    def is_locked(self):
1824
 
        return self._control_files.is_locked()
1825
 
 
1826
 
    def _must_be_locked(self):
1827
 
        if not self.is_locked():
1828
 
            raise errors.ObjectNotLocked(self)
1829
 
 
1830
 
    def lock_read(self):
1831
 
        """Lock the tree for reading.
1832
 
 
1833
 
        This also locks the branch, and can be unlocked via self.unlock().
1834
 
 
1835
 
        :return: A bzrlib.lock.LogicalLockResult.
1836
 
        """
1837
 
        if not self.is_locked():
1838
 
            self._reset_data()
1839
 
        self.branch.lock_read()
1840
 
        try:
1841
 
            self._control_files.lock_read()
1842
 
            return LogicalLockResult(self.unlock)
1843
 
        except:
1844
 
            self.branch.unlock()
1845
 
            raise
1846
 
 
1847
 
    def lock_tree_write(self):
1848
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1849
 
 
1850
 
        :return: A bzrlib.lock.LogicalLockResult.
1851
 
        """
1852
 
        if not self.is_locked():
1853
 
            self._reset_data()
1854
 
        self.branch.lock_read()
1855
 
        try:
1856
 
            self._control_files.lock_write()
1857
 
            return LogicalLockResult(self.unlock)
1858
 
        except:
1859
 
            self.branch.unlock()
1860
 
            raise
1861
 
 
1862
 
    def lock_write(self):
1863
 
        """See MutableTree.lock_write, and WorkingTree.unlock.
1864
 
 
1865
 
        :return: A bzrlib.lock.LogicalLockResult.
1866
 
        """
1867
 
        if not self.is_locked():
1868
 
            self._reset_data()
1869
 
        self.branch.lock_write()
1870
 
        try:
1871
 
            self._control_files.lock_write()
1872
 
            return LogicalLockResult(self.unlock)
1873
 
        except:
1874
 
            self.branch.unlock()
1875
 
            raise
1876
 
 
1877
 
    def get_physical_lock_status(self):
1878
 
        return self._control_files.get_physical_lock_status()
1879
 
 
1880
1838
    @needs_tree_write_lock
1881
1839
    def _write_inventory(self, inv):
1882
1840
        """Write inventory as the current inventory."""
1950
1908
            if entry.parent_id == orig_root_id:
1951
1909
                entry.parent_id = inv.root.file_id
1952
1910
 
 
1911
    def all_file_ids(self):
 
1912
        """See Tree.iter_all_file_ids"""
 
1913
        return set(self.inventory)
 
1914
 
1953
1915
    @needs_tree_write_lock
1954
1916
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1955
1917
        """See MutableTree.set_parent_trees."""
1974
1936
                # parent tree from the repository.
1975
1937
                self._cache_basis_inventory(leftmost_parent_id)
1976
1938
            else:
1977
 
                inv = leftmost_parent_tree.root_inventory
 
1939
                inv = leftmost_parent_tree.inventory
1978
1940
                xml = self._create_basis_xml_from_inventory(
1979
1941
                                        leftmost_parent_id, inv)
1980
1942
                self._write_basis_inventory(xml)
2077
2039
 
2078
2040
    def has_id(self, file_id):
2079
2041
        # files that have been deleted are excluded
2080
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2081
 
        if not inv.has_id(inv_file_id):
 
2042
        inv = self.inventory
 
2043
        if not inv.has_id(file_id):
2082
2044
            return False
2083
 
        path = inv.id2path(inv_file_id)
 
2045
        path = inv.id2path(file_id)
2084
2046
        return osutils.lexists(self.abspath(path))
2085
2047
 
2086
2048
    def has_or_had_id(self, file_id):
2087
 
        if file_id == self.get_root_id():
 
2049
        if file_id == self.inventory.root.file_id:
2088
2050
            return True
2089
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2090
 
        return inv.has_id(inv_file_id)
 
2051
        return self.inventory.has_id(file_id)
2091
2052
 
2092
 
    def all_file_ids(self):
 
2053
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
2054
    def __iter__(self):
2093
2055
        """Iterate through file_ids for this tree.
2094
2056
 
2095
2057
        file_ids are in a WorkingTree if they are in the working inventory
2096
2058
        and the working file exists.
2097
2059
        """
2098
 
        ret = set()
2099
 
        for path, ie in self.iter_entries_by_dir():
2100
 
            ret.add(ie.file_id)
2101
 
        return ret
 
2060
        inv = self._inventory
 
2061
        for path, ie in inv.iter_entries():
 
2062
            if osutils.lexists(self.abspath(path)):
 
2063
                yield ie.file_id
2102
2064
 
2103
2065
    @needs_tree_write_lock
2104
2066
    def set_last_revision(self, new_revision):
2160
2122
                _mod_revision.NULL_REVISION)
2161
2123
        else:
2162
2124
            rt = self.branch.repository.revision_tree(revision_ids[0])
2163
 
        self._write_inventory(rt.root_inventory)
 
2125
        self._write_inventory(rt.inventory)
2164
2126
        self.set_parent_ids(revision_ids)
2165
2127
 
2166
2128
    def flush(self):
2178
2140
    def get_file_mtime(self, file_id, path=None):
2179
2141
        """See Tree.get_file_mtime."""
2180
2142
        if not path:
2181
 
            path = self.id2path(file_id)
 
2143
            path = self.inventory.id2path(file_id)
2182
2144
        try:
2183
2145
            return os.lstat(self.abspath(path)).st_mtime
2184
2146
        except OSError, e:
2187
2149
            raise
2188
2150
 
2189
2151
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2190
 
        inv, file_id = self._path2inv_file_id(path)
 
2152
        file_id = self.path2id(path)
2191
2153
        if file_id is None:
2192
2154
            # For unversioned files on win32, we just assume they are not
2193
2155
            # executable
2194
2156
            return False
2195
 
        return inv[file_id].executable
 
2157
        return self._inventory[file_id].executable
2196
2158
 
2197
2159
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2198
2160
        mode = stat_result.st_mode
2199
2161
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2200
2162
 
2201
 
    def is_executable(self, file_id, path=None):
2202
 
        if not self._supports_executable():
2203
 
            inv, inv_file_id = self._unpack_file_id(file_id)
2204
 
            return inv[inv_file_id].executable
2205
 
        else:
 
2163
    if not supports_executable():
 
2164
        def is_executable(self, file_id, path=None):
 
2165
            return self._inventory[file_id].executable
 
2166
 
 
2167
        _is_executable_from_path_and_stat = \
 
2168
            _is_executable_from_path_and_stat_from_basis
 
2169
    else:
 
2170
        def is_executable(self, file_id, path=None):
2206
2171
            if not path:
2207
2172
                path = self.id2path(file_id)
2208
2173
            mode = os.lstat(self.abspath(path)).st_mode
2209
2174
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2210
2175
 
2211
 
    def _is_executable_from_path_and_stat(self, path, stat_result):
2212
 
        if not self._supports_executable():
2213
 
            return self._is_executable_from_path_and_stat_from_basis(path, stat_result)
2214
 
        else:
2215
 
            return self._is_executable_from_path_and_stat_from_stat(path, stat_result)
 
2176
        _is_executable_from_path_and_stat = \
 
2177
            _is_executable_from_path_and_stat_from_stat
2216
2178
 
2217
2179
    @needs_tree_write_lock
2218
2180
    def _add(self, files, ids, kinds):
2221
2183
        # should probably put it back with the previous ID.
2222
2184
        # the read and write working inventory should not occur in this
2223
2185
        # function - they should be part of lock_write and unlock.
2224
 
        # FIXME: nested trees
2225
 
        inv = self.root_inventory
 
2186
        inv = self.inventory
2226
2187
        for f, file_id, kind in zip(files, ids, kinds):
2227
2188
            if file_id is None:
2228
2189
                inv.add_path(f, kind=kind)
2269
2230
                parent_tree = self.branch.repository.revision_tree(parent_id)
2270
2231
            parent_tree.lock_read()
2271
2232
            try:
2272
 
                try:
2273
 
                    kind = parent_tree.kind(file_id)
2274
 
                except errors.NoSuchId:
 
2233
                if not parent_tree.has_id(file_id):
2275
2234
                    continue
2276
 
                if kind != 'file':
 
2235
                ie = parent_tree.inventory[file_id]
 
2236
                if ie.kind != 'file':
2277
2237
                    # Note: this is slightly unnecessary, because symlinks and
2278
2238
                    # directories have a "text" which is the empty text, and we
2279
2239
                    # know that won't mess up annotations. But it seems cleaner
2280
2240
                    continue
2281
 
                parent_text_key = (
2282
 
                    file_id, parent_tree.get_file_revision(file_id))
 
2241
                parent_text_key = (file_id, ie.revision)
2283
2242
                if parent_text_key not in maybe_file_parent_keys:
2284
2243
                    maybe_file_parent_keys.append(parent_text_key)
2285
2244
            finally:
2300
2259
                       for key, line in annotator.annotate_flat(this_key)]
2301
2260
        return annotations
2302
2261
 
2303
 
    def _put_rio(self, filename, stanzas, header):
2304
 
        self._must_be_locked()
2305
 
        my_file = _mod_rio.rio_file(stanzas, header)
2306
 
        self._transport.put_file(filename, my_file,
2307
 
            mode=self.bzrdir._get_file_mode())
2308
 
 
2309
 
    @needs_tree_write_lock
2310
 
    def set_merge_modified(self, modified_hashes):
2311
 
        def iter_stanzas():
2312
 
            for file_id, hash in modified_hashes.iteritems():
2313
 
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
2314
 
                    hash=hash)
2315
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
2316
 
 
2317
2262
    @needs_read_lock
2318
2263
    def merge_modified(self):
2319
2264
        """Return a dictionary of files modified by a merge.
2339
2284
            for s in _mod_rio.RioReader(hashfile):
2340
2285
                # RioReader reads in Unicode, so convert file_ids back to utf8
2341
2286
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2342
 
                if not self.has_id(file_id):
 
2287
                if not self.inventory.has_id(file_id):
2343
2288
                    continue
2344
2289
                text_hash = s.get("hash")
2345
2290
                if text_hash == self.get_file_sha1(file_id):
2375
2320
        other_tree.lock_tree_write()
2376
2321
        try:
2377
2322
            new_parents = other_tree.get_parent_ids()
2378
 
            other_root = other_tree.root_inventory.root
 
2323
            other_root = other_tree.inventory.root
2379
2324
            other_root.parent_id = new_root_parent
2380
2325
            other_root.name = osutils.basename(other_tree_path)
2381
 
            self.root_inventory.add(other_root)
2382
 
            add_children(self.root_inventory, other_root)
2383
 
            self._write_inventory(self.root_inventory)
 
2326
            self.inventory.add(other_root)
 
2327
            add_children(self.inventory, other_root)
 
2328
            self._write_inventory(self.inventory)
2384
2329
            # normally we don't want to fetch whole repositories, but i think
2385
2330
            # here we really do want to consolidate the whole thing.
2386
2331
            for parent_id in other_tree.get_parent_ids():
2424
2369
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2425
2370
        if tree_transport.base != branch_transport.base:
2426
2371
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2427
 
            tree_bzrdir.set_branch_reference(new_branch)
 
2372
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
 
2373
                target_branch=new_branch)
2428
2374
        else:
2429
2375
            tree_bzrdir = branch_bzrdir
2430
2376
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2431
2377
        wt.set_parent_ids(self.get_parent_ids())
2432
 
        # FIXME: Support nested trees
2433
 
        my_inv = self.root_inventory
 
2378
        my_inv = self.inventory
2434
2379
        child_inv = inventory.Inventory(root_id=None)
2435
2380
        new_root = my_inv[file_id]
2436
2381
        my_inv.remove_recursive_id(file_id)
2456
2401
        if not self.is_locked():
2457
2402
            raise errors.ObjectNotLocked(self)
2458
2403
 
 
2404
        inv = self.inventory
2459
2405
        if from_dir is None and include_root is True:
2460
 
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
 
2406
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2461
2407
        # Convert these into local objects to save lookup times
2462
2408
        pathjoin = osutils.pathjoin
2463
2409
        file_kind = self._kind
2470
2416
 
2471
2417
        # directory file_id, relative path, absolute path, reverse sorted children
2472
2418
        if from_dir is not None:
2473
 
            inv, from_dir_id = self._path2inv_file_id(from_dir)
 
2419
            from_dir_id = inv.path2id(from_dir)
2474
2420
            if from_dir_id is None:
2475
2421
                # Directory not versioned
2476
2422
                return
2477
2423
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2478
2424
        else:
2479
 
            inv = self.root_inventory
2480
2425
            from_dir_id = inv.root.file_id
2481
2426
            from_dir_abspath = self.basedir
2482
2427
        children = os.listdir(from_dir_abspath)
2605
2550
        rename_entries = []
2606
2551
        rename_tuples = []
2607
2552
 
2608
 
        invs_to_write = set()
2609
 
 
2610
2553
        # check for deprecated use of signature
2611
2554
        if to_dir is None:
2612
2555
            raise TypeError('You must supply a target directory')
2613
2556
        # check destination directory
2614
2557
        if isinstance(from_paths, basestring):
2615
2558
            raise ValueError()
 
2559
        inv = self.inventory
2616
2560
        to_abs = self.abspath(to_dir)
2617
2561
        if not isdir(to_abs):
2618
2562
            raise errors.BzrMoveFailedError('',to_dir,
2620
2564
        if not self.has_filename(to_dir):
2621
2565
            raise errors.BzrMoveFailedError('',to_dir,
2622
2566
                errors.NotInWorkingDirectory(to_dir))
2623
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2567
        to_dir_id = inv.path2id(to_dir)
2624
2568
        if to_dir_id is None:
2625
2569
            raise errors.BzrMoveFailedError('',to_dir,
2626
2570
                errors.NotVersionedError(path=to_dir))
2627
2571
 
2628
 
        to_dir_ie = to_inv[to_dir_id]
 
2572
        to_dir_ie = inv[to_dir_id]
2629
2573
        if to_dir_ie.kind != 'directory':
2630
2574
            raise errors.BzrMoveFailedError('',to_dir,
2631
2575
                errors.NotADirectory(to_abs))
2633
2577
        # create rename entries and tuples
2634
2578
        for from_rel in from_paths:
2635
2579
            from_tail = splitpath(from_rel)[-1]
2636
 
            from_inv, from_id = self._path2inv_file_id(from_rel)
 
2580
            from_id = inv.path2id(from_rel)
2637
2581
            if from_id is None:
2638
2582
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2639
2583
                    errors.NotVersionedError(path=from_rel))
2640
2584
 
2641
 
            from_entry = from_inv[from_id]
 
2585
            from_entry = inv[from_id]
2642
2586
            from_parent_id = from_entry.parent_id
2643
2587
            to_rel = pathjoin(to_dir, from_tail)
2644
2588
            rename_entry = InventoryWorkingTree._RenameEntry(
2663
2607
            # restore the inventory on error
2664
2608
            self._inventory_is_modified = original_modified
2665
2609
            raise
2666
 
        #FIXME: Should potentially also write the from_invs
2667
 
        self._write_inventory(to_inv)
 
2610
        self._write_inventory(inv)
2668
2611
        return rename_tuples
2669
2612
 
2670
2613
    @needs_tree_write_lock
2690
2633
 
2691
2634
        Everything else results in an error.
2692
2635
        """
 
2636
        inv = self.inventory
2693
2637
        rename_entries = []
2694
2638
 
2695
2639
        # create rename entries and tuples
2696
2640
        from_tail = splitpath(from_rel)[-1]
2697
 
        from_inv, from_id = self._path2inv_file_id(from_rel)
 
2641
        from_id = inv.path2id(from_rel)
2698
2642
        if from_id is None:
2699
2643
            # if file is missing in the inventory maybe it's in the basis_tree
2700
2644
            basis_tree = self.branch.basis_tree()
2703
2647
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2704
2648
                    errors.NotVersionedError(path=from_rel))
2705
2649
            # put entry back in the inventory so we can rename it
2706
 
            from_entry = basis_tree.root_inventory[from_id].copy()
2707
 
            from_inv.add(from_entry)
 
2650
            from_entry = basis_tree.inventory[from_id].copy()
 
2651
            inv.add(from_entry)
2708
2652
        else:
2709
 
            from_inv, from_inv_id = self._unpack_file_id(from_id)
2710
 
            from_entry = from_inv[from_inv_id]
 
2653
            from_entry = inv[from_id]
2711
2654
        from_parent_id = from_entry.parent_id
2712
2655
        to_dir, to_tail = os.path.split(to_rel)
2713
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2656
        to_dir_id = inv.path2id(to_dir)
2714
2657
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2715
2658
                                     from_id=from_id,
2716
2659
                                     from_tail=from_tail,
2738
2681
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2739
2682
 
2740
2683
        self._move(rename_entries)
2741
 
        self._write_inventory(to_inv)
 
2684
        self._write_inventory(inv)
2742
2685
 
2743
2686
    class _RenameEntry(object):
2744
2687
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2760
2703
 
2761
2704
        Also does basic plausability tests.
2762
2705
        """
2763
 
        # FIXME: Handling of nested trees
2764
 
        inv = self.root_inventory
 
2706
        inv = self.inventory
2765
2707
 
2766
2708
        for rename_entry in rename_entries:
2767
2709
            # store to local variables for easier reference
2812
2754
                # something is wrong, so lets determine what exactly
2813
2755
                if not self.has_filename(from_rel) and \
2814
2756
                   not self.has_filename(to_rel):
2815
 
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
2816
 
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
 
2757
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
 
2758
                        errors.PathsDoNotExist(paths=(str(from_rel),
 
2759
                        str(to_rel))))
2817
2760
                else:
2818
2761
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2819
2762
            rename_entry.only_change_inv = only_change_inv
2825
2768
        Depending on the value of the flag 'only_change_inv', the
2826
2769
        file will be moved on the file system or not.
2827
2770
        """
 
2771
        inv = self.inventory
2828
2772
        moved = []
2829
2773
 
2830
2774
        for entry in rename_entries:
2837
2781
 
2838
2782
    def _rollback_move(self, moved):
2839
2783
        """Try to rollback a previous move in case of an filesystem error."""
 
2784
        inv = self.inventory
2840
2785
        for entry in moved:
2841
2786
            try:
2842
2787
                self._move_entry(WorkingTree._RenameEntry(
2851
2796
                        " Error message is: %s" % e)
2852
2797
 
2853
2798
    def _move_entry(self, entry):
2854
 
        inv = self.root_inventory
 
2799
        inv = self.inventory
2855
2800
        from_rel_abs = self.abspath(entry.from_rel)
2856
2801
        to_rel_abs = self.abspath(entry.to_rel)
2857
2802
        if from_rel_abs == to_rel_abs:
2898
2843
 
2899
2844
    def stored_kind(self, file_id):
2900
2845
        """See Tree.stored_kind"""
2901
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2902
 
        return inv[inv_file_id].kind
 
2846
        return self.inventory[file_id].kind
2903
2847
 
2904
2848
    def extras(self):
2905
2849
        """Yield all unversioned files in this WorkingTree.
2912
2856
        This is the same order used by 'osutils.walkdirs'.
2913
2857
        """
2914
2858
        ## TODO: Work from given directory downwards
2915
 
        for path, dir_entry in self.iter_entries_by_dir():
2916
 
            if dir_entry.kind != 'directory':
2917
 
                continue
 
2859
        for path, dir_entry in self.inventory.directories():
2918
2860
            # mutter("search for unknowns in %r", path)
2919
2861
            dirabs = self.abspath(path)
2920
2862
            if not isdir(dirabs):
2957
2899
        """
2958
2900
        _directory = 'directory'
2959
2901
        # get the root in the inventory
2960
 
        inv, top_id = self._path2inv_file_id(prefix)
 
2902
        inv = self.inventory
 
2903
        top_id = inv.path2id(prefix)
2961
2904
        if top_id is None:
2962
2905
            pending = []
2963
2906
        else:
2984
2927
                if dir[2] == _directory:
2985
2928
                    pending.append(dir)
2986
2929
 
2987
 
    @needs_write_lock
2988
 
    def update_feature_flags(self, updated_flags):
2989
 
        """Update the feature flags for this branch.
2990
 
 
2991
 
        :param updated_flags: Dictionary mapping feature names to necessities
2992
 
            A necessity can be None to indicate the feature should be removed
2993
 
        """
2994
 
        self._format._update_feature_flags(updated_flags)
2995
 
        self.control_transport.put_bytes('format', self._format.as_string())
2996
 
 
2997
2930
 
2998
2931
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2999
2932
    """Registry for working tree formats."""
3055
2988
 
3056
2989
    supports_versioned_directories = None
3057
2990
 
 
2991
    @classmethod
 
2992
    def find_format_string(klass, controldir):
 
2993
        """Return format name for the working tree object in controldir."""
 
2994
        try:
 
2995
            transport = controldir.get_workingtree_transport(None)
 
2996
            return transport.get_bytes("format")
 
2997
        except errors.NoSuchFile:
 
2998
            raise errors.NoWorkingTree(base=transport.base)
 
2999
 
 
3000
    @classmethod
 
3001
    def find_format(klass, controldir):
 
3002
        """Return the format for the working tree object in controldir."""
 
3003
        try:
 
3004
            format_string = klass.find_format_string(controldir)
 
3005
            return format_registry.get(format_string)
 
3006
        except KeyError:
 
3007
            raise errors.UnknownFormatError(format=format_string,
 
3008
                                            kind="working tree")
 
3009
 
3058
3010
    def initialize(self, controldir, revision_id=None, from_branch=None,
3059
3011
                   accelerator_tree=None, hardlink=False):
3060
3012
        """Initialize a new working tree in controldir.
3085
3037
        """Return the current default format."""
3086
3038
        return format_registry.get_default()
3087
3039
 
 
3040
    def get_format_string(self):
 
3041
        """Return the ASCII format string that identifies this format."""
 
3042
        raise NotImplementedError(self.get_format_string)
 
3043
 
3088
3044
    def get_format_description(self):
3089
3045
        """Return the short description for this format."""
3090
3046
        raise NotImplementedError(self.get_format_description)
3151
3107
        return self._matchingbzrdir
3152
3108
 
3153
3109
 
3154
 
class WorkingTreeFormatMetaDir(bzrdir.BzrFormat, WorkingTreeFormat):
3155
 
    """Base class for working trees that live in bzr meta directories."""
3156
 
 
3157
 
    def __init__(self):
3158
 
        WorkingTreeFormat.__init__(self)
3159
 
        bzrdir.BzrFormat.__init__(self)
3160
 
 
3161
 
    @classmethod
3162
 
    def find_format_string(klass, controldir):
3163
 
        """Return format name for the working tree object in controldir."""
3164
 
        try:
3165
 
            transport = controldir.get_workingtree_transport(None)
3166
 
            return transport.get_bytes("format")
3167
 
        except errors.NoSuchFile:
3168
 
            raise errors.NoWorkingTree(base=transport.base)
3169
 
 
3170
 
    @classmethod
3171
 
    def find_format(klass, controldir):
3172
 
        """Return the format for the working tree object in controldir."""
3173
 
        format_string = klass.find_format_string(controldir)
3174
 
        return klass._find_format(format_registry, 'working tree',
3175
 
                format_string)
3176
 
 
3177
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
3178
 
            basedir=None):
3179
 
        WorkingTreeFormat.check_support_status(self,
3180
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
3181
 
            basedir=basedir)
3182
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
3183
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
3184
 
 
3185
 
 
3186
3110
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3187
3111
    "bzrlib.workingtree_4", "WorkingTreeFormat4")
3188
3112
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",