/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Martin Pool
  • Date: 2009-12-14 06:06:59 UTC
  • mfrom: (4889 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4891.
  • Revision ID: mbp@sourcefrog.net-20091214060659-1ucv8hpnky0cbnaj
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""WorkingTree object and friends.
18
18
 
19
19
A WorkingTree represents the editable working copy of a branch.
20
 
Operations which represent the WorkingTree are also done here, 
21
 
such as renaming or adding files.  The WorkingTree has an inventory 
22
 
which is updated by these operations.  A commit produces a 
 
20
Operations which represent the WorkingTree are also done here,
 
21
such as renaming or adding files.  The WorkingTree has an inventory
 
22
which is updated by these operations.  A commit produces a
23
23
new revision based on the workingtree and its inventory.
24
24
 
25
25
At the moment every WorkingTree has its own branch.  Remote
48
48
import itertools
49
49
import operator
50
50
import stat
51
 
from time import time
52
 
import warnings
53
51
import re
54
52
 
55
53
import bzrlib
57
55
    branch,
58
56
    bzrdir,
59
57
    conflicts as _mod_conflicts,
60
 
    dirstate,
61
58
    errors,
62
59
    generate_ids,
63
60
    globbing,
 
61
    graph as _mod_graph,
64
62
    hashcache,
65
63
    ignores,
 
64
    inventory,
66
65
    merge,
67
66
    revision as _mod_revision,
68
67
    revisiontree,
69
 
    repository,
70
 
    textui,
71
68
    trace,
72
69
    transform,
73
70
    ui,
74
 
    urlutils,
 
71
    views,
75
72
    xml5,
76
 
    xml6,
77
73
    xml7,
78
74
    )
79
75
import bzrlib.branch
80
76
from bzrlib.transport import get_transport
81
 
import bzrlib.ui
82
 
from bzrlib.workingtree_4 import WorkingTreeFormat4
 
77
from bzrlib.workingtree_4 import (
 
78
    WorkingTreeFormat4,
 
79
    WorkingTreeFormat5,
 
80
    WorkingTreeFormat6,
 
81
    )
83
82
""")
84
83
 
85
84
from bzrlib import symbol_versioning
86
85
from bzrlib.decorators import needs_read_lock, needs_write_lock
87
 
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
88
 
from bzrlib.lockable_files import LockableFiles, TransportLock
 
86
from bzrlib.lockable_files import LockableFiles
89
87
from bzrlib.lockdir import LockDir
90
88
import bzrlib.mutabletree
91
89
from bzrlib.mutabletree import needs_tree_write_lock
92
90
from bzrlib import osutils
93
91
from bzrlib.osutils import (
94
 
    compact_date,
95
92
    file_kind,
96
93
    isdir,
97
94
    normpath,
98
95
    pathjoin,
99
 
    rand_chars,
100
96
    realpath,
101
97
    safe_unicode,
102
98
    splitpath,
103
99
    supports_executable,
104
100
    )
 
101
from bzrlib.filters import filtered_input_file
105
102
from bzrlib.trace import mutter, note
106
103
from bzrlib.transport.local import LocalTransport
107
104
from bzrlib.progress import DummyProgress, ProgressPhase
108
 
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
 
105
from bzrlib.revision import CURRENT_REVISION
109
106
from bzrlib.rio import RioReader, rio_file, Stanza
110
 
from bzrlib.symbol_versioning import (deprecated_passed,
111
 
        deprecated_method,
112
 
        deprecated_function,
113
 
        DEPRECATED_PARAMETER,
114
 
        )
 
107
from bzrlib.symbol_versioning import (
 
108
    deprecated_passed,
 
109
    DEPRECATED_PARAMETER,
 
110
    )
115
111
 
116
112
 
117
113
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
123
119
class TreeEntry(object):
124
120
    """An entry that implements the minimum interface used by commands.
125
121
 
126
 
    This needs further inspection, it may be better to have 
 
122
    This needs further inspection, it may be better to have
127
123
    InventoryEntries without ids - though that seems wrong. For now,
128
124
    this is a parallel hierarchy to InventoryEntry, and needs to become
129
125
    one of several things: decorates to that hierarchy, children of, or
132
128
    no InventoryEntry available - i.e. for unversioned objects.
133
129
    Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
134
130
    """
135
 
 
 
131
 
136
132
    def __eq__(self, other):
137
133
        # yes, this us ugly, TODO: best practice __eq__ style.
138
134
        return (isinstance(other, TreeEntry)
139
135
                and other.__class__ == self.__class__)
140
 
 
 
136
 
141
137
    def kind_character(self):
142
138
        return "???"
143
139
 
185
181
    not listed in the Inventory and vice versa.
186
182
    """
187
183
 
 
184
    # override this to set the strategy for storing views
 
185
    def _make_views(self):
 
186
        return views.DisabledViews(self)
 
187
 
188
188
    def __init__(self, basedir='.',
189
189
                 branch=DEPRECATED_PARAMETER,
190
190
                 _inventory=None,
215
215
        else:
216
216
            # assume all other formats have their own control files.
217
217
            self._control_files = _control_files
 
218
        self._transport = self._control_files._transport
218
219
        # update the whole cache up front and write to disk if anything changed;
219
220
        # in the future we might want to do this more selectively
220
221
        # two possible ways offer themselves : in self._unlock, write the cache
224
225
        wt_trans = self.bzrdir.get_workingtree_transport(None)
225
226
        cache_filename = wt_trans.local_abspath('stat-cache')
226
227
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
227
 
                                              self._control_files._file_mode)
 
228
            self.bzrdir._get_file_mode(),
 
229
            self._content_filter_stack_provider())
228
230
        hc = self._hashcache
229
231
        hc.read()
230
232
        # is this scan needed ? it makes things kinda slow.
245
247
            # permitted to do this.
246
248
            self._set_inventory(_inventory, dirty=False)
247
249
        self._detect_case_handling()
 
250
        self._rules_searcher = None
 
251
        self.views = self._make_views()
248
252
 
249
253
    def _detect_case_handling(self):
250
254
        wt_trans = self.bzrdir.get_workingtree_transport(None)
276
280
        self._control_files.break_lock()
277
281
        self.branch.break_lock()
278
282
 
 
283
    def _get_check_refs(self):
 
284
        """Return the references needed to perform a check of this tree.
 
285
        
 
286
        The default implementation returns no refs, and is only suitable for
 
287
        trees that have no local caching and can commit on ghosts at any time.
 
288
 
 
289
        :seealso: bzrlib.check for details about check_refs.
 
290
        """
 
291
        return []
 
292
 
279
293
    def requires_rich_root(self):
280
294
        return self._format.requires_rich_root
281
295
 
282
296
    def supports_tree_reference(self):
283
297
        return False
284
298
 
 
299
    def supports_content_filtering(self):
 
300
        return self._format.supports_content_filtering()
 
301
 
 
302
    def supports_views(self):
 
303
        return self.views.supports_views()
 
304
 
285
305
    def _set_inventory(self, inv, dirty):
286
306
        """Set the internal cached inventory.
287
307
 
301
321
 
302
322
        """
303
323
        if path is None:
304
 
            path = os.path.getcwdu()
 
324
            path = osutils.getcwd()
305
325
        control = bzrdir.BzrDir.open(path, _unsupported)
306
326
        return control.open_workingtree(_unsupported)
307
 
        
 
327
 
308
328
    @staticmethod
309
329
    def open_containing(path=None):
310
330
        """Open an existing working tree which has its root about path.
311
 
        
 
331
 
312
332
        This probes for a working tree at path and searches upwards from there.
313
333
 
314
334
        Basically we keep looking up until we find the control directory or
375
395
 
376
396
    def basis_tree(self):
377
397
        """Return RevisionTree for the current last revision.
378
 
        
 
398
 
