~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

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,
61
60
    revision as _mod_revision,
62
61
    revisiontree,
63
62
    rio as _mod_rio,
 
63
    shelf,
64
64
    transform,
65
65
    transport,
66
66
    ui,
68
68
    xml5,
69
69
    xml7,
70
70
    )
71
 
from bzrlib.workingtree_4 import (
72
 
    WorkingTreeFormat4,
73
 
    WorkingTreeFormat5,
74
 
    WorkingTreeFormat6,
75
 
    )
76
71
""")
77
72
 
78
 
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
 
79
80
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
81
from bzrlib.i18n import gettext
80
82
from bzrlib.lock import LogicalLockResult
81
 
from bzrlib.lockable_files import LockableFiles
82
 
from bzrlib.lockdir import LockDir
83
83
import bzrlib.mutabletree
84
84
from bzrlib.mutabletree import needs_tree_write_lock
85
85
from bzrlib import osutils
91
91
    realpath,
92
92
    safe_unicode,
93
93
    splitpath,
94
 
    supports_executable,
95
94
    )
96
95
from bzrlib.trace import mutter, note
97
 
from bzrlib.transport.local import LocalTransport
98
96
from bzrlib.revision import CURRENT_REVISION
99
97
from bzrlib.symbol_versioning import (
100
98
    deprecated_passed,
180
178
 
181
179
    def __init__(self, basedir='.',
182
180
                 branch=DEPRECATED_PARAMETER,
183
 
                 _control_files=None,
184
181
                 _internal=False,
 
182
                 _transport=None,
185
183
                 _format=None,
186
184
                 _bzrdir=None):
187
185
        """Construct a WorkingTree instance. This is not a public API.
200
198
        else:
201
199
            self._branch = self.bzrdir.open_branch()
202
200
        self.basedir = realpath(basedir)
203
 
        self._control_files = _control_files
204
 
        self._transport = self._control_files._transport
205
 
        # update the whole cache up front and write to disk if anything changed;
206
 
        # in the future we might want to do this more selectively
207
 
        # two possible ways offer themselves : in self._unlock, write the cache
208
 
        # if needed, or, when the cache sees a change, append it to the hash
209
 
        # cache file, and have the parser take the most recent entry for a
210
 
        # given path only.
211
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
212
 
        cache_filename = wt_trans.local_abspath('stat-cache')
213
 
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
214
 
            self.bzrdir._get_file_mode(),
215
 
            self._content_filter_stack_provider())
216
 
        hc = self._hashcache
217
 
        hc.read()
218
 
        # is this scan needed ? it makes things kinda slow.
219
 
        #hc.scan()
220
 
 
221
 
        if hc.needs_write:
222
 
            mutter("write hc")
223
 
            hc.write()
224
 
 
225
 
        self._detect_case_handling()
 
201
        self._transport = _transport
226
202
        self._rules_searcher = None
227
203
        self.views = self._make_views()
228
204
 
238
214
        """True if filename is the name of a control file in this tree.
239
215
 
240
216
        :param filename: A filename within the tree. This is a relative path
241
 
        from the root of this tree.
 
217
            from the root of this tree.
242
218
 
243
219
        This is true IF and ONLY IF the filename is part of the meta data
244
220
        that bzr controls in this tree. I.E. a random .bzr directory placed
246
222
        """
247
223
        return self.bzrdir.is_control_filename(filename)
248
224
 
249
 
    def _detect_case_handling(self):
250
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
251
 
        try:
252
 
            wt_trans.stat(self._format.case_sensitive_filename)
253
 
        except errors.NoSuchFile:
254
 
            self.case_sensitive = True
255
 
        else:
256
 
            self.case_sensitive = False
257
 
 
258
 
        self._setup_directory_is_tree_reference()
259
 
 
260
225
    branch = property(
261
226
        fget=lambda self: self._branch,
262
227
        doc="""The branch this WorkingTree is connected to.
265
230
            the working tree has been constructed from.
266
231
            """)
267
232
 
 
233
    def has_versioned_directories(self):
 
234
        """See `Tree.has_versioned_directories`."""
 
235
        return self._format.supports_versioned_directories
 
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
 
268
243
    def break_lock(self):
269
244
        """Break a lock if one is present from another instance.
270
245
 
273
248
 
274
249
        This will probe the repository for its lock as well.
275
250
        """
276
 
        self._control_files.break_lock()
277
 
        self.branch.break_lock()
278
 
 
279
 
    def _get_check_refs(self):
280
 
        """Return the references needed to perform a check of this tree.
281
 
        
282
 
        The default implementation returns no refs, and is only suitable for
283
 
        trees that have no local caching and can commit on ghosts at any time.
284
 
 
285
 
        :seealso: bzrlib.check for details about check_refs.
286
 
        """
287
 
        return []
 
251
        raise NotImplementedError(self.break_lock)
288
252
 
289
253
    def requires_rich_root(self):
290
254
        return self._format.requires_rich_root
298
262
    def supports_views(self):
299
263
        return self.views.supports_views()
300
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
 
301
273
    @staticmethod
302
274
    def open(path=None, _unsupported=False):
303
275
        """Open an existing working tree at path.
305
277
        """
306
278
        if path is None:
307
279
            path = osutils.getcwd()
308
 
        control = bzrdir.BzrDir.open(path, _unsupported)
309
 
        return control.open_workingtree(_unsupported)
 
280
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
 
281
        return control.open_workingtree(unsupported=_unsupported)
310
282
 
311
283
    @staticmethod
312
284
    def open_containing(path=None):
323
295
        """
324
296
        if path is None:
325
297
            path = osutils.getcwd()
326
 
        control, relpath = bzrdir.BzrDir.open_containing(path)
 
298
        control, relpath = controldir.ControlDir.open_containing(path)
327
299
        return control.open_workingtree(), relpath
328
300
 
329
301
    @staticmethod
349
321
                if view_files:
350
322
                    file_list = view_files
351
323
                    view_str = views.view_display_str(view_files)
352
 
                    note("Ignoring files outside view. View is %s" % view_str)
 
324
                    note(gettext("Ignoring files outside view. View is %s") % view_str)
353
325
            return tree, file_list
354
326
        if default_directory == u'.':
355
327
            seed = file_list[0]
412
384
            else:
413
385
                return True, tree
414
386
        t = transport.get_transport(location)
415
 
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
 
387
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
416
388
                                              list_current=list_current)
417
389
        return [tr for tr in iterator if tr is not None]
418
390
 
419
 
    def all_file_ids(self):
420
 
        """See Tree.iter_all_file_ids"""
421
 
        raise NotImplementedError(self.all_file_ids)
422
 
 
423
391
    def __repr__(self):
424
392
        return "<%s of %s>" % (self.__class__.__name__,
425
393
                               getattr(self, 'basedir', None))
514
482
        finally:
515
483
            file.close()
516
484
 
517
 
    def _get_ancestors(self, default_revision):
518
 
        ancestors = set([default_revision])
519
 
        for parent_id in self.get_parent_ids():
520
 
            ancestors.update(self.branch.repository.get_ancestry(
521
 
                             parent_id, topo_sorted=False))
522
 
        return ancestors
523
 
 
524
485
    def get_parent_ids(self):
525
486
        """See Tree.get_parent_ids.
526
487
 
547
508
        raise NotImplementedError(self.get_root_id)
548
509
 
549
510
    @needs_read_lock
550
 
    def clone(self, to_bzrdir, revision_id=None):
 
511
    def clone(self, to_controldir, revision_id=None):
551
512
        """Duplicate this working tree into to_bzr, including all state.
552
513
 
553
514
        Specifically modified files are kept as modified, but
554
515
        ignored and unknown files are discarded.
555
516
 
556
 
        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()
557
518
 
558
519
        revision
559
520
            If not None, the cloned tree will have its last revision set to
561
522
            and this one merged in.
562
523
        """
563
524
        # assumes the target bzr dir format is compatible.
564
 
        result = to_bzrdir.create_workingtree()
 
525
        result = to_controldir.create_workingtree()
565
526
        self.copy_content_into(result, revision_id)
566
527
        return result
