~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Patch Queue Manager
  • Date: 2016-02-01 19:13:13 UTC
  • mfrom: (6614.2.2 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20160201191313-wdfvmfff1djde6oq
(vila) Release 2.7.0 (Vincent Ladeuil)

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
32
33
 
33
34
from cStringIO import StringIO
34
35
import os
46
47
 
47
48
from bzrlib import (
48
49
    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,
63
64
    transform,
64
65
    transport,
65
66
    ui,
69
70
    )
70
71
""")
71
72
 
72
 
from bzrlib import symbol_versioning
 
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
 
73
80
from bzrlib.decorators import needs_read_lock, needs_write_lock
74
81
from bzrlib.i18n import gettext
75
82
from bzrlib.lock import LogicalLockResult
84
91
    realpath,
85
92
    safe_unicode,
86
93
    splitpath,
87
 
    supports_executable,
88
94
    )
89
95
from bzrlib.trace import mutter, note
90
96
from bzrlib.revision import CURRENT_REVISION
172
178
 
173
179
    def __init__(self, basedir='.',
174
180
                 branch=DEPRECATED_PARAMETER,
175
 
                 _control_files=None,
176
181
                 _internal=False,
 
182
                 _transport=None,
177
183
                 _format=None,
178
184
                 _bzrdir=None):
179
185
        """Construct a WorkingTree instance. This is not a public API.
192
198
        else:
193
199
            self._branch = self.bzrdir.open_branch()
194
200
        self.basedir = realpath(basedir)
195
 
        self._control_files = _control_files
196
 
        self._transport = self._control_files._transport
 
201
        self._transport = _transport
197
202
        self._rules_searcher = None
198
203
        self.views = self._make_views()
199
204
 
229
234
        """See `Tree.has_versioned_directories`."""
230
235
        return self._format.supports_versioned_directories
231
236
 
 
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
 
232
243
    def break_lock(self):
233
244
        """Break a lock if one is present from another instance.
234
245
 
237
248
 
238
249
        This will probe the repository for its lock as well.
239
250
        """
240
 
        self._control_files.break_lock()
241
 
        self.branch.break_lock()
 
251
        raise NotImplementedError(self.break_lock)
242
252
 
243
253
    def requires_rich_root(self):
244
254
        return self._format.requires_rich_root
252
262
    def supports_views(self):
253
263
        return self.views.supports_views()
254
264
 
 
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
 
255
273
    @staticmethod
256
274
    def open(path=None, _unsupported=False):
257
275
        """Open an existing working tree at path.
259
277
        """
260
278
        if path is None:
261
279
            path = osutils.getcwd()
262
 
        control = controldir.ControlDir.open(path, _unsupported)
263
 
        return control.open_workingtree(_unsupported)
 
280
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
 
281
        return control.open_workingtree(unsupported=_unsupported)
264
282
 
265
283
    @staticmethod
266
284
    def open_containing(path=None):
370
388
                                              list_current=list_current)
371
389
        return [tr for tr in iterator if tr is not None]
372
390
 
373
 
    def all_file_ids(self):
374
 
        """See Tree.iter_all_file_ids"""
375
 
        raise NotImplementedError(self.all_file_ids)
376
 
 
377
391
    def __repr__(self):
378
392
        return "<%s of %s>" % (self.__class__.__name__,
379
393
                               getattr(self, 'basedir', None))
521
535
        else:
522
536
            # TODO now merge from tree.last_revision to revision (to preserve
523
537
            # user local changes)
524
 
            merge.transform_tree(tree, self)
 
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)
525
544
            if revision_id == _mod_revision.NULL_REVISION:
526
545
                new_parents = []
527
546
            else:
741
760
 
742
761
    @needs_tree_write_lock
743
762
    def set_merge_modified(self, modified_hashes):
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)
 
763
        """Set the merge modified hashes."""
 
764
        raise NotImplementedError(self.set_merge_modified)
749
765
 
750
766
    def _sha_from_stat(self, path, stat_result):
751
767
        """Get a sha digest from the tree's stat cache.
757
773
        """
758
774
        return None
759
775
 
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
 
 
766
776
    @needs_write_lock # because merge pulls data into the branch.
767
777
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
768
778
                          merge_type=None, force=False):
1123
1133
        else:
1124
1134
            mode = stat_value.st_mode
1125
1135
            kind = osutils.file_kind_from_stat_mode(mode)
1126
 
            if not supports_executable():
 
1136
            if not self._supports_executable():