379
399
        If the left most parent is a ghost then the returned tree will be an
380
 
        empty tree - one obtained by calling repository.revision_tree(None).
 
400
        empty tree - one obtained by calling
 
401
        repository.revision_tree(NULL_REVISION).
381
402
        """
382
403
        try:
383
404
            revision_id = self.get_parent_ids()[0]
385
406
            # no parents, return an empty revision tree.
386
407
            # in the future this should return the tree for
387
408
            # 'empty:' - the implicit root empty tree.
388
 
            return self.branch.repository.revision_tree(None)
 
409
            return self.branch.repository.revision_tree(
 
410
                       _mod_revision.NULL_REVISION)
389
411
        try:
390
412
            return self.revision_tree(revision_id)
391
413
        except errors.NoSuchRevision:
395
417
        # at this point ?
396
418
        try:
397
419
            return self.branch.repository.revision_tree(revision_id)
398
 
        except errors.RevisionNotPresent:
 
420
        except (errors.RevisionNotPresent, errors.NoSuchRevision):
399
421
            # the basis tree *may* be a ghost or a low level error may have
400
 
            # occured. If the revision is present, its a problem, if its not
 
422
            # occurred. If the revision is present, its a problem, if its not
401
423
            # its a ghost.
402
424
            if self.branch.repository.has_revision(revision_id):
403
425
                raise
404
426
            # the basis tree is a ghost so return an empty tree.
405
 
            return self.branch.repository.revision_tree(None)
 
427
            return self.branch.repository.revision_tree(
 
428
                       _mod_revision.NULL_REVISION)
406
429
 
407
430
    def _cleanup(self):
408
431
        self._flush_ignore_list_cache()
409
432
 
410
433
    def relpath(self, path):
411
434
        """Return the local path portion from a given path.
412
 
        
413
 
        The path may be absolute or relative. If its a relative path it is 
 
435
 
 
436
        The path may be absolute or relative. If its a relative path it is
414
437
        interpreted relative to the python current working directory.
415
438
        """
416
439
        return osutils.relpath(self.basedir, path)
418
441
    def has_filename(self, filename):
419
442
        return osutils.lexists(self.abspath(filename))
420
443
 
421
 
    def get_file(self, file_id, path=None):
 
444
    def get_file(self, file_id, path=None, filtered=True):
 
445
        return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
 
446
 
 
447
    def get_file_with_stat(self, file_id, path=None, filtered=True,
 
448
        _fstat=os.fstat):
 
449
        """See Tree.get_file_with_stat."""
422
450
        if path is None:
423
451
            path = self.id2path(file_id)
424
 
        return self.get_file_byname(path)
425
 
 
426
 
    def get_file_text(self, file_id):
427
 
        return self.get_file(file_id).read()
428
 
 
429
 
    def get_file_byname(self, filename):
430
 
        return file(self.abspath(filename), 'rb')
 
452
        file_obj = self.get_file_byname(path, filtered=False)
 
453
        stat_value = _fstat(file_obj.fileno())
 
454
        if filtered and self.supports_content_filtering():
 
455
            filters = self._content_filter_stack(path)
 
456
            file_obj = filtered_input_file(file_obj, filters)
 
457
        return (file_obj, stat_value)
 
458
 
 
459
    def get_file_text(self, file_id, path=None, filtered=True):
 
460
        return self.get_file(file_id, path=path, filtered=filtered).read()
 
461
 
 
462
    def get_file_byname(self, filename, filtered=True):
 
463
        path = self.abspath(filename)
 
464
        f = file(path, 'rb')
 
465
        if filtered and self.supports_content_filtering():
 
466
            filters = self._content_filter_stack(filename)
 
467
            return filtered_input_file(f, filters)
 
468
        else:
 
469
            return f
 
470
 
 
471
    def get_file_lines(self, file_id, path=None, filtered=True):
 
472
        """See Tree.get_file_lines()"""
 
473
        file = self.get_file(file_id, path, filtered=filtered)
 
474
        try:
 
475
            return file.readlines()
 
476
        finally:
 
477
            file.close()
431
478
 
432
479
    @needs_read_lock
433
480
    def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
440
487
        incorrectly attributed to CURRENT_REVISION (but after committing, the
441
488
        attribution will be correct).
442
489
        """
443
 
        basis = self.basis_tree()
444
 
        basis.lock_read()
445
 
        try:
446
 
            changes = self.iter_changes(basis, True, [self.id2path(file_id)],
447
 
                require_versioned=True).next()
448
 
            changed_content, kind = changes[2], changes[6]
449
 
            if not changed_content:
450
 
                return basis.annotate_iter(file_id)
451
 
            if kind[1] is None:
452
 
                return None
453
 
            import annotate
454
 
            if kind[0] != 'file':
455
 
                old_lines = []
456
 
            else:
457
 
                old_lines = list(basis.annotate_iter(file_id))
458
 
            old = [old_lines]
459
 
            for tree in self.branch.repository.revision_trees(
460
 
                self.get_parent_ids()[1:]):
461
 
                if file_id not in tree:
462
 
                    continue
463
 
                old.append(list(tree.annotate_iter(file_id)))
464
 
            return annotate.reannotate(old, self.get_file(file_id).readlines(),
465
 
                                       default_revision)
466
 
        finally:
467
 
            basis.unlock()
 
490
        maybe_file_parent_keys = []
 
491
        for parent_id in self.get_parent_ids():
 
492
            try:
 
493
                parent_tree = self.revision_tree(parent_id)
 
494
            except errors.NoSuchRevisionInTree:
 
495
                parent_tree = self.branch.repository.revision_tree(parent_id)
 
496
            parent_tree.lock_read()
 
497
            try:
 
498
                if file_id not in parent_tree:
 
499
                    continue
 
500
                ie = parent_tree.inventory[file_id]
 
501
                if ie.kind != 'file':
 
502
                    # Note: this is slightly unnecessary, because symlinks and
 
503
                    # directories have a "text" which is the empty text, and we
 
504
                    # know that won't mess up annotations. But it seems cleaner
 
505
                    continue
 
506
                parent_text_key = (file_id, ie.revision)
 
507
                if parent_text_key not in maybe_file_parent_keys:
 
508
                    maybe_file_parent_keys.append(parent_text_key)
 
509
            finally:
 
510
                parent_tree.unlock()
 
511
        graph = _mod_graph.Graph(self.branch.repository.texts)
 
512
        heads = graph.heads(maybe_file_parent_keys)
 
513
        file_parent_keys = []
 
514
        for key in maybe_file_parent_keys:
 
515
            if key in heads:
 
516
                file_parent_keys.append(key)
 
517
 
 
518
        # Now we have the parents of this content
 
519
        annotator = self.branch.repository.texts.get_annotator()
 
520
        text = self.get_file(file_id).read()
 
521
        this_key =(file_id, default_revision)
 
522
        annotator.add_special_text(this_key, file_parent_keys, text)
 
523
        annotations = [(key[-1], line)
 
524
                       for key, line in annotator.annotate_flat(this_key)]
 
525
        return annotations
468
526
 
469
527
    def _get_ancestors(self, default_revision):
470
528
        ancestors = set([default_revision])
475
533
 
476
534
    def get_parent_ids(self):
477
535
        """See Tree.get_parent_ids.
478
 
        
 
536
 
479
537
        This implementation reads the pending merges list and last_revision
480
538
        value and uses that to decide what the parents list should be.
481
539
        """
485
543
        else:
486
544
            parents = [last_rev]
487
545
        try:
488
 
            merges_file = self._control_files.get('pending-merges')
 
546
            merges_bytes = self._transport.get_bytes('pending-merges')
489
547
        except errors.NoSuchFile:
490
548
            pass
491
549
        else:
492
 
            for l in merges_file.readlines():
 
