~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Samuel Bronson
  • Date: 2012-08-30 20:36:18 UTC
  • mto: (6015.57.3 2.4)
  • mto: This revision was merged to the branch mainline in revision 6558.
  • Revision ID: naesten@gmail.com-20120830203618-y2dzw91nqpvpgxvx
Update INSTALL for switch to Python 2.6 and up.

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,
54
54
    generate_ids,
55
55
    globbing,
56
56
    graph as _mod_graph,
 
57
    hashcache,
57
58
    ignores,
58
59
    inventory,
59
60
    merge,
69
70
    )
70
71
""")
71
72
 
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
 
 
 
73
from bzrlib import symbol_versioning
79
74
from bzrlib.decorators import needs_read_lock, needs_write_lock
80
 
from bzrlib.i18n import gettext
81
75
from bzrlib.lock import LogicalLockResult
82
76
import bzrlib.mutabletree
83
77
from bzrlib.mutabletree import needs_tree_write_lock
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
 
197
        # update the whole cache up front and write to disk if anything changed;
 
198
        # in the future we might want to do this more selectively
 
199
        # two possible ways offer themselves : in self._unlock, write the cache
 
200
        # if needed, or, when the cache sees a change, append it to the hash
 
201
        # cache file, and have the parser take the most recent entry for a
 
202
        # given path only.
 
203
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
204
        cache_filename = wt_trans.local_abspath('stat-cache')
 
205
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
 
206
            self.bzrdir._get_file_mode(),
 
207
            self._content_filter_stack_provider())
 
208
        hc = self._hashcache
 
209
        hc.read()
 
210
        # is this scan needed ? it makes things kinda slow.
 
211
        #hc.scan()
 
212
 
 
213
        if hc.needs_write:
 
214
            mutter("write hc")
 
215
            hc.write()
 
216
 
 
217
        self._detect_case_handling()
201
218
        self._rules_searcher = None
202
219
        self.views = self._make_views()
203
220
 
221
238
        """
222
239
        return self.bzrdir.is_control_filename(filename)
223
240
 
 
241
    def _detect_case_handling(self):
 
242
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
243
        try:
 
244
            wt_trans.stat(self._format.case_sensitive_filename)
 
245
        except errors.NoSuchFile:
 
246
            self.case_sensitive = True
 
247
        else:
 
248
            self.case_sensitive = False
 
249
 
 
250
        self._setup_directory_is_tree_reference()
 
251
 
224
252
    branch = property(
225
253
        fget=lambda self: self._branch,
226
254
        doc="""The branch this WorkingTree is connected to.
229
257
            the working tree has been constructed from.
230
258
            """)
231
259
 
232
 
    def has_versioned_directories(self):
233
 
        """See `Tree.has_versioned_directories`."""
234
 
        return self._format.supports_versioned_directories
235
 
 
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
260
    def break_lock(self):
243
261
        """Break a lock if one is present from another instance.
244
262
 
247
265
 
248
266
        This will probe the repository for its lock as well.
249
267
        """
250
 
        raise NotImplementedError(self.break_lock)
 
268
        self._control_files.break_lock()
 
269
        self.branch.break_lock()
251
270
 
252
271
    def requires_rich_root(self):
253
272
        return self._format.requires_rich_root
261
280
    def supports_views(self):
262
281
        return self.views.supports_views()
263
282
 
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
283
    @staticmethod
273
284
    def open(path=None, _unsupported=False):
274
285
        """Open an existing working tree at path.
276
287
        """
277
288
        if path is None:
278
289
            path = osutils.getcwd()
279
 
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
280
 
        return control.open_workingtree(unsupported=_unsupported)
 
290
        control = bzrdir.BzrDir.open(path, _unsupported)
 
291
        return control.open_workingtree(_unsupported)
281
292
 
282
293
    @staticmethod
283
294
    def open_containing(path=None):
