~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

(gz) Remove bzrlib/util/elementtree/ package (Martin Packman)

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,
54
54
    generate_ids,
55
55
    globbing,
56
56
    graph as _mod_graph,
57
 
    hashcache,
58
57
    ignores,
59
58
    inventory,
60
59
    merge,
70
69
    )
71
70
""")
72
71
 
73
 
from bzrlib import symbol_versioning
 
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
 
74
79
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
80
from bzrlib.i18n import gettext
75
81
from bzrlib.lock import LogicalLockResult
76
82
import bzrlib.mutabletree
77
83
from bzrlib.mutabletree import needs_tree_write_lock
84
90
    realpath,
85
91
    safe_unicode,
86
92
    splitpath,
87
 
    supports_executable,
88
93
    )
89
94
from bzrlib.trace import mutter, note
90
95
from bzrlib.revision import CURRENT_REVISION
172
177
 
173
178
    def __init__(self, basedir='.',
174
179
                 branch=DEPRECATED_PARAMETER,
175
 
                 _control_files=None,
176
180
                 _internal=False,
 
181
                 _transport=None,
177
182
                 _format=None,
178
183
                 _bzrdir=None):
179
184
        """Construct a WorkingTree instance. This is not a public API.
192
197
        else:
193
198
            self._branch = self.bzrdir.open_branch()
194
199
        self.basedir = realpath(basedir)
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()
 
200
        self._transport = _transport
218
201
        self._rules_searcher = None
219
202
        self.views = self._make_views()
220
203
 
238
221
        """
239
222
        return self.bzrdir.is_control_filename(filename)
240
223
 
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
 
 
252
224
    branch = property(
253
225
        fget=lambda self: self._branch,
254
226
        doc="""The branch this WorkingTree is connected to.
257
229
            the working tree has been constructed from.
258
230
            """)
259
231
 
 
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
 
260
242
    def break_lock(self):
261
243
        """Break a lock if one is present from another instance.
262
244
 
265
247
 
266
248
        This will probe the repository for its lock as well.
267
249
        """
268
 
        self._control_files.break_lock()
269
 
        self.branch.break_lock()
 
250
        raise NotImplementedError(self.break_lock)
270
251
 
271
252
    def requires_rich_root(self):
272
253
        return self._format.requires_rich_root
287
268
        """
288
269
        if path is None:
289
270
            path = osutils.getcwd()
290
 
        control = bzrdir.BzrDir.open(path, _unsupported)
291
 
        return control.open_workingtree(_unsupported)
 
271
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
 
272
        return control.open_workingtree(unsupported=_unsupported)
292
273
 
293
274
    @staticmethod
294
275
    def open_containing(path=None):
305
286
        """
306
287
        if path is None:
307
288
            path = osutils.getcwd()
308
 
        control, relpath = bzrdir.BzrDir.open_containing(path)
 
289
        control, relpath = controldir.ControlDir.open_containing(path)
309
290
        return control.open_workingtree(), relpath
310
291
 
311
292
    @staticmethod
331
312
                if view_files:
332
313
                    file_list = view_files
333
314
                    view_str = views.view_display_str(view_files)
334
 
                    note("Ignoring files outside view. View is %s" % view_str)
 
315
                    note(gettext("Ignoring files outside view. View is %s") % view_str)
335
316
            return tree, file_list
336
317
        if default_directory == u'.':
337
318
            seed = file_list[0]
394
375
            else:
395
376
                return True, tree
396
377
        t = transport.get_transport(location)
397
 
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
 
378
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
398
379
                                              list_current=list_current)
399
380
        return [tr for tr in iterator if tr is not None]
400
381
 
496
477
        finally:
497
478
            file.close()
498
479
 
499
 
    def _get_ancestors(self, default_revision):
500
 
        ancestors = set([default_revision])
501
 
        for parent_id in self.get_parent_ids():
502
 
            ancestors.update(self.branch.repository.get_ancestry(
503
 
                             parent_id, topo_sorted=False))
504
 
        return ancestors
505
 
 
506
480
    def get_parent_ids(self):
507
481
        """See Tree.get_parent_ids.