550
            for l in osutils.split_lines(merges_bytes):
493
551
                revision_id = l.rstrip('\n')
494
552
                parents.append(revision_id)
495
553
        return parents
498
556
    def get_root_id(self):
499
557
        """Return the id of this trees root"""
500
558
        return self._inventory.root.file_id
501
 
        
 
559
 
502
560
    def _get_store_filename(self, file_id):
503
561
        ## XXX: badly named; this is not in the store at all
504
562
        return self.abspath(self.id2path(file_id))
506
564
    @needs_read_lock
507
565
    def clone(self, to_bzrdir, revision_id=None):
508
566
        """Duplicate this working tree into to_bzr, including all state.
509
 
        
 
567
 
510
568
        Specifically modified files are kept as modified, but
511
569
        ignored and unknown files are discarded.
512
570
 
513
571
        If you want to make a new line of development, see bzrdir.sprout()
514
572
 
515
573
        revision
516
 
            If not None, the cloned tree will have its last revision set to 
517
 
            revision, and and difference between the source trees last revision
 
574
            If not None, the cloned tree will have its last revision set to
 
575
            revision, and difference between the source trees last revision
518
576
            and this one merged in.
519
577
        """
520
578
        # assumes the target bzr dir format is compatible.
521
 
        result = self._format.initialize(to_bzrdir)
 
579
        result = to_bzrdir.create_workingtree()
522
580
        self.copy_content_into(result, revision_id)
523
581
        return result
524
582
 
554
612
 
555
613
    def get_file_size(self, file_id):
556
614
        """See Tree.get_file_size"""
 
615
        # XXX: this returns the on-disk size; it should probably return the
 
616
        # canonical size
557
617
        try:
558
618
            return os.path.getsize(self.id2abspath(file_id))
559
619
        except OSError, e:
575
635
 
576
636
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
577
637
        file_id = self.path2id(path)
 
638
        if file_id is None:
 
639
            # For unversioned files on win32, we just assume they are not
 
640
            # executable
 
641
            return False
578
642
        return self._inventory[file_id].executable
579
643
 
580
644
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
602
666
        """See MutableTree._add."""
603
667
        # TODO: Re-adding a file that is removed in the working copy
604
668
        # should probably put it back with the previous ID.
605
 
        # the read and write working inventory should not occur in this 
 
669
        # the read and write working inventory should not occur in this
606
670
        # function - they should be part of lock_write and unlock.
607
671
        inv = self.inventory
608
672
        for f, file_id, kind in zip(files, ids, kinds):
690
754
            raise
691
755
        kind = _mapper(stat_result.st_mode)
692
756
        if kind == 'file':
693
 
            size = stat_result.st_size
694
 
            # try for a stat cache lookup
695
 
            executable = self._is_executable_from_path_and_stat(path, stat_result)
696
 
            return (kind, size, executable, self._sha_from_stat(
697
 
                path, stat_result))
 
757
            return self._file_content_summary(path, stat_result)
698
758
        elif kind == 'directory':
699
759
            # perhaps it looks like a plain directory, but it's really a
700
760
            # reference.
702
762
                kind = 'tree-reference'
703
763
            return kind, None, None, None
704
764
        elif kind == 'symlink':
705
 
            return ('symlink', None, None, os.readlink(abspath))
 
765
            target = osutils.readlink(abspath)
 
766
            return ('symlink', None, None, target)
706
767
        else:
707
768
            return (kind, None, None, None)
708
769
 
 
770
    def _file_content_summary(self, path, stat_result):
 
771
        size = stat_result.st_size
 
772
        executable = self._is_executable_from_path_and_stat(path, stat_result)
 
773
        # try for a stat cache lookup
 
774
        return ('file', size, executable, self._sha_from_stat(
 
775
            path, stat_result))
 
776
 
709
777
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
710
778
        """Common ghost checking functionality from set_parent_*.
711
779
 
720
788
 
721
789
    def _set_merges_from_parent_ids(self, parent_ids):
722
790
        merges = parent_ids[1:]
723
 
        self._control_files.put_bytes('pending-merges', '\n'.join(merges))
 
791
        self._transport.put_bytes('pending-merges', '\n'.join(merges),
 
792
            mode=self.bzrdir._get_file_mode())
 
793
 
 
794
    def _filter_parent_ids_by_ancestry(self, revision_ids):
 
795
        """Check that all merged revisions are proper 'heads'.
 
796
 
 
797
        This will always return the first revision_id, and any merged revisions
 
798
        which are
 
799
        """
 
800
        if len(revision_ids) == 0:
 
801
            return revision_ids
 
802
        graph = self.branch.repository.get_graph()
 
803
        heads = graph.heads(revision_ids)
 
804
        new_revision_ids = revision_ids[:1]
 
805
        for revision_id in revision_ids[1:]:
 
806
            if revision_id in heads and revision_id not in new_revision_ids:
 
807
                new_revision_ids.append(revision_id)
 
808
        if new_revision_ids != revision_ids:
 
809
            trace.mutter('requested to set revision_ids = %s,'
 
810
                         ' but filtered to %s', revision_ids, new_revision_ids)
 
811
        return new_revision_ids
724
812
 
725
813
    @needs_tree_write_lock
726
814
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
727
815
        """Set the parent ids to revision_ids.
728
 
        
 
816
 
729
817
        See also set_parent_trees. This api will try to retrieve the tree data
730
818
        for each element of revision_ids from the trees repository. If you have
731
819
        tree data already available, it is more efficient to use
740
828
        for revision_id in revision_ids:
741
829
            _mod_revision.check_not_reserved_id(revision_id)
742
830
 
 
831
        revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
 
832
 
743
833
        if len(revision_ids) > 0:
744
834
            self.set_last_revision(revision_ids[0])
745
835
        else:
757
847
        self._check_parents_for_ghosts(parent_ids,
758
848
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
759
849
 
 
850
        parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
 
851
 
760
852
        if len(parent_ids) == 0:
761
853
            leftmost_parent_id = _mod_revision.NULL_REVISION
762
854
            leftmost_parent_tree = None
802
894
    def _put_rio(self, filename, stanzas, header):
803
895
        self._must_be_locked()
804
896
        my_file = rio_file(stanzas, header)
805
 
        self._control_files.put(filename, my_file)
 
897
        self._transport.put_file(filename, my_file,
 
898
            mode=self.bzrdir._get_file_mode())
806
899
 
807
900
    @needs_write_lock # because merge pulls data into the branch.
808
901
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
809
 
        merge_type=None):
 
902
                          merge_type=None, force=False):
810
903
        """Merge from a branch into this working tree.
811
904
 
812
905
        :param branch: The branch to merge from.
816
909
            branch.last_revision().
817
910
        """
818
911
        from bzrlib.merge import Merger, Merge3Merger
819
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
912
        pb = ui.ui_factory.nested_progress_bar()
820
913
        try:
821
914
            merger = Merger(self.branch, this_tree=self, pb=pb)
822
915
            merger.pp = ProgressPhase("Merge phase", 5, pb)
823
916
            merger.pp.next_phase()
824
 
            # check that there are no
825
 
            # local alterations
826
 
            merger.check_basis(check_clean=True, require_commits=False)
 
917
            # check that there are no local alterations
 
918
            if not force and self.has_changes():
 
919
                raise errors.UncommittedChanges(self)
827
920
            if to_revision is None:
828
921
                to_revision = _mod_revision.ensure_null(branch.last_revision())
829
922
            merger.other_rev_id = to_revision
859
952
    def merge_modified(self):
860
953
        """Return a dictionary of files modified by a merge.
861
954
 
862
 
        The list is initialized by WorkingTree.set_merge_modified, which is 
 
955
        The list is initialized by WorkingTree.set_merge_modified, which is
863
956
        typically called after we make some automatic updates to the tree
864
957
        because of a merge.
865
958
 
867
960
        still in the working inventory and have that text hash.
868
961
        """
