~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Robert Collins
  • Date: 2010-04-08 04:34:03 UTC
  • mfrom: (5138 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5139.
  • Revision ID: robertc@robertcollins.net-20100408043403-56z0d07vdqrx7f3t
Update bugfix for 528114 to 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-2010 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
 
from bzrlib.progress import DummyProgress, ProgressPhase
108
 
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
 
104
from bzrlib.progress import ProgressPhase
 
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
 
        zero_eight,
115
 
        zero_eleven,
116
 
        zero_thirteen,
117
 
        )
 
107
from bzrlib.symbol_versioning import (
 
108
    deprecated_passed,
 
109
    DEPRECATED_PARAMETER,
 
110
    )
118
111
 
119
112
 
120
113
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
 
114
# TODO: Modifying the conflict objects or their type is currently nearly
 
115
# impossible as there is no clear relationship between the working tree format
 
116
# and the conflict list file format.
121
117
CONFLICT_HEADER_1 = "BZR conflict list format 1"
122
118
 
123
119
ERROR_PATH_NOT_FOUND = 3    # WindowsError errno code, equivalent to ENOENT
124
120
 
125
121
 
126
 
@deprecated_function(zero_thirteen)
127
 
def gen_file_id(name):
128
 
    """Return new file id for the basename 'name'.
129
 
 
130
 
    Use bzrlib.generate_ids.gen_file_id() instead
131
 
    """
132
 
    return generate_ids.gen_file_id(name)
133
 
 
134
 
 
135
 
@deprecated_function(zero_thirteen)
136
 
def gen_root_id():
137
 
    """Return a new tree-root file id.
138
 
 
139
 
    This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
140
 
    """
141
 
    return generate_ids.gen_root_id()
142
 
 
143
 
 
144
122
class TreeEntry(object):
145
123
    """An entry that implements the minimum interface used by commands.
146
124
 
147
 
    This needs further inspection, it may be better to have 
 
125
    This needs further inspection, it may be better to have
148
126
    InventoryEntries without ids - though that seems wrong. For now,
149
127
    this is a parallel hierarchy to InventoryEntry, and needs to become
150
128
    one of several things: decorates to that hierarchy, children of, or
153
131
    no InventoryEntry available - i.e. for unversioned objects.
154
132
    Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
155
133
    """
156
 
 
 
134
 
157
135
    def __eq__(self, other):
158
136
        # yes, this us ugly, TODO: best practice __eq__ style.
159
137
        return (isinstance(other, TreeEntry)
160
138
                and other.__class__ == self.__class__)
161
 
 
 
139
 
162
140
    def kind_character(self):
163
141
        return "???"
164
142
 
206
184
    not listed in the Inventory and vice versa.
207
185
    """
208
186
 
 
187
    # override this to set the strategy for storing views
 
188
    def _make_views(self):
 
189
        return views.DisabledViews(self)
 
190
 
209
191
    def __init__(self, basedir='.',
210
192
                 branch=DEPRECATED_PARAMETER,
211
193
                 _inventory=None,
222
204
        if not _internal:
223
205
            raise errors.BzrError("Please use bzrdir.open_workingtree or "
224
206
                "WorkingTree.open() to obtain a WorkingTree.")
225
 
        assert isinstance(basedir, basestring), \
226
 
            "base directory %r is not a string" % basedir
227
207
        basedir = safe_unicode(basedir)
228
208
        mutter("opening working tree %r", basedir)
229
209
        if deprecated_passed(branch):
237
217
            self._control_files = self.branch.control_files
238
218
        else:
239
219
            # assume all other formats have their own control files.
240
 
            assert isinstance(_control_files, LockableFiles), \
241
 
                    "_control_files must be a LockableFiles, not %r" \
242
 
                    % _control_files
243
220
            self._control_files = _control_files
 
221
        self._transport = self._control_files._transport
244
222
        # update the whole cache up front and write to disk if anything changed;
245
223
        # in the future we might want to do this more selectively
246
224
        # two possible ways offer themselves : in self._unlock, write the cache
250
228
        wt_trans = self.bzrdir.get_workingtree_transport(None)
251
229
        cache_filename = wt_trans.local_abspath('stat-cache')
252
230
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
253
 
                                              self._control_files._file_mode)
 
231
            self.bzrdir._get_file_mode(),
 
232
            self._content_filter_stack_provider())
254
233
        hc = self._hashcache
255
234
        hc.read()
256
235
        # is this scan needed ? it makes things kinda slow.
271
250
            # permitted to do this.
272
251
            self._set_inventory(_inventory, dirty=False)
273
252
        self._detect_case_handling()
 
253
        self._rules_searcher = None
 
254
        self.views = self._make_views()
274
255
 
275
256
    def _detect_case_handling(self):
276
257
        wt_trans = self.bzrdir.get_workingtree_transport(None)
302
283
        self._control_files.break_lock()
303
284
        self.branch.break_lock()
304
285
 
 
286
    def _get_check_refs(self):
 
287
        """Return the references needed to perform a check of this tree.
 
288
        
 
289
        The default implementation returns no refs, and is only suitable for
 
290
        trees that have no local caching and can commit on ghosts at any time.
 
291
 
 
292
        :seealso: bzrlib.check for details about check_refs.
 
293
        """
 
294
        return []
 
295
 
305
296
    def requires_rich_root(self):
306
297
        return self._format.requires_rich_root
307
298
 
308
299
    def supports_tree_reference(self):
309
300
        return False
310
301
 
 
302
    def supports_content_filtering(self):
 
303
        return self._format.supports_content_filtering()
 
304
 
 
305
    def supports_views(self):
 
306
        return self.views.supports_views()
 
307
 
311
308
    def _set_inventory(self, inv, dirty):
312
309
        """Set the internal cached inventory.
313
310
 
318
315
            False then the inventory is the same as that on disk and any
319
316
            serialisation would be unneeded overhead.
320
317
        """
321
 
        assert inv.root is not None
322
318
        self._inventory = inv
323
319
        self._inventory_is_modified = dirty
324
320
 
328
324
 
329
325
        """
330
326
        if path is None:
331
 
            path = os.path.getcwdu()
 
327
            path = osutils.getcwd()
332
328
        control = bzrdir.BzrDir.open(path, _unsupported)
333
329
        return control.open_workingtree(_unsupported)
334
 
        
 
330
 
335
331
    @staticmethod
336
332
    def open_containing(path=None):
337
333
        """Open an existing working tree which has its root about path.
338
 
        
 
334
 
339
335
        This probes for a working tree at path and searches upwards from there.
340
336
 
341
337
        Basically we keep looking up until we find the control directory or
389
385
            if osutils.lexists(self.abspath(path)):
390
386
                yield ie.file_id
391
387
 
 
388
    def all_file_ids(self):
 
389
        """See Tree.iter_all_file_ids"""
 
390
        return set(self.inventory)
 
391
 
392
392
    def __repr__(self):
393
393
        return "<%s of %s>" % (self.__class__.__name__,
394
394
                               getattr(self, 'basedir', None))
398
398
 
399
399
    def basis_tree(self):
400
400
        """Return RevisionTree for the current last revision.
401
 
        
 
401
 
402
402
        If the left most parent is a ghost then the returned tree will be an
403
 
        empty tree - one obtained by calling repository.revision_tree(None).
 
403
        empty tree - one obtained by calling
 
404
        repository.revision_tree(NULL_REVISION).
404
405
        """
405
406
        try:
406
407
            revision_id = self.get_parent_ids()[0]
408
409
            # no parents, return an empty revision tree.
409
410
            # in the future this should return the tree for
410
411
            # 'empty:' - the implicit root empty tree.
411
 
            return self.branch.repository.revision_tree(None)
 
412
            return self.branch.repository.revision_tree(
 
413
                       _mod_revision.NULL_REVISION)
412
414
        try:
413
415
            return self.revision_tree(revision_id)
414
416
        except errors.NoSuchRevision:
418
420
        # at this point ?
419
421
        try:
420
422
            return self.branch.repository.revision_tree(revision_id)
421
 
        except errors.RevisionNotPresent:
 
423
        except (errors.RevisionNotPresent, errors.NoSuchRevision):
422
424
            # the basis tree *may* be a ghost or a low level error may have
423
 
            # occured. If the revision is present, its a problem, if its not
 
425
            # occurred. If the revision is present, its a problem, if its not
424
426
            # its a ghost.
425
427
            if self.branch.repository.has_revision(revision_id):
426
428
                raise
427
429
            # the basis tree is a ghost so return an empty tree.
428
 
            return self.branch.repository.revision_tree(None)
 
430
            return self.branch.repository.revision_tree(
 
431
                       _mod_revision.NULL_REVISION)
429
432
 
430
433
    def _cleanup(self):
431
434
        self._flush_ignore_list_cache()
432
435
 
433
 
    @staticmethod
434
 
    @deprecated_method(zero_eight)
435
 
    def create(branch, directory):
436
 
        """Create a workingtree for branch at directory.
437
 
 
438
 
        If existing_directory already exists it must have a .bzr directory.
439
 
        If it does not exist, it will be created.
440
 
 
441
 
        This returns a new WorkingTree object for the new checkout.
442
 
 
443
 
        TODO FIXME RBC 20060124 when we have checkout formats in place this
444
 
        should accept an optional revisionid to checkout [and reject this if
445
 
        checking out into the same dir as a pre-checkout-aware branch format.]
446
 
 
447
 
        XXX: When BzrDir is present, these should be created through that 
448
 
        interface instead.
449
 
        """
450
 
        warnings.warn('delete WorkingTree.create', stacklevel=3)
451
 
        transport = get_transport(directory)
452
 
        if branch.bzrdir.root_transport.base == transport.base:
453
 
            # same dir 
454
 
            return branch.bzrdir.create_workingtree()
455
 
        # different directory, 
456
 
        # create a branch reference
457
 
        # and now a working tree.
458
 
        raise NotImplementedError
459
 
 
460
 
    @staticmethod
461
 
    @deprecated_method(zero_eight)
462
 
    def create_standalone(directory):
463
 
        """Create a checkout and a branch and a repo at directory.