1127
1137
                executable = entry is not None and entry.executable
1128
1138
            else:
1129
1139
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1148
1158
        return _mod_revision.ensure_null(self.branch.last_revision())
1149
1159
 
1150
1160
    def is_locked(self):
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)
 
1161
        """Check if this tree is locked."""
 
1162
        raise NotImplementedError(self.is_locked)
1156
1163
 
1157
1164
    def lock_read(self):
1158
1165
        """Lock the tree for reading.
1161
1168
 
1162
1169
        :return: A bzrlib.lock.LogicalLockResult.
1163
1170
        """
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
 
1171
        raise NotImplementedError(self.lock_read)
1173
1172
 
1174
1173
    def lock_tree_write(self):
1175
1174
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1176
1175
 
1177
1176
        :return: A bzrlib.lock.LogicalLockResult.
1178
1177
        """
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
 
1178
        raise NotImplementedError(self.lock_tree_write)
1188
1179
 
1189
1180
    def lock_write(self):
1190
1181
        """See MutableTree.lock_write, and WorkingTree.unlock.
1191
1182
 
1192
1183
        :return: A bzrlib.lock.LogicalLockResult.
1193
1184
        """
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
 
1185
        raise NotImplementedError(self.lock_write)
1203
1186
 
1204
1187
    def get_physical_lock_status(self):
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)
 
1188
        raise NotImplementedError(self.get_physical_lock_status)
1210
1189
 
1211
1190
    def set_last_revision(self, new_revision):
1212
1191
        """Change the last revision in the working tree."""
1378
1357
                basis_tree.unlock()
1379
1358
        return conflicts
1380
1359
 
 
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
 
1381
1388
    def revision_tree(self, revision_id):
1382
1389
        """See Tree.revision_tree.
1383
1390
 
1788
1795
        :param branch: A branch to override probing for the branch.
1789
1796
        """
1790
1797
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1791
 
            branch=branch, _control_files=_control_files, _internal=_internal,
1792
 
            _format=_format, _bzrdir=_bzrdir)
 
1798
            branch=branch, _transport=_control_files._transport,
 
1799
            _internal=_internal, _format=_format, _bzrdir=_bzrdir)
1793
1800
 
 
1801
        self._control_files = _control_files
1794
1802
        self._detect_case_handling()
1795
1803
 
1796
1804
        if _inventory is None:
1835
1843
    def _deserialize(selt, in_file):
1836
1844
        return xml5.serializer_v5.read_inventory(in_file)
1837
1845
 
 
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
 
1838
1914
    @needs_tree_write_lock
1839
1915
    def _write_inventory(self, inv):
1840
1916
        """Write inventory as the current inventory."""
1908
1984
            if entry.parent_id == orig_root_id:
1909
1985
                entry.parent_id = inv.root.file_id
1910
1986
 
1911
 
    def all_file_ids(self):
1912
 
        """See Tree.iter_all_file_ids"""
1913
 
        return set(self.inventory)
1914
 
 
1915
1987
    @needs_tree_write_lock
1916
1988
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1917
1989
        """See MutableTree.set_parent_trees."""
1936
2008
                # parent tree from the repository.
1937
2009
                self._cache_basis_inventory(leftmost_parent_id)
1938
2010
            else:
1939
 
                inv = leftmost_parent_tree.inventory
 
2011
                inv = leftmost_parent_tree.root_inventory
1940
2012
                xml = self._create_basis_xml_from_inventory(
1941
2013
                                        leftmost_parent_id, inv)
1942
2014
                self._write_basis_inventory(xml)
2039
2111
 
2040
2112
    def has_id(self, file_id):
2041
2113
        # files that have been deleted are excluded
2042
 
        inv = self.inventory
2043
 
        if not inv.has_id(file_id):
 
2114
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2115
        if not inv.has_id(inv_file_id):
2044
2116
            return False
2045
 
        path = inv.id2path(file_id)
 
2117
        path = inv.id2path(inv_file_id)
2046
2118
        return osutils.lexists(self.abspath(path))
2047
2119
 
2048
2120
    def has_or_had_id(self, file_id):
2049
 
        if file_id == self.inventory.root.file_id:
 
2121
        if file_id == self.get_root_id():
2050
2122
            return True
2051
 
        return self.inventory.has_id(file_id)
 
2123
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2124
        return inv.has_id(inv_file_id)
2052
2125
 
2053
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2054
 
    def __iter__(self):
 
2126
    def all_file_ids(self):
2055
2127
        """Iterate through file_ids for this tree.