294
305
        """
295
306
        if path is None:
296
307
            path = osutils.getcwd()
297
 
        control, relpath = controldir.ControlDir.open_containing(path)
 
308
        control, relpath = bzrdir.BzrDir.open_containing(path)
298
309
        return control.open_workingtree(), relpath
299
310
 
300
311
    @staticmethod
320
331
                if view_files:
321
332
                    file_list = view_files
322
333
                    view_str = views.view_display_str(view_files)
323
 
                    note(gettext("Ignoring files outside view. View is %s") % view_str)
 
334
                    note("Ignoring files outside view. View is %s" % view_str)
324
335
            return tree, file_list
325
336
        if default_directory == u'.':
326
337
            seed = file_list[0]
383
394
            else:
384
395
                return True, tree
385
396
        t = transport.get_transport(location)
386
 
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
 
397
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
387
398
                                              list_current=list_current)
388
399
        return [tr for tr in iterator if tr is not None]
389
400
 
 
401
    def all_file_ids(self):
 
402
        """See Tree.iter_all_file_ids"""
 
403
        raise NotImplementedError(self.all_file_ids)
 
404
 
390
405
    def __repr__(self):
391
406
        return "<%s of %s>" % (self.__class__.__name__,
392
407
                               getattr(self, 'basedir', None))
507
522
        raise NotImplementedError(self.get_root_id)
508
523
 
509
524
    @needs_read_lock
510
 
    def clone(self, to_controldir, revision_id=None):
 
525
    def clone(self, to_bzrdir, revision_id=None):
511
526
        """Duplicate this working tree into to_bzr, including all state.
512
527
 
513
528
        Specifically modified files are kept as modified, but
514
529
        ignored and unknown files are discarded.
515
530
 
516
 
        If you want to make a new line of development, see ControlDir.sprout()
 
531
        If you want to make a new line of development, see bzrdir.sprout()
517
532
 
518
533
        revision
519
534
            If not None, the cloned tree will have its last revision set to
521
536
            and this one merged in.
522
537
        """
523
538
        # assumes the target bzr dir format is compatible.
524
 
        result = to_controldir.create_workingtree()
 
539
        result = to_bzrdir.create_workingtree()
525
540
        self.copy_content_into(result, revision_id)
526
541
        return result
527
542
 
535
550
            # TODO now merge from tree.last_revision to revision (to preserve
536
551
            # user local changes)
537
552
            merge.transform_tree(tree, self)
538
 
            if revision_id == _mod_revision.NULL_REVISION:
539
 
                new_parents = []
540
 
            else:
541
 
                new_parents = [revision_id]
542
 
            tree.set_parent_ids(new_parents)
 
553
            tree.set_parent_ids([revision_id])
543
554
 
544
555
    def id2abspath(self, file_id):
545
556
        return self.abspath(self.id2path(file_id))
584
595
            else:
585
596
                return None
586
597
 
 
598
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
599
        # FIXME: Shouldn't this be in Tree?
 
600
        raise NotImplementedError(self.get_file_sha1)
 
601
 
587
602
    @needs_tree_write_lock
588
603
    def _gather_kinds(self, files, kinds):
589
604
        """See MutableTree._gather_kinds."""
754
769
 
755
770
    @needs_tree_write_lock
756
771
    def set_merge_modified(self, modified_hashes):
757
 
        """Set the merge modified hashes."""
758
 
        raise NotImplementedError(self.set_merge_modified)
 
772
        def iter_stanzas():
 
773
            for file_id, hash in modified_hashes.iteritems():
 
774
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
 
775
                    hash=hash)
 
776
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
759
777
 
760
778
    def _sha_from_stat(self, path, stat_result):
761
779
        """Get a sha digest from the tree's stat cache.
767
785
        """
768
786
        return None
769
787
 
 
788
    def _put_rio(self, filename, stanzas, header):
 
789
        self._must_be_locked()
 
790
        my_file = _mod_rio.rio_file(stanzas, header)
 
791
        self._transport.put_file(filename, my_file,
 
792
            mode=self.bzrdir._get_file_mode())
 
793
 
770
794
    @needs_write_lock # because merge pulls data into the branch.
771
795
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
772
796
                          merge_type=None, force=False):
1012
1036
                                show_base=show_base)
1013
1037
                    basis_root_id = basis_tree.get_root_id()
1014
1038
                    new_root_id = new_basis_tree.get_root_id()
1015
 
                    if new_root_id is not None and basis_root_id != new_root_id:
 
1039
                    if basis_root_id != new_root_id:
1016
1040
                        self.set_root_id(new_root_id)
1017
1041
                finally:
1018
1042
                    basis_tree.unlock()
1019
1043
                # TODO - dedup parents list with things merged by pull ?
1020
1044
                # reuse the revisiontree we merged against to set the new
1021
1045
                # tree data.
1022
 
                parent_trees = []
1023
 
                if self.branch.last_revision() != _mod_revision.NULL_REVISION:
1024
 
                    parent_trees.append(
1025
 
                        (self.branch.last_revision(), new_basis_tree))
 
1046
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1026
1047
                # we have to pull the merge trees out again, because
1027
1048
                # merge_inner has set the ids. - this corner is not yet
1028
1049
                # layered well enough to prevent double handling.
1045
1066
            stream.write(bytes)
1046
1067
        finally:
1047
1068
            stream.close()
 
1069
        # TODO: update the hashcache here ?
1048
1070
 
1049
1071
    def extras(self):
1050
1072
        """Yield all unversioned files in this WorkingTree.