869
962
        try:
870
 
            hashfile = self._control_files.get('merge-hashes')
 
963
            hashfile = self._transport.get('merge-hashes')
871
964
        except errors.NoSuchFile:
872
965
            return {}
873
 
        merge_hashes = {}
874
966
        try:
875
 
            if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
 
967
            merge_hashes = {}
 
968
            try:
 
969
                if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
 
970
                    raise errors.MergeModifiedFormatError()
 
971
            except StopIteration:
876
972
                raise errors.MergeModifiedFormatError()
877
 
        except StopIteration:
878
 
            raise errors.MergeModifiedFormatError()
879
 
        for s in RioReader(hashfile):
880
 
            # RioReader reads in Unicode, so convert file_ids back to utf8
881
 
            file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
882
 
            if file_id not in self.inventory:
883
 
                continue
884
 
            text_hash = s.get("hash")
885
 
            if text_hash == self.get_file_sha1(file_id):
886
 
                merge_hashes[file_id] = text_hash
887
 
        return merge_hashes
 
973
            for s in RioReader(hashfile):
 
974
                # RioReader reads in Unicode, so convert file_ids back to utf8
 
975
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
 
976
                if file_id not in self.inventory:
 
977
                    continue
 
978
                text_hash = s.get("hash")
 
979
                if text_hash == self.get_file_sha1(file_id):
 
980
                    merge_hashes[file_id] = text_hash
 
981
            return merge_hashes
 
982
        finally:
 
983
            hashfile.close()
888
984
 
889
985
    @needs_write_lock
890
986
    def mkdir(self, path, file_id=None):
896
992
        return file_id
897
993
 
898
994
    def get_symlink_target(self, file_id):
899
 
        return os.readlink(self.id2abspath(file_id))
 
995
        abspath = self.id2abspath(file_id)
 
996
        target = osutils.readlink(abspath)
 
997
        return target
900
998
 
901
999
    @needs_write_lock
902
1000
    def subsume(self, other_tree):
952
1050
        return False
953
1051
 
954
1052
    def _directory_may_be_tree_reference(self, relpath):
955
 
        # as a special case, if a directory contains control files then 
 
1053
        # as a special case, if a directory contains control files then
956
1054
        # it's a tree reference, except that the root of the tree is not
957
1055
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
958
1056
        # TODO: We could ask all the control formats whether they
969
1067
    @needs_tree_write_lock
970
1068
    def extract(self, file_id, format=None):
971
1069
        """Extract a subtree from this tree.
972
 
        
 
1070
 
973
1071
        A new branch will be created, relative to the path for this tree.
974
1072
        """
975
1073
        self.flush()
980
1078
                transport = transport.clone(name)
981
1079
                transport.ensure_base()
982
1080
            return transport
983
 
            
 
1081
 
984
1082
        sub_path = self.id2path(file_id)
985
1083
        branch_transport = mkdirs(sub_path)
986
1084
        if format is None:
1003
1101
            branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
1004
1102
        else:
1005
1103
            tree_bzrdir = branch_bzrdir
1006
 
        wt = tree_bzrdir.create_workingtree(NULL_REVISION)
 
1104
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
1007
1105
        wt.set_parent_ids(self.get_parent_ids())
1008
1106
        my_inv = self.inventory
1009
 
        child_inv = Inventory(root_id=None)
 
1107
        child_inv = inventory.Inventory(root_id=None)
1010
1108
        new_root = my_inv[file_id]
1011
1109
        my_inv.remove_recursive_id(file_id)
1012
1110
        new_root.parent_id = None
1030
1128
        sio = StringIO()
1031
1129
        self._serialize(self._inventory, sio)
1032
1130
        sio.seek(0)
1033
 
        self._control_files.put('inventory', sio)
 
1131
        self._transport.put_file('inventory', sio,
 
1132
            mode=self.bzrdir._get_file_mode())
1034
1133
        self._inventory_is_modified = False
1035
1134
 
1036
1135
    def _kind(self, relpath):
1037
1136
        return osutils.file_kind(self.abspath(relpath))
1038
1137
 
1039
 
    def list_files(self, include_root=False):
1040
 
        """Recursively list all files as (path, class, kind, id, entry).
 
1138
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
1139
        """List all files as (path, class, kind, id, entry).
1041
1140
 
1042
1141
        Lists, but does not descend into unversioned directories.
1043
 
 
1044
1142
        This does not include files that have been deleted in this
1045
 
        tree.
 
1143
        tree. Skips the control directory.
1046
1144
 
1047
 
        Skips the control directory.
 
1145
        :param include_root: if True, do not return an entry for the root
 
1146
        :param from_dir: start from this directory or None for the root
 
1147
        :param recursive: whether to recurse into subdirectories or not
1048
1148
        """
1049
1149
        # list_files is an iterator, so @needs_read_lock doesn't work properly
1050
1150
        # with it. So callers should be careful to always read_lock the tree.
1052
1152
            raise errors.ObjectNotLocked(self)
1053
1153
 
1054
1154
        inv = self.inventory
1055
 
        if include_root is True:
 
1155
        if from_dir is None and include_root is True:
1056
1156
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1057
1157
        # Convert these into local objects to save lookup times
1058
1158
        pathjoin = osutils.pathjoin
1065
1165
        fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
1066
1166
 
1067
1167
        # directory file_id, relative path, absolute path, reverse sorted children
1068
 
        children = os.listdir(self.basedir)
 
1168
        if from_dir is not None:
 
1169
            from_dir_id = inv.path2id(from_dir)
 
1170
            if from_dir_id is None:
 
1171
                # Directory not versioned
 
1172
                return
 
1173
            from_dir_abspath = pathjoin(self.basedir, from_dir)
 
1174
        else:
 
1175
            from_dir_id = inv.root.file_id
 
1176
            from_dir_abspath = self.basedir
 
1177
        children = os.listdir(from_dir_abspath)
1069
1178
        children.sort()
1070
 
        # jam 20060527 The kernel sized tree seems equivalent whether we 
 
1179
        # jam 20060527 The kernel sized tree seems equivalent whether we
1071
1180
        # use a deque and popleft to keep them sorted, or if we use a plain
1072
1181
        # list and just reverse() them.
1073
1182
        children = collections.deque(children)
1074
 
        stack = [(inv.root.file_id, u'', self.basedir, children)]
 
1183
        stack = [(from_dir_id, u'', from_dir_abspath, children)]
1075
1184
        while stack:
1076
1185
            from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
1077
1186
 
1093
1202
 
1094
1203
                # absolute path
1095
1204
                fap = from_dir_abspath + '/' + f
1096
 
                
 
1205
 
1097
1206
                f_ie = inv.get_child(from_dir_id, f)
1098
1207
                if f_ie:
1099
1208
                    c = 'V'
1131
1240
                    except KeyError:
1132
1241
                        yield fp[1:], c, fk, None, TreeEntry()
1133
1242
                    continue
1134
 
                
 
1243
 
1135
1244
                if fk != 'directory':
1136
1245
                    continue
1137
1246
 
1138
 
                # But do this child first
1139
 
                new_children = os.listdir(fap)
1140
 
                new_children.sort()
1141
 
                new_children = collections.deque(new_children)
1142
 
                stack.append((f_ie.file_id, fp, fap, new_children))
1143
 
                # Break out of inner loop,
1144
 
                # so that we start outer loop with child
1145
 
                break
 
1247
                # But do this child first if recursing down
 
1248
                if recursive:
 
1249
                    new_children = os.listdir(fap)
 
1250
                    new_children.sort()
 
1251
                    new_children = collections.deque(new_children)
 
1252
                    stack.append((f_ie.file_id, fp, fap, new_children))
 
1253
                    # Break out of inner loop,
 
1254
                    # so that we start outer loop with child
 
1255
                    break
1146
1256
            else:
1147
1257
                # if we finished all children, pop it off the stack
1148
1258
                stack.pop()
1154
1264
        to_dir must exist in the inventory.
1155
1265
 
1156
1266
        If to_dir exists and is a directory, the files are moved into
1157
 
        it, keeping their old names.  
 
1267
        it, keeping their old names.
1158
1268
 
1159
1269
        Note that to_dir is only the last component of the new name;
1160
1270
        this doesn't change the directory.
1288
1398
                only_change_inv = True
1289
1399
            elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1290
1400
                only_change_inv = False
1291
 
            elif (sys.platform == 'win32'
1292
 
                and from_rel.lower() == to_rel.lower()
1293
 
                and self.has_filename(from_rel)):
 
1401
            elif (not self.case_sensitive
 
1402
                  and from_rel.lower() == to_rel.lower()
 
1403
                  and self.has_filename(from_rel)):
1294
1404
                only_change_inv = False
1295
1405
            else:
1296
1406
                # something is wrong, so lets determine what exactly
1326
1436
        inv = self.inventory
1327
1437
        for entry in moved:
1328
1438
            try:
1329
 
                self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
 
1439
                self._move_entry(WorkingTree._RenameEntry(
 
1440
                    entry.to_rel, entry.from_id,
1330
1441
                    entry.to_tail, entry.to_parent_id, entry.from_rel,
1331
1442
                    entry.from_tail, entry.from_parent_id,
1332
1443
                    entry.only_change_inv))
1383
1494
        from_tail = splitpath(from_rel)[-1]
1384
1495
        from_id = inv.path2id(from_rel)
1385
1496
        if from_id is None:
1386
 
            raise errors.BzrRenameFailedError(from_rel,to_rel,
1387
 
                errors.NotVersionedError(path=str(from_rel)))
1388
 
        from_entry = inv[from_id]
 
1497
            # if file is missing in the inventory maybe it's in the basis_tree
 
1498
            basis_tree = self.branch.basis_tree()
 
1499
            from_id = basis_tree.path2id(from_rel)
 
1500
            if from_id is None:
 
1501
                raise errors.BzrRenameFailedError(from_rel,to_rel,
 
1502
                    errors.NotVersionedError(path=str(from_rel)))
 
1503
            # put entry back in the inventory so we can rename it
 
1504
            from_entry = basis_tree.inventory[from_id].copy()
 
1505
            inv.add(from_entry)
 
1506
        else:
 
1507
            from_entry = inv[from_id]
1389
1508
        from_parent_id = from_entry.parent_id
1390
1509
        to_dir, to_tail = os.path.split(to_rel)
1391
1510
        to_dir_id = inv.path2id(to_dir)
1437
1556
        These are files in the working directory that are not versioned or
1438
1557
        control files or ignored.
1439
1558
        """