2056
2128
 
2057
2129
        file_ids are in a WorkingTree if they are in the working inventory
2058
2130
        and the working file exists.
2059
2131
        """
2060
 
        inv = self._inventory
2061
 
        for path, ie in inv.iter_entries():
2062
 
            if osutils.lexists(self.abspath(path)):
2063
 
                yield ie.file_id
 
2132
        ret = set()
 
2133
        for path, ie in self.iter_entries_by_dir():
 
2134
            ret.add(ie.file_id)
 
2135
        return ret
2064
2136
 
2065
2137
    @needs_tree_write_lock
2066
2138
    def set_last_revision(self, new_revision):
2122
2194
                _mod_revision.NULL_REVISION)
2123
2195
        else:
2124
2196
            rt = self.branch.repository.revision_tree(revision_ids[0])
2125
 
        self._write_inventory(rt.inventory)
 
2197
        self._write_inventory(rt.root_inventory)
2126
2198
        self.set_parent_ids(revision_ids)
2127
2199
 
2128
2200
    def flush(self):
2140
2212
    def get_file_mtime(self, file_id, path=None):
2141
2213
        """See Tree.get_file_mtime."""
2142
2214
        if not path:
2143
 
            path = self.inventory.id2path(file_id)
 
2215
            path = self.id2path(file_id)
2144
2216
        try:
2145
2217
            return os.lstat(self.abspath(path)).st_mtime
2146
2218
        except OSError, e:
2149
2221
            raise
2150
2222
 
2151
2223
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2152
 
        file_id = self.path2id(path)
 
2224
        inv, file_id = self._path2inv_file_id(path)
2153
2225
        if file_id is None:
2154
2226
            # For unversioned files on win32, we just assume they are not
2155
2227
            # executable
2156
2228
            return False
2157
 
        return self._inventory[file_id].executable
 
2229
        return inv[file_id].executable
2158
2230
 
2159
2231
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2160
2232
        mode = stat_result.st_mode
2161
2233
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2162
2234
 
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):
 
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:
2171
2240
            if not path:
2172
2241
                path = self.id2path(file_id)
2173
2242
            mode = os.lstat(self.abspath(path)).st_mode
2174
2243
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2175
2244
 
2176
 
        _is_executable_from_path_and_stat = \
2177
 
            _is_executable_from_path_and_stat_from_stat
 
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)
2178
2250
 
2179
2251
    @needs_tree_write_lock
2180
2252
    def _add(self, files, ids, kinds):
2183
2255
        # should probably put it back with the previous ID.
2184
2256
        # the read and write working inventory should not occur in this
2185
2257
        # function - they should be part of lock_write and unlock.
2186
 
        inv = self.inventory
 
2258
        # FIXME: nested trees
 
2259
        inv = self.root_inventory
2187
2260
        for f, file_id, kind in zip(files, ids, kinds):
2188
2261
            if file_id is None:
2189
2262
                inv.add_path(f, kind=kind)
2230
2303
                parent_tree = self.branch.repository.revision_tree(parent_id)
2231
2304
            parent_tree.lock_read()
2232
2305
            try:
2233
 
                if not parent_tree.has_id(file_id):
 
2306
                try:
 
2307
                    kind = parent_tree.kind(file_id)
 
2308
                except errors.NoSuchId:
2234
2309
                    continue
2235
 
                ie = parent_tree.inventory[file_id]
2236
 
                if ie.kind != 'file':
 
2310
                if kind != 'file':
2237
2311
                    # Note: this is slightly unnecessary, because symlinks and
2238
2312
                    # directories have a "text" which is the empty text, and we
2239
2313
                    # know that won't mess up annotations. But it seems cleaner
2240
2314
                    continue
2241
 
                parent_text_key = (file_id, ie.revision)
 
2315
                parent_text_key = (
 
2316
                    file_id, parent_tree.get_file_revision(file_id))
2242
2317
                if parent_text_key not in maybe_file_parent_keys:
2243
2318
                    maybe_file_parent_keys.append(parent_text_key)
2244
2319
            finally:
2259
2334
                       for key, line in annotator.annotate_flat(this_key)]
2260
2335
        return annotations
2261
2336
 
 
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
 
2262
2351
    @needs_read_lock
2263
2352
    def merge_modified(self):
2264
2353
        """Return a dictionary of files modified by a merge.