1127
1149
        else:
1128
1150
            mode = stat_value.st_mode
1129
1151
            kind = osutils.file_kind_from_stat_mode(mode)
1130
 
            if not self._supports_executable():
 
1152
            if not supports_executable():
1131
1153
                executable = entry is not None and entry.executable
1132
1154
            else:
1133
1155
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1152
1174
        return _mod_revision.ensure_null(self.branch.last_revision())
1153
1175
 
1154
1176
    def is_locked(self):
1155
 
        """Check if this tree is locked."""
1156
 
        raise NotImplementedError(self.is_locked)
 
1177
        return self._control_files.is_locked()
 
1178
 
 
1179
    def _must_be_locked(self):
 
1180
        if not self.is_locked():
 
1181
            raise errors.ObjectNotLocked(self)
1157
1182
 
1158
1183
    def lock_read(self):
1159
1184
        """Lock the tree for reading.
1162
1187
 
1163
1188
        :return: A bzrlib.lock.LogicalLockResult.
1164
1189
        """
1165
 
        raise NotImplementedError(self.lock_read)
 
1190
        if not self.is_locked():
 
1191
            self._reset_data()
 
1192
        self.branch.lock_read()
 
1193
        try:
 
1194
            self._control_files.lock_read()
 
1195
            return LogicalLockResult(self.unlock)
 
1196
        except:
 
1197
            self.branch.unlock()
 
1198
            raise
1166
1199
 
1167
1200
    def lock_tree_write(self):
1168
1201
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1169
1202
 
1170
1203
        :return: A bzrlib.lock.LogicalLockResult.
1171
1204
        """
1172
 
        raise NotImplementedError(self.lock_tree_write)
 
1205
        if not self.is_locked():
 
1206
            self._reset_data()
 
1207
        self.branch.lock_read()
 
1208
        try:
 
1209
            self._control_files.lock_write()
 
1210
            return LogicalLockResult(self.unlock)
 
1211
        except:
 
1212
            self.branch.unlock()
 
1213
            raise
1173
1214
 
1174
1215
    def lock_write(self):
1175
1216
        """See MutableTree.lock_write, and WorkingTree.unlock.
1176
1217
 
1177
1218
        :return: A bzrlib.lock.LogicalLockResult.