464
 
 
465
 
        Directory must exist and be empty.
466
 
 
467
 
        please use BzrDir.create_standalone_workingtree
468
 
        """
469
 
        return bzrdir.BzrDir.create_standalone_workingtree(directory)
470
 
 
471
436
    def relpath(self, path):
472
437
        """Return the local path portion from a given path.
473
 
        
474
 
        The path may be absolute or relative. If its a relative path it is 
 
438
 
 
439
        The path may be absolute or relative. If its a relative path it is
475
440
        interpreted relative to the python current working directory.
476
441
        """
477
442
        return osutils.relpath(self.basedir, path)
479
444
    def has_filename(self, filename):
480
445
        return osutils.lexists(self.abspath(filename))
481
446
 
482
 
    def get_file(self, file_id, path=None):
 
447
    def get_file(self, file_id, path=None, filtered=True):
 
448
        return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
 
449
 
 
450
    def get_file_with_stat(self, file_id, path=None, filtered=True,
 
451
        _fstat=os.fstat):
 
452
        """See Tree.get_file_with_stat."""
483
453
        if path is None:
484
454
            path = self.id2path(file_id)
485
 
        return self.get_file_byname(path)
486
 
 
487
 
    def get_file_text(self, file_id):
488
 
        return self.get_file(file_id).read()
489
 
 
490
 
    def get_file_byname(self, filename):
491
 
        return file(self.abspath(filename), 'rb')
 
455
        file_obj = self.get_file_byname(path, filtered=False)
 
456
        stat_value = _fstat(file_obj.fileno())
 
457
        if filtered and self.supports_content_filtering():
 
458
            filters = self._content_filter_stack(path)
 
459
            file_obj = filtered_input_file(file_obj, filters)
 
460
        return (file_obj, stat_value)
 
461
 
 
462
    def get_file_text(self, file_id, path=None, filtered=True):
 
463
        return self.get_file(file_id, path=path, filtered=filtered).read()
 
464
 
 
465
    def get_file_byname(self, filename, filtered=True):
 
466
        path = self.abspath(filename)
 
467
        f = file(path, 'rb')
 
468
        if filtered and self.supports_content_filtering():
 
469
            filters = self._content_filter_stack(filename)
 
470
            return filtered_input_file(f, filters)
 
471
        else:
 
472
            return f
 
473
 
 
474
    def get_file_lines(self, file_id, path=None, filtered=True):
 
475
        """See Tree.get_file_lines()"""
 
476
        file = self.get_file(file_id, path, filtered=filtered)
 
477
        try:
 
478
            return file.readlines()
 
479
        finally:
 
480
            file.close()
492
481
 
493
482
    @needs_read_lock
494
483
    def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
501
490
        incorrectly attributed to CURRENT_REVISION (but after committing, the
502
491
        attribution will be correct).
503
492
        """
504
 
        basis = self.basis_tree()
505
 
        basis.lock_read()
506
 
        try:
507
 
            changes = self._iter_changes(basis, True, [self.id2path(file_id)],
508
 
                require_versioned=True).next()
509
 
            changed_content, kind = changes[2], changes[6]
510
 
            if not changed_content:
511
 
                return basis.annotate_iter(file_id)
512
 
            if kind[1] is None:
513
 
                return None
514
 
            import annotate
515
 
            if kind[0] != 'file':
516
 
                old_lines = []
517
 
            else:
518
 
                old_lines = list(basis.annotate_iter(file_id))
519
 
            old = [old_lines]
520
 
            for tree in self.branch.repository.revision_trees(
521
 
                self.get_parent_ids()[1:]):
522
 
                if file_id not in tree:
523
 
                    continue
524
 
                old.append(list(tree.annotate_iter(file_id)))
525
 
            return annotate.reannotate(old, self.get_file(file_id).readlines(),
526
 
                                       default_revision)
527
 
        finally:
528
 
            basis.unlock()
 
493
        maybe_file_parent_keys = []
 
494
        for parent_id in self.get_parent_ids():
 
495
            try:
 
496
                parent_tree = self.revision_tree(parent_id)
 
497
            except errors.NoSuchRevisionInTree:
 
498
                parent_tree = self.branch.repository.revision_tree(parent_id)
 
499
            parent_tree.lock_read()
 
500
            try:
 
501
                if file_id not in parent_tree:
 
502
                    continue
 
503
                ie = parent_tree.inventory[file_id]
 
504
                if ie.kind != 'file':
 
505
                    # Note: this is slightly unnecessary, because symlinks and
 
506
                    # directories have a "text" which is the empty text, and we
 
507
                    # know that won't mess up annotations. But it seems cleaner
 
508
                    continue
 
509
                parent_text_key = (file_id, ie.revision)
 
510
                if parent_text_key not in maybe_file_parent_keys:
 
511
                    maybe_file_parent_keys.append(parent_text_key)
 
512
            finally:
 
513
                parent_tree.unlock()
 
514
        graph = _mod_graph.Graph(self.branch.repository.texts)
 
515
        heads = graph.heads(maybe_file_parent_keys)
 
516
        file_parent_keys = []
 
517
        for key in maybe_file_parent_keys:
 
518
            if key in heads:
 
519
                file_parent_keys.append(key)
 
520
 
 
521
        # Now we have the parents of this content
 
522
        annotator = self.branch.repository.texts.get_annotator()
 
523
        text = self.get_file(file_id).read()
 
524
        this_key =(file_id, default_revision)
 
525
        annotator.add_special_text(this_key, file_parent_keys, text)
 
526
        annotations = [(key[-1], line)
 
527
                       for key, line in annotator.annotate_flat(this_key)]
 
528
        return annotations
529
529
 
530
530
    def _get_ancestors(self, default_revision):
531
531
        ancestors = set([default_revision])
536
536
 
537
537
    def get_parent_ids(self):
538
538
        """See Tree.get_parent_ids.
539
 
        
 
539
 
540
540
        This implementation reads the pending merges list and last_revision
541
541
        value and uses that to decide what the parents list should be.
542
542
        """
546
546
        else:
547
547
            parents = [last_rev]
548
548
        try:
549
 
            merges_file = self._control_files.get('pending-merges')
 
549
            merges_bytes = self._transport.get_bytes('pending-merges')
550
550
        except errors.NoSuchFile:
551
551
            pass
552
552
        else:
553
 
            for l in merges_file.readlines():
 
553
            for l in osutils.split_lines(merges_bytes):
554
554
                revision_id = l.rstrip('\n')
555
555
                parents.append(revision_id)
556
556
        return parents
559
559
    def get_root_id(self):
560
560
        """Return the id of this trees root"""
561
561
        return self._inventory.root.file_id
562
 
        
 
562
 
563
563
    def _get_store_filename(self, file_id):
564
564
        ## XXX: badly named; this is not in the store at all
565
565
        return self.abspath(self.id2path(file_id))
567
567
    @needs_read_lock
568
568
    def clone(self, to_bzrdir, revision_id=None):
569
569
        """Duplicate this working tree into to_bzr, including all state.
570
 
        
 
570
 
571
571
        Specifically modified files are kept as modified, but
572
572
        ignored and unknown files are discarded.
573
573
 
574
574
        If you want to make a new line of development, see bzrdir.sprout()
575
575
 
576
576
        revision
577
 
            If not None, the cloned tree will have its last revision set to 
578
 
            revision, and and difference between the source trees last revision
 
577
            If not None, the cloned tree will have its last revision set to
 
578
            revision, and difference between the source trees last revision
579
579
            and this one merged in.
580
580
        """
581
581
        # assumes the target bzr dir format is compatible.
582
 
        result = self._format.initialize(to_bzrdir)
 
582
        result = to_bzrdir.create_workingtree()
583
583
        self.copy_content_into(result, revision_id)
584
584
        return result
585
585
 
614
614
    __contains__ = has_id
615
615
 
616
616
    def get_file_size(self, file_id):
617
 
        return os.path.getsize(self.id2abspath(file_id))
 
617
        """See Tree.get_file_size"""
 
618
        # XXX: this returns the on-disk size; it should probably return the
 
619
        # canonical size
 
620
        try:
 
621
            return os.path.getsize(self.id2abspath(file_id))
 
622
        except OSError, e:
 
623
            if e.errno != errno.ENOENT:
 
624
                raise
 
625
            else:
 
626
                return None
618
627
 
619
628
    @needs_read_lock
620
629
    def get_file_sha1(self, file_id, path=None, stat_value=None):
629
638
 
630
639
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
631
640
        file_id = self.path2id(path)
 
641
        if file_id is None:
 
642
            # For unversioned files on win32, we just assume they are not
 
643
            # executable
 
644
            return False
632
645
        return self._inventory[file_id].executable
633
646
 
634
647
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
656
669
        """See MutableTree._add."""
657
670
        # TODO: Re-adding a file that is removed in the working copy
658
671
        # should probably put it back with the previous ID.
659
 
        # the read and write working inventory should not occur in this 
 
672
        # the read and write working inventory should not occur in this
660
673
        # function - they should be part of lock_write and unlock.
661
674
        inv = self.inventory
662
675
        for f, file_id, kind in zip(files, ids, kinds):
663
 
            assert kind is not None
664
676
            if file_id is None:
665
677
                inv.add_path(f, kind=kind)
666
678
            else:
745
757
            raise
746
758
        kind = _mapper(stat_result.st_mode)
747
759
        if kind == 'file':
748
 
            size = stat_result.st_size
749
 
            # try for a stat cache lookup
750
 
            executable = self._is_executable_from_path_and_stat(path, stat_result)
751
 
            return (kind, size, executable, self._sha_from_stat(
752
 
                path, stat_result))
 
760
            return self._file_content_summary(path, stat_result)
753
761
        elif kind == 'directory':
754
762
            # perhaps it looks like a plain directory, but it's really a
755
763
            # reference.
757
765
                kind = 'tree-reference'
758
766
            return kind, None, None, None
759
767
        elif kind == 'symlink':
760
 
            return ('symlink', None, None, os.readlink(abspath))
 