567
528
 
574
535
        else:
575
536
            # TODO now merge from tree.last_revision to revision (to preserve
576
537
            # user local changes)
577
 
            merge.transform_tree(tree, self)
578
 
            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)
579
549
 
580
550
    def id2abspath(self, file_id):
581
551
        return self.abspath(self.id2path(file_id))
582
552
 
 
553
    def _check_for_tree_references(self, iterator):
 
554
        """See if directories have become tree-references."""
 
555
        blocked_parent_ids = set()
 
556
        for path, ie in iterator:
 
557
            if ie.parent_id in blocked_parent_ids:
 
558
                # This entry was pruned because one of its parents became a
 
559
                # TreeReference. If this is a directory, mark it as blocked.
 
560
                if ie.kind == 'directory':
 
561
                    blocked_parent_ids.add(ie.file_id)
 
562
                continue
 
563
            if ie.kind == 'directory' and self._directory_is_tree_reference(path):
 
564
                # This InventoryDirectory needs to be a TreeReference
 
565
                ie = inventory.TreeReference(ie.file_id, ie.name, ie.parent_id)
 
566
                blocked_parent_ids.add(ie.file_id)
 
567
            yield path, ie
 
568
 
 
569
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
 
570
        """See Tree.iter_entries_by_dir()"""
 
571
        # The only trick here is that if we supports_tree_reference then we
 
572
        # need to detect if a directory becomes a tree-reference.
 
573
        iterator = super(WorkingTree, self).iter_entries_by_dir(
 
574
                specific_file_ids=specific_file_ids,
 
575
                yield_parents=yield_parents)
 
576
        if not self.supports_tree_reference():
 
577
            return iterator
 
578
        else:
 
579
            return self._check_for_tree_references(iterator)
 
580
 
583
581
    def get_file_size(self, file_id):
584
582
        """See Tree.get_file_size"""
585
583
        # XXX: this returns the on-disk size; it should probably return the
592
590
            else:
593
591
                return None
594
592
 
595
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
596
 
        # FIXME: Shouldn't this be in Tree?
597
 
        raise NotImplementedError(self.get_file_sha1)
598
 
 
599
593
    @needs_tree_write_lock
600
594
    def _gather_kinds(self, files, kinds):
601
595
        """See MutableTree._gather_kinds."""
616
610
        and setting the list to its value plus revision_id.
617
611
 
618
612
        :param revision_id: The revision id to add to the parent list. It may
619
 
        be a ghost revision as long as its not the first parent to be added,
620
 
        or the allow_leftmost_as_ghost parameter is set True.
 
613
            be a ghost revision as long as its not the first parent to be
 
614
            added, or the allow_leftmost_as_ghost parameter is set True.
621
615
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
622
616
        """
623
617
        parents = self.get_parent_ids() + [revision_id]
766
760
 
767
761
    @needs_tree_write_lock
768
762
    def set_merge_modified(self, modified_hashes):
769
 
        def iter_stanzas():
770
 
            for file_id, hash in modified_hashes.iteritems():
771
 
                yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
772
 
                    hash=hash)
773
 
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
 
763
        """Set the merge modified hashes."""
 
764
        raise NotImplementedError(self.set_merge_modified)
774
765
 
775
766
    def _sha_from_stat(self, path, stat_result):
776
767
        """Get a sha digest from the tree's stat cache.
782
773
        """
783
774
        return None
784
775
 
785
 
    def _put_rio(self, filename, stanzas, header):
786
 
        self._must_be_locked()
787
 
        my_file = _mod_rio.rio_file(stanzas, header)
788
 
        self._transport.put_file(filename, my_file,
789
 
            mode=self.bzrdir._get_file_mode())
790
 
 
791
776
    @needs_write_lock # because merge pulls data into the branch.
792
777
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
793
778
                          merge_type=None, force=False):
853
838
        self.add(path, file_id, 'directory')
854
839
        return file_id
855
840
 
856
 
    def get_symlink_target(self, file_id):
857
 
        abspath = self.id2abspath(file_id)
 
841
    def get_symlink_target(self, file_id, path=None):
 
842
        if path is not None:
 
843
            abspath = self.abspath(path)
 
844
        else:
 
845
            abspath = self.id2abspath(file_id)
858
846
        target = osutils.readlink(abspath)
859
847
        return target
860
848
 
960
948
        file and change the file_id. That is the normal mode. Second, it can
961
949
        only change the file_id without touching any physical file.
962
950
 
963
 
        rename_one uses the second mode if 'after == True' and 'to_rel' is not
964
 
        versioned but present in the working tree.
 
951
        rename_one uses the second mode if 'after == True' and 'to_rel' is
 
952
        either not versioned or newly added, and present in the working tree.
965
953
 
966
954
        rename_one uses the second mode if 'after == False' and 'from_rel' is
967
955
        versioned but no longer in the working tree, and 'to_rel' is not
1012
1000
            new_revision_info = self.branch.last_revision_info()
1013
1001
            if new_revision_info != old_revision_info:
1014
1002
                repository = self.branch.repository
 
1003
                if repository._format.fast_deltas:
 
1004
                    parent_ids = self.get_parent_ids()
 
1005
                    if parent_ids:
 
1006
                        basis_id = parent_ids[0]
 
1007
                        basis_tree = repository.revision_tree(basis_id)
1015
1008
                basis_tree.lock_read()
1016
1009
                try:
1017
1010
                    new_basis_tree = self.branch.basis_tree()
1025
1018
                                show_base=show_base)
1026
1019
                    basis_root_id = basis_tree.get_root_id()
1027
1020
                    new_root_id = new_basis_tree.get_root_id()
1028
 
                    if basis_root_id != new_root_id:
 
1021
                    if new_root_id is not None and basis_root_id != new_root_id:
1029
1022
                        self.set_root_id(new_root_id)
1030
1023
                finally:
1031
1024
                    basis_tree.unlock()
1032
1025
                # TODO - dedup parents list with things merged by pull ?
1033
1026
                # reuse the revisiontree we merged against to set the new
1034
1027
                # tree data.
1035
 
                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))
1036
1032
                # we have to pull the merge trees out again, because
1037
1033
                # merge_inner has set the ids. - this corner is not yet
1038
1034
                # layered well enough to prevent double handling.
1055
1051
            stream.write(bytes)
1056
1052
        finally:
1057
1053
            stream.close()
1058
 
        # TODO: update the hashcache here ?
1059
1054
 
1060
1055
    def extras(self):
1061
1056
        """Yield all unversioned files in this WorkingTree.
1138
1133
        else:
1139
1134
            mode = stat_value.st_mode
1140
1135
            kind = osutils.file_kind_from_stat_mode(mode)
1141
 
            if not supports_executable():
 
1136
            if not self._supports_executable():
1142
1137
                executable = entry is not None and entry.executable
1143
1138
            else:
1144
1139
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1163
1158
        return _mod_revision.ensure_null(self.branch.last_revision())
1164
1159
 
1165
1160
    def is_locked(self):
1166
 
        return self._control_files.is_locked()
1167
 
 
1168
 
    def _must_be_locked(self):
1169
 
        if not self.is_locked():
1170
 
            raise errors.ObjectNotLocked(self)
 
1161
        """Check if this tree is locked."""
 
1162
        raise NotImplementedError(self.is_locked)
1171
1163
 
1172
1164
    def lock_read(self):
1173
1165
        """Lock the tree for reading.
1176
1168
 
1177
1169
        :return: A bzrlib.lock.LogicalLockResult.
1178
1170
        """
1179
 
        if not self.is_locked():
1180
 
            self._reset_data()
1181
 
        self.branch.lock_read()
1182
 
        try:
1183
 
            self._control_files.lock_read()
1184
 
            return LogicalLockResult(self.unlock)
1185
 
        except:
1186
 
            self.branch.unlock()
1187
 
            raise
 
1171
        raise NotImplementedError(self.lock_read)
1188
1172
 
1189
1173
    def lock_tree_write(self):
1190
1174
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
1191
1175
 
1192
1176
        :return: A bzrlib.lock.LogicalLockResult.
