~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Patch Queue Manager
  • Date: 2013-05-20 17:46:29 UTC
  • mfrom: (6573.1.1 bzr)
  • Revision ID: pqm@pqm.ubuntu.com-20130520174629-dp7zujtuclvomuzd
(jameinel) Fix CVE 2013-2009. Avoid allowing multiple wildcards in a single
 SSL cert hostname segment. (Andrew Starr-Bochicchio)

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 = bzrdir.BzrDir.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):
277
295
        """
278
296
        if path is None:
279
297
            path = osutils.getcwd()
280
 
        control, relpath = bzrdir.BzrDir.open_containing(path)
 
298
        control, relpath = controldir.ControlDir.open_containing(path)
281
299
        return control.open_workingtree(), relpath
282
300
 
283
301
    @staticmethod
366
384
            else:
367
385
                return True, tree
368
386
        t = transport.get_transport(location)
369
 
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
 
387
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
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))
494
508
        raise NotImplementedError(self.get_root_id)
495
509
 
496
510
    @needs_read_lock
497
 
    def clone(self, to_bzrdir, revision_id=None):
 
511
    def clone(self, to_controldir, revision_id=None):
498
512
        """Duplicate this working tree into to_bzr, including all state.
499
513
 
500
514
        Specifically modified files are kept as modified, but
501
515
        ignored and unknown files are discarded.
502
516
 
503
 
        If you want to make a new line of development, see bzrdir.sprout()
 
517
        If you want to make a new line of development, see ControlDir.sprout()
504
518
 
505
519
        revision
506
520
            If not None, the cloned tree will have its last revision set to
508
522
            and this one merged in.
509
523
        """
510
524
        # assumes the target bzr dir format is compatible.
511
 
        result = to_bzrdir.create_workingtree()
 
525
        result = to_controldir.create_workingtree()
512
526
        self.copy_content_into(result, revision_id)
513
527
        return result
514
528
 
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)
525
 
            tree.set_parent_ids([revision_id])
 
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)
526
549
 
527
550
    def id2abspath(self, file_id):
528
551
        return self.abspath(self.id2path(file_id))
737
760
 
738
761
    @needs_tree_write_lock
739
762
    def set_merge_modified(self, modified_hashes):
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)
 
763
        """Set the merge modified hashes."""
 
764
        raise NotImplementedError(self.set_merge_modified)
745
765
 
746
766
    def _sha_from_stat(self, path, stat_result):
747
767
        """Get a sha digest from the tree's stat cache.
753
773
        """
754
774
        return None
755
775
 
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
 
 
762
776
    @needs_write_lock # because merge pulls data into the branch.
763
777
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
764
778
                          merge_type=None, force=False):
1004
1018
                                show_base=show_base)
1005
1019
                    basis_root_id = basis_tree.get_root_id()
1006
1020
                    new_root_id = new_basis_tree.get_root_id()
1007
 
                    if basis_root_id != new_root_id:
 
1021
                    if new_root_id is not None and basis_root_id != new_root_id:
1008
1022
                        self.set_root_id(new_root_id)
1009
1023
                finally:
1010
1024
                    basis_tree.unlock()
1011
1025
                # TODO - dedup parents list with things merged by pull ?
1012
1026
                # reuse the revisiontree we merged against to set the new
1013
1027
                # tree data.
1014
 
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
 
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))
1015
1032
                # we have to pull the merge trees out again, because
1016
1033
                # merge_inner has set the ids. - this corner is not yet
1017
1034
                # layered well enough to prevent double handling.
1116
1133
        else:
1117
1134
            mode = stat_value.st_mode
1118
1135
            kind = osutils.file_kind_from_stat_mode(mode)
1119
 
            if not supports_executable():
 
1136
            if not self._supports_executable():
1120
1137
                executable = entry is not None and entry.executable
1121
1138
            else:
1122
1139
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1141
1158
        return _mod_revision.ensure_null(self.branch.last_revision())
1142
1159
 
1143
1160
    def is_locked(self):
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)
 
1161
        """Check if this tree is locked."""
 
1162
        raise NotImplementedError(self.is_locked)
1149
1163
 
1150
1164
    def lock_read(self):
1151
1165
        """Lock the tree for reading.