508
482
 
529
503
        raise NotImplementedError(self.get_root_id)
530
504
 
531
505
    @needs_read_lock
532
 
    def clone(self, to_bzrdir, revision_id=None):
 
506
    def clone(self, to_controldir, revision_id=None):
533
507
        """Duplicate this working tree into to_bzr, including all state.
534
508
 
535
509
        Specifically modified files are kept as modified, but
536
510
        ignored and unknown files are discarded.
537
511
 
538
 
        If you want to make a new line of development, see bzrdir.sprout()
 
512
        If you want to make a new line of development, see ControlDir.sprout()
539
513
 
540
514
        revision
541
515
            If not None, the cloned tree will have its last revision set to
543
517
            and this one merged in.
544
518
        """
545
519
        # assumes the target bzr dir format is compatible.
546
 
        result = to_bzrdir.create_workingtree()
 
520
        result = to_controldir.create_workingtree()
547
521
        self.copy_content_into(result, revision_id)
548
522
        return result
549
523
 
557
531
            # TODO now merge from tree.last_revision to revision (to preserve
558
532
            # user local changes)
559
533
            merge.transform_tree(tree, self)
560
 
            tree.set_parent_ids([revision_id])
 
534
            if revision_id == _mod_revision.NULL_REVISION:
 
535
                new_parents = []
 
536
            else:
 
537
                new_parents = [revision_id]
 
538
            tree.set_parent_ids(new_parents)
561
539
 
562
540
    def id2abspath(self, file_id):
563
541
        return self.abspath(self.id2path(file_id))
602
580
            else:
603
581
                return None
604
582
 
605
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
606
 
        # FIXME: Shouldn't this be in Tree?
607
 
        raise NotImplementedError(self.get_file_sha1)
608
 
 
609
583
    @needs_tree_write_lock
610
584
    def _gather_kinds(self, files, kinds):
611
585
        """See MutableTree._gather_kinds."""
776
750
 
777
751
    @needs_tree_write_lock
778
752
    def set_merge_modified(self, modified_hashes):
779
 
        def iter_stanzas():
780
 
            for file_id, hash in modified_hashes.iteritems():
781
 
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
782
 
                    hash=hash)
783
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
 
753
        """Set the merge modified hashes."""
 
754
        raise NotImplementedError(self.set_merge_modified)
784
755
 
785
756
    def _sha_from_stat(self, path, stat_result):
786
757
        """Get a sha digest from the tree's stat cache.
792
763
        """
793
764
        return None
794
765
 
795
 
    def _put_rio(self, filename, stanzas, header):
796
 
        self._must_be_locked()
797
 
        my_file = _mod_rio.rio_file(stanzas, header)
798
 
        self._transport.put_file(filename, my_file,
799
 
            mode=self.bzrdir._get_file_mode())
800
 
 
801
766
    @needs_write_lock # because merge pulls data into the branch.
802
767
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
803
768
                          merge_type=None, force=False):
973
938
        file and change the file_id. That is the normal mode. Second, it can
974
939
        only change the file_id without touching any physical file.
975
940
 
976
 
        rename_one uses the second mode if 'after == True' and 'to_rel' is not
977
 
        versioned but present in the working tree.
 
941
        rename_one uses the second mode if 'after == True' and 'to_rel' is
 
942
        either not versioned or newly added, and present in the working tree.
978
943
 
979
944
        rename_one uses the second mode if 'after == False' and 'from_rel' is
980
945
        versioned but no longer in the working tree, and 'to_rel' is not
1043
1008
                                show_base=show_base)
1044
1009
                    basis_root_id = basis_tree.get_root_id()
1045
1010
                    new_root_id = new_basis_tree.get_root_id()
1046
 
                    if basis_root_id != new_root_id:
 
1011
                    if new_root_id is not None and basis_root_id != new_root_id:
1047
1012
                        self.set_root_id(new_root_id)
1048
1013
                finally:
1049
1014
                    basis_tree.unlock()
1050
1015
                # TODO - dedup parents list with things merged by pull ?
1051
1016
                # reuse the revisiontree we merged against to set the new
1052
1017
                # tree data.