2284
2373
            for s in _mod_rio.RioReader(hashfile):
2285
2374
                # RioReader reads in Unicode, so convert file_ids back to utf8
2286
2375
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2287
 
                if not self.inventory.has_id(file_id):
 
2376
                if not self.has_id(file_id):
2288
2377
                    continue
2289
2378
                text_hash = s.get("hash")
2290
2379
                if text_hash == self.get_file_sha1(file_id):
2320
2409
        other_tree.lock_tree_write()
2321
2410
        try:
2322
2411
            new_parents = other_tree.get_parent_ids()
2323
 
            other_root = other_tree.inventory.root
 
2412
            other_root = other_tree.root_inventory.root
2324
2413
            other_root.parent_id = new_root_parent
2325
2414
            other_root.name = osutils.basename(other_tree_path)
2326
 
            self.inventory.add(other_root)
2327
 
            add_children(self.inventory, other_root)
2328
 
            self._write_inventory(self.inventory)
 
2415
            self.root_inventory.add(other_root)
 
2416
            add_children(self.root_inventory, other_root)
 
2417
            self._write_inventory(self.root_inventory)
2329
2418
            # normally we don't want to fetch whole repositories, but i think
2330
2419
            # here we really do want to consolidate the whole thing.
2331
2420
            for parent_id in other_tree.get_parent_ids():
2369
2458
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2370
2459
        if tree_transport.base != branch_transport.base:
2371
2460
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2372
 
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
2373
 
                target_branch=new_branch)
 
2461
            tree_bzrdir.set_branch_reference(new_branch)
2374
2462
        else:
2375
2463
            tree_bzrdir = branch_bzrdir
2376
2464
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2377
2465
        wt.set_parent_ids(self.get_parent_ids())
2378
 
        my_inv = self.inventory
 
2466
        # FIXME: Support nested trees
 
2467
        my_inv = self.root_inventory
2379
2468
        child_inv = inventory.Inventory(root_id=None)
2380
2469
        new_root = my_inv[file_id]
2381
2470
        my_inv.remove_recursive_id(file_id)
2401
2490
        if not self.is_locked():
2402
2491
            raise errors.ObjectNotLocked(self)
2403
2492
 
2404
 
        inv = self.inventory
2405
2493
        if from_dir is None and include_root is True:
2406
 
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
 
2494
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
2407
2495
        # Convert these into local objects to save lookup times
2408
2496
        pathjoin = osutils.pathjoin
2409
2497
        file_kind = self._kind
2416
2504
 
2417
2505
        # directory file_id, relative path, absolute path, reverse sorted children
2418
2506
        if from_dir is not None:
2419
 
            from_dir_id = inv.path2id(from_dir)
 
2507
            inv, from_dir_id = self._path2inv_file_id(from_dir)
2420
2508
            if from_dir_id is None:
2421
2509
                # Directory not versioned
2422
2510
                return
2423
2511
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2424
2512
        else:
 
2513
            inv = self.root_inventory
2425
2514
            from_dir_id = inv.root.file_id
2426
2515
            from_dir_abspath = self.basedir
2427
2516
        children = os.listdir(from_dir_abspath)
2550
2639
        rename_entries = []
2551
2640
        rename_tuples = []
2552
2641
 
 
2642
        invs_to_write = set()
 
2643
 
2553
2644
        # check for deprecated use of signature
2554
2645
        if to_dir is None:
2555
2646
            raise TypeError('You must supply a target directory')
2556
2647
        # check destination directory
2557
2648
        if isinstance(from_paths, basestring):
2558
2649
            raise ValueError()
2559
 
        inv = self.inventory
2560
2650
        to_abs = self.abspath(to_dir)
2561
2651
        if not isdir(to_abs):
2562
2652
            raise errors.BzrMoveFailedError('',to_dir,
2564
2654
        if not self.has_filename(to_dir):
2565
2655
            raise errors.BzrMoveFailedError('',to_dir,
2566
2656
                errors.NotInWorkingDirectory(to_dir))
2567
 
        to_dir_id = inv.path2id(to_dir)
 
2657
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2568
2658
        if to_dir_id is None:
2569
2659
            raise errors.BzrMoveFailedError('',to_dir,
2570
2660
                errors.NotVersionedError(path=to_dir))
2571
2661
 
2572
 
        to_dir_ie = inv[to_dir_id]
 
2662
        to_dir_ie = to_inv[to_dir_id]
2573
2663
        if to_dir_ie.kind != 'directory':
2574
2664
            raise errors.BzrMoveFailedError('',to_dir,
2575
2665
                errors.NotADirectory(to_abs))
2577
2667
        # create rename entries and tuples
2578
2668
        for from_rel in from_paths:
2579
2669
            from_tail = splitpath(from_rel)[-1]
2580
 
            from_id = inv.path2id(from_rel)
 
2670
            from_inv, from_id = self._path2inv_file_id(from_rel)
2581
2671
            if from_id is None:
2582
2672
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2583
2673
                    errors.NotVersionedError(path=from_rel))