768
            target = osutils.readlink(abspath)
 
769
            return ('symlink', None, None, target)
761
770
        else:
762
771
            return (kind, None, None, None)
763
772
 
764
 
    @deprecated_method(zero_eleven)
765
 
    @needs_read_lock
766
 
    def pending_merges(self):
767
 
        """Return a list of pending merges.
768
 
 
769
 
        These are revisions that have been merged into the working
770
 
        directory but not yet committed.
771
 
 
772
 
        As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
773
 
        instead - which is available on all tree objects.
774
 
        """
775
 
        return self.get_parent_ids()[1:]
 
773
    def _file_content_summary(self, path, stat_result):
 
774
        size = stat_result.st_size
 
775
        executable = self._is_executable_from_path_and_stat(path, stat_result)
 
776
        # try for a stat cache lookup
 
777
        return ('file', size, executable, self._sha_from_stat(
 
778
            path, stat_result))
776
779
 
777
780
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
778
781
        """Common ghost checking functionality from set_parent_*.
788
791
 
789
792
    def _set_merges_from_parent_ids(self, parent_ids):
790
793
        merges = parent_ids[1:]
791
 
        self._control_files.put_bytes('pending-merges', '\n'.join(merges))
 
794
        self._transport.put_bytes('pending-merges', '\n'.join(merges),
 
795
            mode=self.bzrdir._get_file_mode())
 
796
 
 
797
    def _filter_parent_ids_by_ancestry(self, revision_ids):
 
798
        """Check that all merged revisions are proper 'heads'.
 
799
 
 
800
        This will always return the first revision_id, and any merged revisions
 
801
        which are
 
802
        """
 
803
        if len(revision_ids) == 0:
 
804
            return revision_ids
 
805
        graph = self.branch.repository.get_graph()
 
806
        heads = graph.heads(revision_ids)
 
807
        new_revision_ids = revision_ids[:1]
 
808
        for revision_id in revision_ids[1:]:
 
809
            if revision_id in heads and revision_id not in new_revision_ids:
 
810
                new_revision_ids.append(revision_id)
 
811
        if new_revision_ids != revision_ids:
 
812
            trace.mutter('requested to set revision_ids = %s,'
 
813
                         ' but filtered to %s', revision_ids, new_revision_ids)
 
814
        return new_revision_ids
792
815
 
793
816
    @needs_tree_write_lock
794
817
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
795
818
        """Set the parent ids to revision_ids.
796
 
        
 
819
 
797
820
        See also set_parent_trees. This api will try to retrieve the tree data
798
821
        for each element of revision_ids from the trees repository. If you have
799
822
        tree data already available, it is more efficient to use
808
831
        for revision_id in revision_ids:
809
832
            _mod_revision.check_not_reserved_id(revision_id)
810
833
 
 
834
        revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
 
835
 
811
836
        if len(revision_ids) > 0:
812
837
            self.set_last_revision(revision_ids[0])
813
838
        else:
825
850
        self._check_parents_for_ghosts(parent_ids,
826
851
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
827
852
 
 
853
        parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
 
854
 
828
855
        if len(parent_ids) == 0:
829
856
            leftmost_parent_id = _mod_revision.NULL_REVISION
830
857
            leftmost_parent_tree = None
870
897
    def _put_rio(self, filename, stanzas, header):
871
898
        self._must_be_locked()
872
899
        my_file = rio_file(stanzas, header)
873
 
        self._control_files.put(filename, my_file)
 
900
        self._transport.put_file(filename, my_file,
 
901
            mode=self.bzrdir._get_file_mode())
874
902
 
875
903
    @needs_write_lock # because merge pulls data into the branch.
876
904
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
877
 
        merge_type=None):
 
905
                          merge_type=None, force=False):
878
906
        """Merge from a branch into this working tree.
879
907
 
880
908
        :param branch: The branch to merge from.
884
912
            branch.last_revision().
885
913
        """
886
914
        from bzrlib.merge import Merger, Merge3Merger
887
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
888
 
        try:
889
 
            merger = Merger(self.branch, this_tree=self, pb=pb)
890
 
            merger.pp = ProgressPhase("Merge phase", 5, pb)
891
 
            merger.pp.next_phase()
892
 
            # check that there are no
893
 
            # local alterations
894
 
            merger.check_basis(check_clean=True, require_commits=False)
895
 
            if to_revision is None:
896
 
                to_revision = _mod_revision.ensure_null(branch.last_revision())
897
 
            merger.other_rev_id = to_revision
898
 
            if _mod_revision.is_null(merger.other_rev_id):
899
 
                raise errors.NoCommits(branch)
900
 
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
901
 
            merger.other_basis = merger.other_rev_id
902
 
            merger.other_tree = self.branch.repository.revision_tree(
903
 
                merger.other_rev_id)
904
 
            merger.other_branch = branch
905
 
            merger.pp.next_phase()
906
 
            if from_revision is None:
907
 
                merger.find_base()
908
 
            else:
909
 
                merger.set_base_revision(from_revision, branch)
910
 
            if merger.base_rev_id == merger.other_rev_id:
911
 
                raise errors.PointlessMerge
912
 
            merger.backup_files = False
913
 
            if merge_type is None:
914
 
                merger.merge_type = Merge3Merger
915
 
            else:
916
 
                merger.merge_type = merge_type
917
 
            merger.set_interesting_files(None)
918
 
            merger.show_base = False
919
 
            merger.reprocess = False
920
 
            conflicts = merger.do_merge()
921
 
            merger.set_pending()
922
 
        finally:
923
 
            pb.finished()
 
915
        merger = Merger(self.branch, this_tree=self)
 
916
        # check that there are no local alterations
 
917
        if not force and self.has_changes():
 
918
            raise errors.UncommittedChanges(self)
 
919
        if to_revision is None:
 
920
            to_revision = _mod_revision.ensure_null(branch.last_revision())
 
921
        merger.other_rev_id = to_revision
 
922
        if _mod_revision.is_null(merger.other_rev_id):
 
923
            raise errors.NoCommits(branch)
 
924
        self.branch.fetch(branch, last_revision=merger.other_rev_id)
 
925
        merger.other_basis = merger.other_rev_id
 
926
        merger.other_tree = self.branch.repository.revision_tree(
 
927
            merger.other_rev_id)
 
928
        merger.other_branch = branch
 
929
        if from_revision is None:
 
930
            merger.find_base()
 
931
        else:
 
932
            merger.set_base_revision(from_revision, branch)
 
933
        if merger.base_rev_id == merger.other_rev_id:
 
934
            raise errors.PointlessMerge
 
935
        merger.backup_files = False
 
936
        if merge_type is None:
 
937
            merger.merge_type = Merge3Merger
 
938
        else:
 
939
            merger.merge_type = merge_type
 
940
        merger.set_interesting_files(None)
 
941
        merger.show_base = False
 
942
        merger.reprocess = False
 
943
        conflicts = merger.do_merge()
 
944
        merger.set_pending()
924
945
        return conflicts
925
946
 
926
947
    @needs_read_lock
927
948
    def merge_modified(self):
928
949
        """Return a dictionary of files modified by a merge.
929
950
 
930
 
        The list is initialized by WorkingTree.set_merge_modified, which is 
 
951
        The list is initialized by WorkingTree.set_merge_modified, which is
931
952
        typically called after we make some automatic updates to the tree
932
953
        because of a merge.
933
954
 
935
956
        still in the working inventory and have that text hash.
936
957
        """
937
958
        try:
938
 
            hashfile = self._control_files.get('merge-hashes')
 
959
            hashfile = self._transport.get('merge-hashes')
939
960
        except errors.NoSuchFile:
940
961
            return {}
941
 
        merge_hashes = {}
942
962
        try:
943
 
            if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
 
963
            merge_hashes = {}
 
964
            try:
 
965
                if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
 
966
                    raise errors.MergeModifiedFormatError()
 
967
            except StopIteration:
944
968
                raise errors.MergeModifiedFormatError()
945
 
        except StopIteration:
946
 
            raise errors.MergeModifiedFormatError()
947
 
        for s in RioReader(hashfile):
948
 
            # RioReader reads in Unicode, so convert file_ids back to utf8
949
 
            file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
950
 
            if file_id not in self.inventory:
951
 
                continue
952
 
            text_hash = s.get("hash")
953
 
            if text_hash == self.get_file_sha1(file_id):
954
 
                merge_hashes[file_id] = text_hash
955
 
        return merge_hashes
 
969
            for s in RioReader(hashfile):
 
970
                # RioReader reads in Unicode, so convert file_ids back to utf8
 
971
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
 
972
                if file_id not in self.inventory:
 
973
                    continue
 
974
                text_hash = s.get("hash")
 
975
                if text_hash == self.get_file_sha1(file_id):
 
976
                    merge_hashes[file_id] = text_hash
 
977
            return merge_hashes
 
978
        finally:
 
979
            hashfile.close()
956
980
 
957
981
    @needs_write_lock
958
982
    def mkdir(self, path, file_id=None):
964
988
        return file_id
965
989
 
966
990
    def get_symlink_target(self, file_id):
967
 
        return os.readlink(self.id2abspath(file_id))
 
991
        abspath = self.id2abspath(file_id)
 
992
        target = osutils.readlink(abspath)
 
993
        return target
968
994
 
969
995
    @needs_write_lock
970
996
    def subsume(self, other_tree):
1020
1046
        return False
1021
1047
 
1022
1048
    def _directory_may_be_tree_reference(self, relpath):
1023
 
        # as a special case, if a directory contains control files then 
 
1049
        # as a special case, if a directory contains control files then
1024
1050
        # it's a tree reference, except that the root of the tree is not
1025
1051
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
1026
1052
        # TODO: We could ask all the control formats whether they
1037
1063
    @needs_tree_write_lock
1038
1064
    def extract(self, file_id, format=None):
1039
1065
        """Extract a subtree from this tree.
1040
 
        
 
1066
 
1041
1067
        A new branch will be created, relative to the path for this tree.
1042
1068
        """