1053
 
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
 
1018
                parent_trees = []
 
1019
                if self.branch.last_revision() != _mod_revision.NULL_REVISION:
 
1020
                    parent_trees.append(
 
1021
                        (self.branch.last_revision(), new_basis_tree))
1054
1022
                # we have to pull the merge trees out again, because
1055
1023
                # merge_inner has set the ids. - this corner is not yet
1056
1024
                # layered well enough to prevent double handling.
1073
1041
            stream.write(bytes)
1074
1042
        finally:
1075
1043
            stream.close()
1076
 
        # TODO: update the hashcache here ?
1077
1044
 
1078
1045
    def extras(self):
1079
1046
        """Yield all unversioned files in this WorkingTree.
1156
1123
        else:
1157
1124
            mode = stat_value.st_mode
1158
1125
            kind = osutils.file_kind_from_stat_mode(mode)
1159
 
            if not supports_executable():
 
1126
            if not self._supports_executable():
1160
1127
                executable = entry is not None and entry.executable
1161
1128
            else:
1162
1129
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1181
1148
        return _mod_revision.ensure_null(self.branch.last_revision())
1182
1149
 
1183
1150
    def is_locked(self):
1184
 
        return self._control_files.is_locked()
1185
 
 
1186
 
    def _must_be_locked(self):
1187
 
        if not self.is_locked():
1188
 
            raise errors.ObjectNotLocked(self)
 
1151
        """Check if this tree is locked."""
 
1152
        raise NotImplementedError(self.is_locked)
1189
1153
 
1190
1154
    def lock_read(self):
1191
1155
        """Lock the tree for reading.
1194
1158
 
1195
1159
        :return: A bzrlib.lock.LogicalLockResult.
1196
1160
        """
1197
 
        if not self.is_locked():
1198
 
            self._reset_data()
1199
 
        self.branch.lock_read()
1200
 
        try:
1201
 
            self._control_files.lock_read()
1202
 
            return LogicalLockResult(self.unlock)
1203
 
        except:
1204
 
            self.branch.unlock()
1205
 
            raise
 
1161
        raise NotImplementedError(self.lock_read)
1206
1162
 
1207
1163
    def lock_tree_write(self):
1208
1164
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1209
1165
 
1210
1166
        :return: A bzrlib.lock.LogicalLockResult.
1211
1167
        """
1212
 
        if not self.is_locked():
1213
 
            self._reset_data()
1214
 
        self.branch.lock_read()
1215
 
        try:
1216
 
            self._control_files.lock_write()
1217
 
            return LogicalLockResult(self.unlock)
1218
 
        except:
1219
 
            self.branch.unlock()
1220
 
            raise
 
1168
        raise NotImplementedError(self.lock_tree_write)
1221
1169
 
1222
1170
    def lock_write(self):
1223
1171
        """See MutableTree.lock_write, and WorkingTree.unlock.
1224
1172
 
1225
1173
        :return: A bzrlib.lock.LogicalLockResult.
1226
1174
        """
1227
 
        if not self.is_locked():
1228
 
            self._reset_data()
1229
 
        self.branch.lock_write()
1230
 
        try:
1231
 
            self._control_files.lock_write()
1232
 
            return LogicalLockResult(self.unlock)
1233
 
        except:
1234
 
            self.branch.unlock()
1235
 
            raise
 
1175
        raise NotImplementedError(self.lock_write)
1236
1176
 
1237
1177
    def get_physical_lock_status(self):
1238
 
        return self._control_files.get_physical_lock_status()
1239
 
 
1240
 
    def _reset_data(self):
1241
 
        """Reset transient data that cannot be revalidated."""
1242
 
        raise NotImplementedError(self._reset_data)
 
1178
        raise NotImplementedError(self.get_physical_lock_status)
1243
1179
 
1244
1180
    def set_last_revision(self, new_revision):
1245
1181
        """Change the last revision in the working tree."""
1385
1321
    def revert(self, filenames=None, old_tree=None, backups=True,
1386
1322
               pb=None, report_changes=False):
1387
1323
        from bzrlib.conflicts import resolve
1388
 
        if filenames == []:
1389
 
            filenames = None
1390
 
            symbol_versioning.warn('Using [] to revert all files is deprecated'
1391
 
                ' as of bzr 0.91.  Please use None (the default) instead.',
1392
 
                DeprecationWarning, stacklevel=2)
1393
1324
        if old_tree is None:
1394
1325
            basis_tree = self.basis_tree()
1395
1326
            basis_tree.lock_read()
1544
1475
                                             show_base=show_base)
1545
1476
            if nb_conflicts:
1546
1477
                self.add_parent_tree((old_tip, other_tree))
1547
 
                note('Rerun update after fixing the conflicts.')
 
1478
                note(gettext('Rerun update after fixing the conflicts.'))
1548
1479
                return nb_conflicts
1549
1480
 
1550
1481
        if last_rev != _mod_revision.ensure_null(revision):
1592
1523
            last_rev = parent_trees[0][0]
1593
1524
        return nb_conflicts
1594
1525
 
1595
 
    def _write_hashcache_if_dirty(self):
1596
 
        """Write out the hashcache if it is dirty."""
1597
 
        if self._hashcache.needs_write:
1598
 
            try:
1599
 
                self._hashcache.write()
1600
 
            except OSError, e:
1601
 
                if e.errno not in (errno.EPERM, errno.EACCES):
1602
 
                    raise
1603
 
                # TODO: jam 20061219 Should this be a warning? A single line
1604
 
                #       warning might be sufficient to let the user know what
1605
 
                #       is going on.
1606
 
                mutter('Could not write hashcache for %s\nError: %s',
1607
 
                              self._hashcache.cache_file_name(), e)
1608
 
 
1609
1526
    def set_conflicts(self, arg):
1610
1527
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1611
1528
 
1840
1757
        :param branch: A branch to override probing for the branch.
1841
1758
        """
1842
1759
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1843
 
            branch=branch, _control_files=_control_files, _internal=_internal,
1844
 
            _format=_format, _bzrdir=_bzrdir)
 
1760
            branch=branch, _transport=_control_files._transport,
 
1761
            _internal=_internal, _format=_format, _bzrdir=_bzrdir)
 
1762
 
 
1763
        self._control_files = _control_files
 
1764
        self._detect_case_handling()
1845
1765
 
1846
1766
        if _inventory is None:
1847
1767
            # This will be acquired on lock_read() or lock_write()
1867
1787
        self._inventory = inv
1868
1788
        self._inventory_is_modified = dirty
1869
1789
 
 
1790
    def _detect_case_handling(self):
 
1791
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
1792
        try:
 
1793
            wt_trans.stat(self._format.case_sensitive_filename)
 
1794
        except errors.NoSuchFile:
 
1795
            self.case_sensitive = True
 
1796
        else:
 
1797
            self.case_sensitive = False
 
1798
 
 
1799
        self._setup_directory_is_tree_reference()
 
1800
 
1870
1801
    def _serialize(self, inventory, out_file):
1871
1802
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1872
1803
            working=True)
1874
1805
    def _deserialize(selt, in_file):
1875
1806
        return xml5.serializer_v5.read_inventory(in_file)
1876
1807
 
 
1808
    def break_lock(self):
 
1809
        """Break a lock if one is present from another instance.
 
1810
 
 
1811
        Uses the ui factory to ask for confirmation if the lock may be from
 
1812
        an active process.
 
1813
 
 
1814
        This will probe the repository for its lock as well.
 
1815
        """
 
1816
        self._control_files.break_lock()
 
1817
        self.branch.break_lock()
 
1818
 
 
1819
    def is_locked(self):
 
1820
        return self._control_files.is_locked()
 
1821
 
 
1822
    def _must_be_locked(self):
 
1823
        if not self.is_locked():
 
1824
            raise errors.ObjectNotLocked(self)
 
1825
 
 
1826
    def lock_read(self):
 
1827
        """Lock the tree for reading.
 
1828
 
 
1829
        This also locks the branch, and can be unlocked via self.unlock().
 
1830
 
 
1831
        :return: A bzrlib.lock.LogicalLockResult.
 