1193
1177
        """
1194
 
        if not self.is_locked():
1195
 
            self._reset_data()
1196
 
        self.branch.lock_read()
1197
 
        try:
1198
 
            self._control_files.lock_write()
1199
 
            return LogicalLockResult(self.unlock)
1200
 
        except:
1201
 
            self.branch.unlock()
1202
 
            raise
 
1178
        raise NotImplementedError(self.lock_tree_write)
1203
1179
 
1204
1180
    def lock_write(self):
1205
1181
        """See MutableTree.lock_write, and WorkingTree.unlock.
1206
1182
 
1207
1183
        :return: A bzrlib.lock.LogicalLockResult.
1208
1184
        """
1209
 
        if not self.is_locked():
1210
 
            self._reset_data()
1211
 
        self.branch.lock_write()
1212
 
        try:
1213
 
            self._control_files.lock_write()
1214
 
            return LogicalLockResult(self.unlock)
1215
 
        except:
1216
 
            self.branch.unlock()
1217
 
            raise
 
1185
        raise NotImplementedError(self.lock_write)
1218
1186
 
1219
1187
    def get_physical_lock_status(self):
1220
 
        return self._control_files.get_physical_lock_status()
1221
 
 
1222
 
    def _reset_data(self):
1223
 
        """Reset transient data that cannot be revalidated."""
1224
 
        raise NotImplementedError(self._reset_data)
 
1188
        raise NotImplementedError(self.get_physical_lock_status)
1225
1189
 
1226
1190
    def set_last_revision(self, new_revision):
1227
1191
        """Change the last revision in the working tree."""
1236
1200
        if _mod_revision.is_null(new_revision):
1237
1201
            self.branch.set_last_revision_info(0, new_revision)
1238
1202
            return False
 
1203
        _mod_revision.check_not_reserved_id(new_revision)
1239
1204
        try:
1240
1205
            self.branch.generate_revision_history(new_revision)
1241
1206
        except errors.NoSuchRevision:
1366
1331
    def revert(self, filenames=None, old_tree=None, backups=True,
1367
1332
               pb=None, report_changes=False):
1368
1333
        from bzrlib.conflicts import resolve
1369
 
        if filenames == []:
1370
 
            filenames = None
1371
 
            symbol_versioning.warn('Using [] to revert all files is deprecated'
1372
 
                ' as of bzr 0.91.  Please use None (the default) instead.',
1373
 
                DeprecationWarning, stacklevel=2)
1374
1334
        if old_tree is None:
1375
1335
            basis_tree = self.basis_tree()
1376
1336
            basis_tree.lock_read()
1397
1357
                basis_tree.unlock()
1398
1358
        return conflicts
1399
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
 
1400
1388
    def revision_tree(self, revision_id):
1401
1389
        """See Tree.revision_tree.
1402
1390
 
1459
1447
        - Restore the wt.basis->wt.state changes.
1460
1448
 
1461
1449
        There isn't a single operation at the moment to do that, so we:
 
1450
 
1462
1451
        - Merge current state -> basis tree of the master w.r.t. the old tree
1463
1452
          basis.
1464
1453
        - Do a 'normal' merge of the old branch basis if it is relevant.
1524
1513
                                             show_base=show_base)
1525
1514
            if nb_conflicts:
1526
1515
                self.add_parent_tree((old_tip, other_tree))
1527
 
                note('Rerun update after fixing the conflicts.')
 
1516
                note(gettext('Rerun update after fixing the conflicts.'))
1528
1517
                return nb_conflicts
1529
1518
 
1530
1519
        if last_rev != _mod_revision.ensure_null(revision):
1572
1561
            last_rev = parent_trees[0][0]
1573
1562
        return nb_conflicts
1574
1563
 
1575
 
    def _write_hashcache_if_dirty(self):
1576
 
        """Write out the hashcache if it is dirty."""
1577
 
        if self._hashcache.needs_write:
1578
 
            try:
1579
 
                self._hashcache.write()
1580
 
            except OSError, e:
1581
 
                if e.errno not in (errno.EPERM, errno.EACCES):
1582
 
                    raise
1583
 
                # TODO: jam 20061219 Should this be a warning? A single line
1584
 
                #       warning might be sufficient to let the user know what
1585
 
                #       is going on.
1586
 
                mutter('Could not write hashcache for %s\nError: %s',
1587
 
                              self._hashcache.cache_file_name(), e)
1588
 
 
1589
1564
    def set_conflicts(self, arg):
1590
1565
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1591
1566
 
1719
1694
    def _walkdirs(self, prefix=""):
1720
1695
        """Walk the directories of this tree.
1721
1696
 
1722
 
           :prefix: is used as the directrory to start with.
1723
 
           returns a generator which yields items in the form:
1724
 
                ((curren_directory_path, fileid),
1725
 
                 [(file1_path, file1_name, file1_kind, None, file1_id,
1726
 
                   file1_kind), ... ])
 
1697
        :param prefix: is used as the directrory to start with.
 
1698
        :returns: a generator which yields items in the form::
 
1699
 
 
1700
            ((curren_directory_path, fileid),
 
1701
             [(file1_path, file1_name, file1_kind, None, file1_id,
 
1702
               file1_kind), ... ])
1727
1703
        """
1728
1704
        raise NotImplementedError(self._walkdirs)
1729
1705
 
1735
1711
        are considered 'resolved', because bzr always puts conflict markers
1736
1712
        into files that have text conflicts.  The corresponding .THIS .BASE and
1737
1713
        .OTHER files are deleted, as per 'resolve'.
 
1714
 
1738
1715
        :return: a tuple of ConflictLists: (un_resolved, resolved).
1739
1716
        """
1740
1717
        un_resolved = _mod_conflicts.ConflictList()
1759
1736
        self.set_conflicts(un_resolved)
1760
1737
        return un_resolved, resolved
1761
1738
 
1762
 
    @needs_read_lock
1763
 
    def _check(self, references):
1764
 
        """Check the tree for consistency.
1765
 
 
1766
 
        :param references: A dict with keys matching the items returned by
1767
 
            self._get_check_refs(), and values from looking those keys up in
1768
 
            the repository.
1769
 
        """
1770
 
        tree_basis = self.basis_tree()
1771
 
        tree_basis.lock_read()
1772
 
        try:
1773
 
            repo_basis = references[('trees', self.last_revision())]
1774
 
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
1775
 
                raise errors.BzrCheckError(
1776
 
                    "Mismatched basis inventory content.")
1777
 
            self._validate()
1778
 
        finally:
1779
 
            tree_basis.unlock()
1780
 
 
1781
1739
    def _validate(self):
1782
1740
        """Validate internal structures.
1783
1741
 
1789
1747
        """
1790
1748
        return
1791
1749
 
1792
 
    @needs_read_lock
1793
1750
    def check_state(self):
1794
1751
        """Check that the working state is/isn't valid."""
1795
 
        check_refs = self._get_check_refs()
1796
 
        refs = {}
1797
 
        for ref in check_refs:
1798
 
            kind, value = ref
1799
 
            if kind == 'trees':
1800
 
                refs[ref] = self.branch.repository.revision_tree(value)
1801
 
        self._check(refs)
 
1752
        raise NotImplementedError(self.check_state)
1802
1753
 
1803
1754
    def reset_state(self, revision_ids=None):
1804
1755
        """Reset the state of the working tree.
1844
1795
        :param branch: A branch to override probing for the branch.
1845
1796
        """
1846
1797
        super(InventoryWorkingTree, self).__init__(basedir=basedir,
1847
 
            branch=branch, _control_files=_control_files, _internal=_internal,
1848
 
            _format=_format, _bzrdir=_bzrdir)
 
1798
            branch=branch, _transport=_control_files._transport,
 
1799
            _internal=_internal, _format=_format, _bzrdir=_bzrdir)
 
1800
 
 
1801
        self._control_files = _control_files
 
1802
        self._detect_case_handling()
1849
1803
 
1850
1804
        if _inventory is None:
1851
1805
            # This will be acquired on lock_read() or lock_write()
1871
1825
        self._inventory = inv
1872
1826
        self._inventory_is_modified = dirty