1043
1069
        self.flush()
1048
1074
                transport = transport.clone(name)
1049
1075
                transport.ensure_base()
1050
1076
            return transport
1051
 
            
 
1077
 
1052
1078
        sub_path = self.id2path(file_id)
1053
1079
        branch_transport = mkdirs(sub_path)
1054
1080
        if format is None:
1068
1094
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
1069
1095
        if tree_transport.base != branch_transport.base:
1070
1096
            tree_bzrdir = format.initialize_on_transport(tree_transport)
1071
 
            branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
 
1097
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
 
1098
                target_branch=new_branch)
1072
1099
        else:
1073
1100
            tree_bzrdir = branch_bzrdir
1074
 
        wt = tree_bzrdir.create_workingtree(NULL_REVISION)
 
1101
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
1075
1102
        wt.set_parent_ids(self.get_parent_ids())
1076
1103
        my_inv = self.inventory
1077
 
        child_inv = Inventory(root_id=None)
 
1104
        child_inv = inventory.Inventory(root_id=None)
1078
1105
        new_root = my_inv[file_id]
1079
1106
        my_inv.remove_recursive_id(file_id)
1080
1107
        new_root.parent_id = None
1098
1125
        sio = StringIO()
1099
1126
        self._serialize(self._inventory, sio)
1100
1127
        sio.seek(0)
1101
 
        self._control_files.put('inventory', sio)
 
1128
        self._transport.put_file('inventory', sio,
 
1129
            mode=self.bzrdir._get_file_mode())
1102
1130
        self._inventory_is_modified = False
1103
1131
 
1104
1132
    def _kind(self, relpath):
1105
1133
        return osutils.file_kind(self.abspath(relpath))
1106
1134
 
1107
 
    def list_files(self, include_root=False):
1108
 
        """Recursively list all files as (path, class, kind, id, entry).
 
1135
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
1136
        """List all files as (path, class, kind, id, entry).
1109
1137
 
1110
1138
        Lists, but does not descend into unversioned directories.
1111
 
 
1112
1139
        This does not include files that have been deleted in this
1113
 
        tree.
 
1140
        tree. Skips the control directory.
1114
1141
 
1115
 
        Skips the control directory.
 
1142
        :param include_root: if True, return an entry for the root
 
1143
        :param from_dir: start from this directory or None for the root
 
1144
        :param recursive: whether to recurse into subdirectories or not
1116
1145
        """
1117
1146
        # list_files is an iterator, so @needs_read_lock doesn't work properly
1118
1147
        # with it. So callers should be careful to always read_lock the tree.
1120
1149
            raise errors.ObjectNotLocked(self)
1121
1150
 
1122
1151
        inv = self.inventory
1123
 
        if include_root is True:
 
1152
        if from_dir is None and include_root is True:
1124
1153
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1125
1154
        # Convert these into local objects to save lookup times
1126
1155
        pathjoin = osutils.pathjoin
1133
1162
        fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
1134
1163
 
1135
1164
        # directory file_id, relative path, absolute path, reverse sorted children
1136
 
        children = os.listdir(self.basedir)
 
1165
        if from_dir is not None:
 
1166
            from_dir_id = inv.path2id(from_dir)
 
1167
            if from_dir_id is None:
 
1168
                # Directory not versioned
 
1169
                return
 
1170
            from_dir_abspath = pathjoin(self.basedir, from_dir)
 
1171
        else:
 
1172
            from_dir_id = inv.root.file_id
 
1173
            from_dir_abspath = self.basedir
 
1174
        children = os.listdir(from_dir_abspath)
1137
1175
        children.sort()
1138
 
        # jam 20060527 The kernel sized tree seems equivalent whether we 
 
1176
        # jam 20060527 The kernel sized tree seems equivalent whether we
1139
1177
        # use a deque and popleft to keep them sorted, or if we use a plain
1140
1178
        # list and just reverse() them.
1141
1179
        children = collections.deque(children)
1142
 
        stack = [(inv.root.file_id, u'', self.basedir, children)]
 
1180
        stack = [(from_dir_id, u'', from_dir_abspath, children)]
1143
1181
        while stack:
1144
1182
            from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
1145
1183
 
1161
1199
 
1162
1200
                # absolute path
1163
1201
                fap = from_dir_abspath + '/' + f
1164
 
                
 
1202
 
1165
1203
                f_ie = inv.get_child(from_dir_id, f)
1166
1204
                if f_ie:
1167
1205
                    c = 'V'
1199
1237
                    except KeyError:
1200
1238
                        yield fp[1:], c, fk, None, TreeEntry()
1201
1239
                    continue
1202
 
                
 
1240
 
1203
1241
                if fk != 'directory':
1204
1242
                    continue
1205
1243
 
1206
 
                # But do this child first
1207
 
                new_children = os.listdir(fap)
1208
 
                new_children.sort()
1209
 
                new_children = collections.deque(new_children)
1210
 
                stack.append((f_ie.file_id, fp, fap, new_children))
1211
 
                # Break out of inner loop,
1212
 
                # so that we start outer loop with child
1213
 
                break
 
1244
                # But do this child first if recursing down
 
1245
                if recursive:
 
1246
                    new_children = os.listdir(fap)
 
1247
                    new_children.sort()
 
1248
                    new_children = collections.deque(new_children)
 
1249
                    stack.append((f_ie.file_id, fp, fap, new_children))
 
1250
                    # Break out of inner loop,
 
1251
                    # so that we start outer loop with child
 
1252
                    break
1214
1253
            else:
1215
1254
                # if we finished all children, pop it off the stack
1216
1255
                stack.pop()
1222
1261
        to_dir must exist in the inventory.
1223
1262
 
1224
1263
        If to_dir exists and is a directory, the files are moved into
1225
 
        it, keeping their old names.  
 
1264
        it, keeping their old names.
1226
1265
 
1227
1266
        Note that to_dir is only the last component of the new name;
1228
1267
        this doesn't change the directory.
1265
1304
                                       DeprecationWarning)
1266
1305
 
1267
1306
        # check destination directory
1268
 
        assert not isinstance(from_paths, basestring)
 
1307
        if isinstance(from_paths, basestring):
 
1308
            raise ValueError()
1269
1309
        inv = self.inventory
1270
1310
        to_abs = self.abspath(to_dir)
1271
1311
        if not isdir(to_abs):
1355
1395
                only_change_inv = True
1356
1396
            elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1357
1397
                only_change_inv = False
1358
 
            elif (sys.platform == 'win32'
1359
 
                and from_rel.lower() == to_rel.lower()
1360
 
                and self.has_filename(from_rel)):
 
1398
            elif (not self.case_sensitive
 
1399
                  and from_rel.lower() == to_rel.lower()
 
1400
                  and self.has_filename(from_rel)):
1361
1401
                only_change_inv = False
1362
1402
            else:
1363
1403
                # something is wrong, so lets determine what exactly
1393
1433
        inv = self.inventory
1394
1434
        for entry in moved:
1395
1435
            try:
1396
 
                self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
 
1436
                self._move_entry(WorkingTree._RenameEntry(
 
1437
                    entry.to_rel, entry.from_id,
1397
1438
                    entry.to_tail, entry.to_parent_id, entry.from_rel,
1398
1439
                    entry.from_tail, entry.from_parent_id,
1399
1440
                    entry.only_change_inv))
1450
1491
        from_tail = splitpath(from_rel)[-1]
1451
1492
        from_id = inv.path2id(from_rel)
1452
1493
        if from_id is None:
1453
 
            raise errors.BzrRenameFailedError(from_rel,to_rel,
1454
 
                errors.NotVersionedError(path=str(from_rel)))
1455
 
        from_entry = inv[from_id]
 
1494
            # if file is missing in the inventory maybe it's in the basis_tree
 
1495
            basis_tree = self.branch.basis_tree()
 
1496
            from_id = basis_tree.path2id(from_rel)
 
1497
            if from_id is None:
 
1498
                raise errors.BzrRenameFailedError(from_rel,to_rel,
 
1499
                    errors.NotVersionedError(path=str(from_rel)))
 
1500
            # put entry back in the inventory so we can rename it
 
1501
            from_entry = basis_tree.inventory[from_id].copy()
 
1502
            inv.add(from_entry)
 
1503
        else:
 
1504
            from_entry = inv[from_id]
1456
1505
        from_parent_id = from_entry.parent_id
1457
1506
        to_dir, to_tail = os.path.split(to_rel)
1458
1507
        to_dir_id = inv.path2id(to_dir)
1504
1553
        These are files in the working directory that are not versioned or
1505
1554
        control files or ignored.
1506
1555
        """
1507
 
        # force the extras method to be fully executed before returning, to 
 
1556
        # force the extras method to be fully executed before returning, to
1508
1557
        # prevent race conditions with the lock
1509
1558
        return iter(
1510
1559
            [subp for subp in self.extras() if not self.is_ignored(subp)])
1520
1569
        :raises: NoSuchId if any fileid is not currently versioned.
1521
1570
        """
1522
1571
        for file_id in file_ids:
 
1572
            if file_id not in self._inventory:
 
1573
                raise errors.NoSuchId(self, file_id)
 
1574
        for file_id in file_ids:
1523
1575
            if self._inventory.has_id(file_id):
1524
1576
                self._inventory.remove_recursive_id(file_id)
1525
 
            else:
1526
 
                raise errors.NoSuchId(self, file_id)
1527
1577
        if len(file_ids):
1528
 
            # in the future this should just set a dirty bit to wait for the 
 
1578
            # in the future this should just set a dirty bit to wait for the
1529
1579
            # final unlock. However, until all methods of workingtree start
1530
 
            # with the current in -memory inventory rather than triggering 
 
1580
            # with the current in -memory inventory rather than triggering
1531
1581
            # a read, it is more complex - we need to teach read_inventory
1532
1582
            # to know when to read, and when to not read first... and possibly
1533
1583
            # to save first when the in memory one may be corrupted.
1534
1584
            # so for now, we just only write it if it is indeed dirty.
1535
1585
            # - RBC 20060907
1536
1586
            self._write_inventory(self._inventory)
1537
 
    
1538
 
    @deprecated_method(zero_eight)
1539
 
    def iter_conflicts(self):
1540
 
        """List all files in the tree that have text or content conflicts.