1154
1168
 
1155
1169
        :return: A bzrlib.lock.LogicalLockResult.
1156
1170
        """
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
 
1171
        raise NotImplementedError(self.lock_read)
1166
1172
 
1167
1173
    def lock_tree_write(self):
1168
1174
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1169
1175
 
1170
1176
        :return: A bzrlib.lock.LogicalLockResult.
1171
1177
        """
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
 
1178
        raise NotImplementedError(self.lock_tree_write)
1181
1179
 
1182
1180
    def lock_write(self):
1183
1181
        """See MutableTree.lock_write, and WorkingTree.unlock.
1184
1182
 
1185
1183
        :return: A bzrlib.lock.LogicalLockResult.
1186
1184
        """
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
 
1185
        raise NotImplementedError(self.lock_write)
1196
1186
 
1197
1187
    def get_physical_lock_status(self):
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)
 
1188
        raise NotImplementedError(self.get_physical_lock_status)
1203
1189
 
1204
1190
    def set_last_revision(self, new_revision):
1205
1191
        """Change the last revision in the working tree."""
1371
1357
                basis_tree.unlock()
1372
1358
        return conflicts
1373
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
 
1374
1388
    def revision_tree(self, revision_id):
1375
1389
        """See Tree.revision_tree.
1376
1390
 
1781
1795
        :param branch: A branch to override probing for the branch.
1782
1796
        """
1783
1797
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1784
 
            branch=branch, _control_files=_control_files, _internal=_internal,
1785
 
            _format=_format, _bzrdir=_bzrdir)
 
1798
            branch=branch, _transport=_control_files._transport,
 
1799
            _internal=_internal, _format=_format, _bzrdir=_bzrdir)
1786
1800
 
 
1801
        self._control_files = _control_files
1787
1802
        self._detect_case_handling()
1788
1803
 
1789
1804
        if _inventory is None:
1828
1843
    def _deserialize(selt, in_file):
1829
1844
        return xml5.serializer_v5.read_inventory(in_file)
1830
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
 
1831
1914
    @needs_tree_write_lock
1832
1915
    def _write_inventory(self, inv):
1833
1916
        """Write inventory as the current inventory."""
1901
1984
            if entry.parent_id == orig_root_id:
1902
1985
                entry.parent_id = inv.root.file_id
1903
1986
 
1904
 
    def all_file_ids(self):
1905
 
        """See Tree.iter_all_file_ids"""
1906
 
        return set(self.inventory)
1907
 
 
1908
1987
    @needs_tree_write_lock
1909
1988
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1910
1989
        """See MutableTree.set_parent_trees."""
1929
2008
                # parent tree from the repository.
1930
2009
                self._cache_basis_inventory(leftmost_parent_id)
1931
2010
            else:
1932
 
                inv = leftmost_parent_tree.inventory
 
2011
                inv = leftmost_parent_tree.root_inventory
1933
2012
                xml = self._create_basis_xml_from_inventory(
1934
2013
                                        leftmost_parent_id, inv)
1935
2014
                self._write_basis_inventory(xml)
2032
2111
 
2033
2112
    def has_id(self, file_id):
2034
2113
        # files that have been deleted are excluded
2035
 
        inv = self.inventory
2036
 
        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):
2037
2116
            return False
2038
 
        path = inv.id2path(file_id)
 
2117
        path = inv.id2path(inv_file_id)
2039
2118
        return osutils.lexists(self.abspath(path))
2040
2119
 
2041
2120
    def has_or_had_id(self, file_id):
2042
 
        if file_id == self.inventory.root.file_id:
 
2121
        if file_id == self.get_root_id():
2043
2122
            return True