1440
 
        # force the extras method to be fully executed before returning, to 
 
1559
        # force the extras method to be fully executed before returning, to
1441
1560
        # prevent race conditions with the lock
1442
1561
        return iter(
1443
1562
            [subp for subp in self.extras() if not self.is_ignored(subp)])
1453
1572
        :raises: NoSuchId if any fileid is not currently versioned.
1454
1573
        """
1455
1574
        for file_id in file_ids:
 
1575
            if file_id not in self._inventory:
 
1576
                raise errors.NoSuchId(self, file_id)
 
1577
        for file_id in file_ids:
1456
1578
            if self._inventory.has_id(file_id):
1457
1579
                self._inventory.remove_recursive_id(file_id)
1458
 
            else:
1459
 
                raise errors.NoSuchId(self, file_id)
1460
1580
        if len(file_ids):
1461
 
            # in the future this should just set a dirty bit to wait for the 
 
1581
            # in the future this should just set a dirty bit to wait for the
1462
1582
            # final unlock. However, until all methods of workingtree start
1463
 
            # with the current in -memory inventory rather than triggering 
 
1583
            # with the current in -memory inventory rather than triggering
1464
1584
            # a read, it is more complex - we need to teach read_inventory
1465
1585
            # to know when to read, and when to not read first... and possibly
1466
1586
            # to save first when the in memory one may be corrupted.
1467
1587
            # so for now, we just only write it if it is indeed dirty.
1468
1588
            # - RBC 20060907
1469
1589
            self._write_inventory(self._inventory)
1470
 
    
 
1590
 
1471
1591
    def _iter_conflicts(self):
1472
1592
        conflicted = set()
1473
1593
        for info in self.list_files():
1481
1601
 
1482
1602
    @needs_write_lock
1483
1603
    def pull(self, source, overwrite=False, stop_revision=None,
1484
 
             change_reporter=None, possible_transports=None):
1485
 
        top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1604
             change_reporter=None, possible_transports=None, local=False):
 
1605
        top_pb = ui.ui_factory.nested_progress_bar()
1486
1606
        source.lock_read()
1487
1607
        try:
1488
1608
            pp = ProgressPhase("Pull phase", 2, top_pb)
1490
1610
            old_revision_info = self.branch.last_revision_info()
1491
1611
            basis_tree = self.basis_tree()
1492
1612
            count = self.branch.pull(source, overwrite, stop_revision,
1493
 
                                     possible_transports=possible_transports)
 
1613
                                     possible_transports=possible_transports,
 
1614
                                     local=local)
1494
1615
            new_revision_info = self.branch.last_revision_info()
1495
1616
            if new_revision_info != old_revision_info:
1496
1617
                pp.next_phase()
1497
1618
                repository = self.branch.repository
1498
 
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1619
                pb = ui.ui_factory.nested_progress_bar()
1499
1620
                basis_tree.lock_read()
1500
1621
                try:
1501
1622
                    new_basis_tree = self.branch.basis_tree()
1516
1637
                # reuse the revisiontree we merged against to set the new
1517
1638
                # tree data.
1518
1639
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1519
 
                # we have to pull the merge trees out again, because 
1520
 
                # merge_inner has set the ids. - this corner is not yet 
 
1640
                # we have to pull the merge trees out again, because
 
1641
                # merge_inner has set the ids. - this corner is not yet
1521
1642
                # layered well enough to prevent double handling.
1522
1643
                # XXX TODO: Fix the double handling: telling the tree about
1523
1644
                # the already known parent data is wasteful.
1561
1682
 
1562
1683
            fl = []
1563
1684
            for subf in os.listdir(dirabs):
1564
 
                if subf == '.bzr':
 
1685
                if self.bzrdir.is_control_filename(subf):
1565
1686
                    continue
1566
1687
                if subf not in dir_entry.children:
1567
 
                    subf_norm, can_access = osutils.normalized_filename(subf)
 
1688
                    try:
 
1689
                        (subf_norm,
 
1690
                         can_access) = osutils.normalized_filename(subf)
 
1691
                    except UnicodeDecodeError:
 
1692
                        path_os_enc = path.encode(osutils._fs_enc)
 
1693
                        relpath = path_os_enc + '/' + subf
 
1694
                        raise errors.BadFilenameEncoding(relpath,
 
1695
                                                         osutils._fs_enc)
1568
1696
                    if subf_norm != subf and can_access:
1569
1697
                        if subf_norm not in dir_entry.children:
1570
1698
                            fl.append(subf_norm)
1571
1699
                    else:
1572
1700
                        fl.append(subf)
1573
 
            
 
1701
 
1574
1702
            fl.sort()
1575
1703
            for subf in fl:
1576
1704
                subp = pathjoin(path, subf)
1716
1844
    def _reset_data(self):
1717
1845
        """Reset transient data that cannot be revalidated."""
1718
1846
        self._inventory_is_modified = False
1719
 
        result = self._deserialize(self._control_files.get('inventory'))
 
1847
        f = self._transport.get('inventory')
 
1848
        try:
 
1849
            result = self._deserialize(f)
 
1850
        finally:
 
1851
            f.close()
1720
1852
        self._set_inventory(result, dirty=False)
1721
1853
 
1722
1854
    @needs_tree_write_lock
1727
1859
 
1728
1860
    def _change_last_revision(self, new_revision):
1729
1861
        """Template method part of set_last_revision to perform the change.