1541
 
        DEPRECATED.  Use conflicts instead."""
1542
 
        return self._iter_conflicts()
1543
1587
 
1544
1588
    def _iter_conflicts(self):
1545
1589
        conflicted = set()
1554
1598
 
1555
1599
    @needs_write_lock
1556
1600
    def pull(self, source, overwrite=False, stop_revision=None,
1557
 
             change_reporter=None, possible_transports=None):
1558
 
        top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1601
             change_reporter=None, possible_transports=None, local=False):
1559
1602
        source.lock_read()
1560
1603
        try:
1561
 
            pp = ProgressPhase("Pull phase", 2, top_pb)
1562
 
            pp.next_phase()
1563
1604
            old_revision_info = self.branch.last_revision_info()
1564
1605
            basis_tree = self.basis_tree()
1565
1606
            count = self.branch.pull(source, overwrite, stop_revision,
1566
 
                                     possible_transports=possible_transports)
 
1607
                                     possible_transports=possible_transports,
 
1608
                                     local=local)
1567
1609
            new_revision_info = self.branch.last_revision_info()
1568
1610
            if new_revision_info != old_revision_info:
1569
 
                pp.next_phase()
1570
1611
                repository = self.branch.repository
1571
 
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
1572
1612
                basis_tree.lock_read()
1573
1613
                try:
1574
1614
                    new_basis_tree = self.branch.basis_tree()
1577
1617
                                new_basis_tree,
1578
1618
                                basis_tree,
1579
1619
                                this_tree=self,
1580
 
                                pb=pb,
 
1620
                                pb=None,
1581
1621
                                change_reporter=change_reporter)
1582
 
                    if (basis_tree.inventory.root is None and
1583
 
                        new_basis_tree.inventory.root is not None):
1584
 
                        self.set_root_id(new_basis_tree.get_root_id())
 
1622
                    basis_root_id = basis_tree.get_root_id()
 
1623
                    new_root_id = new_basis_tree.get_root_id()
 
1624
                    if basis_root_id != new_root_id:
 
1625
                        self.set_root_id(new_root_id)
1585
1626
                finally:
1586
 
                    pb.finished()
1587
1627
                    basis_tree.unlock()
1588
1628
                # TODO - dedup parents list with things merged by pull ?
1589
1629
                # reuse the revisiontree we merged against to set the new
1590
1630
                # tree data.
1591
1631
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1592
 
                # we have to pull the merge trees out again, because 
1593
 
                # merge_inner has set the ids. - this corner is not yet 
 
1632
                # we have to pull the merge trees out again, because
 
1633
                # merge_inner has set the ids. - this corner is not yet
1594
1634
                # layered well enough to prevent double handling.
1595
1635
                # XXX TODO: Fix the double handling: telling the tree about
1596
1636
                # the already known parent data is wasteful.
1602
1642
            return count
1603
1643
        finally:
1604
1644
            source.unlock()
1605
 
            top_pb.finished()
1606
1645
 
1607
1646
    @needs_write_lock
1608
1647
    def put_file_bytes_non_atomic(self, file_id, bytes):
1634
1673
 
1635
1674
            fl = []
1636
1675
            for subf in os.listdir(dirabs):
1637
 
                if subf == '.bzr':
 
1676
                if self.bzrdir.is_control_filename(subf):
1638
1677
                    continue
1639
1678
                if subf not in dir_entry.children:
1640
 
                    subf_norm, can_access = osutils.normalized_filename(subf)
 
1679
                    try:
 
1680
                        (subf_norm,
 
1681
                         can_access) = osutils.normalized_filename(subf)
 
1682
                    except UnicodeDecodeError:
 
1683
                        path_os_enc = path.encode(osutils._fs_enc)
 
1684
                        relpath = path_os_enc + '/' + subf
 
1685
                        raise errors.BadFilenameEncoding(relpath,
 
1686
                                                         osutils._fs_enc)
1641
1687
                    if subf_norm != subf and can_access:
1642
1688
                        if subf_norm not in dir_entry.children:
1643
1689
                            fl.append(subf_norm)
1644
1690
                    else:
1645
1691
                        fl.append(subf)
1646
 
            
 
1692
 
1647
1693
            fl.sort()
1648
1694
            for subf in fl:
1649
1695
                subp = pathjoin(path, subf)
1686
1732
        r"""Check whether the filename matches an ignore pattern.
1687
1733
 
1688
1734
        Patterns containing '/' or '\' need to match the whole path;
1689
 
        others match against only the last component.
 
1735
        others match against only the last component.  Patterns starting
 
1736
        with '!' are ignore exceptions.  Exceptions take precedence
 
1737
        over regular patterns and cause the filename to not be ignored.
1690
1738
 
1691
1739
        If the file is ignored, returns the pattern which caused it to
1692
1740
        be ignored, otherwise None.  So this can simply be used as a
1693
1741
        boolean if desired."""
1694
1742
        if getattr(self, '_ignoreglobster', None) is None:
1695
 
            self._ignoreglobster = globbing.Globster(self.get_ignore_list())
 
1743
            self._ignoreglobster = globbing.ExceptionGlobster(self.get_ignore_list())
1696
1744
        return self._ignoreglobster.match(filename)
1697
1745
 
1698
1746
    def kind(self, file_id):
1699
1747
        return file_kind(self.id2abspath(file_id))
1700
1748
 
 
1749
    def stored_kind(self, file_id):
 
1750
        """See Tree.stored_kind"""
 
1751
        return self.inventory[file_id].kind
 
1752
 
1701
1753
    def _comparison_data(self, entry, path):
1702
1754
        abspath = self.abspath(path)
1703
1755
        try:
1785
1837
    def _reset_data(self):
1786
1838
        """Reset transient data that cannot be revalidated."""
1787
1839
        self._inventory_is_modified = False
1788
 
        result = self._deserialize(self._control_files.get('inventory'))
 
1840
        f = self._transport.get('inventory')
 
1841
        try:
 
1842
            result = self._deserialize(f)
 
1843
        finally:
 
1844
            f.close()
1789
1845
        self._set_inventory(result, dirty=False)
1790
1846
 
1791
1847
    @needs_tree_write_lock
1796
1852
 
1797
1853
    def _change_last_revision(self, new_revision):
1798
1854
        """Template method part of set_last_revision to perform the change.
1799
 
        
 
1855
 
1800
1856
        This is used to allow WorkingTree3 instances to not affect branch
1801
1857
        when their last revision is set.
1802
1858
        """
1812
1868
 
1813
1869
    def _write_basis_inventory(self, xml):
1814
1870
        """Write the basis inventory XML to the basis-inventory file"""
1815
 
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
1816
1871
        path = self._basis_inventory_name()
1817
1872
        sio = StringIO(xml)
1818
 
        self._control_files.put(path, sio)
 
1873
        self._transport.put_file(path, sio,
 
1874
            mode=self.bzrdir._get_file_mode())
1819
1875
 
1820
1876
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
1821
1877
        """Create the text that will be saved in basis-inventory"""
1828
1884
        # as commit already has that ready-to-use [while the format is the
1829
1885
        # same, that is].
1830
1886
        try:
1831
 
            # this double handles the inventory - unpack and repack - 
 
1887
            # this double handles the inventory - unpack and repack -
1832
1888
            # but is easier to understand. We can/should put a conditional
1833
1889
            # in here based on whether the inventory is in the latest format
1834
1890
            # - perhaps we should repack all inventories on a repository
1835
1891
            # upgrade ?
1836
1892
            # the fast path is to copy the raw xml from the repository. If the
1837
 
            # xml contains 'revision_id="', then we assume the right 
 
1893
            # xml contains 'revision_id="', then we assume the right
1838
1894
            # revision_id is set. We must check for this full string, because a
1839
1895
            # root node id can legitimately look like 'revision_id' but cannot
1840
1896
            # contain a '"'.
1841
 
            xml = self.branch.repository.get_inventory_xml(new_revision)
 
1897
            xml = self.branch.repository._get_inventory_xml(new_revision)
1842
1898
            firstline = xml.split('\n', 1)[0]
1843
 
            if (not 'revision_id="' in firstline or 
 
1899
            if (not 'revision_id="' in firstline or
1844
1900
                'format="7"' not in firstline):
1845
 
                inv = self.branch.repository.deserialise_inventory(
1846
 
                    new_revision, xml)
 
1901
                inv = self.branch.repository._serializer.read_inventory_from_string(
 
1902
                    xml, new_revision)
1847
1903
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
1848
1904
            self._write_basis_inventory(xml)
1849
1905
        except (errors.NoSuchRevision, errors.RevisionNotPresent):
1852
1908
    def read_basis_inventory(self):
1853
1909
        """Read the cached basis inventory."""
1854
1910
        path = self._basis_inventory_name()
1855
 
        return self._control_files.get(path).read()
1856
 
        
 
1911
        return self._transport.get_bytes(path)
 
1912
 
1857
1913
    @needs_read_lock
1858
1914
    def read_working_inventory(self):
1859
1915
        """Read the working inventory.
1860
 
        
 
1916
 
1861
1917
        :raises errors.InventoryModified: read_working_inventory will fail
1862
1918
            when the current in memory inventory has been modified.