2044
 
        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)
2045
2125
 
2046
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2047
 
    def __iter__(self):
 
2126
    def all_file_ids(self):
2048
2127
        """Iterate through file_ids for this tree.
2049
2128
 
2050
2129
        file_ids are in a WorkingTree if they are in the working inventory
2051
2130
        and the working file exists.
2052
2131
        """
2053
 
        inv = self._inventory
2054
 
        for path, ie in inv.iter_entries():
2055
 
            if osutils.lexists(self.abspath(path)):
2056
 
                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
2057
2136
 
2058
2137
    @needs_tree_write_lock
2059
2138
    def set_last_revision(self, new_revision):
2115
2194
                _mod_revision.NULL_REVISION)
2116
2195
        else:
2117
2196
            rt = self.branch.repository.revision_tree(revision_ids[0])
2118
 
        self._write_inventory(rt.inventory)
 
2197
        self._write_inventory(rt.root_inventory)
2119
2198
        self.set_parent_ids(revision_ids)
2120
2199
 
2121
2200
    def flush(self):
2133
2212
    def get_file_mtime(self, file_id, path=None):
2134
2213
        """See Tree.get_file_mtime."""
2135
2214
        if not path:
2136
 
            path = self.inventory.id2path(file_id)
 
2215
            path = self.id2path(file_id)
2137
2216
        try:
2138
2217
            return os.lstat(self.abspath(path)).st_mtime
2139
2218
        except OSError, e:
2142
2221
            raise
2143
2222
 
2144
2223
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2145
 
        file_id = self.path2id(path)
 
2224
        inv, file_id = self._path2inv_file_id(path)
2146
2225
        if file_id is None:
2147
2226
            # For unversioned files on win32, we just assume they are not
2148
2227
            # executable
2149
2228
            return False
2150
 
        return self._inventory[file_id].executable
 
2229
        return inv[file_id].executable
2151
2230
 
2152
2231
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2153
2232
        mode = stat_result.st_mode
2154
2233
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2155
2234
 
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):
 
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:
2164
2240
            if not path:
2165
2241
                path = self.id2path(file_id)
2166
2242
            mode = os.lstat(self.abspath(path)).st_mode
2167
2243
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2168
2244
 
2169
 
        _is_executable_from_path_and_stat = \
2170
 
            _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)
2171
2250
 
2172
2251
    @needs_tree_write_lock
2173
2252
    def _add(self, files, ids, kinds):
2176
2255
        # should probably put it back with the previous ID.
2177
2256
        # the read and write working inventory should not occur in this
2178
2257
        # function - they should be part of lock_write and unlock.
2179
 
        inv = self.inventory
 
2258
        # FIXME: nested trees
 
2259
        inv = self.root_inventory
2180
2260
        for f, file_id, kind in zip(files, ids, kinds):
2181
2261
            if file_id is None:
2182
2262
                inv.add_path(f, kind=kind)
2223
2303
                parent_tree = self.branch.repository.revision_tree(parent_id)
2224
2304
            parent_tree.lock_read()
2225
2305
            try:
2226
 
                if not parent_tree.has_id(file_id):
 
2306
                try:
 
2307
                    kind = parent_tree.kind(file_id)
 
2308
                except errors.NoSuchId:
2227
2309
                    continue
2228
 
                ie = parent_tree.inventory[file_id]
2229
 
                if ie.kind != 'file':
 
2310
                if kind != 'file':
2230
2311
                    # Note: this is slightly unnecessary, because symlinks and
2231
2312
                    # directories have a "text" which is the empty text, and we
2232
2313
                    # know that won't mess up annotations. But it seems cleaner
2233
2314
                    continue
2234
 
                parent_text_key = (file_id, ie.revision)
 
2315
                parent_text_key = (
 
2316
                    file_id, parent_tree.get_file_revision(file_id))
2235
2317
                if parent_text_key not in maybe_file_parent_keys:
2236
2318
                    maybe_file_parent_keys.append(parent_text_key)
2237
2319
            finally:
2252
2334
                       for key, line in annotator.annotate_flat(this_key)]
2253
2335
        return annotations
2254
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
 
2255
2351
    @needs_read_lock
2256
2352
    def merge_modified(self):
2257
2353
        """Return a dictionary of files modified by a merge.
2277
2373
            for s in _mod_rio.RioReader(hashfile):
2278
2374
                # RioReader reads in Unicode, so convert file_ids back to utf8
2279
2375
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2280
 
                if not self.inventory.has_id(file_id):
 
2376
                if not self.has_id(file_id):
2281
2377
                    continue
2282
2378
                text_hash = s.get("hash")
2283
2379
                if text_hash == self.get_file_sha1(file_id):
2313
2409
        other_tree.lock_tree_write()
2314
2410
        try:
2315
2411
            new_parents = other_tree.get_parent_ids()
2316
 
            other_root = other_tree.inventory.root
 
2412
            other_root = other_tree.root_inventory.root
2317
2413
            other_root.parent_id = new_root_parent
2318
2414
            other_root.name = osutils.basename(other_tree_path)
2319
 
            self.inventory.add(other_root)
2320
 
            add_children(self.inventory, other_root)
2321
 
            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)
2322
2418
            # normally we don't want to fetch whole repositories, but i think
2323
2419
            # here we really do want to consolidate the whole thing.
2324
2420
            for parent_id in other_tree.get_parent_ids():
2362
2458
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2363
2459
        if tree_transport.base != branch_transport.base:
2364
2460
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2365
 
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
2366
 
                target_branch=new_branch)
 
2461
            tree_bzrdir.set_branch_reference(new_branch)
2367
2462
        else:
2368
2463
            tree_bzrdir = branch_bzrdir
2369
2464
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2370
2465
        wt.set_parent_ids(self.get_parent_ids())
2371
 
        my_inv = self.inventory
 
2466
        # FIXME: Support nested trees
 
2467
        my_inv = self.root_inventory
2372
2468
        child_inv = inventory.Inventory(root_id=None)
2373
2469
        new_root = my_inv[file_id]
2374
2470
        my_inv.remove_recursive_id(file_id)
2394
2490
        if not self.is_locked():
2395
2491
            raise errors.ObjectNotLocked(self)
2396
2492
 
2397
 
        inv = self.inventory
2398
2493
        if from_dir is None and include_root is True:
2399
 
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
 
2494
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
2400
2495
        # Convert these into local objects to save lookup times
2401
2496
        pathjoin = osutils.pathjoin
2402
2497
        file_kind = self._kind
2409
2504
 
2410
2505
        # directory file_id, relative path, absolute path, reverse sorted children
2411
2506
        if from_dir is not None:
2412
 
            from_dir_id = inv.path2id(from_dir)
 
2507
            inv, from_dir_id = self._path2inv_file_id(from_dir)
2413
2508
            if from_dir_id is None:
2414
2509
                # Directory not versioned
2415
2510
                return
2416
2511
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2417
2512
        else:
 
2513
            inv = self.root_inventory
2418
2514
            from_dir_id = inv.root.file_id
2419
2515
            from_dir_abspath = self.basedir
2420
2516
        children = os.listdir(from_dir_abspath)
2543
2639
        rename_entries = []
2544
2640
        rename_tuples = []
2545
2641
 
 
2642
        invs_to_write = set()
 
2643
 
2546
2644
        # check for deprecated use of signature
2547
2645
        if to_dir is None:
2548
2646
            raise TypeError('You must supply a target directory')
2549
2647
        # check destination directory
2550
2648
        if isinstance(from_paths, basestring):
2551
2649
            raise ValueError()
2552
 
        inv = self.inventory
2553
2650
        to_abs = self.abspath(to_dir)
2554
2651
        if not isdir(to_abs):
2555
2652
            raise errors.BzrMoveFailedError('',to_dir,
2557
2654
        if not self.has_filename(to_dir):
2558
2655
            raise errors.BzrMoveFailedError('',to_dir,
2559
2656
                errors.NotInWorkingDirectory(to_dir))
2560
 
        to_dir_id = inv.path2id(to_dir)
 
2657
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2561
2658
        if to_dir_id is None:
2562
2659
            raise errors.BzrMoveFailedError('',to_dir,
2563
2660
                errors.NotVersionedError(path=to_dir))
2564
2661
 
2565
 
        to_dir_ie = inv[to_dir_id]
 
2662
        to_dir_ie = to_inv[to_dir_id]
2566
2663
        if to_dir_ie.kind != 'directory':
2567
2664
            raise errors.BzrMoveFailedError('',to_dir,
2568
2665
                errors.NotADirectory(to_abs))
2570
2667
        # create rename entries and tuples
2571
2668
        for from_rel in from_paths:
2572
2669
            from_tail = splitpath(from_rel)[-1]
2573
 
            from_id = inv.path2id(from_rel)
 
2670
            from_inv, from_id = self._path2inv_file_id(from_rel)
2574
2671
            if from_id is None:
2575
2672
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2576
2673
                    errors.NotVersionedError(path=from_rel))
2577
2674
 
2578
 
            from_entry = inv[from_id]
 
2675
            from_entry = from_inv[from_id]
2579
2676
            from_parent_id = from_entry.parent_id
2580
2677
            to_rel = pathjoin(to_dir, from_tail)
2581
2678
            rename_entry = InventoryWorkingTree._RenameEntry(
2600
2697
            # restore the inventory on error
2601
2698
            self._inventory_is_modified = original_modified
2602
2699
            raise
2603
 
        self._write_inventory(inv)
 
2700
        #FIXME: Should potentially also write the from_invs
 
2701
        self._write_inventory(to_inv)
2604
2702
        return rename_tuples
2605
2703
 
2606
2704
    @needs_tree_write_lock
2626
2724
 
2627
2725
        Everything else results in an error.
2628
2726
        """