1730
 
        
 
1862
 
1731
1863
        This is used to allow WorkingTree3 instances to not affect branch
1732
1864
        when their last revision is set.
1733
1865
        """
1745
1877
        """Write the basis inventory XML to the basis-inventory file"""
1746
1878
        path = self._basis_inventory_name()
1747
1879
        sio = StringIO(xml)
1748
 
        self._control_files.put(path, sio)
 
1880
        self._transport.put_file(path, sio,
 
1881
            mode=self.bzrdir._get_file_mode())
1749
1882
 
1750
1883
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1751
1884
        """Create the text that will be saved in basis-inventory"""
1758
1891
        # as commit already has that ready-to-use [while the format is the
1759
1892
        # same, that is].
1760
1893
        try:
1761
 
            # this double handles the inventory - unpack and repack - 
 
1894
            # this double handles the inventory - unpack and repack -
1762
1895
            # but is easier to understand. We can/should put a conditional
1763
1896
            # in here based on whether the inventory is in the latest format
1764
1897
            # - perhaps we should repack all inventories on a repository
1765
1898
            # upgrade ?
1766
1899
            # the fast path is to copy the raw xml from the repository. If the
1767
 
            # xml contains 'revision_id="', then we assume the right 
 
1900
            # xml contains 'revision_id="', then we assume the right
1768
1901
            # revision_id is set. We must check for this full string, because a
1769
1902
            # root node id can legitimately look like 'revision_id' but cannot
1770
1903
            # contain a '"'.
1771
1904
            xml = self.branch.repository.get_inventory_xml(new_revision)
1772
1905
            firstline = xml.split('\n', 1)[0]
1773
 
            if (not 'revision_id="' in firstline or 
 
1906
            if (not 'revision_id="' in firstline or
1774
1907
                'format="7"' not in firstline):
1775
 
                inv = self.branch.repository.deserialise_inventory(
1776
 
                    new_revision, xml)
 
1908
                inv = self.branch.repository._serializer.read_inventory_from_string(
 
1909
                    xml, new_revision)
1777
1910
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
1778
1911
            self._write_basis_inventory(xml)
1779
1912
        except (errors.NoSuchRevision, errors.RevisionNotPresent):
1782
1915
    def read_basis_inventory(self):
1783
1916
        """Read the cached basis inventory."""
1784
1917
        path = self._basis_inventory_name()
1785
 
        return self._control_files.get(path).read()
1786
 
        
 
1918
        return self._transport.get_bytes(path)
 
1919
 
1787
1920
    @needs_read_lock
1788
1921
    def read_working_inventory(self):
1789
1922
        """Read the working inventory.
1790
 
        
 
1923
 
1791
1924
        :raises errors.InventoryModified: read_working_inventory will fail
1792
1925
            when the current in memory inventory has been modified.
1793
1926
        """
1794
 
        # conceptually this should be an implementation detail of the tree. 
 
1927
        # conceptually this should be an implementation detail of the tree.
1795
1928
        # XXX: Deprecate this.
1796
1929
        # ElementTree does its own conversion from UTF-8, so open in
1797
1930
        # binary.
1798
1931
        if self._inventory_is_modified:
1799
1932
            raise errors.InventoryModified(self)
1800
 
        result = self._deserialize(self._control_files.get('inventory'))
 
1933
        f = self._transport.get('inventory')
 
1934
        try:
 
1935
            result = self._deserialize(f)
 
1936
        finally:
 
1937
            f.close()
1801
1938
        self._set_inventory(result, dirty=False)
1802
1939
        return result
1803
1940
 
1818
1955
 
1819
1956
        new_files=set()
1820
1957
        unknown_nested_files=set()
 
1958
        if to_file is None:
 
1959
            to_file = sys.stdout
1821
1960
 
1822
1961
        def recurse_directory_to_add_files(directory):
1823
1962
            # Recurse directory and add all files
1824
1963
            # so we can check if they have changed.
1825
1964
            for parent_info, file_infos in\
1826
 
                osutils.walkdirs(self.abspath(directory),
1827
 
                    directory):
1828
 
                for relpath, basename, kind, lstat, abspath in file_infos:
 
1965
                self.walkdirs(directory):
 
1966
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
1829
1967
                    # Is it versioned or ignored?
1830
1968
                    if self.path2id(relpath) or self.is_ignored(relpath):
1831
1969
                        # Add nested content for deletion.
1841
1979
            filename = self.relpath(abspath)
1842
1980
            if len(filename) > 0:
1843
1981
                new_files.add(filename)
1844
 
                if osutils.isdir(abspath):
1845
 
                    recurse_directory_to_add_files(filename)
 
1982
                recurse_directory_to_add_files(filename)
1846
1983
 
1847
1984
        files = list(new_files)
1848
1985
 
1881
2018
                        tree_delta.unversioned.extend((unknown_file,))
1882
2019
                raise errors.BzrRemoveChangedFilesError(tree_delta)
1883
2020
 
1884
 
        # Build inv_delta and delete files where applicaple,
 
2021
        # Build inv_delta and delete files where applicable,
1885
2022
        # do this before any modifications to inventory.
1886
2023
        for f in files:
1887
2024
            fid = self.path2id(f)
1895
2032
                        new_status = 'I'
1896
2033
                    else:
1897
2034
                        new_status = '?'
1898
 
                    textui.show_status(new_status, self.kind(fid), f,
1899
 
                                       to_file=to_file)
 
2035
                    # XXX: Really should be a more abstract reporter interface
 
2036
                    kind_ch = osutils.kind_marker(self.kind(fid))
 
2037
                    to_file.write(new_status + '       ' + f + kind_ch + '\n')
1900
2038
                # Unversion file
1901
2039
                inv_delta.append((f, None, fid, None))
1902
2040
                message = "removed %s" % (f,)
1944
2082
            if filenames is None and len(self.get_parent_ids()) > 1:
1945
2083
                parent_trees = []
1946
2084
                last_revision = self.last_revision()
1947
 
                if last_revision != NULL_REVISION:
 
2085
                if last_revision != _mod_revision.NULL_REVISION:
1948
2086
                    if basis_tree is None:
1949
2087
                        basis_tree = self.basis_tree()
1950
2088
                        basis_tree.lock_read()
1988
2126
    def set_inventory(self, new_inventory_list):
1989
2127
        from bzrlib.inventory import (Inventory,
1990
2128
                                      InventoryDirectory,
1991
 
                                      InventoryEntry,
1992
2129
                                      InventoryFile,
1993
2130
                                      InventoryLink)
1994
2131
        inv = Inventory(self.get_root_id())
1996
2133
            name = os.path.basename(path)
1997
2134
            if name == "":
1998
2135
                continue
1999
 
            # fixme, there should be a factory function inv,add_?? 
 
2136
            # fixme, there should be a factory function inv,add_??
2000
2137
            if kind == 'directory':
2001
2138
                inv.add(InventoryDirectory(file_id, name, parent))
2002
2139
            elif kind == 'file':
2010
2147
    @needs_tree_write_lock
2011
2148
    def set_root_id(self, file_id):
2012
2149
        """Set the root id for this tree."""
2013
 
        # for compatability 
 
2150
        # for compatability
2014
2151
        if file_id is None:
2015
2152
            raise ValueError(
2016
2153
                'WorkingTree.set_root_id with fileid=None')
2020
2157
    def _set_root_id(self, file_id):
2021
2158
        """Set the root id for this tree, in a format specific manner.