1873
1827
 
 
1828
    def _detect_case_handling(self):
 
1829
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
1830
        try:
 
1831
            wt_trans.stat(self._format.case_sensitive_filename)
 
1832
        except errors.NoSuchFile:
 
1833
            self.case_sensitive = True
 
1834
        else:
 
1835
            self.case_sensitive = False
 
1836
 
 
1837
        self._setup_directory_is_tree_reference()
 
1838
 
1874
1839
    def _serialize(self, inventory, out_file):
1875
1840
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1876
1841
            working=True)
1878
1843
    def _deserialize(selt, in_file):
1879
1844
        return xml5.serializer_v5.read_inventory(in_file)
1880
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
 
1881
1914
    @needs_tree_write_lock
1882
1915
    def _write_inventory(self, inv):
1883
1916
        """Write inventory as the current inventory."""
1951
1984
            if entry.parent_id == orig_root_id:
1952
1985
                entry.parent_id = inv.root.file_id
1953
1986
 
1954
 
    def all_file_ids(self):
1955
 
        """See Tree.iter_all_file_ids"""
1956
 
        return set(self.inventory)
1957
 
 
1958
1987
    @needs_tree_write_lock
1959
1988
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1960
1989
        """See MutableTree.set_parent_trees."""
1979
2008
                # parent tree from the repository.
1980
2009
                self._cache_basis_inventory(leftmost_parent_id)
1981
2010
            else:
1982
 
                inv = leftmost_parent_tree.inventory
 
2011
                inv = leftmost_parent_tree.root_inventory
1983
2012
                xml = self._create_basis_xml_from_inventory(
1984
2013
                                        leftmost_parent_id, inv)
1985
2014
                self._write_basis_inventory(xml)
2020
2049
        inventory.revision_id = revision_id
2021
2050
        return xml7.serializer_v7.write_inventory_to_string(inventory)
2022
2051
 
 
2052
    @needs_tree_write_lock
 
2053
    def set_conflicts(self, conflicts):
 
2054
        self._put_rio('conflicts', conflicts.to_stanzas(),
 
2055
                      CONFLICT_HEADER_1)
 
2056
 
 
2057
    @needs_tree_write_lock
 
2058
    def add_conflicts(self, new_conflicts):
 
2059
        conflict_set = set(self.conflicts())
 
2060
        conflict_set.update(set(list(new_conflicts)))
 
2061
        self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
 
2062
                                       key=_mod_conflicts.Conflict.sort_key)))
 
2063
 
 
2064
    @needs_read_lock
 
2065
    def conflicts(self):
 
2066
        try:
 
2067
            confile = self._transport.get('conflicts')
 
2068
        except errors.NoSuchFile:
 
2069
            return _mod_conflicts.ConflictList()
 
2070
        try:
 
2071
            try:
 
2072
                if confile.next() != CONFLICT_HEADER_1 + '\n':
 
2073
                    raise errors.ConflictFormatError()
 
2074
            except StopIteration:
 
2075
                raise errors.ConflictFormatError()
 
2076
            reader = _mod_rio.RioReader(confile)
 
2077
            return _mod_conflicts.ConflictList.from_stanzas(reader)
 
2078
        finally:
 
2079
            confile.close()
 
2080
 
2023
2081
    def read_basis_inventory(self):
2024
2082
        """Read the cached basis inventory."""
2025
2083
        path = self._basis_inventory_name()
2053
2111
 
2054
2112
    def has_id(self, file_id):
2055
2113
        # files that have been deleted are excluded
2056
 
        inv = self.inventory
2057
 
        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):
2058
2116
            return False
2059
 
        path = inv.id2path(file_id)
 
2117
        path = inv.id2path(inv_file_id)
2060
2118
        return osutils.lexists(self.abspath(path))
2061
2119
 
2062
2120
    def has_or_had_id(self, file_id):
2063
 
        if file_id == self.inventory.root.file_id:
 
2121
        if file_id == self.get_root_id():
2064
2122
            return True
2065
 
        return self.inventory.has_id(file_id)
2066
 
 
2067
 
    __contains__ = has_id
2068
 
 
2069
 
    # should be deprecated - this is slow and in any case treating them as a
2070
 
    # container is (we now know) bad style -- mbp 20070302
2071
 
    ## @deprecated_method(zero_fifteen)
2072
 
    def __iter__(self):
 
2123
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2124
        return inv.has_id(inv_file_id)
 
2125
 
 
2126
    def all_file_ids(self):
2073
2127
        """Iterate through file_ids for this tree.
2074
2128
 
2075
2129
        file_ids are in a WorkingTree if they are in the working inventory
2076
2130
        and the working file exists.
2077
2131
        """
2078
 
        inv = self._inventory
2079
 
        for path, ie in inv.iter_entries():
2080
 
            if osutils.lexists(self.abspath(path)):
2081
 
                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
2082
2136
 
2083
2137
    @needs_tree_write_lock
2084
2138
    def set_last_revision(self, new_revision):
2086
2140
        if self._change_last_revision(new_revision):
2087
2141
            self._cache_basis_inventory(new_revision)
2088
2142
 
 
2143
    def _get_check_refs(self):
 
2144
        """Return the references needed to perform a check of this tree.
 
2145
        
 
2146
        The default implementation returns no refs, and is only suitable for
 
2147
        trees that have no local caching and can commit on ghosts at any time.
 
2148
 
 
2149
        :seealso: bzrlib.check for details about check_refs.
 
2150
        """
 
2151
        return []
 
2152
 
 
2153
    @needs_read_lock
 
2154
    def _check(self, references):
 
2155
        """Check the tree for consistency.
 
2156
 
 
2157
        :param references: A dict with keys matching the items returned by
 
2158
            self._get_check_refs(), and values from looking those keys up in
 
2159
            the repository.
 
2160
        """
 
2161
        tree_basis = self.basis_tree()
 
2162
        tree_basis.lock_read()
 
2163
        try:
 
2164
            repo_basis = references[('trees', self.last_revision())]
 
2165
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
 
2166
                raise errors.BzrCheckError(
 
2167
                    "Mismatched basis inventory content.")
 
2168
            self._validate()
 
2169
        finally:
 
2170
            tree_basis.unlock()
 
2171
 
 
2172
    @needs_read_lock
 
2173
    def check_state(self):
 
2174
        """Check that the working state is/isn't valid."""
 
2175
        check_refs = self._get_check_refs()
 
2176
        refs = {}
 
2177
        for ref in check_refs:
 
2178
            kind, value = ref
 
2179
            if kind == 'trees':
 
2180
                refs[ref] = self.branch.repository.revision_tree(value)
 
2181
        self._check(refs)
 
2182
 
2089
2183
    @needs_tree_write_lock
2090
2184
    def reset_state(self, revision_ids=None):