1832
        """
 
1833
        if not self.is_locked():
 
1834
            self._reset_data()
 
1835
        self.branch.lock_read()
 
1836
        try:
 
1837
            self._control_files.lock_read()
 
1838
            return LogicalLockResult(self.unlock)
 
1839
        except:
 
1840
            self.branch.unlock()
 
1841
            raise
 
1842
 
 
1843
    def lock_tree_write(self):
 
1844
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
1845
 
 
1846
        :return: A bzrlib.lock.LogicalLockResult.
 
1847
        """
 
1848
        if not self.is_locked():
 
1849
            self._reset_data()
 
1850
        self.branch.lock_read()
 
1851
        try:
 
1852
            self._control_files.lock_write()
 
1853
            return LogicalLockResult(self.unlock)
 
1854
        except:
 
1855
            self.branch.unlock()
 
1856
            raise
 
1857
 
 
1858
    def lock_write(self):
 
1859
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
1860
 
 
1861
        :return: A bzrlib.lock.LogicalLockResult.
 
1862
        """
 
1863
        if not self.is_locked():
 
1864
            self._reset_data()
 
1865
        self.branch.lock_write()
 
1866
        try:
 
1867
            self._control_files.lock_write()
 
1868
            return LogicalLockResult(self.unlock)
 
1869
        except:
 
1870
            self.branch.unlock()
 
1871
            raise
 
1872
 
 
1873
    def get_physical_lock_status(self):
 
1874
        return self._control_files.get_physical_lock_status()
 
1875
 
1877
1876
    @needs_tree_write_lock
1878
1877
    def _write_inventory(self, inv):
1879
1878
        """Write inventory as the current inventory."""
2089
2088
            return True
2090
2089
        return self.inventory.has_id(file_id)
2091
2090
 
2092
 
    __contains__ = has_id
2093
 
 
2094
2091
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2095
2092
    def __iter__(self):
2096
2093
        """Iterate through file_ids for this tree.
2178
2175
            mode=self.bzrdir._get_file_mode())
2179
2176
        self._inventory_is_modified = False
2180
2177
 
2181
 
    @needs_read_lock
2182
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
2183
 
        if not path:
2184
 
            path = self._inventory.id2path(file_id)
2185
 
        return self._hashcache.get_sha1(path, stat_value)
2186
 
 
2187
2178
    def get_file_mtime(self, file_id, path=None):
2188
2179
        """See Tree.get_file_mtime."""
2189
2180
        if not path:
2190
2181
            path = self.inventory.id2path(file_id)
2191
 
        return os.lstat(self.abspath(path)).st_mtime
 
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
2192
2188
 
2193
2189
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2194
2190
        file_id = self.path2id(path)
2202
2198
        mode = stat_result.st_mode
2203
2199
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2204
2200
 
2205
 
    if not supports_executable():
2206
 
        def is_executable(self, file_id, path=None):
 
2201
    def is_executable(self, file_id, path=None):
 
2202
        if not self._supports_executable():
2207
2203
            return self._inventory[file_id].executable
2208
 
 
2209
 
        _is_executable_from_path_and_stat = \
2210
 
            _is_executable_from_path_and_stat_from_basis
2211
 
    else:
2212
 
        def is_executable(self, file_id, path=None):
 
2204
        else:
2213
2205
            if not path:
2214
2206
                path = self.id2path(file_id)
2215
2207
            mode = os.lstat(self.abspath(path)).st_mode
2216
2208
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2217
2209
 
2218
 
        _is_executable_from_path_and_stat = \
2219
 
            _is_executable_from_path_and_stat_from_stat
 
2210
    def _is_executable_from_path_and_stat(self, path, stat_result):
 
2211
        if not self._supports_executable():
 
2212
            return self._is_executable_from_path_and_stat_from_basis(path, stat_result)
 
2213
        else:
 
2214
            return self._is_executable_from_path_and_stat_from_stat(path, stat_result)
2220
2215
 
2221
2216
    @needs_tree_write_lock
2222
2217
    def _add(self, files, ids, kinds):
2272
2267
                parent_tree = self.branch.repository.revision_tree(parent_id)