2022
2159
 
2023
 
        :param file_id: The file id to assign to the root. It must not be 
 
2160
        :param file_id: The file id to assign to the root. It must not be
2024
2161
            present in the current inventory or an error will occur. It must
2025
2162
            not be None, but rather a valid file id.
2026
2163
        """
2045
2182
 
2046
2183
    def unlock(self):
2047
2184
        """See Branch.unlock.
2048
 
        
 
2185
 
2049
2186
        WorkingTree locking just uses the Branch locking facilities.
2050
2187
        This is current because all working trees have an embedded branch
2051
2188
        within them. IF in the future, we were to make branch data shareable
2052
 
        between multiple working trees, i.e. via shared storage, then we 
 
2189
        between multiple working trees, i.e. via shared storage, then we
2053
2190
        would probably want to lock both the local tree, and the branch.
2054
2191
        """
2055
2192
        raise NotImplementedError(self.unlock)
2107
2244
        # cant set that until we update the working trees last revision to be
2108
2245
        # one from the new branch, because it will just get absorbed by the
2109
2246
        # parent de-duplication logic.
2110
 
        # 
 
2247
        #
2111
2248
        # We MUST save it even if an error occurs, because otherwise the users
2112
2249
        # local work is unreferenced and will appear to have been lost.
2113
 
        # 
 
2250
        #
2114
2251
        result = 0
2115
2252
        try:
2116
2253
            last_rev = self.get_parent_ids()[0]
2138
2275
            parent_trees = [(self.branch.last_revision(), to_tree)]
2139
2276
            merges = self.get_parent_ids()[1:]
2140
2277
            # Ideally we ask the tree for the trees here, that way the working
2141
 
            # tree can decide whether to give us teh entire tree or give us a
 
2278
            # tree can decide whether to give us the entire tree or give us a
2142
2279
            # lazy initialised tree. dirstate for instance will have the trees
2143
2280
            # in ram already, whereas a last-revision + basis-inventory tree
2144
2281
            # will not, but also does not need them when setting parents.
2286
2423
                    # value.
2287
2424
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
2288
2425
                        ('.bzr', '.bzr'))
2289
 
                    if cur_disk_dir_content[bzrdir_loc][0] == '.bzr':
 
2426
                    if (bzrdir_loc < len(cur_disk_dir_content)
 
2427
                        and self.bzrdir.is_control_filename(
 
2428
                            cur_disk_dir_content[bzrdir_loc][0])):
2290
2429
                        # we dont yield the contents of, or, .bzr itself.
2291
2430
                        del cur_disk_dir_content[bzrdir_loc]
2292
2431
            if inv_finished:
2382
2521
                relroot = ""
2383
2522
            # FIXME: stash the node in pending
2384
2523
            entry = inv[top_id]
2385
 
            for name, child in entry.sorted_children():
2386
 
                dirblock.append((relroot + name, name, child.kind, None,
2387
 
                    child.file_id, child.kind
2388
 
                    ))
 
2524
            if entry.kind == 'directory':
 
2525
                for name, child in entry.sorted_children():
 
2526
                    dirblock.append((relroot + name, name, child.kind, None,
 
2527
                        child.file_id, child.kind
 
2528
                        ))
2389
2529
            yield (currentdir[0], entry.file_id), dirblock
2390
2530
            # push the user specified dirs from dirblock
2391
2531
            for dir in reversed(dirblock):
2424
2564
        self.set_conflicts(un_resolved)
2425
2565
        return un_resolved, resolved
2426
2566
 
 
2567
    @needs_read_lock
 
2568
    def _check(self, references):
 
2569
        """Check the tree for consistency.
 
2570
 
 
2571
        :param references: A dict with keys matching the items returned by
 
2572
            self._get_check_refs(), and values from looking those keys up in
 
2573
            the repository.
 
2574
        """
 
2575
        tree_basis = self.basis_tree()
 
2576
        tree_basis.lock_read()
 
2577
        try:
 
2578
            repo_basis = references[('trees', self.last_revision())]
 
2579
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
 
2580
                raise errors.BzrCheckError(
 
2581
                    "Mismatched basis inventory content.")
 
2582
            self._validate()
 
2583
        finally:
 
2584
            tree_basis.unlock()
 
2585
 
2427
2586
    def _validate(self):
2428
2587
        """Validate internal structures.
2429
2588
 
2435
2594
        """
2436
2595
        return
2437
2596
 
 
2597
    def _get_rules_searcher(self, default_searcher):
 
2598
        """See Tree._get_rules_searcher."""
 
2599
        if self._rules_searcher is None:
 
2600
            self._rules_searcher = super(WorkingTree,
 
2601
                self)._get_rules_searcher(default_searcher)
 
2602
        return self._rules_searcher
 
2603
 
 
2604
    def get_shelf_manager(self):
 
2605
        """Return the ShelfManager for this WorkingTree."""
 
2606
        from bzrlib.shelf import ShelfManager
 
2607
        return ShelfManager(self, self._transport)
 
2608
 
2438
2609
 
2439
2610
class WorkingTree2(WorkingTree):
2440
2611
    """This is the Format 2 working tree.
2441
2612
 
2442
 
    This was the first weave based working tree. 
 
2613
    This was the first weave based working tree.
2443
2614
     - uses os locks for locking.
2444
2615
     - uses the branch last-revision.
2445
2616
    """
2455
2626
        if self._inventory is None:
2456
2627
            self.read_working_inventory()
2457
2628
 
 
2629
    def _get_check_refs(self):
 
2630
        """Return the references needed to perform a check of this tree."""
 
2631
        return [('trees', self.last_revision())]
 
2632
 
2458
2633
    def lock_tree_write(self):
2459
2634
        """See WorkingTree.lock_tree_write().
2460
2635
 
2478
2653
            if self._inventory_is_modified:
2479
2654
                self.flush()
2480
2655
            self._write_hashcache_if_dirty()
2481
 
                    
 
2656
 
2482
2657
        # reverse order of locking.
2483
2658
        try:
2484
2659
            return self._control_files.unlock()
2500
2675
    def _last_revision(self):
2501
2676
        """See Mutable.last_revision."""
2502
2677
        try:
2503
 
            return self._control_files.get('last-revision').read()
 
2678
            return self._transport.get_bytes('last-revision')
2504
2679
        except errors.NoSuchFile:
2505
2680
            return _mod_revision.NULL_REVISION
2506
2681
 
2507
2682
    def _change_last_revision(self, revision_id):
2508
2683
        """See WorkingTree._change_last_revision."""
2509
 
        if revision_id is None or revision_id == NULL_REVISION:
 
2684
        if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
2510
2685
            try:
2511
 
                self._control_files._transport.delete('last-revision')
 
2686
                self._transport.delete('last-revision')
2512
2687
            except errors.NoSuchFile:
2513
2688
                pass
2514
2689
            return False
2515
2690
        else:
2516
 
            self._control_files.put_bytes('last-revision', revision_id)
 
2691
            self._transport.put_bytes('last-revision', revision_id,
 
2692
                mode=self.bzrdir._get_file_mode())
2517
2693
            return True
2518
2694
 
 
2695
    def _get_check_refs(self):
 
2696
        """Return the references needed to perform a check of this tree."""
 
2697
        return [('trees', self.last_revision())]
 
2698
 
2519
2699
    @needs_tree_write_lock
2520
2700
    def set_conflicts(self, conflicts):