1863
1919
        """
1864
 
        # conceptually this should be an implementation detail of the tree. 
 
1920
        # conceptually this should be an implementation detail of the tree.
1865
1921
        # XXX: Deprecate this.
1866
1922
        # ElementTree does its own conversion from UTF-8, so open in
1867
1923
        # binary.
1868
1924
        if self._inventory_is_modified:
1869
1925
            raise errors.InventoryModified(self)
1870
 
        result = self._deserialize(self._control_files.get('inventory'))
 
1926
        f = self._transport.get('inventory')
 
1927
        try:
 
1928
            result = self._deserialize(f)
 
1929
        finally:
 
1930
            f.close()
1871
1931
        self._set_inventory(result, dirty=False)
1872
1932
        return result
1873
1933
 
1888
1948
 
1889
1949
        new_files=set()
1890
1950
        unknown_nested_files=set()
 
1951
        if to_file is None:
 
1952
            to_file = sys.stdout
1891
1953
 
1892
1954
        def recurse_directory_to_add_files(directory):
1893
1955
            # Recurse directory and add all files
1894
1956
            # so we can check if they have changed.
1895
1957
            for parent_info, file_infos in\
1896
 
                osutils.walkdirs(self.abspath(directory),
1897
 
                    directory):
1898
 
                for relpath, basename, kind, lstat, abspath in file_infos:
 
1958
                self.walkdirs(directory):
 
1959
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
1899
1960
                    # Is it versioned or ignored?
1900
1961
                    if self.path2id(relpath) or self.is_ignored(relpath):
1901
1962
                        # Add nested content for deletion.
1911
1972
            filename = self.relpath(abspath)
1912
1973
            if len(filename) > 0:
1913
1974
                new_files.add(filename)
1914
 
                if osutils.isdir(abspath):
1915
 
                    recurse_directory_to_add_files(filename)
 
1975
                recurse_directory_to_add_files(filename)
1916
1976
 
1917
1977
        files = list(new_files)
1918
1978
 
1927
1987
            has_changed_files = len(unknown_nested_files) > 0
1928
1988
            if not has_changed_files:
1929
1989
                for (file_id, path, content_change, versioned, parent_id, name,
1930
 
                     kind, executable) in self._iter_changes(self.basis_tree(),
 
1990
                     kind, executable) in self.iter_changes(self.basis_tree(),
1931
1991
                         include_unchanged=True, require_versioned=False,
1932
1992
                         want_unversioned=True, specific_files=files):
1933
1993
                    if versioned == (False, False):
1936
1996
                            # ... but not ignored
1937
1997
                            has_changed_files = True
1938
1998
                            break
1939
 
                    elif content_change and (kind[1] != None):
 
1999
                    elif content_change and (kind[1] is not None):
1940
2000
                        # Versioned and changed, but not deleted
1941
2001
                        has_changed_files = True
1942
2002
                        break
1951
2011
                        tree_delta.unversioned.extend((unknown_file,))
1952
2012
                raise errors.BzrRemoveChangedFilesError(tree_delta)
1953
2013
 
1954
 
        # Build inv_delta and delete files where applicaple,
 
2014
        # Build inv_delta and delete files where applicable,
1955
2015
        # do this before any modifications to inventory.
1956
2016
        for f in files:
1957
2017
            fid = self.path2id(f)
1965
2025
                        new_status = 'I'
1966
2026
                    else:
1967
2027
                        new_status = '?'
1968
 
                    textui.show_status(new_status, self.kind(fid), f,
1969
 
                                       to_file=to_file)
 
2028
                    # XXX: Really should be a more abstract reporter interface
 
2029
                    kind_ch = osutils.kind_marker(self.kind(fid))
 
2030
                    to_file.write(new_status + '       ' + f + kind_ch + '\n')
1970
2031
                # Unversion file
1971
2032
                inv_delta.append((f, None, fid, None))
1972
2033
                message = "removed %s" % (f,)
1995
2056
 
1996
2057
    @needs_tree_write_lock
1997
2058
    def revert(self, filenames=None, old_tree=None, backups=True,
1998
 
               pb=DummyProgress(), report_changes=False):
 
2059
               pb=None, report_changes=False):
1999
2060
        from bzrlib.conflicts import resolve
2000
2061
        if filenames == []:
2001
2062
            filenames = None
2014
2075
            if filenames is None and len(self.get_parent_ids()) > 1:
2015
2076
                parent_trees = []
2016
2077
                last_revision = self.last_revision()
2017
 
                if last_revision != NULL_REVISION:
 
2078
                if last_revision != _mod_revision.NULL_REVISION:
2018
2079
                    if basis_tree is None:
2019
2080
                        basis_tree = self.basis_tree()
2020
2081
                        basis_tree.lock_read()
2058
2119
    def set_inventory(self, new_inventory_list):
2059
2120
        from bzrlib.inventory import (Inventory,
2060
2121
                                      InventoryDirectory,
2061
 
                                      InventoryEntry,
2062
2122
                                      InventoryFile,
2063
2123
                                      InventoryLink)
2064
2124
        inv = Inventory(self.get_root_id())
2066
2126
            name = os.path.basename(path)
2067
2127
            if name == "":
2068
2128
                continue
2069
 
            # fixme, there should be a factory function inv,add_?? 
 
2129
            # fixme, there should be a factory function inv,add_??
2070
2130
            if kind == 'directory':
2071
2131
                inv.add(InventoryDirectory(file_id, name, parent))
2072
2132
            elif kind == 'file':
2080
2140
    @needs_tree_write_lock
2081
2141
    def set_root_id(self, file_id):
2082
2142
        """Set the root id for this tree."""
2083
 
        # for compatability 
 
2143
        # for compatability
2084
2144
        if file_id is None:
2085
 
            symbol_versioning.warn(symbol_versioning.zero_twelve
2086
 
                % 'WorkingTree.set_root_id with fileid=None',
2087
 
                DeprecationWarning,
2088
 
                stacklevel=3)
2089
 
            file_id = ROOT_ID
2090
 
        else:
2091
 
            file_id = osutils.safe_file_id(file_id)
 
2145
            raise ValueError(
 
2146
                'WorkingTree.set_root_id with fileid=None')
 
2147
        file_id = osutils.safe_file_id(file_id)
2092
2148
        self._set_root_id(file_id)
2093
2149
 
2094
2150
    def _set_root_id(self, file_id):
2095
2151
        """Set the root id for this tree, in a format specific manner.
2096
2152
 
2097
 
        :param file_id: The file id to assign to the root. It must not be 
 
2153
        :param file_id: The file id to assign to the root. It must not be
2098
2154
            present in the current inventory or an error will occur. It must
2099
2155
            not be None, but rather a valid file id.
2100
2156
        """
2119
2175
 
2120
2176
    def unlock(self):
2121
2177
        """See Branch.unlock.
2122
 
        
 
2178
 
2123
2179
        WorkingTree locking just uses the Branch locking facilities.
2124
2180
        This is current because all working trees have an embedded branch
2125
2181
        within them. IF in the future, we were to make branch data shareable
2126
 
        between multiple working trees, i.e. via shared storage, then we 
 
2182
        between multiple working trees, i.e. via shared storage, then we
2127
2183
        would probably want to lock both the local tree, and the branch.
2128
2184
        """
2129
2185
        raise NotImplementedError(self.unlock)
2130
2186
 
2131
 
    def update(self, change_reporter=None, possible_transports=None):
 
2187
    _marker = object()
 
2188
 
 
2189
    def update(self, change_reporter=None, possible_transports=None,
 
2190
               revision=None, old_tip=_marker):
2132
2191
        """Update a working tree along its branch.
2133
2192
 
2134
2193
        This will update the branch if its bound too, which means we have
2152
2211
        - Merge current state -> basis tree of the master w.r.t. the old tree
2153
2212
          basis.
2154
2213
        - Do a 'normal' merge of the old branch basis if it is relevant.
 
2214
 
 
2215
        :param revision: The target revision to update to. Must be in the
 
2216
            revision history.
 
2217
        :param old_tip: If branch.update() has already been run, the value it
 
2218
            returned (old tip of the branch or None). _marker is used
 
2219
            otherwise.
2155
2220
        """
2156
 
        if self.branch.get_master_branch(possible_transports) is not None:
 
2221
        if self.branch.get_bound_location() is not None:
2157
2222
            self.lock_write()
2158
 
            update_branch = True
 
2223
            update_branch = (old_tip is self._marker)
2159
2224
        else:
2160
2225
            self.lock_tree_write()
2161
2226
            update_branch = False
2163
2228
            if update_branch:
2164
2229
                old_tip = self.branch.update(possible_transports)
2165
2230
            else:
2166
 
                old_tip = None
2167
 
            return self._update_tree(old_tip, change_reporter)
 
2231
                if old_tip is self._marker:
 
2232
                    old_tip = None
 
2233
            return self._update_tree(old_tip, change_reporter, revision)
2168
2234
        finally:
2169
2235
            self.unlock()
2170
2236
 
2171
2237
    @needs_tree_write_lock
2172
 
    def _update_tree(self, old_tip=None, change_reporter=None):
 
2238
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None):
2173
2239
        """Update a tree to the master branch.
2174
2240
 
2175
2241
        :param old_tip: if supplied, the previous tip revision the branch,
2181
2247
        # cant set that until we update the working trees last revision to be
2182
2248
        # one from the new branch, because it will just get absorbed by the
2183
2249
        # parent de-duplication logic.
2184
 
        # 
 
2250
        #
2185
2251
        # We MUST save it even if an error occurs, because otherwise the users
2186
2252
        # local work is unreferenced and will appear to have been lost.
2187
 
        # 
2188
 
        result = 0
 
2253
        #
 
2254
        nb_conflicts = 0
2189
2255
        try:
2190
2256
            last_rev = self.get_parent_ids()[0]
2191
2257
        except IndexError:
2192
2258
            last_rev = _mod_revision.NULL_REVISION
2193
 
        if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
2194
 
            # merge tree state up to new branch tip.
 
2259
        if revision is None:
 
2260
            revision = self.branch.last_revision()
 
2261
        else:
 
2262
            if revision not in self.branch.revision_history():
 
2263
                raise errors.NoSuchRevision(self.branch, revision)
 
2264
 
 
2265
        old_tip = old_tip or _mod_revision.NULL_REVISION
 
2266
 
 
2267
        if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
 
2268
            # the branch we are bound to was updated
 
2269
            # merge those changes in first
 
2270
            base_tree  = self.basis_tree()
 
2271
            other_tree = self.branch.repository.revision_tree(old_tip)
 
2272
            nb_conflicts = merge.merge_inner(self.branch, other_tree,
 
2273
                                             base_tree, this_tree=self,
 
2274
                                             change_reporter=change_reporter)
 