1178
1219
        """
1179
 
        raise NotImplementedError(self.lock_write)
 
1220
        if not self.is_locked():
 
1221
            self._reset_data()
 
1222
        self.branch.lock_write()
 
1223
        try:
 
1224
            self._control_files.lock_write()
 
1225
            return LogicalLockResult(self.unlock)
 
1226
        except:
 
1227
            self.branch.unlock()
 
1228
            raise
1180
1229
 
1181
1230
    def get_physical_lock_status(self):
1182
 
        raise NotImplementedError(self.get_physical_lock_status)
 
1231
        return self._control_files.get_physical_lock_status()
 
1232
 
 
1233
    def _reset_data(self):
 
1234
        """Reset transient data that cannot be revalidated."""
 
1235
        raise NotImplementedError(self._reset_data)
1183
1236
 
1184
1237
    def set_last_revision(self, new_revision):
1185
1238
        """Change the last revision in the working tree."""
1479
1532
                                             show_base=show_base)
1480
1533
            if nb_conflicts:
1481
1534
                self.add_parent_tree((old_tip, other_tree))
1482
 
                note(gettext('Rerun update after fixing the conflicts.'))
 
1535
                note('Rerun update after fixing the conflicts.')
1483
1536
                return nb_conflicts
1484
1537
 
1485
1538
        if last_rev != _mod_revision.ensure_null(revision):
1527
1580
            last_rev = parent_trees[0][0]
1528
1581
        return nb_conflicts
1529
1582
 
 
1583
    def _write_hashcache_if_dirty(self):
 
1584
        """Write out the hashcache if it is dirty."""
 
1585
        if self._hashcache.needs_write:
 
1586
            try:
 
1587
                self._hashcache.write()
 
1588
            except OSError, e:
 
1589
                if e.errno not in (errno.EPERM, errno.EACCES):
 
1590
                    raise
 
1591
                # TODO: jam 20061219 Should this be a warning? A single line
 
1592
                #       warning might be sufficient to let the user know what
 
1593
                #       is going on.
 
1594
                mutter('Could not write hashcache for %s\nError: %s',
 
1595
                              self._hashcache.cache_file_name(), e)
 
1596
 
1530
1597
    def set_conflicts(self, arg):
1531
1598
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1532
1599
 
1761
1828
        :param branch: A branch to override probing for the branch.
1762
1829
        """
1763
1830
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1764
 
            branch=branch, _transport=_control_files._transport,
1765
 
            _internal=_internal, _format=_format, _bzrdir=_bzrdir)
1766
 
 
1767
 
        self._control_files = _control_files
1768
 
        self._detect_case_handling()
 
1831
            branch=branch, _control_files=_control_files, _internal=_internal,
 
1832
            _format=_format, _bzrdir=_bzrdir)
1769
1833
 
1770
1834
        if _inventory is None:
1771
1835
            # This will be acquired on lock_read() or lock_write()
1791
1855
        self._inventory = inv
1792
1856
        self._inventory_is_modified = dirty
1793
1857
 
1794
 
    def _detect_case_handling(self):
1795
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
1796
 
        try:
1797
 
            wt_trans.stat(self._format.case_sensitive_filename)
1798
 
        except errors.NoSuchFile:
1799
 
            self.case_sensitive = True
1800
 
        else:
1801
 
            self.case_sensitive = False
1802
 
 
1803
 
        self._setup_directory_is_tree_reference()
1804
 
 
1805
1858
    def _serialize(self, inventory, out_file):
1806
1859
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1807
1860
            working=True)
1809
1862
    def _deserialize(selt, in_file):
1810
1863
        return xml5.serializer_v5.read_inventory(in_file)
1811
1864
 
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
1865
    @needs_tree_write_lock
1881
1866
    def _write_inventory(self, inv):
1882
1867
        """Write inventory as the current inventory."""
1950
1935
            if entry.parent_id == orig_root_id:
1951
1936
                entry.parent_id = inv.root.file_id
1952
1937
 
 
1938
    def all_file_ids(self):
 
1939
        """See Tree.iter_all_file_ids"""
 
1940
        return set(self.inventory)
 
1941
 
1953
1942
    @needs_tree_write_lock
1954
1943
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1955
1944
        """See MutableTree.set_parent_trees."""
2077
2066
 
2078
2067
    def has_id(self, file_id):
2079
2068
        # 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):
 
2069
        inv = self.inventory
 
2070
        if not inv.has_id(file_id):
2082
2071
            return False
2083
 
        path = inv.id2path(inv_file_id)
 
2072
        path = inv.id2path(file_id)
2084
2073
        return osutils.lexists(self.abspath(path))
2085
2074
 
2086
2075
    def has_or_had_id(self, file_id):
2087
2076
        if file_id == self.inventory.root.file_id:
2088
2077
            return True
2089
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2090
 
        return inv.has_id(inv_file_id)
 
2078
        return self.inventory.has_id(file_id)
2091
2079
 
2092
 
    def all_file_ids(self):
 
2080
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
2081
    def __iter__(self):
2093
2082
        """Iterate through file_ids for this tree.
2094
2083
 
2095
2084
        file_ids are in a WorkingTree if they are in the working inventory
2096
2085
        and the working file exists.
2097
2086
        """
2098
 
        ret = set()
2099
 
        for path, ie in self.iter_entries_by_dir():
2100
 
            ret.add(ie.file_id)
2101
 
        return ret
 