2584
2674
 
2585
 
            from_entry = inv[from_id]
 
2675
            from_entry = from_inv[from_id]
2586
2676
            from_parent_id = from_entry.parent_id
2587
2677
            to_rel = pathjoin(to_dir, from_tail)
2588
2678
            rename_entry = InventoryWorkingTree._RenameEntry(
2607
2697
            # restore the inventory on error
2608
2698
            self._inventory_is_modified = original_modified
2609
2699
            raise
2610
 
        self._write_inventory(inv)
 
2700
        #FIXME: Should potentially also write the from_invs
 
2701
        self._write_inventory(to_inv)
2611
2702
        return rename_tuples
2612
2703
 
2613
2704
    @needs_tree_write_lock
2633
2724
 
2634
2725
        Everything else results in an error.
2635
2726
        """
2636
 
        inv = self.inventory
2637
2727
        rename_entries = []
2638
2728
 
2639
2729
        # create rename entries and tuples
2640
2730
        from_tail = splitpath(from_rel)[-1]
2641
 
        from_id = inv.path2id(from_rel)
 
2731
        from_inv, from_id = self._path2inv_file_id(from_rel)
2642
2732
        if from_id is None:
2643
2733
            # if file is missing in the inventory maybe it's in the basis_tree
2644
2734
            basis_tree = self.branch.basis_tree()
2647
2737
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2648
2738
                    errors.NotVersionedError(path=from_rel))
2649
2739
            # put entry back in the inventory so we can rename it
2650
 
            from_entry = basis_tree.inventory[from_id].copy()
2651
 
            inv.add(from_entry)
 
2740
            from_entry = basis_tree.root_inventory[from_id].copy()
 
2741
            from_inv.add(from_entry)
2652
2742
        else:
2653
 
            from_entry = inv[from_id]
 
2743
            from_inv, from_inv_id = self._unpack_file_id(from_id)
 
2744
            from_entry = from_inv[from_inv_id]
2654
2745
        from_parent_id = from_entry.parent_id
2655
2746
        to_dir, to_tail = os.path.split(to_rel)
2656
 
        to_dir_id = inv.path2id(to_dir)
 
2747
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2657
2748
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2658
2749
                                     from_id=from_id,
2659
2750
                                     from_tail=from_tail,
2681
2772
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2682
2773
 
2683
2774
        self._move(rename_entries)
2684
 
        self._write_inventory(inv)
 
2775
        self._write_inventory(to_inv)
2685
2776
 
2686
2777
    class _RenameEntry(object):
2687
2778
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2703
2794
 
2704
2795
        Also does basic plausability tests.
2705
2796
        """
2706
 
        inv = self.inventory
 
2797
        # FIXME: Handling of nested trees
 
2798
        inv = self.root_inventory
2707
2799
 
2708
2800
        for rename_entry in rename_entries:
2709
2801
            # store to local variables for easier reference
2754
2846
                # something is wrong, so lets determine what exactly
2755
2847
                if not self.has_filename(from_rel) and \
2756
2848
                   not self.has_filename(to_rel):
2757
 
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
2758
 
                        errors.PathsDoNotExist(paths=(str(from_rel),
2759
 
                        str(to_rel))))
 
2849
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
 
2850
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
2760
2851
                else:
2761
2852
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2762
2853
            rename_entry.only_change_inv = only_change_inv
2768
2859
        Depending on the value of the flag 'only_change_inv', the
2769
2860
        file will be moved on the file system or not.