2091
2185
        """Reset the state of the working tree.
2100
2194
                _mod_revision.NULL_REVISION)
2101
2195
        else:
2102
2196
            rt = self.branch.repository.revision_tree(revision_ids[0])
2103
 
        self._write_inventory(rt.inventory)
 
2197
        self._write_inventory(rt.root_inventory)
2104
2198
        self.set_parent_ids(revision_ids)
2105
2199
 
2106
2200
    def flush(self):
2115
2209
            mode=self.bzrdir._get_file_mode())
2116
2210
        self._inventory_is_modified = False
2117
2211
 
2118
 
    @needs_read_lock
2119
 
    def get_file_sha1(self, file_id, path=None, stat_value=None):
2120
 
        if not path:
2121
 
            path = self._inventory.id2path(file_id)
2122
 
        return self._hashcache.get_sha1(path, stat_value)
2123
 
 
2124
2212
    def get_file_mtime(self, file_id, path=None):
2125
2213
        """See Tree.get_file_mtime."""
2126
2214
        if not path:
2127
 
            path = self.inventory.id2path(file_id)
2128
 
        return os.lstat(self.abspath(path)).st_mtime
 
2215
            path = self.id2path(file_id)
 
2216
        try:
 
2217
            return os.lstat(self.abspath(path)).st_mtime
 
2218
        except OSError, e:
 
2219
            if e.errno == errno.ENOENT:
 
2220
                raise errors.FileTimestampUnavailable(path)
 
2221
            raise
2129
2222
 
2130
2223
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2131
 
        file_id = self.path2id(path)
 
2224
        inv, file_id = self._path2inv_file_id(path)
2132
2225
        if file_id is None:
2133
2226
            # For unversioned files on win32, we just assume they are not
2134
2227
            # executable
2135
2228
            return False
2136
 
        return self._inventory[file_id].executable
 
2229
        return inv[file_id].executable
2137
2230
 
2138
2231
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2139
2232
        mode = stat_result.st_mode
2140
2233
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2141
2234
 
2142
 
    if not supports_executable():
2143
 
        def is_executable(self, file_id, path=None):
2144
 
            return self._inventory[file_id].executable
2145
 
 
2146
 
        _is_executable_from_path_and_stat = \
2147
 
            _is_executable_from_path_and_stat_from_basis
2148
 
    else:
2149
 
        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:
2150
2240
            if not path:
2151
2241
                path = self.id2path(file_id)
2152
2242
            mode = os.lstat(self.abspath(path)).st_mode
2153
2243
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2154
2244
 
2155
 
        _is_executable_from_path_and_stat = \
2156
 
            _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)
2157
2250
 
2158
2251
    @needs_tree_write_lock
2159
2252
    def _add(self, files, ids, kinds):
2162
2255
        # should probably put it back with the previous ID.
2163
2256
        # the read and write working inventory should not occur in this
2164
2257
        # function - they should be part of lock_write and unlock.
2165
 
        inv = self.inventory
 
2258
        # FIXME: nested trees
 
2259
        inv = self.root_inventory
2166
2260
        for f, file_id, kind in zip(files, ids, kinds):
2167
2261
            if file_id is None:
2168
2262
                inv.add_path(f, kind=kind)
2209
2303
                parent_tree = self.branch.repository.revision_tree(parent_id)
2210
2304
            parent_tree.lock_read()
2211
2305
            try:
2212
 
                if file_id not in parent_tree:
 
2306
                try:
 
2307
                    kind = parent_tree.kind(file_id)
 
2308
                except errors.NoSuchId:
2213
2309
                    continue
2214
 
                ie = parent_tree.inventory[file_id]
2215
 
                if ie.kind != 'file':
 
2310
                if kind != 'file':
2216
2311
                    # Note: this is slightly unnecessary, because symlinks and
2217
2312
                    # directories have a "text" which is the empty text, and we
2218
2313
                    # know that won't mess up annotations. But it seems cleaner
2219
2314
                    continue
2220
 
                parent_text_key = (file_id, ie.revision)
 
2315
                parent_text_key = (
 
2316
                    file_id, parent_tree.get_file_revision(file_id))
2221
2317
                if parent_text_key not in maybe_file_parent_keys:
2222
2318
                    maybe_file_parent_keys.append(parent_text_key)
2223
2319
            finally:
2238
2334
                       for key, line in annotator.annotate_flat(this_key)]
2239
2335
        return annotations
2240
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
 
2241
2351
    @needs_read_lock
2242
2352
    def merge_modified(self):
2243
2353
        """Return a dictionary of files modified by a merge.
2263
2373
            for s in _mod_rio.RioReader(hashfile):
2264
2374
                # RioReader reads in Unicode, so convert file_ids back to utf8
2265
2375
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2266
 
                if file_id not in self.inventory:
 
2376
                if not self.has_id(file_id):
2267
2377
                    continue
2268
2378
                text_hash = s.get("hash")
2269
2379
                if text_hash == self.get_file_sha1(file_id):
2299
2409
        other_tree.lock_tree_write()
2300
2410
        try:
2301
2411
            new_parents = other_tree.get_parent_ids()
2302
 
            other_root = other_tree.inventory.root
 
2412
            other_root = other_tree.root_inventory.root
2303
2413
            other_root.parent_id = new_root_parent
2304
2414
            other_root.name = osutils.basename(other_tree_path)
2305
 
            self.inventory.add(other_root)
2306
 
            add_children(self.inventory, other_root)
2307
 
            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)
2308
2418
            # normally we don't want to fetch whole repositories, but i think
2309
2419
            # here we really do want to consolidate the whole thing.
2310
2420
            for parent_id in other_tree.get_parent_ids():
2348
2458
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
2349
2459
        if tree_transport.base != branch_transport.base:
2350
2460
            tree_bzrdir = format.initialize_on_transport(tree_transport)
2351
 
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
2352
 
                target_branch=new_branch)
 
2461
            tree_bzrdir.set_branch_reference(new_branch)
2353
2462
        else:
2354
2463
            tree_bzrdir = branch_bzrdir
2355
2464
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2356
2465
        wt.set_parent_ids(self.get_parent_ids())
2357
 
        my_inv = self.inventory
 
2466
        # FIXME: Support nested trees
 
2467
        my_inv = self.root_inventory
2358
2468
        child_inv = inventory.Inventory(root_id=None)
2359
2469
        new_root = my_inv[file_id]
2360
2470
        my_inv.remove_recursive_id(file_id)
2380
2490
        if not self.is_locked():
2381
2491
            raise errors.ObjectNotLocked(self)
2382
2492
 
2383
 
        inv = self.inventory
2384
2493
        if from_dir is None and include_root is True:
2385
 
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
 
2494
            yield ('', 'V', 'directory', self.get_root_id(), self.root_inventory.root)
2386
2495
        # Convert these into local objects to save lookup times
2387
2496
        pathjoin = osutils.pathjoin
2388
2497
        file_kind = self._kind
2395
2504
 
2396
2505
        # directory file_id, relative path, absolute path, reverse sorted children
2397
2506
        if from_dir is not None:
2398
 
            from_dir_id = inv.path2id(from_dir)
 
2507
            inv, from_dir_id = self._path2inv_file_id(from_dir)
2399
2508
            if from_dir_id is None:
2400
2509
                # Directory not versioned
2401
2510
                return
2402
2511
            from_dir_abspath = pathjoin(self.basedir, from_dir)
2403
2512
        else:
 
2513
            inv = self.root_inventory
2404
2514
            from_dir_id = inv.root.file_id
2405
2515
            from_dir_abspath = self.basedir
2406
2516
        children = os.listdir(from_dir_abspath)
2510
2620
        inventory. The second mode only updates the inventory without
2511
2621
        touching the file on the filesystem.
2512
2622
 
2513
 
        move uses the second mode if 'after == True' and the target is not
2514
 
        versioned but present in the working tree.
 
2623
        move uses the second mode if 'after == True' and the target is
 
2624
        either not versioned or newly added, and present in the working tree.
2515
2625
 
2516
2626
        move uses the second mode if 'after == False' and the source is
2517
2627
        versioned but no longer in the working tree, and the target is not
2529
2639
        rename_entries = []
2530
2640
        rename_tuples = []
2531
2641
 
 
2642
        invs_to_write = set()
 
2643
 
2532
2644
        # check for deprecated use of signature
2533
2645
        if to_dir is None:
2534
2646
            raise TypeError('You must supply a target directory')
2535
2647
        # check destination directory
2536
2648
        if isinstance(from_paths, basestring):
2537
2649
            raise ValueError()
2538
 
        inv = self.inventory
2539
2650
        to_abs = self.abspath(to_dir)
2540
2651
        if not isdir(to_abs):