2087
        inv = self._inventory
 
2088
        for path, ie in inv.iter_entries():
 
2089
            if osutils.lexists(self.abspath(path)):
 
2090
                yield ie.file_id
2102
2091
 
2103
2092
    @needs_tree_write_lock
2104
2093
    def set_last_revision(self, new_revision):
2175
2164
            mode=self.bzrdir._get_file_mode())
2176
2165
        self._inventory_is_modified = False
2177
2166
 
 
2167
    @needs_read_lock
 
2168
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
2169
        if not path:
 
2170
            path = self._inventory.id2path(file_id)
 
2171
        return self._hashcache.get_sha1(path, stat_value)
 
2172
 
2178
2173
    def get_file_mtime(self, file_id, path=None):
2179
2174
        """See Tree.get_file_mtime."""
2180
2175
        if not path:
2181
 
            path = self.id2path(file_id)
2182
 
        try:
2183
 
            return os.lstat(self.abspath(path)).st_mtime
2184
 
        except OSError, e:
2185
 
            if e.errno == errno.ENOENT:
2186
 
                raise errors.FileTimestampUnavailable(path)
2187
 
            raise
 
2176
            path = self.inventory.id2path(file_id)
 
2177
        return os.lstat(self.abspath(path)).st_mtime
2188
2178
 
2189
2179
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2190
 
        inv, file_id = self._path2inv_file_id(path)
 
2180
        file_id = self.path2id(path)
2191
2181
        if file_id is None:
2192
2182
            # For unversioned files on win32, we just assume they are not
2193
2183
            # executable
2194
2184
            return False
2195
 
        return inv[file_id].executable
 
2185
        return self._inventory[file_id].executable
2196
2186
 
2197
2187
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2198
2188
        mode = stat_result.st_mode
2199
2189
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2200
2190
 
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:
 
2191
    if not supports_executable():
 
2192
        def is_executable(self, file_id, path=None):
 
2193
            return self._inventory[file_id].executable
 
2194
 
 
2195
        _is_executable_from_path_and_stat = \
 
2196
            _is_executable_from_path_and_stat_from_basis
 
2197
    else:
 
2198
        def is_executable(self, file_id, path=None):
2206
2199
            if not path:
2207
2200
                path = self.id2path(file_id)
2208
2201
            mode = os.lstat(self.abspath(path)).st_mode
2209
2202
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2210
2203
 
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)
 
2204
        _is_executable_from_path_and_stat = \
 
2205
            _is_executable_from_path_and_stat_from_stat
2216
2206
 
2217
2207
    @needs_tree_write_lock
2218
2208
    def _add(self, files, ids, kinds):
2268
2258
                parent_tree = self.branch.repository.revision_tree(parent_id)
2269
2259
            parent_tree.lock_read()
2270
2260
            try:
2271
 
                try:
2272
 
                    kind = parent_tree.kind(file_id)
2273
 
                except errors.NoSuchId:
 
2261
                if not parent_tree.has_id(file_id):
2274
2262
                    continue
2275
 
                if kind != 'file':
 
2263
                ie = parent_tree.inventory[file_id]
 
2264
                if ie.kind != 'file':
2276
2265
                    # Note: this is slightly unnecessary, because symlinks and
2277
2266
                    # directories have a "text" which is the empty text, and we
2278
2267
                    # know that won't mess up annotations. But it seems cleaner
2279
2268
                    continue
2280
 
                parent_text_key = (
2281
 
                    file_id, parent_tree.get_file_revision(file_id))
 
2269
                parent_text_key = (file_id, ie.revision)
2282
2270
                if parent_text_key not in maybe_file_parent_keys:
2283
2271
                    maybe_file_parent_keys.append(parent_text_key)
2284
2272
            finally:
2299
2287
                       for key, line in annotator.annotate_flat(this_key)]
2300
2288
        return annotations
2301
2289
 
2302
 
    def _put_rio(self, filename, stanzas, header):
2303
 
        self._must_be_locked()
2304
 
        my_file = _mod_rio.rio_file(stanzas, header)
2305
 
        self._transport.put_file(filename, my_file,
2306
 
            mode=self.bzrdir._get_file_mode())
2307
 
 
2308
 
    @needs_tree_write_lock
2309
 
    def set_merge_modified(self, modified_hashes):
