~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: wang
  • Date: 2006-10-29 13:41:32 UTC
  • mto: (2104.4.1 wang_65714)
  • mto: This revision was merged to the branch mainline in revision 2109.
  • Revision ID: wang@ubuntu-20061029134132-3d7f4216f20c4aef
Replace python's difflib by patiencediff because the worst case 
performance is cubic for difflib and people commiting large data 
files are often hurt by this. The worst case performance of patience is 
quadratic. Fix bug 65714.

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
WorkingTree.open(dir).
30
30
"""
31
31
 
32
 
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
33
 
CONFLICT_HEADER_1 = "BZR conflict list format 1"
34
 
 
35
32
# TODO: Give the workingtree sole responsibility for the working inventory;
36
33
# remove the variable and references to it from the branch.  This may require
37
34
# updating the commit code so as to update the inventory within the working
39
36
# At the moment they may alias the inventory and have old copies of it in
40
37
# memory.  (Now done? -- mbp 20060309)
41
38
 
42
 
from binascii import hexlify
 
39
from cStringIO import StringIO
 
40
import os
 
41
import re
 
42
 
 
43
from bzrlib.lazy_import import lazy_import
 
44
lazy_import(globals(), """
43
45
import collections
44
46
from copy import deepcopy
45
 
from cStringIO import StringIO
46
47
import errno
47
48
import fnmatch
48
 
import os
49
 
import re
50
49
import stat
51
50
from time import time
52
51
import warnings
53
52
 
54
53
import bzrlib
55
 
from bzrlib import bzrdir, errors, ignores, osutils, urlutils
56
 
from bzrlib.atomicfile import AtomicFile
 
54
from bzrlib import (
 
55
    bzrdir,
 
56
    conflicts as _mod_conflicts,
 
57
    errors,
 
58
    ignores,
 
59
    merge,
 
60
    osutils,
 
61
    textui,
 
62
    transform,
 
63
    urlutils,
 
64
    xml5,
 
65
    xml6,
 
66
    )
57
67
import bzrlib.branch
58
 
from bzrlib.conflicts import Conflict, ConflictList, CONFLICT_SUFFIXES
 
68
from bzrlib.transport import get_transport
 
69
import bzrlib.ui
 
70
""")
 
71
 
 
72
from bzrlib import symbol_versioning
59
73
from bzrlib.decorators import needs_read_lock, needs_write_lock
60
74
from bzrlib.errors import (BzrCheckError,
61
75
                           BzrError,
67
81
                           MergeModifiedFormatError,
68
82
                           UnsupportedOperation,
69
83
                           )
70
 
from bzrlib.inventory import InventoryEntry, Inventory
 
84
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
71
85
from bzrlib.lockable_files import LockableFiles, TransportLock
72
86
from bzrlib.lockdir import LockDir
73
 
from bzrlib.merge import merge_inner, transform_tree
 
87
import bzrlib.mutabletree
 
88
from bzrlib.mutabletree import needs_tree_write_lock
74
89
from bzrlib.osutils import (
75
 
                            abspath,
76
 
                            compact_date,
77
 
                            file_kind,
78
 
                            isdir,
79
 
                            getcwd,
80
 
                            pathjoin,
81
 
                            pumpfile,
82
 
                            safe_unicode,
83
 
                            splitpath,
84
 
                            rand_chars,
85
 
                            normpath,
86
 
                            realpath,
87
 
                            relpath,
88
 
                            rename,
89
 
                            supports_executable,
90
 
                            )
 
90
    compact_date,
 
91
    file_kind,
 
92
    isdir,
 
93
    pathjoin,
 
94
    safe_unicode,
 
95
    splitpath,
 
96
    rand_chars,
 
97
    normpath,
 
98
    realpath,
 
99
    supports_executable,
 
100
    )
 
101
from bzrlib.trace import mutter, note
 
102
from bzrlib.transport.local import LocalTransport
 
103
import bzrlib.tree
91
104
from bzrlib.progress import DummyProgress, ProgressPhase
92
105
from bzrlib.revision import NULL_REVISION
 
106
import bzrlib.revisiontree
93
107
from bzrlib.rio import RioReader, rio_file, Stanza
94
108
from bzrlib.symbol_versioning import (deprecated_passed,
95
109
        deprecated_method,
96
110
        deprecated_function,
97
111
        DEPRECATED_PARAMETER,
98
112
        zero_eight,
 
113
        zero_eleven,
99
114
        )
100
 
from bzrlib.trace import mutter, note
101
 
from bzrlib.transform import build_tree
102
 
from bzrlib.transport import get_transport
103
 
from bzrlib.transport.local import LocalTransport
104
 
from bzrlib.textui import show_status
105
 
import bzrlib.tree
106
 
import bzrlib.ui
107
 
import bzrlib.xml5
108
 
 
 
115
 
 
116
 
 
117
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
 
118
CONFLICT_HEADER_1 = "BZR conflict list format 1"
109
119
 
110
120
# the regex removes any weird characters; we don't escape them 
111
121
# but rather just pull them out
155
165
 
156
166
def gen_root_id():
157
167
    """Return a new tree-root file id."""
158
 
    return gen_file_id('TREE_ROOT')
 
168
    return gen_file_id('tree_root')
159
169
 
160
170
 
161
171
class TreeEntry(object):
213
223
        return ''
214
224
 
215
225
 
216
 
class WorkingTree(bzrlib.tree.Tree):
 
226
class WorkingTree(bzrlib.mutabletree.MutableTree):
217
227
    """Working copy tree.
218
228
 
219
229
    The inventory is held in the `Branch` working-inventory, and the
250
260
            self.basedir = wt.basedir
251
261
            self._control_files = wt._control_files
252
262
            self._hashcache = wt._hashcache
253
 
            self._set_inventory(wt._inventory)
 
263
            self._set_inventory(wt._inventory, dirty=False)
254
264
            self._format = wt._format
255
265
            self.bzrdir = wt.bzrdir
256
266
        from bzrlib.hashcache import HashCache
298
308
            hc.write()
299
309
 
300
310
        if _inventory is None:
301
 
            self._set_inventory(self.read_working_inventory())
 
311
            self._inventory_is_modified = False
 
312
            self.read_working_inventory()
302
313
        else:
303
 
            self._set_inventory(_inventory)
 
314
            # the caller of __init__ has provided an inventory,
 
315
            # we assume they know what they are doing - as its only
 
316
            # the Format factory and creation methods that are
 
317
            # permitted to do this.
 
318
            self._set_inventory(_inventory, dirty=False)
304
319
 