2275
            if nb_conflicts:
 
2276
                self.add_parent_tree((old_tip, other_tree))
 
2277
                trace.note('Rerun update after fixing the conflicts.')
 
2278
                return nb_conflicts
 
2279
 
 
2280
        if last_rev != _mod_revision.ensure_null(revision):
 
2281
            # the working tree is up to date with the branch
 
2282
            # we can merge the specified revision from master
 
2283
            to_tree = self.branch.repository.revision_tree(revision)
 
2284
            to_root_id = to_tree.get_root_id()
 
2285
 
2195
2286
            basis = self.basis_tree()
2196
2287
            basis.lock_read()
2197
2288
            try:
2198
 
                to_tree = self.branch.basis_tree()
2199
 
                if basis.inventory.root is None:
2200
 
                    self.set_root_id(to_tree.get_root_id())
 
2289
                if (basis.inventory.root is None
 
2290
                    or basis.inventory.root.file_id != to_root_id):
 
2291
                    self.set_root_id(to_root_id)
2201
2292
                    self.flush()
2202
 
                result += merge.merge_inner(
2203
 
                                      self.branch,
2204
 
                                      to_tree,
2205
 
                                      basis,
2206
 
                                      this_tree=self,
2207
 
                                      change_reporter=change_reporter)
2208
2293
            finally:
2209
2294
                basis.unlock()
 
2295
 
 
2296
            # determine the branch point
 
2297
            graph = self.branch.repository.get_graph()
 
2298
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
 
2299
                                                last_rev)
 
2300
            base_tree = self.branch.repository.revision_tree(base_rev_id)
 
2301
 
 
2302
            nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
 
2303
                                             this_tree=self,
 
2304
                                             change_reporter=change_reporter)
 
2305
            self.set_last_revision(revision)
2210
2306
            # TODO - dedup parents list with things merged by pull ?
2211
2307
            # reuse the tree we've updated to to set the basis:
2212
 
            parent_trees = [(self.branch.last_revision(), to_tree)]
 
2308
            parent_trees = [(revision, to_tree)]
2213
2309
            merges = self.get_parent_ids()[1:]
2214
2310
            # Ideally we ask the tree for the trees here, that way the working
2215
 
            # tree can decide whether to give us teh entire tree or give us a
 
2311
            # tree can decide whether to give us the entire tree or give us a
2216
2312
            # lazy initialised tree. dirstate for instance will have the trees
2217
2313
            # in ram already, whereas a last-revision + basis-inventory tree
2218
2314
            # will not, but also does not need them when setting parents.
2219
2315
            for parent in merges:
2220
2316
                parent_trees.append(
2221
2317
                    (parent, self.branch.repository.revision_tree(parent)))
2222
 
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
 
2318
            if not _mod_revision.is_null(old_tip):
2223
2319
                parent_trees.append(
2224
2320
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
2225
2321
            self.set_parent_trees(parent_trees)
2226
2322
            last_rev = parent_trees[0][0]
2227
 
        else:
2228
 
            # the working tree had the same last-revision as the master
2229
 
            # branch did. We may still have pivot local work from the local
2230
 
            # branch into old_tip:
2231
 
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2232
 
                self.add_parent_tree_id(old_tip)
2233
 
        if (old_tip is not None and not _mod_revision.is_null(old_tip)
2234
 
            and old_tip != last_rev):
2235
 
            # our last revision was not the prior branch last revision
2236
 
            # and we have converted that last revision to a pending merge.
2237
 
            # base is somewhere between the branch tip now
2238
 
            # and the now pending merge
2239
 
 
2240
 
            # Since we just modified the working tree and inventory, flush out
2241
 
            # the current state, before we modify it again.
2242
 
            # TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2243
 
            #       requires it only because TreeTransform directly munges the
2244
 
            #       inventory and calls tree._write_inventory(). Ultimately we
2245
 
            #       should be able to remove this extra flush.
2246
 
            self.flush()
2247
 
            graph = self.branch.repository.get_graph()
2248
 
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2249
 
                                                old_tip)
2250
 
            base_tree = self.branch.repository.revision_tree(base_rev_id)
2251
 
            other_tree = self.branch.repository.revision_tree(old_tip)
2252
 
            result += merge.merge_inner(
2253
 
                                  self.branch,
2254
 
                                  other_tree,
2255
 
                                  base_tree,
2256
 
                                  this_tree=self,
2257
 
                                  change_reporter=change_reporter)
2258
 
        return result
 
2323
        return nb_conflicts
2259
2324
 
2260
2325
    def _write_hashcache_if_dirty(self):
2261
2326
        """Write out the hashcache if it is dirty."""
2360
2425
                    # value.
2361
2426
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
2362
2427
                        ('.bzr', '.bzr'))
2363
 
                    if cur_disk_dir_content[bzrdir_loc][0] == '.bzr':
 
2428
                    if (bzrdir_loc < len(cur_disk_dir_content)
 
2429
                        and self.bzrdir.is_control_filename(
 
2430
                            cur_disk_dir_content[bzrdir_loc][0])):
2364
2431
                        # we dont yield the contents of, or, .bzr itself.
2365
2432
                        del cur_disk_dir_content[bzrdir_loc]
2366
2433
            if inv_finished:
2456
2523
                relroot = ""
2457
2524
            # FIXME: stash the node in pending
2458
2525
            entry = inv[top_id]
2459
 
            for name, child in entry.sorted_children():
2460
 
                dirblock.append((relroot + name, name, child.kind, None,
2461
 
                    child.file_id, child.kind
2462
 
                    ))
 
2526
            if entry.kind == 'directory':
 
2527
                for name, child in entry.sorted_children():
 
2528
                    dirblock.append((relroot + name, name, child.kind, None,
 
2529
                        child.file_id, child.kind
 
2530
                        ))
2463
2531
            yield (currentdir[0], entry.file_id), dirblock
2464
2532
            # push the user specified dirs from dirblock
2465
2533
            for dir in reversed(dirblock):
2498
2566
        self.set_conflicts(un_resolved)
2499
2567
        return un_resolved, resolved
2500
2568
 
 
2569
    @needs_read_lock
 
2570
    def _check(self, references):
 
2571
        """Check the tree for consistency.
 
2572
 
 
2573
        :param references: A dict with keys matching the items returned by
 
2574
            self._get_check_refs(), and values from looking those keys up in
 
2575
            the repository.
 
2576
        """
 
2577
        tree_basis = self.basis_tree()
 
2578
        tree_basis.lock_read()
 
2579
        try:
 
2580
            repo_basis = references[('trees', self.last_revision())]
 
2581
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
 
2582
                raise errors.BzrCheckError(
 
2583
                    "Mismatched basis inventory content.")
 
2584
            self._validate()
 
2585
        finally:
 
2586
            tree_basis.unlock()
 
2587
 
2501
2588
    def _validate(self):
2502
2589
        """Validate internal structures.
2503
2590
 
2509
2596
        """
2510
2597
        return
2511
2598
 
 
2599
    def _get_rules_searcher(self, default_searcher):
 
2600
        """See Tree._get_rules_searcher."""
 
2601
        if self._rules_searcher is None:
 
2602
            self._rules_searcher = super(WorkingTree,
 
2603
                self)._get_rules_searcher(default_searcher)
 
2604
        return self._rules_searcher
 
2605
 
 
2606
    def get_shelf_manager(self):
 
2607
        """Return the ShelfManager for this WorkingTree."""
 
2608
        from bzrlib.shelf import ShelfManager
 
2609
        return ShelfManager(self, self._transport)
 
2610
 
2512
2611
 
2513
2612
class WorkingTree2(WorkingTree):
2514
2613
    """This is the Format 2 working tree.
2515
2614
 
2516
 
    This was the first weave based working tree. 
 
2615
    This was the first weave based working tree.
2517
2616
     - uses os locks for locking.
2518
2617
     - uses the branch last-revision.
2519
2618
    """
2529
2628
        if self._inventory is None:
2530
2629
            self.read_working_inventory()
2531
2630
 
 
2631
    def _get_check_refs(self):
 
2632
        """Return the references needed to perform a check of this tree."""
 
2633
        return [('trees', self.last_revision())]
 
2634
 
2532
2635
    def lock_tree_write(self):