2310
 
        def iter_stanzas():
2311
 
            for file_id, hash in modified_hashes.iteritems():
2312
 
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
2313
 
                    hash=hash)
2314
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
2315
 
 
2316
2290
    @needs_read_lock
2317
2291
    def merge_modified(self):
2318
2292
        """Return a dictionary of files modified by a merge.
2338
2312
            for s in _mod_rio.RioReader(hashfile):
2339
2313
                # RioReader reads in Unicode, so convert file_ids back to utf8
2340
2314
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2341
 
                if not self.has_id(file_id):
 
2315
                if not self.inventory.has_id(file_id):
2342
2316
                    continue
2343
2317
                text_hash = s.get("hash")
2344
2318
                if text_hash == self.get_file_sha1(file_id):
2423
2397
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2424
2398
        if tree_transport.base != branch_transport.base:
2425
2399
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2426
 
            tree_bzrdir.set_branch_reference(new_branch)
 
2400
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
 
2401
                target_branch=new_branch)
2427
2402
        else:
2428
2403
            tree_bzrdir = branch_bzrdir
2429
2404
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2454
2429
        if not self.is_locked():
2455
2430
            raise errors.ObjectNotLocked(self)
2456
2431
 
 
2432
        inv = self.inventory
2457
2433
        if from_dir is None and include_root is True:
2458
 
            yield ('', 'V', 'directory', self.get_root_id(), self.inventory.root)
 
2434
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2459
2435
        # Convert these into local objects to save lookup times
2460
2436
        pathjoin = osutils.pathjoin
2461
2437
        file_kind = self._kind
2468
2444
 
2469
2445
        # directory file_id, relative path, absolute path, reverse sorted children
2470
2446
        if from_dir is not None:
2471
 
            inv, from_dir_id = self._path2inv_file_id(from_dir)
 
2447
            from_dir_id = inv.path2id(from_dir)
2472
2448
            if from_dir_id is None:
2473
2449
                # Directory not versioned
2474
2450
                return
2475
2451
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2476
2452
        else:
2477
 
            inv = self.inventory
2478
2453
            from_dir_id = inv.root.file_id
2479
2454
            from_dir_abspath = self.basedir
2480
2455
        children = os.listdir(from_dir_abspath)
2609
2584
        # check destination directory
2610
2585
        if isinstance(from_paths, basestring):
2611
2586
            raise ValueError()
 
2587
        inv = self.inventory
2612
2588
        to_abs = self.abspath(to_dir)
2613
2589
        if not isdir(to_abs):
2614
2590
            raise errors.BzrMoveFailedError('',to_dir,
2616
2592
        if not self.has_filename(to_dir):
2617
2593
            raise errors.BzrMoveFailedError('',to_dir,
2618
2594
                errors.NotInWorkingDirectory(to_dir))
2619
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2595
        to_dir_id = inv.path2id(to_dir)
2620
2596
        if to_dir_id is None:
2621
2597
            raise errors.BzrMoveFailedError('',to_dir,
2622
2598
                errors.NotVersionedError(path=to_dir))
2623
2599
 
2624
 
        to_dir_ie = to_inv[to_dir_id]
 
2600
        to_dir_ie = inv[to_dir_id]
2625
2601
        if to_dir_ie.kind != 'directory':
2626
2602
            raise errors.BzrMoveFailedError('',to_dir,
2627
2603
                errors.NotADirectory(to_abs))
2629
2605
        # create rename entries and tuples
2630
2606
        for from_rel in from_paths:
2631
2607
            from_tail = splitpath(from_rel)[-1]
2632
 
            from_inv, from_id = self._path2inv_file_id(from_rel)
 
2608
            from_id = inv.path2id(from_rel)
2633
2609
            if from_id is None:
2634
2610
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2635
2611
                    errors.NotVersionedError(path=from_rel))
2636
2612
 
2637
 
            from_entry = from_inv[from_id]
 
2613
            from_entry = inv[from_id]
2638
2614
            from_parent_id = from_entry.parent_id
2639
2615
            to_rel = pathjoin(to_dir, from_tail)
2640
2616
            rename_entry = InventoryWorkingTree._RenameEntry(
2659
2635
            # restore the inventory on error
2660
2636
            self._inventory_is_modified = original_modified
2661
2637
            raise
2662
 
        #FIXME: Should potentially also write the from_invs
2663
 
        self._write_inventory(to_inv)
 
2638
        self._write_inventory(inv)
2664
2639
        return rename_tuples
2665
2640
 
2666
2641
    @needs_tree_write_lock
2686
2661
 
2687
2662
        Everything else results in an error.
2688
2663
        """
 