2273
2268
            parent_tree.lock_read()
2274
2269
            try:
2275
 
                if file_id not in parent_tree:
 
2270
                if not parent_tree.has_id(file_id):
2276
2271
                    continue
2277
2272
                ie = parent_tree.inventory[file_id]
2278
2273
                if ie.kind != 'file':
2301
2296
                       for key, line in annotator.annotate_flat(this_key)]
2302
2297
        return annotations
2303
2298
 
 
2299
    def _put_rio(self, filename, stanzas, header):
 
2300
        self._must_be_locked()
 
2301
        my_file = _mod_rio.rio_file(stanzas, header)
 
2302
        self._transport.put_file(filename, my_file,
 
2303
            mode=self.bzrdir._get_file_mode())
 
2304
 
 
2305
    @needs_tree_write_lock
 
2306
    def set_merge_modified(self, modified_hashes):
 
2307
        def iter_stanzas():
 
2308
            for file_id, hash in modified_hashes.iteritems():
 
2309
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
 
2310
                    hash=hash)
 
2311
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
 
2312
 
2304
2313
    @needs_read_lock
2305
2314
    def merge_modified(self):
2306
2315
        """Return a dictionary of files modified by a merge.
2326
2335
            for s in _mod_rio.RioReader(hashfile):
2327
2336
                # RioReader reads in Unicode, so convert file_ids back to utf8
2328
2337
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2329
 
                if file_id not in self.inventory:
 
2338
                if not self.inventory.has_id(file_id):
2330
2339
                    continue
2331
2340
                text_hash = s.get("hash")
2332
2341
                if text_hash == self.get_file_sha1(file_id):
2573
2582
        inventory. The second mode only updates the inventory without
2574
2583
        touching the file on the filesystem.
2575
2584
 
2576
 
        move uses the second mode if 'after == True' and the target is not
2577
 
        versioned but present in the working tree.
 
2585
        move uses the second mode if 'after == True' and the target is
 
2586
        either not versioned or newly added, and present in the working tree.
2578
2587
 
2579
2588
        move uses the second mode if 'after == False' and the source is
2580
2589
        versioned but no longer in the working tree, and the target is not
2727
2736
 
2728
2737
    class _RenameEntry(object):
2729
2738
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2730
 
                     to_rel, to_tail, to_parent_id, only_change_inv=False):
 
2739
                     to_rel, to_tail, to_parent_id, only_change_inv=False,
 
2740
                     change_id=False):
2731
2741
            self.from_rel = from_rel
2732
2742
            self.from_id = from_id
2733
2743
            self.from_tail = from_tail
2735
2745
            self.to_rel = to_rel
2736
2746
            self.to_tail = to_tail
2737
2747
            self.to_parent_id = to_parent_id
 
2748
            self.change_id = change_id
2738
2749
            self.only_change_inv = only_change_inv
2739
2750
 
2740
2751
    def _determine_mv_mode(self, rename_entries, after=False):
2752
2763
            to_rel = rename_entry.to_rel
2753
2764
            to_id = inv.path2id(to_rel)
2754
2765
            only_change_inv = False
 
2766
            change_id = False
2755
2767
 
2756
2768
            # check the inventory for source and destination
2757
2769
            if from_id is None:
2758
2770
                raise errors.BzrMoveFailedError(from_rel,to_rel,
2759
2771
                    errors.NotVersionedError(path=from_rel))
2760
2772
            if to_id is not None:
2761
 
                raise errors.BzrMoveFailedError(from_rel,to_rel,
2762
 
                    errors.AlreadyVersionedError(path=to_rel))
 
2773
                allowed = False
 
2774
                # allow it with --after but only if dest is newly added
 
2775
                if after:
 
2776
                    basis = self.basis_tree()
 
2777
                    basis.lock_read()
 
2778
                    try:
 
2779
                        if not basis.has_id(to_id):
 
2780
                            rename_entry.change_id = True
 
2781
                            allowed = True
 
2782
                    finally:
 
2783
                        basis.unlock()
 
2784
                if not allowed:
 
2785
                    raise errors.BzrMoveFailedError(from_rel,to_rel,
 
2786
                        errors.AlreadyVersionedError(path=to_rel))
2763
2787
 
2764
2788
            # try to determine the mode for rename (only change inv or change
2765
2789
            # inv and file system)
2781
2805
                # something is wrong, so lets determine what exactly
2782
2806
                if not self.has_filename(from_rel) and \
2783
2807
                   not self.has_filename(to_rel):
2784
 
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
2785
 
                        errors.PathsDoNotExist(paths=(str(from_rel),
2786
 
                        str(to_rel))))
 
2808
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
 
2809
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
2787
2810
                else:
2788
2811
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2789
2812
            rename_entry.only_change_inv = only_change_inv
2836
2859
            except OSError, e:
2837
2860
                raise errors.BzrMoveFailedError(entry.from_rel,
2838
2861
                    entry.to_rel, e[1])
 
2862
        if entry.change_id:
 
2863
            to_id = inv.path2id(entry.to_rel)
 
2864
            inv.remove_recursive_id(to_id)
2839
2865
        inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2840
2866
 
2841
2867
    @needs_tree_write_lock
2849
2875
        :raises: NoSuchId if any fileid is not currently versioned.
2850
2876
        """