2629
 
        inv = self.inventory
2630
2727
        rename_entries = []
2631
2728
 
2632
2729
        # create rename entries and tuples
2633
2730
        from_tail = splitpath(from_rel)[-1]
2634
 
        from_id = inv.path2id(from_rel)
 
2731
        from_inv, from_id = self._path2inv_file_id(from_rel)
2635
2732
        if from_id is None:
2636
2733
            # if file is missing in the inventory maybe it's in the basis_tree
2637
2734
            basis_tree = self.branch.basis_tree()
2640
2737
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2641
2738
                    errors.NotVersionedError(path=from_rel))
2642
2739
            # put entry back in the inventory so we can rename it
2643
 
            from_entry = basis_tree.inventory[from_id].copy()
2644
 
            inv.add(from_entry)
 
2740
            from_entry = basis_tree.root_inventory[from_id].copy()
 
2741
            from_inv.add(from_entry)
2645
2742
        else:
2646
 
            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]
2647
2745
        from_parent_id = from_entry.parent_id
2648
2746
        to_dir, to_tail = os.path.split(to_rel)
2649
 
        to_dir_id = inv.path2id(to_dir)
 
2747
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2650
2748
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2651
2749
                                     from_id=from_id,
2652
2750
                                     from_tail=from_tail,
2674
2772
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2675
2773
 
2676
2774
        self._move(rename_entries)
2677
 
        self._write_inventory(inv)
 
2775
        self._write_inventory(to_inv)
2678
2776
 
2679
2777
    class _RenameEntry(object):
2680
2778
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2696
2794
 
2697
2795
        Also does basic plausability tests.