2664
        inv = self.inventory
2689
2665
        rename_entries = []
2690
2666
 
2691
2667
        # create rename entries and tuples
2692
2668
        from_tail = splitpath(from_rel)[-1]
2693
 
        from_inv, from_id = self._path2inv_file_id(from_rel)
 
2669
        from_id = inv.path2id(from_rel)
2694
2670
        if from_id is None:
2695
2671
            # if file is missing in the inventory maybe it's in the basis_tree
2696
2672
            basis_tree = self.branch.basis_tree()
2700
2676
                    errors.NotVersionedError(path=from_rel))
2701
2677
            # put entry back in the inventory so we can rename it
2702
2678
            from_entry = basis_tree.inventory[from_id].copy()
2703
 
            from_inv.add(from_entry)
 
2679
            inv.add(from_entry)
2704
2680
        else:
2705
 
            from_inv, from_inv_id = self._unpack_file_id(from_id)
2706
 
            from_entry = from_inv[from_inv_id]
 
2681
            from_entry = inv[from_id]
2707
2682
        from_parent_id = from_entry.parent_id
2708
2683
        to_dir, to_tail = os.path.split(to_rel)
2709
 
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
 
2684
        to_dir_id = inv.path2id(to_dir)
2710
2685
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2711
2686
                                     from_id=from_id,
2712
2687
                                     from_tail=from_tail,
2734
2709
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2735
2710
 
2736
2711
        self._move(rename_entries)
2737
 
        self._write_inventory(to_inv)
 
2712
        self._write_inventory(inv)
2738
2713
 
2739
2714
    class _RenameEntry(object):
2740
2715
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2807
2782
                # something is wrong, so lets determine what exactly
2808
2783
                if not self.has_filename(from_rel) and \
2809
2784
                   not self.has_filename(to_rel):
2810
 
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
2811
 
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
 
2785
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
 
2786
                        errors.PathsDoNotExist(paths=(str(from_rel),
 
2787
                        str(to_rel))))
2812
2788
                else:
2813
2789
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2814
2790
            rename_entry.only_change_inv = only_change_inv
2820
2796
        Depending on the value of the flag 'only_change_inv', the
2821
2797
        file will be moved on the file system or not.
2822
2798
        """
 
2799
        inv = self.inventory
2823
2800
        moved = []
2824
2801
 
2825
2802
        for entry in rename_entries:
2832
2809
 
2833
2810
    def _rollback_move(self, moved):
2834
2811
        """Try to rollback a previous move in case of an filesystem error."""
 
2812
        inv = self.inventory
2835
2813
        for entry in moved:
2836
2814
            try:
2837
2815
                self._move_entry(WorkingTree._RenameEntry(
2893
2871
 
2894
2872
    def stored_kind(self, file_id):
2895
2873
        """See Tree.stored_kind"""
2896
 
        inv, inv_file_id = self._unpack_file_id(file_id)
2897
 
        return inv[inv_file_id].kind
 
2874
        return self.inventory[file_id].kind
2898
2875
 
2899
2876
    def extras(self):
2900
2877
        """Yield all unversioned files in this WorkingTree.
2950
2927
        """
2951
2928
        _directory = 'directory'
2952
2929
        # get the root in the inventory
2953
 
        inv, top_id = self._path2inv_file_id(prefix)
 
2930
        inv = self.inventory
 
2931
        top_id = inv.path2id(prefix)
2954
2932
        if top_id is None:
2955
2933
            pending = []
2956
2934
        else:
2977
2955
                if dir[2] == _directory:
2978
2956
                    pending.append(dir)
2979
2957
 
2980
 
    @needs_write_lock
2981
 
    def update_feature_flags(self, updated_flags):
2982
 
        """Update the feature flags for this branch.
2983
 
 
2984
 
        :param updated_flags: Dictionary mapping feature names to necessities
2985
 
            A necessity can be None to indicate the feature should be removed