2541
2652
            raise errors.BzrMoveFailedError('',to_dir,
2543
2654
        if not self.has_filename(to_dir):
2544
2655
            raise errors.BzrMoveFailedError('',to_dir,
2545
2656
                errors.NotInWorkingDirectory(to_dir))
2546
 
        to_dir_id = inv.path2id(to_dir)
 
2657
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2547
2658
        if to_dir_id is None:
2548
2659
            raise errors.BzrMoveFailedError('',to_dir,
2549
2660
                errors.NotVersionedError(path=to_dir))
2550
2661
 
2551
 
        to_dir_ie = inv[to_dir_id]
 
2662
        to_dir_ie = to_inv[to_dir_id]
2552
2663
        if to_dir_ie.kind != 'directory':
2553
2664
            raise errors.BzrMoveFailedError('',to_dir,
2554
2665
                errors.NotADirectory(to_abs))
2556
2667
        # create rename entries and tuples
2557
2668
        for from_rel in from_paths:
2558
2669
            from_tail = splitpath(from_rel)[-1]
2559
 
            from_id = inv.path2id(from_rel)
 
2670
            from_inv, from_id = self._path2inv_file_id(from_rel)
2560
2671
            if from_id is None:
2561
2672
                raise errors.BzrMoveFailedError(from_rel,to_dir,
2562
2673
                    errors.NotVersionedError(path=from_rel))
2563
2674
 
2564
 
            from_entry = inv[from_id]
 
2675
            from_entry = from_inv[from_id]
2565
2676
            from_parent_id = from_entry.parent_id
2566
2677
            to_rel = pathjoin(to_dir, from_tail)
2567
2678
            rename_entry = InventoryWorkingTree._RenameEntry(
2586
2697
            # restore the inventory on error
2587
2698
            self._inventory_is_modified = original_modified
2588
2699
            raise
2589
 
        self._write_inventory(inv)
 
2700
        #FIXME: Should potentially also write the from_invs
 
2701
        self._write_inventory(to_inv)
2590
2702
        return rename_tuples
2591
2703
 
2592
2704
    @needs_tree_write_lock
2612
2724
 
2613
2725
        Everything else results in an error.
2614
2726
        """
2615
 
        inv = self.inventory
2616
2727
        rename_entries = []
2617
2728
 
2618
2729
        # create rename entries and tuples
2619
2730
        from_tail = splitpath(from_rel)[-1]
2620
 
        from_id = inv.path2id(from_rel)
 
2731
        from_inv, from_id = self._path2inv_file_id(from_rel)
2621
2732
        if from_id is None:
2622
2733
            # if file is missing in the inventory maybe it's in the basis_tree
2623
2734
            basis_tree = self.branch.basis_tree()
2626
2737
                raise errors.BzrRenameFailedError(from_rel,to_rel,
2627
2738
                    errors.NotVersionedError(path=from_rel))
2628
2739
            # put entry back in the inventory so we can rename it
2629
 
            from_entry = basis_tree.inventory[from_id].copy()
2630
 
            inv.add(from_entry)
 
2740
            from_entry = basis_tree.root_inventory[from_id].copy()
 
2741
            from_inv.add(from_entry)
2631
2742
        else:
2632
 
            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]
2633
2745
        from_parent_id = from_entry.parent_id
2634
2746
        to_dir, to_tail = os.path.split(to_rel)
2635
 
        to_dir_id = inv.path2id(to_dir)
 
2747
        to_inv, to_dir_id = self._path2inv_file_id(to_dir)
2636
2748
        rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2637
2749
                                     from_id=from_id,
2638
2750
                                     from_tail=from_tail,
2660
2772
               from_id, from_rel, to_rel, to_dir, to_dir_id)
2661
2773
 
2662
2774
        self._move(rename_entries)
2663
 
        self._write_inventory(inv)
 
2775
        self._write_inventory(to_inv)
2664
2776
 
2665
2777
    class _RenameEntry(object):
2666
2778
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2667
 
                     to_rel, to_tail, to_parent_id, only_change_inv=False):
 
2779
                     to_rel, to_tail, to_parent_id, only_change_inv=False,
 
2780
                     change_id=False):
2668
2781
            self.from_rel = from_rel
2669
2782
            self.from_id = from_id
2670
2783
            self.from_tail = from_tail
2672
2785
            self.to_rel = to_rel
2673
2786
            self.to_tail = to_tail
2674
2787
            self.to_parent_id = to_parent_id
 
2788
            self.change_id = change_id
2675
2789
            self.only_change_inv = only_change_inv
2676
2790
 
2677
2791
    def _determine_mv_mode(self, rename_entries, after=False):
2680
2794
 
2681
2795
        Also does basic plausability tests.
2682
2796
        """
2683
 
        inv = self.inventory
 
2797
        # FIXME: Handling of nested trees
 
2798
        inv = self.root_inventory
2684
2799
 
2685
2800
        for rename_entry in rename_entries:
2686
2801
            # store to local variables for easier reference
2689
2804
            to_rel = rename_entry.to_rel
2690
2805
            to_id = inv.path2id(to_rel)
2691
2806
            only_change_inv = False
 
2807
            change_id = False
2692
2808
 
2693
2809
            # check the inventory for source and destination
2694
2810
            if from_id is None:
2695
2811
                raise errors.BzrMoveFailedError(from_rel,to_rel,
2696
2812
                    errors.NotVersionedError(path=from_rel))
2697
2813
            if to_id is not None:
2698
 
                raise errors.BzrMoveFailedError(from_rel,to_rel,
2699
 
                    errors.AlreadyVersionedError(path=to_rel))
 
2814
                allowed = False
 
2815
                # allow it with --after but only if dest is newly added
 
2816
                if after:
 
2817
                    basis = self.basis_tree()
 
2818
                    basis.lock_read()
 
2819
                    try:
 
2820
                        if not basis.has_id(to_id):
 
2821
                            rename_entry.change_id = True
 
2822
                            allowed = True
 
2823
                    finally:
 
2824
                        basis.unlock()
 
2825
                if not allowed:
 
2826
                    raise errors.BzrMoveFailedError(from_rel,to_rel,
 
2827
                        errors.AlreadyVersionedError(path=to_rel))
2700
2828
 
2701
2829
            # try to determine the mode for rename (only change inv or change
2702
2830
            # inv and file system)
2718
2846
                # something is wrong, so lets determine what exactly
2719
2847
                if not self.has_filename(from_rel) and \
2720
2848
                   not self.has_filename(to_rel):
2721
 
                    raise errors.BzrRenameFailedError(from_rel,to_rel,
2722
 
                        errors.PathsDoNotExist(paths=(str(from_rel),
2723
 
                        str(to_rel))))
 
2849
                    raise errors.BzrRenameFailedError(from_rel, to_rel,
 
2850
                        errors.PathsDoNotExist(paths=(from_rel, to_rel)))
2724
2851
                else:
2725
2852
                    raise errors.RenameFailedFilesExist(from_rel, to_rel)
2726
2853
            rename_entry.only_change_inv = only_change_inv
2732
2859
        Depending on the value of the flag 'only_change_inv', the
2733
2860
        file will be moved on the file system or not.
2734
2861
        """
2735
 
        inv = self.inventory
2736
2862
        moved = []
2737
2863
 
2738
2864
        for entry in rename_entries:
2745
2871
 
2746
2872
    def _rollback_move(self, moved):
2747
2873
        """Try to rollback a previous move in case of an filesystem error."""
2748
 
        inv = self.inventory
2749
2874
        for entry in moved:
2750
2875
            try:
2751
2876
                self._move_entry(WorkingTree._RenameEntry(
2760
2885
                        " Error message is: %s" % e)
2761
2886
 
2762
2887
    def _move_entry(self, entry):
2763
 
        inv = self.inventory
 
2888
        inv = self.root_inventory
2764
2889
        from_rel_abs = self.abspath(entry.from_rel)
2765
2890
        to_rel_abs = self.abspath(entry.to_rel)
2766
2891
        if from_rel_abs == to_rel_abs:
2773
2898
            except OSError, e:
2774
2899
                raise errors.BzrMoveFailedError(entry.from_rel,
2775
2900
                    entry.to_rel, e[1])
 
2901
        if entry.change_id:
 
2902
            to_id = inv.path2id(entry.to_rel)
 
2903
            inv.remove_recursive_id(to_id)
2776
2904
        inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2777
2905
 
2778
2906
    @needs_tree_write_lock
2786
2914
        :raises: NoSuchId if any fileid is not currently versioned.
2787
2915
        """
2788
2916
        for file_id in file_ids:
2789
 
            if file_id not in self._inventory:
 
2917
            if not self._inventory.has_id(file_id):
2790
2918
                raise errors.NoSuchId(self, file_id)
2791
2919
        for file_id in file_ids:
2792
2920
            if self._inventory.has_id(file_id):
2804
2932
 
2805
2933
    def stored_kind(self, file_id):
2806
2934
        """See Tree.stored_kind"""
2807
 
        return self.inventory[file_id].kind
 
2935
        inv, inv_file_id = self._unpack_file_id(file_id)
 
2936
        return inv[inv_file_id].kind
2808
2937
 
2809
2938
    def extras(self):
2810
2939
        """Yield all unversioned files in this WorkingTree.
2817
2946
        This is the same order used by 'osutils.walkdirs'.
2818
2947
        """
2819
2948
        ## TODO: Work from given directory downwards
2820
 
        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
2821
2952
            # mutter("search for unknowns in %r", path)
2822
2953
            dirabs = self.abspath(path)
2823
2954
            if not isdir(dirabs):
2851
2982
    def _walkdirs(self, prefix=""):
2852
2983
        """Walk the directories of this tree.
2853
2984
 
2854
 
           :prefix: is used as the directrory to start with.
2855
 
           returns a generator which yields items in the form:
2856
 
                ((curren_directory_path, fileid),
2857
 
                 [(file1_path, file1_name, file1_kind, None, file1_id,
2858
 
                   file1_kind), ... ])
 
2985
        :param prefix: is used as the directrory to start with.
 
2986
        :returns: a generator which yields items in the form::
 
2987
 
 
2988
            ((curren_directory_path, fileid),
 
2989
             [(file1_path, file1_name, file1_kind, None, file1_id,
 
2990
               file1_kind), ... ])
2859
2991
        """
2860
2992
        _directory = 'directory'
2861
2993
        # get the root in the inventory
2862
 
        inv = self.inventory
2863
 
        top_id = inv.path2id(prefix)
 
2994
        inv, top_id = self._path2inv_file_id(prefix)
2864
2995
        if top_id is None:
2865
2996
            pending = []
2866
2997
        else:
2887
3018
                if dir[2] == _directory:
2888
3019
                    pending.append(dir)
2889
3020
 
2890
 
 
2891
 
class WorkingTree3(InventoryWorkingTree):
2892
 
    """This is the Format 3 working tree.
2893
 
 
2894
 
    This differs from the base WorkingTree by:
2895
 
     - having its own file lock
2896
 
     - having its own last-revision property.
2897
 
 
2898
 
    This is new in bzr 0.8
2899
 
    """
2900
 
 
2901
 
    @needs_read_lock
2902
 
    def _last_revision(self):
2903
 
        """See Mutable.last_revision."""
2904
 
        try:
2905
 
            return self._transport.get_bytes('last-revision')
2906
 
        except errors.NoSuchFile:
2907
 
            return _mod_revision.NULL_REVISION
2908
 
 
2909
 
    def _change_last_revision(self, revision_id):
2910
 
        """See WorkingTree._change_last_revision."""
2911
 
        if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
2912
 
            try:
2913
 
                self._transport.delete('last-revision')
2914
 
            except errors.NoSuchFile:
2915
 
                pass
2916
 
            return False
2917
 
        else:
2918
 
            self._transport.put_bytes('last-revision', revision_id,
2919
 
                mode=self.bzrdir._get_file_mode())
2920
 
            return True
2921
 
 
2922
 
    def _get_check_refs(self):
2923
 
        """Return the references needed to perform a check of this tree."""
2924
 
        return [('trees', self.last_revision())]
2925
 
 
2926
 
    @needs_tree_write_lock
2927
 
    def set_conflicts(self, conflicts):
2928
 
        self._put_rio('conflicts', conflicts.to_stanzas(),
2929
 
                      CONFLICT_HEADER_1)
2930
 
 
2931
 
    @needs_tree_write_lock
2932
 
    def add_conflicts(self, new_conflicts):
2933
 
        conflict_set = set(self.conflicts())
2934
 
        conflict_set.update(set(list(new_conflicts)))
2935
 
        self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
2936
 
                                       key=_mod_conflicts.Conflict.sort_key)))