2770
2861
        """
2771
 
        inv = self.inventory
2772
2862
        moved = []
2773
2863
 
2774
2864
        for entry in rename_entries:
2781
2871
 
2782
2872
    def _rollback_move(self, moved):
2783
2873
        """Try to rollback a previous move in case of an filesystem error."""
2784
 
        inv = self.inventory
2785
2874
        for entry in moved:
2786
2875
            try:
2787
2876
                self._move_entry(WorkingTree._RenameEntry(
2796
2885
                        " Error message is: %s" % e)
2797
2886
 
2798
2887
    def _move_entry(self, entry):
2799
 
        inv = self.inventory
 
2888
        inv = self.root_inventory
2800
2889
        from_rel_abs = self.abspath(entry.from_rel)
2801
2890
        to_rel_abs = self.abspath(entry.to_rel)
2802
2891
        if from_rel_abs == to_rel_abs:
2843
2932
 
2844
2933
    def stored_kind(self, file_id):
2845
2934
        """See Tree.stored_kind"""
2846
 
        return self.inventory[file_id].kind
 
2935
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2936
        return inv[inv_file_id].kind
2847
2937
 
2848
2938
    def extras(self):
2849
2939
        """Yield all unversioned files in this WorkingTree.
2856
2946
        This is the same order used by 'osutils.walkdirs'.
2857
2947
        """
2858
2948
        ## TODO: Work from given directory downwards
2859
 
        for path, dir_entry in self.inventory.directories():
 
2949
        for path, dir_entry in self.iter_entries_by_dir():
 
2950
            if dir_entry.kind != 'directory':
 
2951
                continue
2860
2952
            # mutter("search for unknowns in %r", path)
2861
2953
            dirabs = self.abspath(path)
2862
2954
            if not isdir(dirabs):
2899
2991
        """
2900
2992
        _directory = 'directory'
2901
2993
        # get the root in the inventory
2902
 
        inv = self.inventory
2903
 
        top_id = inv.path2id(prefix)
 
2994
        inv, top_id = self._path2inv_file_id(prefix)
2904
2995
        if top_id is None:
2905
2996
            pending = []
2906
2997
        else:
2927
3018
                if dir[2] == _directory:
2928
3019
                    pending.append(dir)
2929
3020
 
 
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
 
2930
3031
 
2931
3032
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2932
3033
    """Registry for working tree formats."""
2988
3089
 
2989
3090
    supports_versioned_directories = None
2990
3091
 
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
 
 
3010
3092
    def initialize(self, controldir, revision_id=None, from_branch=None,
3011
3093
                   accelerator_tree=None, hardlink=False):
3012
3094
        """Initialize a new working tree in controldir.
3030
3112
    def __ne__(self, other):
3031
3113
        return not (self == other)
3032
3114
 
3033
 
    @classmethod
3034
 
    @symbol_versioning.deprecated_method(
3035
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3036
 
    def get_default_format(klass):
3037
 
        """Return the current default format."""
3038
 
        return format_registry.get_default()
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
 
 
3044
3115
    def get_format_description(self):
3045
3116
        """Return the short description for this format."""
3046
3117
        raise NotImplementedError(self.get_format_description)
3062
3133
        """True if this format supports stored views."""
3063
3134
        return False
3064
3135
 
3065
 
    @classmethod
3066
 
    @symbol_versioning.deprecated_method(
3067
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3068
 
    def register_format(klass, format):
3069
 
        format_registry.register(format)
3070
 
 
3071
 
    @classmethod
3072
 
    @symbol_versioning.deprecated_method(
3073
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3074
 
    def register_extra_format(klass, format):
3075
 
        format_registry.register_extra(format)
3076
 
 
3077
 
    @classmethod
3078
 
    @symbol_versioning.deprecated_method(
3079
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3080
 
    def unregister_extra_format(klass, format):
3081
 
        format_registry.unregister_extra(format)
3082
 
 
3083
 
    @classmethod
3084
 
    @symbol_versioning.deprecated_method(
3085
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3086
 
    def get_formats(klass):
3087
 
        return format_registry._get_all()
3088
 
 
3089
 
    @classmethod
3090
 
    @symbol_versioning.deprecated_method(
3091
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3092
 
    def set_default_format(klass, format):
3093
 
        format_registry.set_default(format)
3094
 
 
3095
 
    @classmethod
3096
 
    @symbol_versioning.deprecated_method(
3097
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3098
 
    def unregister_format(klass, format):
3099
 
        format_registry.remove(format)
3100
 
 
3101
 
    def get_controldir_for_branch(self):
3102
 
        """Get the control directory format for creating branches.
3103
 
 
3104
 
        This is to support testing of working tree formats that can not exist
3105
 
        in the same control directory as a branch.
3106
 
        """
3107
 
        return self._matchingbzrdir
 
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)
3108
3215
 
3109
3216
 
3110
3217
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",