2986
 
        """
2987
 
        self._format._update_feature_flags(updated_flags)
2988
 
        self.control_transport.put_bytes('format', self._format.as_string())
2989
 
 
2990
2958
 
2991
2959
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2992
2960
    """Registry for working tree formats."""
3048
3016
 
3049
3017
    supports_versioned_directories = None
3050
3018
 
3051
 
    def initialize(self, controldir, revision_id=None, from_branch=None,
 
3019
    @classmethod
 
3020
    def find_format_string(klass, a_bzrdir):
 
3021
        """Return format name for the working tree object in a_bzrdir."""
 
3022
        try:
 
3023
            transport = a_bzrdir.get_workingtree_transport(None)
 
3024
            return transport.get_bytes("format")
 
3025
        except errors.NoSuchFile:
 
3026
            raise errors.NoWorkingTree(base=transport.base)
 
3027
 
 
3028
    @classmethod
 
3029
    def find_format(klass, a_bzrdir):
 
3030
        """Return the format for the working tree object in a_bzrdir."""
 
3031
        try:
 
3032
            format_string = klass.find_format_string(a_bzrdir)
 
3033
            format_string = bzrdir.extract_format_string(format_string)
 
3034
            return format_registry.get(format_string)
 
3035
        except KeyError:
 
3036
            raise errors.UnknownFormatError(format=format_string,
 
3037
                                            kind="working tree")
 
3038
 
 
3039
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
3052
3040
                   accelerator_tree=None, hardlink=False):
3053
 
        """Initialize a new working tree in controldir.
 
3041
        """Initialize a new working tree in a_bzrdir.
3054
3042
 
3055
 
        :param controldir: ControlDir to initialize the working tree in.
 
3043
        :param a_bzrdir: BzrDir to initialize the working tree in.
3056
3044
        :param revision_id: allows creating a working tree at a different
3057
3045
            revision than the branch is at.
3058
3046
        :param from_branch: Branch to checkout
3078
3066
        """Return the current default format."""
3079
3067
        return format_registry.get_default()
3080
3068
 
 
3069
    def get_format_string(self):
 
3070
        """Return the ASCII format string that identifies this format."""
 
3071
        raise NotImplementedError(self.get_format_string)
 
3072
 
3081
3073
    def get_format_description(self):
3082
3074
        """Return the short description for this format."""
3083
3075
        raise NotImplementedError(self.get_format_description)
3135
3127
    def unregister_format(klass, format):
3136
3128
        format_registry.remove(format)
3137
3129
 
3138
 
    def get_controldir_for_branch(self):
3139
 
        """Get the control directory format for creating branches.
3140
 
 
3141
 
        This is to support testing of working tree formats that can not exist
3142
 
        in the same control directory as a branch.
3143
 
        """
3144
 
        return self._matchingbzrdir
3145
 
 
3146
 
 
3147
 
class WorkingTreeFormatMetaDir(bzrdir.BzrFormat, WorkingTreeFormat):
3148
 
    """Base class for working trees that live in bzr meta directories."""
3149
 
 
3150
 
    def __init__(self):
3151
 
        WorkingTreeFormat.__init__(self)
3152
 
        bzrdir.BzrFormat.__init__(self)
3153
 
 
3154
 
    @classmethod
3155
 
    def find_format_string(klass, controldir):
3156
 
        """Return format name for the working tree object in controldir."""
3157
 
        try:
3158
 
            transport = controldir.get_workingtree_transport(None)
3159
 
            return transport.get_bytes("format")
3160
 
        except errors.NoSuchFile:
3161
 
            raise errors.NoWorkingTree(base=transport.base)
3162
 
 
3163
 
    @classmethod
3164
 
    def find_format(klass, controldir):
3165
 
        """Return the format for the working tree object in controldir."""
3166
 
        format_string = klass.find_format_string(controldir)
3167
 
        return klass._find_format(format_registry, 'working tree',
3168
 
                format_string)
3169
 
 
3170
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
3171
 
            basedir=None):
3172
 
        WorkingTreeFormat.check_support_status(self,
3173
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
3174
 
            basedir=basedir)
3175
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
3176
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
3177
 
 
3178
3130
 
3179
3131
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3180
3132
    "bzrlib.workingtree_4", "WorkingTreeFormat4")