2533
2636
        """See WorkingTree.lock_tree_write().
2534
2637
 
2552
2655
            if self._inventory_is_modified:
2553
2656
                self.flush()
2554
2657
            self._write_hashcache_if_dirty()
2555
 
                    
 
2658
 
2556
2659
        # reverse order of locking.
2557
2660
        try:
2558
2661
            return self._control_files.unlock()
2574
2677
    def _last_revision(self):
2575
2678
        """See Mutable.last_revision."""
2576
2679
        try:
2577
 
            return self._control_files.get('last-revision').read()
 
2680
            return self._transport.get_bytes('last-revision')
2578
2681
        except errors.NoSuchFile:
2579
2682
            return _mod_revision.NULL_REVISION
2580
2683
 
2581
2684
    def _change_last_revision(self, revision_id):
2582
2685
        """See WorkingTree._change_last_revision."""
2583
 
        if revision_id is None or revision_id == NULL_REVISION:
 
2686
        if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
2584
2687
            try:
2585
 
                self._control_files._transport.delete('last-revision')
 
2688
                self._transport.delete('last-revision')
2586
2689
            except errors.NoSuchFile:
2587
2690
                pass
2588
2691
            return False
2589
2692
        else:
2590
 
            self._control_files.put_bytes('last-revision', revision_id)
 
2693
            self._transport.put_bytes('last-revision', revision_id,
 
2694
                mode=self.bzrdir._get_file_mode())
2591
2695
            return True
2592
2696
 
 
2697
    def _get_check_refs(self):
 
2698
        """Return the references needed to perform a check of this tree."""
 
2699
        return [('trees', self.last_revision())]
 
2700
 
2593
2701
    @needs_tree_write_lock
2594
2702
    def set_conflicts(self, conflicts):
2595
 
        self._put_rio('conflicts', conflicts.to_stanzas(), 
 
2703
        self._put_rio('conflicts', conflicts.to_stanzas(),
2596
2704
                      CONFLICT_HEADER_1)
2597
2705
 
2598
2706
    @needs_tree_write_lock
2605
2713
    @needs_read_lock
2606
2714
    def conflicts(self):
2607
2715
        try:
2608
 
            confile = self._control_files.get('conflicts')
 
2716
            confile = self._transport.get('conflicts')
2609
2717
        except errors.NoSuchFile:
2610
2718
            return _mod_conflicts.ConflictList()
2611
2719
        try:
2612
 
            if confile.next() != CONFLICT_HEADER_1 + '\n':
 
2720
            try:
 
2721
                if confile.next() != CONFLICT_HEADER_1 + '\n':
 
2722
                    raise errors.ConflictFormatError()
 
2723
            except StopIteration:
2613
2724
                raise errors.ConflictFormatError()
2614
 
        except StopIteration:
2615
 
            raise errors.ConflictFormatError()
2616
 
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
 
2725
            return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
 
2726
        finally:
 
2727
            confile.close()
2617
2728
 
2618
2729
    def unlock(self):
2619
2730
        # do non-implementation specific cleanup
2636
2747
            return path[:-len(suffix)]
2637
2748
 
2638
2749
 
2639
 
@deprecated_function(zero_eight)
2640
 
def is_control_file(filename):
2641
 
    """See WorkingTree.is_control_filename(filename)."""
2642
 
    ## FIXME: better check
2643
 
    filename = normpath(filename)
2644
 
    while filename != '':
2645
 
        head, tail = os.path.split(filename)
2646
 
        ## mutter('check %r for control file' % ((head, tail),))
2647
 
        if tail == '.bzr':
2648
 
            return True
2649
 
        if filename == head:
2650
 
            break
2651
 
        filename = head
2652
 
    return False
2653
 
 
2654
 
 
2655
2750
class WorkingTreeFormat(object):
2656
2751
    """An encapsulation of the initialization and open routines for a format.
2657
2752
 
2660
2755
     * a format string,
2661
2756
     * an open routine.
2662
2757
 
2663
 
    Formats are placed in an dict by their format string for reference 
 
2758
    Formats are placed in an dict by their format string for reference
2664
2759
    during workingtree opening. Its not required that these be instances, they
2665
 
    can be classes themselves with class methods - it simply depends on 
 
2760
    can be classes themselves with class methods - it simply depends on
2666
2761
    whether state is needed for a given format or not.
2667
2762
 
2668
2763
    Once a format is deprecated, just deprecate the initialize and open
2669
 
    methods on the format class. Do not deprecate the object, as the 
 
2764
    methods on the format class. Do not deprecate the object, as the
2670
2765
    object will be created every time regardless.
2671
2766
    """
2672
2767
 
2685
2780
        """Return the format for the working tree object in a_bzrdir."""
2686
2781
        try:
2687
2782
            transport = a_bzrdir.get_workingtree_transport(None)
2688
 
            format_string = transport.get("format").read()
 
2783
            format_string = transport.get_bytes("format")
2689
2784
            return klass._formats[format_string]
2690
2785
        except errors.NoSuchFile:
2691
2786
            raise errors.NoWorkingTree(base=transport.base)
2692
2787
        except KeyError:
2693
 
            raise errors.UnknownFormatError(format=format_string)
 
2788
            raise errors.UnknownFormatError(format=format_string,
 
2789
                                            kind="working tree")
2694
2790
 
2695
2791
    def __eq__(self, other):
2696
2792
        return self.__class__ is other.__class__
2715
2811
        """Is this format supported?
2716
2812
 
2717
2813
        Supported formats can be initialized and opened.
2718
 
        Unsupported formats may not support initialization or committing or 
 
2814
        Unsupported formats may not support initialization or committing or
2719
2815
        some other features depending on the reason for not being supported.
2720
2816
        """
2721
2817
        return True
2722
2818
 
 
2819
    def supports_content_filtering(self):
 
2820
        """True if this format supports content filtering."""
 
2821
        return False
 
2822
 
 
2823
    def supports_views(self):
 
2824
        """True if this format supports stored views."""
 
2825
        return False
 
2826
 
2723
2827
    @classmethod
2724
2828
    def register_format(klass, format):
2725
2829
        klass._formats[format.get_format_string()] = format
2730
2834
 
2731
2835
    @classmethod
2732
2836
    def unregister_format(klass, format):
2733
 
        assert klass._formats[format.get_format_string()] is format
2734
2837
        del klass._formats[format.get_format_string()]
2735
2838
 
2736
2839
 
2737
2840
class WorkingTreeFormat2(WorkingTreeFormat):
2738
 
    """The second working tree format. 
 
2841
    """The second working tree format.
2739
2842
 
2740
2843
    This format modified the hash cache from the format 1 hash cache.
2741
2844
    """
2746
2849
        """See WorkingTreeFormat.get_format_description()."""
2747
2850
        return "Working tree format 2"
2748
2851
 
2749
 
    def stub_initialize_remote(self, control_files):
2750
 
        """As a special workaround create critical control files for a remote working tree
2751
 
        
 
2852
    def _stub_initialize_on_transport(self, transport, file_mode):
 
2853
        """Workaround: create control files for a remote working tree.
 
2854
 
2752
2855
        This ensures that it can later be updated and dealt with locally,
2753
 
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with 
 
2856
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2754
2857
        no working tree.  (See bug #43064).
2755
2858
        """
2756
2859
        sio = StringIO()
2757
 
        inv = Inventory()
 
2860
        inv = inventory.Inventory()
2758
2861
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
2759
2862
        sio.seek(0)
2760
 
        control_files.put('inventory', sio)
2761
 
 
2762
 
        control_files.put_bytes('pending-merges', '')
2763
 
        
 
2863
        transport.put_file('inventory', sio, file_mode)
 
2864
        transport.put_bytes('pending-merges', '', file_mode)
2764
2865
 
2765
2866
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2766
 
                   accelerator_tree=None):
 
2867
                   accelerator_tree=None, hardlink=False):
2767
2868
        """See WorkingTreeFormat.initialize()."""
2768
2869
        if not isinstance(a_bzrdir.transport, LocalTransport):
2769
2870
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2778
2879
            branch.generate_revision_history(revision_id)
2779
2880
        finally:
2780
2881
            branch.unlock()
2781
 
        inv = Inventory()
 
2882
        inv = inventory.Inventory()
2782
2883
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2783
2884
                         branch,
2784
2885
                         inv,
2829
2930
        - is new in bzr 0.8
2830
2931
        - uses a LockDir to guard access for writes.
2831
2932
    """
2832
 
    
 
2933
 
2833
2934
    upgrade_recommended = True
2834
2935
 
2835
2936
    def get_format_string(self):
2852
2953
 
2853
2954
    def _open_control_files(self, a_bzrdir):
2854
2955
        transport = a_bzrdir.get_workingtree_transport(None)
2855
 
        return LockableFiles(transport, self._lock_file_name, 
 
2956
        return LockableFiles(transport, self._lock_file_name,
2856
2957
                             self._lock_class)
2857
2958
 
2858
2959
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2859
 
                   accelerator_tree=None):
 
2960
                   accelerator_tree=None, hardlink=False):
2860
2961
        """See WorkingTreeFormat.initialize().
2861
 
        
 
2962
 
2862
2963
        :param revision_id: if supplied, create a working tree at a different
2863
2964
            revision than the branch is at.
2864
2965
        :param accelerator_tree: A tree which can be used for retrieving file
2865
2966
            contents more quickly than the revision tree, i.e. a workingtree.
2866
2967
            The revision tree will be used for cases where accelerator_tree's
2867
2968
            content is different.
 
2969
        :param hardlink: If true, hard-link files from accelerator_tree,
 
2970
            where possible.
2868
2971
        """
2869
2972
        if not isinstance(a_bzrdir.transport, LocalTransport):
2870
2973
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2872
2975
        control_files = self._open_control_files(a_bzrdir)
2873
2976
        control_files.create_lock()
2874
2977
        control_files.lock_write()
2875
 
        control_files.put_utf8('format', self.get_format_string())
 
2978
        transport.put_bytes('format', self.get_format_string(),
 
2979
            mode=a_bzrdir._get_file_mode())
2876
2980
        if from_branch is not None:
2877
2981
            branch = from_branch
2878
2982
        else:
2898
3002
            # only set an explicit root id if there is one to set.
2899
3003
            if basis_tree.inventory.root is not None:
2900
3004
                wt.set_root_id(basis_tree.get_root_id())
2901
 
            if revision_id == NULL_REVISION:
 
3005
            if revision_id == _mod_revision.NULL_REVISION:
2902
3006
                wt.set_parent_trees([])
2903
3007
            else:
2904
3008
                wt.set_parent_trees([(revision_id, basis_tree)])
2911
3015
        return wt
2912
3016
 
2913
3017
    def _initial_inventory(self):
2914
 
        return Inventory()
 
3018
        return inventory.Inventory()
2915
3019
 
2916
3020
    def __init__(self):
2917
3021
        super(WorkingTreeFormat3, self).__init__()
2932
3036
 
2933
3037
    def _open(self, a_bzrdir, control_files):
2934
3038
        """Open the tree itself.
2935
 
        
 
3039
 
2936
3040
        :param a_bzrdir: the dir for the tree.
2937
3041
        :param control_files: the control files for the tree.
2938
3042
        """
2946
3050
        return self.get_format_string()
2947
3051
 
2948
3052
 
2949
 
__default_format = WorkingTreeFormat4()
 
3053
__default_format = WorkingTreeFormat6()
2950
3054
WorkingTreeFormat.register_format(__default_format)
 
3055
WorkingTreeFormat.register_format(WorkingTreeFormat5())
 
3056
WorkingTreeFormat.register_format(WorkingTreeFormat4())
2951
3057
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2952
3058
WorkingTreeFormat.set_default_format(__default_format)
2953
3059
# formats which have no format string are not discoverable