2698
2796
        """
2699
 
        inv = self.inventory
 
2797
        # FIXME: Handling of nested trees
 
2798
        inv = self.root_inventory
2700
2799
 
2701
2800
        for rename_entry in rename_entries:
2702
2801
            # store to local variables for easier reference
2747
2846
                # something is wrong, so lets determine what exactly
2748
2847
                if not self.has_filename(from_rel) and \
2749
2848
                   not self.has_filename(to_rel):
2750
 
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
2751
 
                        errors.PathsDoNotExist(paths=(str(from_rel),
2752
 
                        str(to_rel))))
 
2849
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
 
2850
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
2753
2851
                else:
2754
2852
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2755
2853
            rename_entry.only_change_inv = only_change_inv
2761
2859
        Depending on the value of the flag 'only_change_inv', the
2762
2860
        file will be moved on the file system or not.
2763
2861
        """
2764
 
        inv = self.inventory
2765
2862
        moved = []
2766
2863
 
2767
2864
        for entry in rename_entries:
2774
2871
 
2775
2872
    def _rollback_move(self, moved):
2776
2873
        """Try to rollback a previous move in case of an filesystem error."""
2777
 
        inv = self.inventory
2778
2874
        for entry in moved:
2779
2875
            try:
2780
2876
                self._move_entry(WorkingTree._RenameEntry(
2789
2885
                        " Error message is: %s" % e)
2790
2886
 
2791
2887
    def _move_entry(self, entry):
2792
 
        inv = self.inventory
 
2888
        inv = self.root_inventory
2793
2889
        from_rel_abs = self.abspath(entry.from_rel)
2794
2890
        to_rel_abs = self.abspath(entry.to_rel)
2795
2891
        if from_rel_abs == to_rel_abs:
2836
2932
 
2837
2933
    def stored_kind(self, file_id):
2838
2934
        """See Tree.stored_kind"""
2839
 
        return self.inventory[file_id].kind
 
2935
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2936
        return inv[inv_file_id].kind
2840
2937
 
2841
2938
    def extras(self):
2842
2939
        """Yield all unversioned files in this WorkingTree.
2849
2946
        This is the same order used by 'osutils.walkdirs'.
2850
2947
        """
2851
2948
        ## TODO: Work from given directory downwards
2852
 
        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
2853
2952
            # mutter("search for unknowns in %r", path)
2854
2953
            dirabs = self.abspath(path)
2855
2954
            if not isdir(dirabs):
2892
2991
        """
2893
2992
        _directory = 'directory'
2894
2993
        # get the root in the inventory
2895
 
        inv = self.inventory
2896
 
        top_id = inv.path2id(prefix)
 
2994
        inv, top_id = self._path2inv_file_id(prefix)
2897
2995
        if top_id is None:
2898
2996
            pending = []
2899
2997
        else:
2920
3018
                if dir[2] == _directory:
2921
3019
                    pending.append(dir)
2922
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
 
2923
3031
 
2924
3032
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2925
3033
    """Registry for working tree formats."""
2981
3089
 
2982
3090
    supports_versioned_directories = None
2983
3091
 
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,
 
3092
    def initialize(self, controldir, revision_id=None, from_branch=None,
3004
3093
                   accelerator_tree=None, hardlink=False):
3005
 
        """Initialize a new working tree in a_bzrdir.
 
3094
        """Initialize a new working tree in controldir.
3006
3095
 
3007
 
        :param a_bzrdir: BzrDir to initialize the working tree in.
 
3096
        :param controldir: ControlDir to initialize the working tree in.
3008
3097
        :param revision_id: allows creating a working tree at a different
3009
3098
            revision than the branch is at.
3010
3099
        :param from_branch: Branch to checkout
3023
3112
    def __ne__(self, other):
3024
3113
        return not (self == other)
3025
3114
 
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
 
 
3037
3115
    def get_format_description(self):
3038
3116
        """Return the short description for this format."""
3039
3117
        raise NotImplementedError(self.get_format_description)
3055
3133
        """True if this format supports stored views."""
3056
3134
        return False
3057
3135
 
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
 
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)
3101
3215
 
3102
3216
 
3103
3217
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",