2937
 
 
2938
 
    @needs_read_lock
2939
 
    def conflicts(self):
2940
 
        try:
2941
 
            confile = self._transport.get('conflicts')
2942
 
        except errors.NoSuchFile:
2943
 
            return _mod_conflicts.ConflictList()
2944
 
        try:
2945
 
            try:
2946
 
                if confile.next() != CONFLICT_HEADER_1 + '\n':
2947
 
                    raise errors.ConflictFormatError()
2948
 
            except StopIteration:
2949
 
                raise errors.ConflictFormatError()
2950
 
            reader = _mod_rio.RioReader(confile)
2951
 
            return _mod_conflicts.ConflictList.from_stanzas(reader)
2952
 
        finally:
2953
 
            confile.close()
2954
 
 
2955
 
    def unlock(self):
2956
 
        # do non-implementation specific cleanup
2957
 
        self._cleanup()
2958
 
        if self._control_files._lock_count == 1:
2959
 
            # _inventory_is_modified is always False during a read lock.
2960
 
            if self._inventory_is_modified:
2961
 
                self.flush()
2962
 
            self._write_hashcache_if_dirty()
2963
 
        # reverse order of locking.
2964
 
        try:
2965
 
            return self._control_files.unlock()
2966
 
        finally:
2967
 
            self.branch.unlock()
 
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())
2968
3030
 
2969
3031
 
2970
3032
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2973
3035
    def __init__(self, other_registry=None):
2974
3036
        super(WorkingTreeFormatRegistry, self).__init__(other_registry)
2975
3037
        self._default_format = None
 
3038
        self._default_format_key = None
2976
3039
 
2977
3040
    def get_default(self):
2978
3041
        """Return the current default format."""
 
3042
        if (self._default_format_key is not None and
 
3043
            self._default_format is None):
 
3044
            self._default_format = self.get(self._default_format_key)
2979
3045
        return self._default_format
2980
3046
 
2981
3047
    def set_default(self, format):
 
3048
        """Set the default format."""
2982
3049
        self._default_format = format
 
3050
        self._default_format_key = None
 
3051
 
 
3052
    def set_default_key(self, format_string):
 
3053
        """Set the default format by its format string."""
 
3054
        self._default_format_key = format_string
 
3055
        self._default_format = None
2983
3056
 
2984
3057
 
2985
3058
format_registry = WorkingTreeFormatRegistry()
3014
3087
    missing_parent_conflicts = False
3015
3088
    """If this format supports missing parent conflicts."""
3016
3089
 
3017
 
    @classmethod
3018
 
    def find_format(klass, a_bzrdir):
3019
 
        """Return the format for the working tree object in a_bzrdir."""
3020
 
        try:
3021
 
            transport = a_bzrdir.get_workingtree_transport(None)
3022
 
            format_string = transport.get_bytes("format")
3023
 
            return format_registry.get(format_string)
3024
 
        except errors.NoSuchFile:
3025
 
            raise errors.NoWorkingTree(base=transport.base)
3026
 
        except KeyError:
3027
 
            raise errors.UnknownFormatError(format=format_string,
3028
 
                                            kind="working tree")
 
3090
    supports_versioned_directories = None
3029
3091
 
3030
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
 
3092
    def initialize(self, controldir, revision_id=None, from_branch=None,
3031
3093
                   accelerator_tree=None, hardlink=False):
3032
 
        """Initialize a new working tree in a_bzrdir.
 
3094
        """Initialize a new working tree in controldir.
3033
3095
 
3034
 
        :param a_bzrdir: BzrDir to initialize the working tree in.
 
3096
        :param controldir: ControlDir to initialize the working tree in.
3035
3097
        :param revision_id: allows creating a working tree at a different
3036
3098
            revision than the branch is at.
3037
3099
        :param from_branch: Branch to checkout
3050
3112
    def __ne__(self, other):
3051
3113
        return not (self == other)
3052
3114
 
3053
 
    @classmethod