2851
2877
        for file_id in file_ids:
2852
 
            if file_id not in self._inventory:
 
2878
            if not self._inventory.has_id(file_id):
2853
2879
                raise errors.NoSuchId(self, file_id)
2854
2880
        for file_id in file_ids:
2855
2881
            if self._inventory.has_id(file_id):
2951
2977
                if dir[2] == _directory:
2952
2978
                    pending.append(dir)
2953
2979
 
 
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
 
2954
2990
 
2955
2991
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2956
2992
    """Registry for working tree formats."""
3010
3046
    missing_parent_conflicts = False
3011
3047
    """If this format supports missing parent conflicts."""
3012
3048
 
3013
 
    @classmethod
3014
 
    def find_format_string(klass, a_bzrdir):
3015
 
        """Return format name for the working tree object in a_bzrdir."""
3016
 
        try:
3017
 
            transport = a_bzrdir.get_workingtree_transport(None)
3018
 
            return transport.get_bytes("format")
3019
 
        except errors.NoSuchFile:
3020
 
            raise errors.NoWorkingTree(base=transport.base)
3021
 
 
3022
 
    @classmethod
3023
 
    def find_format(klass, a_bzrdir):
3024
 
        """Return the format for the working tree object in a_bzrdir."""
3025
 
        try:
3026
 
            format_string = klass.find_format_string(a_bzrdir)
3027
 
            return format_registry.get(format_string)
3028
 
        except KeyError:
3029
 
            raise errors.UnknownFormatError(format=format_string,
3030
 
                                            kind="working tree")
3031
 
 
3032
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
3049
    supports_versioned_directories = None
 
3050
 
 
3051
    def initialize(self, controldir, revision_id=None, from_branch=None,
3033
3052
                   accelerator_tree=None, hardlink=False):
3034
 
        """Initialize a new working tree in a_bzrdir.
 
3053
        """Initialize a new working tree in controldir.
3035
3054
 
3036
 
        :param a_bzrdir: BzrDir to initialize the working tree in.
 
3055
        :param controldir: ControlDir to initialize the working tree in.
3037
3056
        :param revision_id: allows creating a working tree at a different
3038
3057
            revision than the branch is at.
3039
3058
        :param from_branch: Branch to checkout
3059
3078
        """Return the current default format."""
3060
3079
        return format_registry.get_default()
3061
3080
 
3062
 
    def get_format_string(self):
3063
 
        """Return the ASCII format string that identifies this format."""
3064
 
        raise NotImplementedError(self.get_format_string)
3065
 
 
3066
3081
    def get_format_description(self):
3067
3082
        """Return the short description for this format."""
3068
3083
        raise NotImplementedError(self.get_format_description)
3120
3135
    def unregister_format(klass, format):
3121
3136
        format_registry.remove(format)
3122
3137
 
 
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
 
3123
3178
 
3124
3179
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3125
3180
    "bzrlib.workingtree_4", "WorkingTreeFormat4")