2521
 
        self._put_rio('conflicts', conflicts.to_stanzas(), 
 
2701
        self._put_rio('conflicts', conflicts.to_stanzas(),
2522
2702
                      CONFLICT_HEADER_1)
2523
2703
 
2524
2704
    @needs_tree_write_lock
2531
2711
    @needs_read_lock
2532
2712
    def conflicts(self):
2533
2713
        try:
2534
 
            confile = self._control_files.get('conflicts')
 
2714
            confile = self._transport.get('conflicts')
2535
2715
        except errors.NoSuchFile:
2536
2716
            return _mod_conflicts.ConflictList()
2537
2717
        try:
2538
 
            if confile.next() != CONFLICT_HEADER_1 + '\n':
 
2718
            try:
 
2719
                if confile.next() != CONFLICT_HEADER_1 + '\n':
 
2720
                    raise errors.ConflictFormatError()
 
2721
            except StopIteration:
2539
2722
                raise errors.ConflictFormatError()
2540
 
        except StopIteration:
2541
 
            raise errors.ConflictFormatError()
2542
 
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
 
2723
            return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
 
2724
        finally:
 
2725
            confile.close()
2543
2726
 
2544
2727
    def unlock(self):
2545
2728
        # do non-implementation specific cleanup
2570
2753
     * a format string,
2571
2754
     * an open routine.
2572
2755
 
2573
 
    Formats are placed in an dict by their format string for reference 
 
2756
    Formats are placed in an dict by their format string for reference
2574
2757
    during workingtree opening. Its not required that these be instances, they
2575
 
    can be classes themselves with class methods - it simply depends on 
 
2758
    can be classes themselves with class methods - it simply depends on
2576
2759
    whether state is needed for a given format or not.
2577
2760
 
2578
2761
    Once a format is deprecated, just deprecate the initialize and open
2579
 
    methods on the format class. Do not deprecate the object, as the 
 
2762
    methods on the format class. Do not deprecate the object, as the
2580
2763
    object will be created every time regardless.
2581
2764
    """
2582
2765
 
2595
2778
        """Return the format for the working tree object in a_bzrdir."""
2596
2779
        try:
2597
2780
            transport = a_bzrdir.get_workingtree_transport(None)
2598
 
            format_string = transport.get("format").read()
 
2781
            format_string = transport.get_bytes("format")
2599
2782
            return klass._formats[format_string]
2600
2783
        except errors.NoSuchFile:
2601
2784
            raise errors.NoWorkingTree(base=transport.base)
2626
2809
        """Is this format supported?
2627
2810
 
2628
2811
        Supported formats can be initialized and opened.
2629
 
        Unsupported formats may not support initialization or committing or 
 
2812
        Unsupported formats may not support initialization or committing or
2630
2813
        some other features depending on the reason for not being supported.
2631
2814
        """
2632
2815
        return True
2633
2816
 
 
2817
    def supports_content_filtering(self):
 
2818
        """True if this format supports content filtering."""
 
2819
        return False
 
2820
 
 
2821
    def supports_views(self):
 
2822
        """True if this format supports stored views."""
 
2823
        return False
 
2824
 
2634
2825
    @classmethod
2635
2826
    def register_format(klass, format):
2636
2827
        klass._formats[format.get_format_string()] = format
2645
2836
 
2646
2837
 
2647
2838
class WorkingTreeFormat2(WorkingTreeFormat):
2648
 
    """The second working tree format. 
 
2839
    """The second working tree format.
2649
2840
 
2650
2841
    This format modified the hash cache from the format 1 hash cache.
2651
2842
    """
2656
2847
        """See WorkingTreeFormat.get_format_description()."""
2657
2848
        return "Working tree format 2"
2658
2849
 
2659
 
    def stub_initialize_remote(self, control_files):
2660
 
        """As a special workaround create critical control files for a remote working tree
2661
 
        
 
2850
    def _stub_initialize_on_transport(self, transport, file_mode):
 
2851
        """Workaround: create control files for a remote working tree.
 
2852
 
2662
2853
        This ensures that it can later be updated and dealt with locally,
2663
 
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with 
 
2854
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2664
2855
        no working tree.  (See bug #43064).
2665
2856
        """
2666
2857
        sio = StringIO()
2667
 
        inv = Inventory()
 
2858
        inv = inventory.Inventory()
2668
2859
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
2669
2860
        sio.seek(0)
2670
 
        control_files.put('inventory', sio)
2671
 
 
2672
 
        control_files.put_bytes('pending-merges', '')
2673
 
        
 
2861
        transport.put_file('inventory', sio, file_mode)
 
2862
        transport.put_bytes('pending-merges', '', file_mode)
2674
2863
 
2675
2864
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2676
2865
                   accelerator_tree=None, hardlink=False):
2688
2877
            branch.generate_revision_history(revision_id)
2689
2878
        finally:
2690
2879
            branch.unlock()
2691
 
        inv = Inventory()
 
2880
        inv = inventory.Inventory()
2692
2881
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2693
2882
                         branch,
2694
2883
                         inv,
2739
2928
        - is new in bzr 0.8
2740
2929
        - uses a LockDir to guard access for writes.
2741
2930
    """
2742
 
    
 
2931
 
2743
2932
    upgrade_recommended = True
2744
2933
 
2745
2934
    def get_format_string(self):
2762
2951
 
2763
2952
    def _open_control_files(self, a_bzrdir):
2764
2953
        transport = a_bzrdir.get_workingtree_transport(None)
2765
 
        return LockableFiles(transport, self._lock_file_name, 
 
2954
        return LockableFiles(transport, self._lock_file_name,
2766
2955
                             self._lock_class)
2767
2956
 
2768
2957
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2769
2958
                   accelerator_tree=None, hardlink=False):
2770
2959
        """See WorkingTreeFormat.initialize().
2771
 
        
 
2960
 
2772
2961
        :param revision_id: if supplied, create a working tree at a different
2773
2962
            revision than the branch is at.
2774
2963
        :param accelerator_tree: A tree which can be used for retrieving file
2784
2973
        control_files = self._open_control_files(a_bzrdir)
2785
2974
        control_files.create_lock()
2786
2975
        control_files.lock_write()
2787
 
        control_files.put_utf8('format', self.get_format_string())
 
2976
        transport.put_bytes('format', self.get_format_string(),
 
2977
            mode=a_bzrdir._get_file_mode())
2788
2978
        if from_branch is not None:
2789
2979
            branch = from_branch
2790
2980
        else:
2810
3000
            # only set an explicit root id if there is one to set.
2811
3001
            if basis_tree.inventory.root is not None:
2812
3002
                wt.set_root_id(basis_tree.get_root_id())
2813
 
            if revision_id == NULL_REVISION:
 
3003
            if revision_id == _mod_revision.NULL_REVISION:
2814
3004
                wt.set_parent_trees([])
2815
3005
            else:
2816
3006
                wt.set_parent_trees([(revision_id, basis_tree)])
2823
3013
        return wt
2824
3014
 
2825
3015
    def _initial_inventory(self):
2826
 
        return Inventory()
 
3016
        return inventory.Inventory()
2827
3017
 
2828
3018
    def __init__(self):
2829
3019
        super(WorkingTreeFormat3, self).__init__()
2844
3034
 
2845
3035
    def _open(self, a_bzrdir, control_files):
2846
3036
        """Open the tree itself.
2847
 
        
 
3037
 
2848
3038
        :param a_bzrdir: the dir for the tree.
2849
3039
        :param control_files: the control files for the tree.
2850
3040
        """
2858
3048
        return self.get_format_string()
2859
3049
 
2860
3050
 
2861
 
__default_format = WorkingTreeFormat4()
 
3051
__default_format = WorkingTreeFormat6()
2862
3052
WorkingTreeFormat.register_format(__default_format)
 
3053
WorkingTreeFormat.register_format(WorkingTreeFormat5())
 
3054
WorkingTreeFormat.register_format(WorkingTreeFormat4())
2863
3055
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2864
3056
WorkingTreeFormat.set_default_format(__default_format)
2865
3057
# formats which have no format string are not discoverable