305
320
    branch = property(
306
321
        fget=lambda self: self._branch,
321
336
        self._control_files.break_lock()
322
337
        self.branch.break_lock()
323
338
 
324
 
    def _set_inventory(self, inv):
 
339
    def _set_inventory(self, inv, dirty):
 
340
        """Set the internal cached inventory.
 
341
 
 
342
        :param inv: The inventory to set.
 
343
        :param dirty: A boolean indicating whether the inventory is the same
 
344
            logical inventory as whats on disk. If True the inventory is not
 
345
            the same and should be written to disk or data will be lost, if
 
346
            False then the inventory is the same as that on disk and any
 
347
            serialisation would be unneeded overhead.
 
348
        """
 
349
        assert inv.root is not None
325
350
        self._inventory = inv
326
 
        self.path2id = self._inventory.path2id
327
 
 
328
 
    def is_control_filename(self, filename):
329
 
        """True if filename is the name of a control file in this tree.
330
 
        
331
 
        :param filename: A filename within the tree. This is a relative path
332
 
        from the root of this tree.
333
 
 
334
 
        This is true IF and ONLY IF the filename is part of the meta data
335
 
        that bzr controls in this tree. I.E. a random .bzr directory placed
336
 
        on disk will not be a control file for this tree.
337
 
        """
338
 
        return self.bzrdir.is_control_filename(filename)
 
351
        self._inventory_is_modified = dirty
339
352
 
340
353
    @staticmethod
341
354
    def open(path=None, _unsupported=False):
393
406
        return pathjoin(self.basedir, filename)
394
407
    
395
408
    def basis_tree(self):
396
 
        """Return RevisionTree for the current last revision."""
397
 
        revision_id = self.last_revision()
398
 
        if revision_id is not None:
 
409
        """Return RevisionTree for the current last revision.
 
410
        
 
411
        If the left most parent is a ghost then the returned tree will be an
 
412
        empty tree - one obtained by calling repository.revision_tree(None).
 
413
        """
 
414
        try:
 
415
            revision_id = self.get_parent_ids()[0]
 
416
        except IndexError:
 
417
            # no parents, return an empty revision tree.
 
418
            # in the future this should return the tree for
 
419
            # 'empty:' - the implicit root empty tree.
 
420
            return self.branch.repository.revision_tree(None)
 
421
        else:
399
422
            try:
400
423
                xml = self.read_basis_inventory()
401
 
                inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
402
 
            except NoSuchFile:
403
 
                inv = None
404
 
            if inv is not None and inv.revision_id == revision_id:
405
 
                return bzrlib.tree.RevisionTree(self.branch.repository, inv,
406
 
                                                revision_id)
407
 
        # FIXME? RBC 20060403 should we cache the inventory here ?
408
 
        return self.branch.repository.revision_tree(revision_id)
 
424
                inv = xml6.serializer_v6.read_inventory_from_string(xml)
 
425
                if inv is not None and inv.revision_id == revision_id:
 
426
                    return bzrlib.revisiontree.RevisionTree(
 
427
                        self.branch.repository, inv, revision_id)
 
428
            except (NoSuchFile, errors.BadInventoryFormat):
 
429
                pass
 
430
        # No cached copy available, retrieve from the repository.
 
431
        # FIXME? RBC 20060403 should we cache the inventory locally
 
432
        # at this point ?
 
433
        try:
 
434
            return self.branch.repository.revision_tree(revision_id)
 
435
        except errors.RevisionNotPresent:
 
436
            # the basis tree *may* be a ghost or a low level error may have
 
437
            # occured. If the revision is present, its a problem, if its not
 
438
            # its a ghost.
 
439
            if self.branch.repository.has_revision(revision_id):
 
440
                raise
 
441
            # the basis tree is a ghost so return an empty tree.
 
442
            return self.branch.repository.revision_tree(None)
409
443
 
410
444
    @staticmethod
411
445
    @deprecated_method(zero_eight)
451
485
        The path may be absolute or relative. If its a relative path it is 
452
486
        interpreted relative to the python current working directory.
453
487
        """
454
 
        return relpath(self.basedir, path)
 
488
        return osutils.relpath(self.basedir, path)
455
489
 
456
490
    def has_filename(self, filename):
457
491
        return osutils.lexists(self.abspath(filename))
471
505
        This implementation reads the pending merges list and last_revision
472
506
        value and uses that to decide what the parents list should be.
473
507
        """
474
 
        last_rev = self.last_revision()
 
508
        last_rev = self._last_revision()
475
509
        if last_rev is None:
476
510
            parents = []
477
511
        else:
478
512
            parents = [last_rev]
479
 
        other_parents = self.pending_merges()
480
 
        return parents + other_parents
 
513
        try:
 
514
            merges_file = self._control_files.get_utf8('pending-merges')
 
515
        except NoSuchFile:
 
516
            pass
 
517
        else:
 
518
            for l in merges_file.readlines():
 
519
                parents.append(l.rstrip('\n'))
 
520
        return parents
481
521
 
 
522
    @needs_read_lock
482
523
    def get_root_id(self):
483
524
        """Return the id of this trees root"""
484
 
        inv = self.read_working_inventory()
485
 
        return inv.root.file_id
 
525
        return self._inventory.root.file_id
486
526
        
487
527
    def _get_store_filename(self, file_id):
488
528
        ## XXX: badly named; this is not in the store at all
514
554
    @needs_read_lock
515
555
    def copy_content_into(self, tree, revision_id=None):
516
556
        """Copy the current content and user files of this tree into tree."""
 
557
        tree.set_root_id(self.get_root_id())
517
558
        if revision_id is None:
518
 
            transform_tree(tree, self)
 
559
            merge.transform_tree(tree, self)
519
560
        else:
520
 
            # TODO now merge from tree.last_revision to revision
521
 
            transform_tree(tree, self)
522
 
            tree.set_last_revision(revision_id)
523
 
 
524
 
    @needs_write_lock
525
 
    def commit(self, message=None, revprops=None, *args, **kwargs):
526
 
        # avoid circular imports
527
 
        from bzrlib.commit import Commit
528
 
        if revprops is None:
529
 
            revprops = {}
530
 
        if not 'branch-nick' in revprops:
531
 
            revprops['branch-nick'] = self.branch.nick
532
 
        # args for wt.commit start at message from the Commit.commit method,
533
 
        # but with branch a kwarg now, passing in args as is results in the
534
 
        #message being used for the branch
535
 
        args = (DEPRECATED_PARAMETER, message, ) + args
536
 
        committed_id = Commit().commit( working_tree=self, revprops=revprops,
537
 
            *args, **kwargs)
538
 
        self._set_inventory(self.read_working_inventory())
539
 
        return committed_id
 
561
            # TODO now merge from tree.last_revision to revision (to preserve
 
562
            # user local changes)
 
563
            merge.transform_tree(tree, self)
 
564
            tree.set_parent_ids([revision_id])
540
565
 
541
566
    def id2abspath(self, file_id):
542
567
        return self.abspath(self.id2path(file_id))
581
606
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
582
607
 
583
608
    @needs_write_lock
584
 
    def add(self, files, ids=None):
585
 
        """Make files versioned.
586
 
 
587
 
        Note that the command line normally calls smart_add instead,
588
 
        which can automatically recurse.
589
 
 
590
 
        This adds the files to the inventory, so that they will be
591
 
        recorded by the next commit.
592
 
 
593
 
        files
594
 
            List of paths to add, relative to the base of the tree.
595
 
 
596
 
        ids
597
 
            If set, use these instead of automatically generated ids.
598
 
            Must be the same length as the list of files, but may
599
 
            contain None for ids that are to be autogenerated.
600
 
 
601
 
        TODO: Perhaps have an option to add the ids even if the files do
602
 
              not (yet) exist.
603
 
 
604
 
        TODO: Perhaps callback with the ids and paths as they're added.
605
 
        """
 
609
    def _add(self, files, ids, kinds):
 
610
        """See MutableTree._add."""
606
611
        # TODO: Re-adding a file that is removed in the working copy
607
612
        # should probably put it back with the previous ID.
608
 
        if isinstance(files, basestring):
609
 
            assert(ids is None or isinstance(ids, basestring))
610
 
            files = [files]
611
 
            if ids is not None:
612
 
                ids = [ids]
613
 
 
614
 
        if ids is None:
615
 
            ids = [None] * len(files)
616
 
        else:
617
 
            assert(len(ids) == len(files))
618
 
 
 
613
        # the read and write working inventory should not occur in this 
 
614
        # function - they should be part of lock_write and unlock.
619
615
        inv = self.read_working_inventory()
620
 
        for f,file_id in zip(files, ids):
621
 
            if self.is_control_filename(f):
622
 
                raise errors.ForbiddenControlFileError(filename=f)
623
 
 
624
 
            fp = splitpath(f)
625
 
 
626
 
            if len(fp) == 0:
627
 
                raise BzrError("cannot add top-level %r" % f)
628
 
 
629
 
            fullpath = normpath(self.abspath(f))
630
 
            try:
631
 
                kind = file_kind(fullpath)
632
 
            except OSError, e:
633
 
                if e.errno == errno.ENOENT:
634
 
                    raise NoSuchFile(fullpath)
635
 
            if not InventoryEntry.versionable_kind(kind):
636
 
                raise errors.BadFileKindError(filename=f, kind=kind)
 
616
        for f, file_id, kind in zip(files, ids, kinds):
 
617
            assert kind is not None
637
618
            if file_id is None:
638
619
                inv.add_path(f, kind=kind)
639
620
            else:
640
621
                inv.add_path(f, kind=kind, file_id=file_id)
641
 
 
642
622
        self._write_inventory(inv)
643
623
 
 
624
    @needs_tree_write_lock
 
625
    def _gather_kinds(self, files, kinds):
 
626
        """See MutableTree._gather_kinds."""
 
627
        for pos, f in enumerate(files):
 
628
            if kinds[pos] is None:
 
629
                fullpath = normpath(self.abspath(f))
 
630
                try:
 
631
                    kinds[pos] = file_kind(fullpath)
 
632
                except OSError, e:
 
633
                    if e.errno == errno.ENOENT:
 
634
                        raise NoSuchFile(fullpath)
 
635
 
644
636
    @needs_write_lock
 
637
    def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
 
638
        """Add revision_id as a parent.
 
639
 
 
640
        This is equivalent to retrieving the current list of parent ids
 
641
        and setting the list to its value plus revision_id.
 
642
 
 
643
        :param revision_id: The revision id to add to the parent list. It may
 
644
        be a ghost revision as long as its not the first parent to be added,
 
645
        or the allow_leftmost_as_ghost parameter is set True.
 
646
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
 
647
        """
 
648
        parents = self.get_parent_ids() + [revision_id]
 
649
        self.set_parent_ids(parents,
 
650
            allow_leftmost_as_ghost=len(parents) > 1 or allow_leftmost_as_ghost)
 
651
 
 
652
    @needs_tree_write_lock
 
653
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
 
654
        """Add revision_id, tree tuple as a parent.
 
655
 
 
656
        This is equivalent to retrieving the current list of parent trees
 
657
        and setting the list to its value plus parent_tuple. See also
 
658
        add_parent_tree_id - if you only have a parent id available it will be
 
659
        simpler to use that api. If you have the parent already available, using
 
660
        this api is preferred.
 
661
 
 
662
        :param parent_tuple: The (revision id, tree) to add to the parent list.
 
663
            If the revision_id is a ghost, pass None for the tree.
 
664
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
 
665
        """
 
666
        parent_ids = self.get_parent_ids() + [parent_tuple[0]]
 
667
        if len(parent_ids) > 1:
 
668
            # the leftmost may have already been a ghost, preserve that if it
 
669
            # was.
 
670
            allow_leftmost_as_ghost = True
 
671
        self.set_parent_ids(parent_ids,
 
672
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
673
 
 
674
    @needs_tree_write_lock
645
675
    def add_pending_merge(self, *revision_ids):
646
676
        # TODO: Perhaps should check at this point that the
647
677
        # history of the revision is actually present?
648
 
        p = self.pending_merges()
 
678
        parents = self.get_parent_ids()
649
679
        updated = False
650
680
        for rev_id in revision_ids:
651
 
            if rev_id in p:
 
681
            if rev_id in parents:
652
682
                continue
653
 
            p.append(rev_id)
 
683
            parents.append(rev_id)
654
684
            updated = True
655
685
        if updated:
656
 
            self.set_pending_merges(p)
 
686
            self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
657
687
 
 
688
    @deprecated_method(zero_eleven)
658
689
    @needs_read_lock
659
690
    def pending_merges(self):
660
691
        """Return a list of pending merges.
661
692
 
662
693
        These are revisions that have been merged into the working
663
694
        directory but not yet committed.
664
 
        """
665
 
        try:
666
 
            merges_file = self._control_files.get_utf8('pending-merges')
667
 
        except NoSuchFile:
668
 
            return []
669
 
        p = []
670
 
        for l in merges_file.readlines():
671
 
            p.append(l.rstrip('\n'))
672
 
        return p
673
 
 
674
 
    @needs_write_lock
 
695
 
 
696
        As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
 
697
        instead - which is available on all tree objects.
 
698
        """
 
699
        return self.get_parent_ids()[1:]
 
700
 
 
701
    def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
 
702
        """Common ghost checking functionality from set_parent_*.
 
703
 
 
704
        This checks that the left hand-parent exists if there are any
 
705
        revisions present.
 
706
        """
 
707
        if len(revision_ids) > 0:
 
708
            leftmost_id = revision_ids[0]
 
709
            if (not allow_leftmost_as_ghost and not
 
710
                self.branch.repository.has_revision(leftmost_id)):
 
711
                raise errors.GhostRevisionUnusableHere(leftmost_id)
 
712
 
 
713
    def _set_merges_from_parent_ids(self, parent_ids):
 
714
        merges = parent_ids[1:]
 
715
        self._control_files.put_utf8('pending-merges', '\n'.join(merges))
 
716
 
 
717
    @needs_tree_write_lock
 
718
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
 
719
        """Set the parent ids to revision_ids.
 
720
        
 
721
        See also set_parent_trees. This api will try to retrieve the tree data
 
722
        for each element of revision_ids from the trees repository. If you have
 
723
        tree data already available, it is more efficient to use
 
724
        set_parent_trees rather than set_parent_ids. set_parent_ids is however
 
725
        an easier API to use.
 
726
 
 
727
        :param revision_ids: The revision_ids to set as the parent ids of this
 
728
            working tree. Any of these may be ghosts.
 
729
        """
 
730
        self._check_parents_for_ghosts(revision_ids,
 
731
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
732
 
 
733
        if len(revision_ids) > 0:
 
734
            self.set_last_revision(revision_ids[0])
 
735
        else:
 
736
            self.set_last_revision(None)
 
737
 
 
738
        self._set_merges_from_parent_ids(revision_ids)
 
739
 
 
740
    @needs_tree_write_lock
 
741
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
 
742
        """See MutableTree.set_parent_trees."""
 
743
        parent_ids = [rev for (rev, tree) in parents_list]
 
744
 
 
745
        self._check_parents_for_ghosts(parent_ids,
 
746
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
747
 
 
748
        if len(parent_ids) == 0:
 
749
            leftmost_parent_id = None
 
750
            leftmost_parent_tree = None
 
751
        else:
 
752
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
 
753
 
 
754
        if self._change_last_revision(leftmost_parent_id):
 
755
            if leftmost_parent_tree is None:
 
756
                # If we don't have a tree, fall back to reading the
 
757
                # parent tree from the repository.
 
758
                self._cache_basis_inventory(leftmost_parent_id)
 
759
            else:
 
760
                inv = leftmost_parent_tree.inventory
 
761
                xml = self._create_basis_xml_from_inventory(
 
762
                                        leftmost_parent_id, inv)
 
763
                self._write_basis_inventory(xml)
 
764
        self._set_merges_from_parent_ids(parent_ids)
 
765
 
 
766
    @needs_tree_write_lock
675
767
    def set_pending_merges(self, rev_list):
676
 
        self._control_files.put_utf8('pending-merges', '\n'.join(rev_list))
 
768
        parents = self.get_parent_ids()
 
769
        leftmost = parents[:1]
 
770
        new_parents = leftmost + rev_list
 
771
        self.set_parent_ids(new_parents)
677
772
 
678
 
    @needs_write_lock
 
773
    @needs_tree_write_lock
679
774
    def set_merge_modified(self, modified_hashes):
680
775
        def iter_stanzas():
681
776
            for file_id, hash in modified_hashes.iteritems():
682
777
                yield Stanza(file_id=file_id, hash=hash)
683
778
        self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
684
779
 
685
 
    @needs_write_lock
 
780
    @needs_tree_write_lock
686
781
    def _put_rio(self, filename, stanzas, header):
687
782
        my_file = rio_file(stanzas, header)
688
783
        self._control_files.put(filename, my_file)
689
784
 
 
785
    @needs_write_lock # because merge pulls data into the branch.
 
786
    def merge_from_branch(self, branch, to_revision=None):
 
787
        """Merge from a branch into this working tree.
 
788
 
 
789
        :param branch: The branch to merge from.
 
790
        :param to_revision: If non-None, the merge will merge to to_revision, but 
 
791
            not beyond it. to_revision does not need to be in the history of
 
792
            the branch when it is supplied. If None, to_revision defaults to
 
793
            branch.last_revision().
 
794
        """
 
795
        from bzrlib.merge import Merger, Merge3Merger
 
796
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
797
        try:
 
798
            merger = Merger(self.branch, this_tree=self, pb=pb)
 
799
            merger.pp = ProgressPhase("Merge phase", 5, pb)
 
800
            merger.pp.next_phase()
 
801
            # check that there are no
 
802
            # local alterations
 
803
            merger.check_basis(check_clean=True, require_commits=False)
 
804
            if to_revision is None:
 
805
                to_revision = branch.last_revision()
 
806
            merger.other_rev_id = to_revision
 
807
            if merger.other_rev_id is None:
 
808
                raise error.NoCommits(branch)
 
809
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
 
810
            merger.other_basis = merger.other_rev_id
 
811
            merger.other_tree = self.branch.repository.revision_tree(
 
812
                merger.other_rev_id)
 
813
            merger.pp.next_phase()
 
814
            merger.find_base()
 
815
            if merger.base_rev_id == merger.other_rev_id:
 
816
                raise errors.PointlessMerge
 
817
            merger.backup_files = False
 
818
            merger.merge_type = Merge3Merger
 
819
            merger.set_interesting_files(None)
 
820
            merger.show_base = False
 
821
            merger.reprocess = False
 
822
            conflicts = merger.do_merge()
 
823
            merger.set_pending()
 
824
        finally:
 
825
            pb.finished()
 
826
        return conflicts
 
827
 
690
828
    @needs_read_lock
691
829
    def merge_modified(self):
692
830
        try:
708
846
                merge_hashes[file_id] = hash
709
847
        return merge_hashes
710
848
 
 
849
    @needs_write_lock
 
850
    def mkdir(self, path, file_id=None):
 
851
        """See MutableTree.mkdir()."""
 
852
        if file_id is None:
 
853
            file_id = gen_file_id(os.path.basename(path))
 
854
        os.mkdir(self.abspath(path))
 
855
        self.add(path, file_id, 'directory')
 
856
        return file_id
 
857
 
711
858
    def get_symlink_target(self, file_id):
712
859
        return os.readlink(self.id2abspath(file_id))
713
860
 
719
866
        else:
720
867
            return '?'
721
868
 
722
 
    def list_files(self):
 
869
    def flush(self):
 
870
        """Write the in memory inventory to disk."""
 
871
        # TODO: Maybe this should only write on dirty ?
 
872
        if self._control_files._lock_mode != 'w':
 
873
            raise errors.NotWriteLocked(self)
 
874
        sio = StringIO()
 
875
        xml5.serializer_v5.write_inventory(self._inventory, sio)
 
876
        sio.seek(0)
 
877
        self._control_files.put('inventory', sio)
 
878
        self._inventory_is_modified = False
 
879
 
 
880
    def list_files(self, include_root=False):
723
881
        """Recursively list all files as (path, class, kind, id, entry).
724
882
 
725
883
        Lists, but does not descend into unversioned directories.
730
888
        Skips the control directory.
731
889
        """
732
890
        inv = self._inventory
 
891
        if include_root is True:
 
892
            yield ('', 'V', 'directory', inv.root.file_id, inv.root)
733
893
        # Convert these into local objects to save lookup times
734
894
        pathjoin = osutils.pathjoin
735
895
        file_kind = osutils.file_kind
828
988
                # if we finished all children, pop it off the stack
829
989
                stack.pop()
830
990
 
831
 
 
832
 
    @needs_write_lock
 
991
    @needs_tree_write_lock
833
992
    def move(self, from_paths, to_name):
834
993
        """Rename files.
835
994
 
854
1013
        if not self.has_filename(to_name):
855
1014
            raise BzrError("destination %r not in working directory" % to_abs)
856
1015
        to_dir_id = inv.path2id(to_name)
857
 
        if to_dir_id == None and to_name != '':
 
1016
        if to_dir_id is None and to_name != '':
858
1017
            raise BzrError("destination %r is not a versioned directory" % to_name)
859
1018
        to_dir_ie = inv[to_dir_id]
860
1019
        if to_dir_ie.kind != 'directory':
866
1025
            if not self.has_filename(f):
867
1026
                raise BzrError("%r does not exist in working tree" % f)
868
1027
            f_id = inv.path2id(f)
869
 
            if f_id == None:
 
1028
            if f_id is None:
870
1029
                raise BzrError("%r is not versioned" % f)
871
1030
            name_tail = splitpath(f)[-1]
872
1031
            dest_path = pathjoin(to_name, name_tail)
879
1038
        # create a file in this interval and then the rename might be
880
1039
        # left half-done.  But we should have caught most problems.
881
1040
        orig_inv = deepcopy(self.inventory)
 
1041
        original_modified = self._inventory_is_modified
882
1042
        try:
 
1043
            if len(from_paths):
 
1044
                self._inventory_is_modified = True
883
1045
            for f in from_paths:
884
1046
                name_tail = splitpath(f)[-1]
885
1047
                dest_path = pathjoin(to_name, name_tail)
886
1048
                result.append((f, dest_path))
887
1049
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
888
1050
                try:
889
 
                    rename(self.abspath(f), self.abspath(dest_path))
 
1051
                    osutils.rename(self.abspath(f), self.abspath(dest_path))
890
1052
                except OSError, e:
891
1053
                    raise BzrError("failed to rename %r to %r: %s" %
892
1054
                                   (f, dest_path, e[1]),
893
1055
                            ["rename rolled back"])
894
1056
        except:
895
1057
            # restore the inventory on error
896
 
            self._set_inventory(orig_inv)
 
1058
            self._set_inventory(orig_inv, dirty=original_modified)
897
1059
            raise
898
1060
        self._write_inventory(inv)
899
1061
        return result
900
1062
 
901
 
    @needs_write_lock
 
1063
    @needs_tree_write_lock
902
1064
    def rename_one(self, from_rel, to_rel):
903
1065
        """Rename one file.
904
1066
 
911
1073
            raise BzrError("can't rename: new working file %r already exists" % to_rel)
912
1074
 
913
1075
        file_id = inv.path2id(from_rel)
914
 
        if file_id == None:
 
1076
        if file_id is None:
915
1077
            raise BzrError("can't rename: old name %r is not versioned" % from_rel)
916
1078
 
917
1079
        entry = inv[file_id]
923
1085
 
924
1086
        to_dir, to_tail = os.path.split(to_rel)
925
1087
        to_dir_id = inv.path2id(to_dir)
926
 
        if to_dir_id == None and to_dir != '':
 
1088
        if to_dir_id is None and to_dir != '':
927
1089
            raise BzrError("can't determine destination directory id for %r" % to_dir)
928
1090
 
929
1091
        mutter("rename_one:")
938
1100
        from_abs = self.abspath(from_rel)
939
1101
        to_abs = self.abspath(to_rel)
940
1102
        try:
941
 
            rename(from_abs, to_abs)
 
1103
            osutils.rename(from_abs, to_abs)
942
1104
        except OSError, e:
943
1105
            inv.rename(file_id, from_parent, from_name)
944
1106
            raise BzrError("failed to rename %r to %r: %s"
956
1118
        for subp in self.extras():
957
1119
            if not self.is_ignored(subp):
958
1120
                yield subp
959
 
 
 
1121
    
 
1122
    @needs_tree_write_lock
 
1123
    def unversion(self, file_ids):
 
1124
        """Remove the file ids in file_ids from the current versioned set.
 
1125
 
 
1126
        When a file_id is unversioned, all of its children are automatically
 
1127
        unversioned.
 
1128
 
 
1129
        :param file_ids: The file ids to stop versioning.
 
1130
        :raises: NoSuchId if any fileid is not currently versioned.
 
1131
        """
 
1132
        for file_id in file_ids:
 
1133
            if self._inventory.has_id(file_id):
 
1134
                self._inventory.remove_recursive_id(file_id)
 
1135
            else:
 
1136
                raise errors.NoSuchId(self, file_id)
 
1137
        if len(file_ids):
 
1138
            # in the future this should just set a dirty bit to wait for the 
 
1139
            # final unlock. However, until all methods of workingtree start
 
1140
            # with the current in -memory inventory rather than triggering 
 
1141
            # a read, it is more complex - we need to teach read_inventory
 
1142
            # to know when to read, and when to not read first... and possibly
 
1143
            # to save first when the in memory one may be corrupted.
 
1144
            # so for now, we just only write it if it is indeed dirty.
 
1145
            # - RBC 20060907
 
1146
            self._write_inventory(self._inventory)
 
1147
    
960
1148
    @deprecated_method(zero_eight)
961
1149
    def iter_conflicts(self):
962
1150
        """List all files in the tree that have text or content conflicts.
994
1182
                repository = self.branch.repository
995
1183
                pb = bzrlib.ui.ui_factory.nested_progress_bar()
996
1184
                try:
997
 
                    merge_inner(self.branch,
998
 
                                self.branch.basis_tree(),
999
 
                                basis_tree, 
1000
 
                                this_tree=self, 
 
1185
                    new_basis_tree = self.branch.basis_tree()
 
1186
                    merge.merge_inner(
 
1187
                                self.branch,
 
1188
                                new_basis_tree,
 
1189
                                basis_tree,
 
1190
                                this_tree=self,
1001
1191
                                pb=pb)
 
1192
                    if (basis_tree.inventory.root is None and
 
1193
                        new_basis_tree.inventory.root is not None):
 
1194
                        self.set_root_id(new_basis_tree.inventory.root.file_id)
1002
1195
                finally:
1003
1196
                    pb.finished()
1004
 
                self.set_last_revision(self.branch.last_revision())
 
1197
                # TODO - dedup parents list with things merged by pull ?
 
1198
                # reuse the revisiontree we merged against to set the new
 
1199
                # tree data.
 
1200
                parent_trees = [(self.branch.last_revision(), new_basis_tree)]
 
1201
                # we have to pull the merge trees out again, because 
 
1202
                # merge_inner has set the ids. - this corner is not yet 
 
1203
                # layered well enough to prevent double handling.
 
1204
                merges = self.get_parent_ids()[1:]
 
1205
                parent_trees.extend([
 
1206
                    (parent, repository.revision_tree(parent)) for
 
1207
                     parent in merges])
 
1208
                self.set_parent_trees(parent_trees)
1005
1209
            return count
1006
1210
        finally:
1007
1211
            source.unlock()
1008
1212
            top_pb.finished()
1009
1213
 
 
1214
    @needs_write_lock
 
1215
    def put_file_bytes_non_atomic(self, file_id, bytes):
 
1216
        """See MutableTree.put_file_bytes_non_atomic."""
 
1217
        stream = file(self.id2abspath(file_id), 'wb')
 
1218
        try:
 
1219
            stream.write(bytes)
 
1220
        finally:
 
1221
            stream.close()
 
1222
        # TODO: update the hashcache here ?
 
1223
 
1010
1224
    def extras(self):
1011
1225
        """Yield all unknown files in this WorkingTree.
1012
1226
 
1100
1314
        """Yield list of PATH, IGNORE_PATTERN"""
1101
1315
        for subp in self.extras():
1102
1316
            pat = self.is_ignored(subp)
1103
 
            if pat != None:
 
1317
            if pat is not None:
1104
1318
                yield subp, pat
1105
1319
 
1106
1320
    def get_ignore_list(self):
1161
1375
        for regex, mapping in rules:
1162
1376
            match = regex.match(filename)
1163
1377
            if match is not None:
1164
 
                # one or more of the groups in mapping will have a non-None group 
1165
 
                # match.
 
1378
                # one or more of the groups in mapping will have a non-None
 
1379
                # group match.
1166
1380
                groups = match.groups()
1167
1381
                rules = [mapping[group] for group in 
1168
1382
                    mapping if groups[group] is not None]
1172
1386
    def kind(self, file_id):
1173
1387
        return file_kind(self.id2abspath(file_id))
1174
1388
 
1175
 
    @needs_read_lock
1176
1389
    def last_revision(self):
1177
 
        """Return the last revision id of this working tree.
1178
 
 
1179
 
        In early branch formats this was == the branch last_revision,
1180
 
        but that cannot be relied upon - for working tree operations,
1181
 
        always use tree.last_revision().
 
1390
        """Return the last revision of the branch for this tree.
 
1391
 
 
1392
        This format tree does not support a separate marker for last-revision
 
1393
        compared to the branch.
 
1394
 
 
1395
        See MutableTree.last_revision
1182
1396
        """
 
1397
        return self._last_revision()
 
1398
 
 
1399
    @needs_read_lock
 
1400
    def _last_revision(self):
 
1401
        """helper for get_parent_ids."""
1183
1402
        return self.branch.last_revision()
1184
1403
 
1185
1404
    def is_locked(self):
1194
1413
            self.branch.unlock()
1195
1414
            raise
1196
1415
 
 
1416
    def lock_tree_write(self):
 
1417
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
1418
        self.branch.lock_read()
 
1419
        try:
 
1420
            return self._control_files.lock_write()
 
1421
        except:
 
1422
            self.branch.unlock()
 
1423
            raise
 
1424
 
1197
1425
    def lock_write(self):
1198
 
        """See Branch.lock_write, and WorkingTree.unlock."""
 
1426
        """See MutableTree.lock_write, and WorkingTree.unlock."""
1199
1427
        self.branch.lock_write()
1200
1428
        try:
1201
1429
            return self._control_files.lock_write()
1207
1435
        return self._control_files.get_physical_lock_status()
1208
1436
 
1209
1437
    def _basis_inventory_name(self):
1210
 
        return 'basis-inventory'
 
1438
        return 'basis-inventory-cache'
1211
1439
 
1212
 
    @needs_write_lock
 
1440
    @needs_tree_write_lock
1213
1441
    def set_last_revision(self, new_revision):
1214
1442
        """Change the last revision in the working tree."""
1215
1443
        if self._change_last_revision(new_revision):
1231
1459
            self.branch.set_revision_history([new_revision])
1232
1460
        return True
1233
1461
 
 
1462
    def _write_basis_inventory(self, xml):
 
1463
        """Write the basis inventory XML to the basis-inventory file"""
 
1464
        assert isinstance(xml, str), 'serialised xml must be bytestring.'
 
1465
        path = self._basis_inventory_name()
 
1466
        sio = StringIO(xml)
 
1467
        self._control_files.put(path, sio)
 
1468
 
 
1469
    def _create_basis_xml_from_inventory(self, revision_id, inventory):
 
1470
        """Create the text that will be saved in basis-inventory"""
 
1471
        inventory.revision_id = revision_id
 
1472
        return xml6.serializer_v6.write_inventory_to_string(inventory)
 
1473
 
1234
1474
    def _cache_basis_inventory(self, new_revision):
1235
1475
        """Cache new_revision as the basis inventory."""
1236
1476
        # TODO: this should allow the ready-to-use inventory to be passed in,
1248
1488
            # root node id can legitimately look like 'revision_id' but cannot
1249
1489
            # contain a '"'.
1250
1490
            xml = self.branch.repository.get_inventory_xml(new_revision)
1251
 
            if not 'revision_id="' in xml.split('\n', 1)[0]:
 
1491
            firstline = xml.split('\n', 1)[0]
 
1492
            if (not 'revision_id="' in firstline or 
 
1493
                'format="6"' not in firstline):
1252
1494
                inv = self.branch.repository.deserialise_inventory(
1253
1495
                    new_revision, xml)
1254
 
                inv.revision_id = new_revision
1255
 
                xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1256
 
            assert isinstance(xml, str), 'serialised xml must be bytestring.'
1257
 
            path = self._basis_inventory_name()
1258
 
            sio = StringIO(xml)
1259
 
            self._control_files.put(path, sio)
 
1496
                xml = self._create_basis_xml_from_inventory(new_revision, inv)
 
1497
            self._write_basis_inventory(xml)
1260
1498
        except (errors.NoSuchRevision, errors.RevisionNotPresent):
1261
1499
            pass
1262
1500
 
1267
1505
        
1268
1506
    @needs_read_lock
1269
1507
    def read_working_inventory(self):
1270
 
        """Read the working inventory."""
 
1508
        """Read the working inventory.
 
1509
        
 
1510
        :raises errors.InventoryModified: read_working_inventory will fail
 
1511
            when the current in memory inventory has been modified.
 
1512
        """
 
1513
        # conceptually this should be an implementation detail of the tree. 
 
1514
        # XXX: Deprecate this.
1271
1515
        # ElementTree does its own conversion from UTF-8, so open in
1272
1516
        # binary.
1273
 
        result = bzrlib.xml5.serializer_v5.read_inventory(
 
1517
        if self._inventory_is_modified:
 
1518
            raise errors.InventoryModified(self)
 
1519
        result = xml5.serializer_v5.read_inventory(
1274
1520
            self._control_files.get('inventory'))
1275
 
        self._set_inventory(result)
 
1521
        self._set_inventory(result, dirty=False)
1276
1522
        return result
1277
1523
 
1278
 
    @needs_write_lock
 
1524
    @needs_tree_write_lock
1279
1525
    def remove(self, files, verbose=False, to_file=None):
1280
1526
        """Remove nominated files from the working inventory..
1281
1527
 
1310
1556
                    new_status = 'I'
1311
1557
                else:
1312
1558
                    new_status = '?'
1313
 
                show_status(new_status, inv[fid].kind, f, to_file=to_file)
 
1559
                textui.show_status(new_status, inv[fid].kind, f,
 
1560
                                   to_file=to_file)
1314
1561
            del inv[fid]
1315
1562
 
1316
1563
        self._write_inventory(inv)
1317
1564
 
1318
 
    @needs_write_lock
 
1565
    @needs_tree_write_lock
1319
1566
    def revert(self, filenames, old_tree=None, backups=True, 
1320
1567
               pb=DummyProgress()):
1321
 
        from transform import revert
1322
 
        from conflicts import resolve
 
1568
        from bzrlib.conflicts import resolve
1323
1569
        if old_tree is None:
1324
1570
            old_tree = self.basis_tree()
1325
 
        conflicts = revert(self, old_tree, filenames, backups, pb)
 
1571
        conflicts = transform.revert(self, old_tree, filenames, backups, pb)
1326
1572
        if not len(filenames):
1327
 
            self.set_pending_merges([])
 
1573
            self.set_parent_ids(self.get_parent_ids()[:1])
1328
1574
            resolve(self)
1329
1575
        else:
1330
1576
            resolve(self, filenames, ignore_misses=True)
1332
1578
 
1333
1579
    # XXX: This method should be deprecated in favour of taking in a proper
1334
1580
    # new Inventory object.
1335
 
    @needs_write_lock
 
1581
    @needs_tree_write_lock
1336
1582
    def set_inventory(self, new_inventory_list):
1337
1583
        from bzrlib.inventory import (Inventory,
1338
1584
                                      InventoryDirectory,
1355
1601
                raise BzrError("unknown kind %r" % kind)
1356
1602
        self._write_inventory(inv)
1357
1603
 
1358
 
    @needs_write_lock
 
1604
    @needs_tree_write_lock
1359
1605
    def set_root_id(self, file_id):
1360
1606
        """Set the root id for this tree."""
1361
 
        inv = self.read_working_inventory()
 
1607
        # for compatability 
 
1608
        if file_id is None:
 
1609
            symbol_versioning.warn(symbol_versioning.zero_twelve
 
1610
                % 'WorkingTree.set_root_id with fileid=None',
 
1611
                DeprecationWarning,
 
1612
                stacklevel=3)
 
1613
            file_id = ROOT_ID
 
1614
        inv = self._inventory
1362
1615
        orig_root_id = inv.root.file_id
 
1616
        # TODO: it might be nice to exit early if there was nothing
 
1617
        # to do, saving us from trigger a sync on unlock.
 
1618
        self._inventory_is_modified = True
 
1619
        # we preserve the root inventory entry object, but
 
1620
        # unlinkit from the byid index
1363
1621
        del inv._byid[inv.root.file_id]
1364
1622
        inv.root.file_id = file_id
 
1623
        # and link it into the index with the new changed id.
1365
1624
        inv._byid[inv.root.file_id] = inv.root
 
1625
        # and finally update all children to reference the new id.
 
1626
        # XXX: this should be safe to just look at the root.children
 
1627
        # list, not the WHOLE INVENTORY.
1366
1628
        for fid in inv:
1367
1629
            entry = inv[fid]
1368
1630
            if entry.parent_id == orig_root_id:
1369
1631
                entry.parent_id = inv.root.file_id
1370
 
        self._write_inventory(inv)
1371
1632
 
1372
1633
    def unlock(self):
1373
1634
        """See Branch.unlock.
1380
1641
        """
1381
1642
        raise NotImplementedError(self.unlock)
1382
1643
 
1383
 
    @needs_write_lock
1384
1644
    def update(self):
1385
1645
        """Update a working tree along its branch.
1386
1646
 
1387
 
        This will update the branch if its bound too, which means we have multiple trees involved:
1388
 
        The new basis tree of the master.
1389
 
        The old basis tree of the branch.
1390
 
        The old basis tree of the working tree.
1391
 
        The current working tree state.
1392
 
        pathologically all three may be different, and non ancestors of each other.
1393
 
        Conceptually we want to:
1394
 
        Preserve the wt.basis->wt.state changes
1395
 
        Transform the wt.basis to the new master basis.
1396
 
        Apply a merge of the old branch basis to get any 'local' changes from it into the tree.
1397
 
        Restore the wt.basis->wt.state changes.
 
1647
        This will update the branch if its bound too, which means we have
 
1648
        multiple trees involved:
 
1649
 
 
1650
        - The new basis tree of the master.
 
1651
        - The old basis tree of the branch.
 
1652
        - The old basis tree of the working tree.
 
1653
        - The current working tree state.
 
1654
 
 
1655
        Pathologically, all three may be different, and non-ancestors of each
 
1656
        other.  Conceptually we want to:
 
1657
 
 
1658
        - Preserve the wt.basis->wt.state changes
 
1659
        - Transform the wt.basis to the new master basis.
 
1660
        - Apply a merge of the old branch basis to get any 'local' changes from
 
1661
          it into the tree.
 
1662
        - Restore the wt.basis->wt.state changes.
1398
1663
 
1399
1664
        There isn't a single operation at the moment to do that, so we:
1400
 
        Merge current state -> basis tree of the master w.r.t. the old tree basis.
1401
 
        Do a 'normal' merge of the old branch basis if it is relevant.
 
1665
        - Merge current state -> basis tree of the master w.r.t. the old tree
 
1666
          basis.
 
1667
        - Do a 'normal' merge of the old branch basis if it is relevant.
1402
1668
        """
1403
 
        old_tip = self.branch.update()
1404
 
        if old_tip is not None:
1405
 
            self.add_pending_merge(old_tip)
1406
 
        self.branch.lock_read()
 
1669
        if self.branch.get_master_branch() is not None:
 
1670
            self.lock_write()
 
1671
            update_branch = True
 
1672
        else:
 
1673
            self.lock_tree_write()
 
1674
            update_branch = False
1407
1675
        try:
1408
 
            result = 0
1409
 
            if self.last_revision() != self.branch.last_revision():
1410
 
                # merge tree state up to new branch tip.
1411
 
                basis = self.basis_tree()
1412
 
                to_tree = self.branch.basis_tree()
1413
 
                result += merge_inner(self.branch,
1414
 
                                      to_tree,
1415
 
                                      basis,
1416
 
                                      this_tree=self)
1417
 
                self.set_last_revision(self.branch.last_revision())
1418
 
            if old_tip and old_tip != self.last_revision():
1419
 
                # our last revision was not the prior branch last revision
1420
 
                # and we have converted that last revision to a pending merge.
1421
 
                # base is somewhere between the branch tip now
1422
 
                # and the now pending merge
1423
 
                from bzrlib.revision import common_ancestor
1424
 
                try:
1425
 
                    base_rev_id = common_ancestor(self.branch.last_revision(),
1426
 
                                                  old_tip,
1427
 
                                                  self.branch.repository)
1428
 
                except errors.NoCommonAncestor:
1429
 
                    base_rev_id = None
1430
 
                base_tree = self.branch.repository.revision_tree(base_rev_id)
1431
 
                other_tree = self.branch.repository.revision_tree(old_tip)
1432
 
                result += merge_inner(self.branch,
1433
 
                                      other_tree,
1434
 
                                      base_tree,
1435
 
                                      this_tree=self)
1436
 
            return result
 
1676
            if update_branch:
 
1677
                old_tip = self.branch.update()
 
1678
            else:
 
1679
                old_tip = None
 
1680
            return self._update_tree(old_tip)
1437
1681
        finally:
1438
 
            self.branch.unlock()
1439
 
 
1440
 
    @needs_write_lock
 
1682
            self.unlock()
 
1683
 
 
1684
    @needs_tree_write_lock
 
1685
    def _update_tree(self, old_tip=None):
 
1686
        """Update a tree to the master branch.
 
1687
 
 
1688
        :param old_tip: if supplied, the previous tip revision the branch,
 
1689
            before it was changed to the master branch's tip.
 
1690
        """
 
1691
        # here if old_tip is not None, it is the old tip of the branch before
 
1692
        # it was updated from the master branch. This should become a pending
 
1693
        # merge in the working tree to preserve the user existing work.  we
 
1694
        # cant set that until we update the working trees last revision to be
 
1695
        # one from the new branch, because it will just get absorbed by the
 
1696
        # parent de-duplication logic.
 
1697
        # 
 
1698
        # We MUST save it even if an error occurs, because otherwise the users
 
1699
        # local work is unreferenced and will appear to have been lost.
 
1700
        # 
 
1701
        result = 0
 
1702
        try:
 
1703
            last_rev = self.get_parent_ids()[0]
 
1704
        except IndexError:
 
1705
            last_rev = None
 
1706
        if last_rev != self.branch.last_revision():
 
1707
            # merge tree state up to new branch tip.
 
1708
            basis = self.basis_tree()
 
1709
            to_tree = self.branch.basis_tree()
 
1710
            if basis.inventory.root is None:
 
1711
                self.set_root_id(to_tree.inventory.root.file_id)
 
1712
            result += merge.merge_inner(
 
1713
                                  self.branch,
 
1714
                                  to_tree,
 
1715
                                  basis,
 
1716
                                  this_tree=self)
 
1717
            # TODO - dedup parents list with things merged by pull ?
 
1718
            # reuse the tree we've updated to to set the basis:
 
1719
            parent_trees = [(self.branch.last_revision(), to_tree)]
 
1720
            merges = self.get_parent_ids()[1:]
 
1721
            # Ideally we ask the tree for the trees here, that way the working
 
1722
            # tree can decide whether to give us teh entire tree or give us a
 
1723
            # lazy initialised tree. dirstate for instance will have the trees
 
1724
            # in ram already, whereas a last-revision + basis-inventory tree
 
1725
            # will not, but also does not need them when setting parents.
 
1726
            for parent in merges:
 
1727
                parent_trees.append(
 
1728
                    (parent, self.branch.repository.revision_tree(parent)))
 
1729
            if old_tip is not None:
 
1730
                parent_trees.append(
 
1731
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
 
1732
            self.set_parent_trees(parent_trees)
 
1733
            last_rev = parent_trees[0][0]
 
1734
        else:
 
1735
            # the working tree had the same last-revision as the master
 
1736
            # branch did. We may still have pivot local work from the local
 
1737
            # branch into old_tip:
 
1738
            if old_tip is not None:
 
1739
                self.add_parent_tree_id(old_tip)
 
1740
        if old_tip and old_tip != last_rev:
 
1741
            # our last revision was not the prior branch last revision
 
1742
            # and we have converted that last revision to a pending merge.
 
1743
            # base is somewhere between the branch tip now
 
1744
            # and the now pending merge
 
1745
            from bzrlib.revision import common_ancestor
 
1746
            try:
 
1747
                base_rev_id = common_ancestor(self.branch.last_revision(),
 
1748
                                              old_tip,
 
1749
                                              self.branch.repository)
 
1750
            except errors.NoCommonAncestor:
 
1751
                base_rev_id = None
 
1752
            base_tree = self.branch.repository.revision_tree(base_rev_id)
 
1753
            other_tree = self.branch.repository.revision_tree(old_tip)
 
1754
            result += merge.merge_inner(
 
1755
                                  self.branch,
 
1756
                                  other_tree,
 
1757
                                  base_tree,
 
1758
                                  this_tree=self)
 
1759
        return result
 
1760
 
 
1761
    @needs_tree_write_lock
1441
1762
    def _write_inventory(self, inv):
1442
1763
        """Write inventory as the current inventory."""
1443
 
        sio = StringIO()
1444
 
        bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
1445
 
        sio.seek(0)
1446
 
        self._control_files.put('inventory', sio)
1447
 
        self._set_inventory(inv)
1448
 
        mutter('wrote working inventory')
 
1764
        self._set_inventory(inv, dirty=True)
 
1765
        self.flush()
1449
1766
 
1450
1767
    def set_conflicts(self, arg):
1451
1768
        raise UnsupportedOperation(self.set_conflicts, self)
1455
1772
 
1456
1773
    @needs_read_lock
1457
1774
    def conflicts(self):
1458
 
        conflicts = ConflictList()
 
1775
        conflicts = _mod_conflicts.ConflictList()
1459
1776
        for conflicted in self._iter_conflicts():
1460
1777
            text = True
1461
1778
            try:
1474
1791
                    if text == False:
1475
1792
                        break
1476
1793
            ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1477
 
            conflicts.append(Conflict.factory(ctype, path=conflicted,
 
1794
            conflicts.append(_mod_conflicts.Conflict.factory(ctype,
 
1795
                             path=conflicted,
1478
1796
                             file_id=self.path2id(conflicted)))
1479
1797
        return conflicts
1480
1798
 
1487
1805
     - uses the branch last-revision.
1488
1806
    """
1489
1807
 
 
1808
    def lock_tree_write(self):
 
1809
        """See WorkingTree.lock_tree_write().
 
1810
 
 
1811
        In Format2 WorkingTrees we have a single lock for the branch and tree
 
1812
        so lock_tree_write() degrades to lock_write().
 
1813
        """
 
1814
        self.branch.lock_write()
 
1815
        try:
 
1816
            return self._control_files.lock_write()
 
1817
        except:
 
1818
            self.branch.unlock()
 
1819
            raise
 
1820
 
1490
1821
    def unlock(self):
1491
1822
        # we share control files:
1492
 
        if self._hashcache.needs_write and self._control_files._lock_count==3:
1493
 
            self._hashcache.write()
 
1823
        if self._control_files._lock_count == 3:
 
1824
            # _inventory_is_modified is always False during a read lock.
 
1825
            if self._inventory_is_modified:
 
1826
                self.flush()
 
1827
            if self._hashcache.needs_write:
 
1828
                self._hashcache.write()
1494
1829
        # reverse order of locking.
1495
1830
        try:
1496
1831
            return self._control_files.unlock()
1509
1844
    """
1510
1845
 
1511
1846
    @needs_read_lock
1512
 
    def last_revision(self):
1513
 
        """See WorkingTree.last_revision."""
 
1847
    def _last_revision(self):
 
1848
        """See Mutable.last_revision."""
1514
1849
        try:
1515
1850
            return self._control_files.get_utf8('last-revision').read()
1516
1851
        except NoSuchFile:
1528
1863
            self._control_files.put_utf8('last-revision', revision_id)
1529
1864
            return True
1530
1865
 
1531
 
    @needs_write_lock
 
1866
    @needs_tree_write_lock
1532
1867
    def set_conflicts(self, conflicts):
1533
1868
        self._put_rio('conflicts', conflicts.to_stanzas(), 
1534
1869
                      CONFLICT_HEADER_1)
1535
1870
 
1536
 
    @needs_write_lock
 
1871
    @needs_tree_write_lock
1537
1872
    def add_conflicts(self, new_conflicts):
1538
1873
        conflict_set = set(self.conflicts())
1539
1874
        conflict_set.update(set(list(new_conflicts)))
1540
 
        self.set_conflicts(ConflictList(sorted(conflict_set,
1541
 
                                               key=Conflict.sort_key)))
 
1875
        self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
 
1876
                                       key=_mod_conflicts.Conflict.sort_key)))
1542
1877
 
1543
1878
    @needs_read_lock
1544
1879
    def conflicts(self):
1545
1880
        try:
1546
1881
            confile = self._control_files.get('conflicts')
1547
1882
        except NoSuchFile:
1548
 
            return ConflictList()
 
1883
            return _mod_conflicts.ConflictList()
1549
1884
        try:
1550
1885
            if confile.next() != CONFLICT_HEADER_1 + '\n':
1551
1886
                raise ConflictFormatError()
1552
1887
        except StopIteration:
1553
1888
            raise ConflictFormatError()
1554
 
        return ConflictList.from_stanzas(RioReader(confile))
 
1889
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1555
1890
 
1556
1891
    def unlock(self):
1557
 
        if self._hashcache.needs_write and self._control_files._lock_count==1:
1558
 
            self._hashcache.write()
 
1892
        if self._control_files._lock_count == 1:
 
1893
            # _inventory_is_modified is always False during a read lock.
 
1894
            if self._inventory_is_modified:
 
1895
                self.flush()
 
1896
            if self._hashcache.needs_write:
 
1897
                self._hashcache.write()
1559
1898
        # reverse order of locking.
1560
1899
        try:
1561
1900
            return self._control_files.unlock()
1564
1903
 
1565
1904
 
1566
1905
def get_conflicted_stem(path):
1567
 
    for suffix in CONFLICT_SUFFIXES:
 
1906
    for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
1568
1907
        if path.endswith(suffix):
1569
1908
            return path[:-len(suffix)]
1570
1909
 
1676
2015
        """
1677
2016
        sio = StringIO()
1678
2017
        inv = Inventory()
1679
 
        bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
 
2018
        xml5.serializer_v5.write_inventory(inv, sio)
1680
2019
        sio.seek(0)
1681
2020
        control_files.put('inventory', sio)
1682
2021
 
1700
2039
            finally:
1701
2040
                branch.unlock()
1702
2041
        revision = branch.last_revision()
1703
 
        inv = Inventory() 
 
2042
        inv = Inventory()
1704
2043
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
1705
2044
                         branch,
1706
2045
                         inv,
1707
2046
                         _internal=True,
1708
2047
                         _format=self,
1709
2048
                         _bzrdir=a_bzrdir)
1710
 
        wt._write_inventory(inv)
1711
 
        wt.set_root_id(inv.root.file_id)
1712
 
        wt.set_last_revision(revision)
1713
 
        wt.set_pending_merges([])
1714
 
        build_tree(wt.basis_tree(), wt)
 
2049
        basis_tree = branch.repository.revision_tree(revision)
 
2050
        if basis_tree.inventory.root is not None:
 
2051
            wt.set_root_id(basis_tree.inventory.root.file_id)
 
2052
        # set the parent list and cache the basis tree.
 
2053
        wt.set_parent_trees([(revision, basis_tree)])
 
2054
        transform.build_tree(basis_tree, wt)
1715
2055
        return wt
1716
2056
 
1717
2057
    def __init__(self):
1779
2119
        branch = a_bzrdir.open_branch()
1780
2120
        if revision_id is None:
1781
2121
            revision_id = branch.last_revision()
1782
 
        inv = Inventory() 
 
2122
        # WorkingTree3 can handle an inventory which has a unique root id.
 
2123
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
 
2124
        # those trees. And because there isn't a format bump inbetween, we
 
2125
        # are maintaining compatibility with older clients.
 
2126
        # inv = Inventory(root_id=gen_root_id())
 
2127
        inv = Inventory()
1783
2128
        wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1784
2129
                         branch,
1785
2130
                         inv,
1787
2132
                         _format=self,
1788
2133
                         _bzrdir=a_bzrdir,
1789
2134
                         _control_files=control_files)
1790
 
        wt.lock_write()
 
2135
        wt.lock_tree_write()
1791
2136
        try:
1792
 
            wt._write_inventory(inv)
1793
 
            wt.set_root_id(inv.root.file_id)
1794
 
            wt.set_last_revision(revision_id)
1795
 
            wt.set_pending_merges([])
1796
 
            build_tree(wt.basis_tree(), wt)
 
2137
            basis_tree = branch.repository.revision_tree(revision_id)
 
2138
            # only set an explicit root id if there is one to set.
 
2139
            if basis_tree.inventory.root is not None:
 
2140
                wt.set_root_id(basis_tree.inventory.root.file_id)
 
2141
            if revision_id == NULL_REVISION:
 
2142
                wt.set_parent_trees([])
 
2143
            else:
 
2144
                wt.set_parent_trees([(revision_id, basis_tree)])
 
2145
            transform.build_tree(basis_tree, wt)
1797
2146
        finally:
 
2147
            # Unlock in this order so that the unlock-triggers-flush in
 
2148
            # WorkingTree is given a chance to fire.
 
2149
            control_files.unlock()
1798
2150
            wt.unlock()
1799
 
            control_files.unlock()
1800
2151
        return wt
1801
2152
 
1802
2153
    def __init__(self):