3054
 
    @symbol_versioning.deprecated_method(
3055
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3056
 
    def get_default_format(klass):
3057
 
        """Return the current default format."""
3058
 
        return format_registry.get_default()
3059
 
 
3060
 
    def get_format_string(self):
3061
 
        """Return the ASCII format string that identifies this format."""
3062
 
        raise NotImplementedError(self.get_format_string)
3063
 
 
3064
3115
    def get_format_description(self):
3065
3116
        """Return the short description for this format."""
3066
3117
        raise NotImplementedError(self.get_format_description)
3082
3133
        """True if this format supports stored views."""
3083
3134
        return False
3084
3135
 
3085
 
    @classmethod
3086
 
    @symbol_versioning.deprecated_method(
3087
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3088
 
    def register_format(klass, format):
3089
 
        format_registry.register(format)
3090
 
 
3091
 
    @classmethod
3092
 
    @symbol_versioning.deprecated_method(
3093
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3094
 
    def register_extra_format(klass, format):
3095
 
        format_registry.register_extra(format)
3096
 
 
3097
 
    @classmethod
3098
 
    @symbol_versioning.deprecated_method(
3099
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3100
 
    def unregister_extra_format(klass, format):
3101
 
        format_registry.unregister_extra(format)
3102
 
 
3103
 
    @classmethod
3104
 
    @symbol_versioning.deprecated_method(
3105
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3106
 
    def get_formats(klass):
3107
 
        return format_registry._get_all()
3108
 
 
3109
 
    @classmethod
3110
 
    @symbol_versioning.deprecated_method(
3111
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3112
 
    def set_default_format(klass, format):
3113
 
        format_registry.set_default(format)
3114
 
 
3115
 
    @classmethod
3116
 
    @symbol_versioning.deprecated_method(
3117
 
        symbol_versioning.deprecated_in((2, 4, 0)))
3118
 
    def unregister_format(klass, format):
3119
 
        format_registry.remove(format)
3120
 
 
3121
 
 
3122
 
class WorkingTreeFormat3(WorkingTreeFormat):
3123
 
    """The second working tree format updated to record a format marker.
3124
 
 
3125
 
    This format:
3126
 
        - exists within a metadir controlling .bzr
3127
 
        - includes an explicit version marker for the workingtree control
3128
 
          files, separate from the BzrDir format
3129
 
        - modifies the hash cache format
3130
 
        - is new in bzr 0.8
3131
 
        - uses a LockDir to guard access for writes.
3132
 
    """
3133
 
 
3134
 
    upgrade_recommended = True
3135
 
 
3136
 
    missing_parent_conflicts = True
3137
 
 
3138
 
    def get_format_string(self):
3139
 
        """See WorkingTreeFormat.get_format_string()."""
3140
 
        return "Bazaar-NG Working Tree format 3"
3141
 
 
3142
 
    def get_format_description(self):
3143
 
        """See WorkingTreeFormat.get_format_description()."""
3144
 
        return "Working tree format 3"
3145
 
 
3146
 
    _lock_file_name = 'lock'
3147
 
    _lock_class = LockDir
3148
 
 
3149
 
    _tree_class = WorkingTree3
3150
 
 
3151
 
    def __get_matchingbzrdir(self):
3152
 
        return bzrdir.BzrDirMetaFormat1()
3153
 
 
3154
 
    _matchingbzrdir = property(__get_matchingbzrdir)
3155
 
 
3156
 
    def _open_control_files(self, a_bzrdir):
3157
 
        transport = a_bzrdir.get_workingtree_transport(None)
3158
 
        return LockableFiles(transport, self._lock_file_name,
3159
 
                             self._lock_class)
3160
 
 
3161
 
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
3162
 
                   accelerator_tree=None, hardlink=False):
3163
 
        """See WorkingTreeFormat.initialize().
3164
 
 
3165
 
        :param revision_id: if supplied, create a working tree at a different
3166
 
            revision than the branch is at.
3167
 
        :param accelerator_tree: A tree which can be used for retrieving file
3168
 
            contents more quickly than the revision tree, i.e. a workingtree.
3169
 
            The revision tree will be used for cases where accelerator_tree's
3170
 
            content is different.
3171
 
        :param hardlink: If true, hard-link files from accelerator_tree,
3172
 
            where possible.
3173
 
        """
3174
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
3175
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
3176
 
        transport = a_bzrdir.get_workingtree_transport(self)
3177
 
        control_files = self._open_control_files(a_bzrdir)
3178
 
        control_files.create_lock()
3179
 
        control_files.lock_write()
3180
 
        transport.put_bytes('format', self.get_format_string(),
3181
 
            mode=a_bzrdir._get_file_mode())
3182
 
        if from_branch is not None:
3183
 
            branch = from_branch
3184
 
        else:
3185
 
            branch = a_bzrdir.open_branch()
3186
 
        if revision_id is None:
3187
 
            revision_id = _mod_revision.ensure_null(branch.last_revision())
3188
 
        # WorkingTree3 can handle an inventory which has a unique root id.
3189
 
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
3190
 
        # those trees. And because there isn't a format bump inbetween, we
3191
 
        # are maintaining compatibility with older clients.
3192
 
        # inv = Inventory(root_id=gen_root_id())
3193
 
        inv = self._initial_inventory()
3194
 
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
3195
 
                         branch,
3196
 
                         inv,
3197
 
                         _internal=True,
3198
 
                         _format=self,
3199
 
                         _bzrdir=a_bzrdir,
3200
 
                         _control_files=control_files)
3201
 
        wt.lock_tree_write()
3202
 
        try:
3203
 
            basis_tree = branch.repository.revision_tree(revision_id)
3204
 
            # only set an explicit root id if there is one to set.
3205
 
            if basis_tree.inventory.root is not None:
3206
 
                wt.set_root_id(basis_tree.get_root_id())
3207
 
            if revision_id == _mod_revision.NULL_REVISION:
3208
 
                wt.set_parent_trees([])
3209
 
            else:
3210
 
                wt.set_parent_trees([(revision_id, basis_tree)])
3211
 
            transform.build_tree(basis_tree, wt)
3212
 
        finally:
3213
 
            # Unlock in this order so that the unlock-triggers-flush in
3214
 
            # WorkingTree is given a chance to fire.
3215
 
            control_files.unlock()
3216
 
            wt.unlock()
3217
 
        return wt
3218
 
 
3219
 
    def _initial_inventory(self):
3220
 
        return inventory.Inventory()
3221
 
 
3222
 
    def __init__(self):
3223
 
        super(WorkingTreeFormat3, self).__init__()
3224
 
 
3225
 
    def open(self, a_bzrdir, _found=False):
3226
 
        """Return the WorkingTree object for a_bzrdir
3227
 
 
3228
 
        _found is a private parameter, do not use it. It is used to indicate
3229
 
               if format probing has already been done.
3230
 
        """
3231
 
        if not _found:
3232
 
            # we are being called directly and must probe.
3233
 
            raise NotImplementedError
3234
 
        if not isinstance(a_bzrdir.transport, LocalTransport):
3235
 
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
3236
 
        wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
3237
 
        return wt
3238
 
 
3239
 
    def _open(self, a_bzrdir, control_files):
3240
 
        """Open the tree itself.
3241
 
 
3242
 
        :param a_bzrdir: the dir for the tree.
3243
 
        :param control_files: the control files for the tree.
3244
 
        """
3245
 
        return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
3246
 
                                _internal=True,
3247
 
                                _format=self,
3248
 
                                _bzrdir=a_bzrdir,
3249
 
                                _control_files=control_files)
3250
 
 
3251
 
    def __str__(self):
3252
 
        return self.get_format_string()
3253
 
 
3254
 
 
3255
 
__default_format = WorkingTreeFormat6()
 
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)
 
3215
 
 
3216
 
3256
3217
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3257
3218
    "bzrlib.workingtree_4", "WorkingTreeFormat4")
3258
3219
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
3259
3220
    "bzrlib.workingtree_4", "WorkingTreeFormat5")
3260
3221
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
3261
3222
    "bzrlib.workingtree_4", "WorkingTreeFormat6")
3262
 
format_registry.register(WorkingTreeFormat3())
3263
 
format_registry.set_default(__default_format)
 
3223
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
 
3224
    "bzrlib.workingtree_3", "WorkingTreeFormat3")
 
3225
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")