/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Martin Pool
  • Date: 2010-06-24 06:53:06 UTC
  • mfrom: (5317 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5343.
  • Revision ID: mbp@sourcefrog.net-20100624065306-qcx1wg84ufuckime
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
WorkingTree.open(dir).
30
30
"""
31
31
 
32
 
# TODO: Give the workingtree sole responsibility for the working inventory;
33
 
# remove the variable and references to it from the branch.  This may require
34
 
# updating the commit code so as to update the inventory within the working
35
 
# copy, and making sure there's only one WorkingTree for any directory on disk.
36
 
# At the moment they may alias the inventory and have old copies of it in
37
 
# memory.  (Now done? -- mbp 20060309)
38
32
 
39
33
from cStringIO import StringIO
40
34
import os
83
77
 
84
78
from bzrlib import symbol_versioning
85
79
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
80
from bzrlib.lock import LogicalLockResult
86
81
from bzrlib.lockable_files import LockableFiles
87
82
from bzrlib.lockdir import LockDir
88
83
import bzrlib.mutabletree
101
96
from bzrlib.filters import filtered_input_file
102
97
from bzrlib.trace import mutter, note
103
98
from bzrlib.transport.local import LocalTransport
104
 
from bzrlib.progress import DummyProgress, ProgressPhase
105
99
from bzrlib.revision import CURRENT_REVISION
106
100
from bzrlib.rio import RioReader, rio_file, Stanza
107
101
from bzrlib.symbol_versioning import (
111
105
 
112
106
 
113
107
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
 
108
# TODO: Modifying the conflict objects or their type is currently nearly
 
109
# impossible as there is no clear relationship between the working tree format
 
110
# and the conflict list file format.
114
111
CONFLICT_HEADER_1 = "BZR conflict list format 1"
115
112
 
116
113
ERROR_PATH_NOT_FOUND = 3    # WindowsError errno code, equivalent to ENOENT
171
168
        return ''
172
169
 
173
170
 
174
 
class WorkingTree(bzrlib.mutabletree.MutableTree):
 
171
class WorkingTree(bzrlib.mutabletree.MutableTree,
 
172
    bzrdir.ControlComponent):
175
173
    """Working copy tree.
176
174
 
177
175
    The inventory is held in the `Branch` working-inventory, and the
250
248
        self._rules_searcher = None
251
249
        self.views = self._make_views()
252
250
 
 
251
    @property
 
252
    def user_transport(self):
 
253
        return self.bzrdir.user_transport
 
254
 
 
255
    @property
 
256
    def control_transport(self):
 
257
        return self._transport
 
258
 
253
259
    def _detect_case_handling(self):
254
260
        wt_trans = self.bzrdir.get_workingtree_transport(None)
255
261
        try:
457
463
        return (file_obj, stat_value)
458
464
 
459
465
    def get_file_text(self, file_id, path=None, filtered=True):
460
 
        return self.get_file(file_id, path=path, filtered=filtered).read()
 
466
        my_file = self.get_file(file_id, path=path, filtered=filtered)
 
467
        try:
 
468
            return my_file.read()
 
469
        finally:
 
470
            my_file.close()
461
471
 
462
472
    def get_file_byname(self, filename, filtered=True):
463
473
        path = self.abspath(filename)
517
527
 
518
528
        # Now we have the parents of this content
519
529
        annotator = self.branch.repository.texts.get_annotator()
520
 
        text = self.get_file(file_id).read()
 
530
        text = self.get_file_text(file_id)
521
531
        this_key =(file_id, default_revision)
522
532
        annotator.add_special_text(this_key, file_parent_keys, text)
523
533
        annotations = [(key[-1], line)
909
919
            branch.last_revision().
910
920
        """
911
921
        from bzrlib.merge import Merger, Merge3Merger
912
 
        pb = ui.ui_factory.nested_progress_bar()
913
 
        try:
914
 
            merger = Merger(self.branch, this_tree=self, pb=pb)
915
 
            merger.pp = ProgressPhase("Merge phase", 5, pb)
916
 
            merger.pp.next_phase()
917
 
            # check that there are no local alterations
918
 
            if not force and self.has_changes():
919
 
                raise errors.UncommittedChanges(self)
920
 
            if to_revision is None:
921
 
                to_revision = _mod_revision.ensure_null(branch.last_revision())
922
 
            merger.other_rev_id = to_revision
923
 
            if _mod_revision.is_null(merger.other_rev_id):
924
 
                raise errors.NoCommits(branch)
925
 
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
926
 
            merger.other_basis = merger.other_rev_id
927
 
            merger.other_tree = self.branch.repository.revision_tree(
928
 
                merger.other_rev_id)
929
 
            merger.other_branch = branch
930
 
            merger.pp.next_phase()
931
 
            if from_revision is None:
932
 
                merger.find_base()
933
 
            else:
934
 
                merger.set_base_revision(from_revision, branch)
935
 
            if merger.base_rev_id == merger.other_rev_id:
936
 
                raise errors.PointlessMerge
937
 
            merger.backup_files = False
938
 
            if merge_type is None:
939
 
                merger.merge_type = Merge3Merger
940
 
            else:
941
 
                merger.merge_type = merge_type
942
 
            merger.set_interesting_files(None)
943
 
            merger.show_base = False
944
 
            merger.reprocess = False
945
 
            conflicts = merger.do_merge()
946
 
            merger.set_pending()
947
 
        finally:
948
 
            pb.finished()
 
922
        merger = Merger(self.branch, this_tree=self)
 
923
        # check that there are no local alterations
 
924
        if not force and self.has_changes():
 
925
            raise errors.UncommittedChanges(self)
 
926
        if to_revision is None:
 
927
            to_revision = _mod_revision.ensure_null(branch.last_revision())
 
928
        merger.other_rev_id = to_revision
 
929
        if _mod_revision.is_null(merger.other_rev_id):
 
930
            raise errors.NoCommits(branch)
 
931
        self.branch.fetch(branch, last_revision=merger.other_rev_id)
 
932
        merger.other_basis = merger.other_rev_id
 
933
        merger.other_tree = self.branch.repository.revision_tree(
 
934
            merger.other_rev_id)
 
935
        merger.other_branch = branch
 
936
        if from_revision is None:
 
937
            merger.find_base()
 
938
        else:
 
939
            merger.set_base_revision(from_revision, branch)
 
940
        if merger.base_rev_id == merger.other_rev_id:
 
941
            raise errors.PointlessMerge
 
942
        merger.backup_files = False
 
943
        if merge_type is None:
 
944
            merger.merge_type = Merge3Merger
 
945
        else:
 
946
            merger.merge_type = merge_type
 
947
        merger.set_interesting_files(None)
 
948
        merger.show_base = False
 
949
        merger.reprocess = False
 
950
        conflicts = merger.do_merge()
 
951
        merger.set_pending()
949
952
        return conflicts
950
953
 
951
954
    @needs_read_lock
1098
1101
        tree_transport = self.bzrdir.root_transport.clone(sub_path)
1099
1102
        if tree_transport.base != branch_transport.base:
1100
1103
            tree_bzrdir = format.initialize_on_transport(tree_transport)
1101
 
            branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
 
1104
            branch.BranchReferenceFormat().initialize(tree_bzrdir,
 
1105
                target_branch=new_branch)
1102
1106
        else:
1103
1107
            tree_bzrdir = branch_bzrdir
1104
1108
        wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
1142
1146
        This does not include files that have been deleted in this
1143
1147
        tree. Skips the control directory.
1144
1148
 
1145
 
        :param include_root: if True, do not return an entry for the root
 
1149
        :param include_root: if True, return an entry for the root
1146
1150
        :param from_dir: start from this directory or None for the root
1147
1151
        :param recursive: whether to recurse into subdirectories or not
1148
1152
        """
1602
1606
    @needs_write_lock
1603
1607
    def pull(self, source, overwrite=False, stop_revision=None,
1604
1608
             change_reporter=None, possible_transports=None, local=False):
1605
 
        top_pb = ui.ui_factory.nested_progress_bar()
1606
1609
        source.lock_read()
1607
1610
        try:
1608
 
            pp = ProgressPhase("Pull phase", 2, top_pb)
1609
 
            pp.next_phase()
1610
1611
            old_revision_info = self.branch.last_revision_info()
1611
1612
            basis_tree = self.basis_tree()
1612
1613
            count = self.branch.pull(source, overwrite, stop_revision,
1614
1615
                                     local=local)
1615
1616
            new_revision_info = self.branch.last_revision_info()
1616
1617
            if new_revision_info != old_revision_info:
1617
 
                pp.next_phase()
1618
1618
                repository = self.branch.repository
1619
 
                pb = ui.ui_factory.nested_progress_bar()
1620
1619
                basis_tree.lock_read()
1621
1620
                try:
1622
1621
                    new_basis_tree = self.branch.basis_tree()
1625
1624
                                new_basis_tree,
1626
1625
                                basis_tree,
1627
1626
                                this_tree=self,
1628
 
                                pb=pb,
 
1627
                                pb=None,
1629
1628
                                change_reporter=change_reporter)
1630
1629
                    basis_root_id = basis_tree.get_root_id()
1631
1630
                    new_root_id = new_basis_tree.get_root_id()
1632
1631
                    if basis_root_id != new_root_id:
1633
1632
                        self.set_root_id(new_root_id)
1634
1633
                finally:
1635
 
                    pb.finished()
1636
1634
                    basis_tree.unlock()
1637
1635
                # TODO - dedup parents list with things merged by pull ?
1638
1636
                # reuse the revisiontree we merged against to set the new
1651
1649
            return count
1652
1650
        finally:
1653
1651
            source.unlock()
1654
 
            top_pb.finished()
1655
1652
 
1656
1653
    @needs_write_lock
1657
1654
    def put_file_bytes_non_atomic(self, file_id, bytes):
1806
1803
            raise errors.ObjectNotLocked(self)
1807
1804
 
1808
1805
    def lock_read(self):
1809
 
        """See Branch.lock_read, and WorkingTree.unlock."""
 
1806
        """Lock the tree for reading.
 
1807
 
 
1808
        This also locks the branch, and can be unlocked via self.unlock().
 
1809
 
 
1810
        :return: A bzrlib.lock.LogicalLockResult.
 
1811
        """
1810
1812
        if not self.is_locked():
1811
1813
            self._reset_data()
1812
1814
        self.branch.lock_read()
1813
1815
        try:
1814
 
            return self._control_files.lock_read()
 
1816
            self._control_files.lock_read()
 
1817
            return LogicalLockResult(self.unlock)
1815
1818
        except:
1816
1819
            self.branch.unlock()
1817
1820
            raise
1818
1821
 
1819
1822
    def lock_tree_write(self):
1820
 
        """See MutableTree.lock_tree_write, and WorkingTree.unlock."""
 
1823
        """See MutableTree.lock_tree_write, and WorkingTree.unlock.
 
1824
 
 
1825
        :return: A bzrlib.lock.LogicalLockResult.
 
1826
        """
1821
1827
        if not self.is_locked():
1822
1828
            self._reset_data()
1823
1829
        self.branch.lock_read()
1824
1830
        try:
1825
 
            return self._control_files.lock_write()
 
1831
            self._control_files.lock_write()
 
1832
            return LogicalLockResult(self.unlock)
1826
1833
        except:
1827
1834
            self.branch.unlock()
1828
1835
            raise
1829
1836
 
1830
1837
    def lock_write(self):
1831
 
        """See MutableTree.lock_write, and WorkingTree.unlock."""
 
1838
        """See MutableTree.lock_write, and WorkingTree.unlock.
 
1839
 
 
1840
        :return: A bzrlib.lock.LogicalLockResult.
 
1841
        """
1832
1842
        if not self.is_locked():
1833
1843
            self._reset_data()
1834
1844
        self.branch.lock_write()
1835
1845
        try:
1836
 
            return self._control_files.lock_write()
 
1846
            self._control_files.lock_write()
 
1847
            return LogicalLockResult(self.unlock)
1837
1848
        except:
1838
1849
            self.branch.unlock()
1839
1850
            raise
1904
1915
            # revision_id is set. We must check for this full string, because a
1905
1916
            # root node id can legitimately look like 'revision_id' but cannot
1906
1917
            # contain a '"'.
1907
 
            xml = self.branch.repository.get_inventory_xml(new_revision)
 
1918
            xml = self.branch.repository._get_inventory_xml(new_revision)
1908
1919
            firstline = xml.split('\n', 1)[0]
1909
1920
            if (not 'revision_id="' in firstline or
1910
1921
                'format="7"' not in firstline):
1964
1975
        def recurse_directory_to_add_files(directory):
1965
1976
            # Recurse directory and add all files
1966
1977
            # so we can check if they have changed.
1967
 
            for parent_info, file_infos in\
1968
 
                self.walkdirs(directory):
 
1978
            for parent_info, file_infos in self.walkdirs(directory):
1969
1979
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
1970
1980
                    # Is it versioned or ignored?
1971
1981
                    if self.path2id(relpath) or self.is_ignored(relpath):
2006
2016
                            # ... but not ignored
2007
2017
                            has_changed_files = True
2008
2018
                            break
2009
 
                    elif content_change and (kind[1] is not None):
2010
 
                        # Versioned and changed, but not deleted
 
2019
                    elif (content_change and (kind[1] is not None) and
 
2020
                            osutils.is_inside_any(files, path[1])):
 
2021
                        # Versioned and changed, but not deleted, and still
 
2022
                        # in one of the dirs to be deleted.
2011
2023
                        has_changed_files = True
2012
2024
                        break
2013
2025
 
2066
2078
 
2067
2079
    @needs_tree_write_lock
2068
2080
    def revert(self, filenames=None, old_tree=None, backups=True,
2069
 
               pb=DummyProgress(), report_changes=False):
 
2081
               pb=None, report_changes=False):
2070
2082
        from bzrlib.conflicts import resolve
2071
2083
        if filenames == []:
2072
2084
            filenames = None
2261
2273
        # We MUST save it even if an error occurs, because otherwise the users
2262
2274
        # local work is unreferenced and will appear to have been lost.
2263
2275
        #
2264
 
        result = 0
 
2276
        nb_conflicts = 0
2265
2277
        try:
2266
2278
            last_rev = self.get_parent_ids()[0]
2267
2279
        except IndexError:
2268
2280
            last_rev = _mod_revision.NULL_REVISION
2269
2281
        if revision is None:
2270
2282
            revision = self.branch.last_revision()
2271
 
        else:
2272
 
            if revision not in self.branch.revision_history():
2273
 
                raise errors.NoSuchRevision(self.branch, revision)
 
2283
 
 
2284
        old_tip = old_tip or _mod_revision.NULL_REVISION
 
2285
 
 
2286
        if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
 
2287
            # the branch we are bound to was updated
 
2288
            # merge those changes in first
 
2289
            base_tree  = self.basis_tree()
 
2290
            other_tree = self.branch.repository.revision_tree(old_tip)
 
2291
            nb_conflicts = merge.merge_inner(self.branch, other_tree,
 
2292
                                             base_tree, this_tree=self,
 
2293
                                             change_reporter=change_reporter)
 
2294
            if nb_conflicts:
 
2295
                self.add_parent_tree((old_tip, other_tree))
 
2296
                trace.note('Rerun update after fixing the conflicts.')
 
2297
                return nb_conflicts
 
2298
 
2274
2299
        if last_rev != _mod_revision.ensure_null(revision):
2275
 
            # merge tree state up to specified revision.
 
2300
            # the working tree is up to date with the branch
 
2301
            # we can merge the specified revision from master
 
2302
            to_tree = self.branch.repository.revision_tree(revision)
 
2303
            to_root_id = to_tree.get_root_id()
 
2304
 
2276
2305
            basis = self.basis_tree()
2277
2306
            basis.lock_read()
2278
2307
            try:
2279
 
                to_tree = self.branch.repository.revision_tree(revision)
2280
 
                to_root_id = to_tree.get_root_id()
2281
2308
                if (basis.inventory.root is None
2282
2309
                    or basis.inventory.root.file_id != to_root_id):
2283
2310
                    self.set_root_id(to_root_id)
2284
2311
                    self.flush()
2285
 
                result += merge.merge_inner(
2286
 
                                      self.branch,
2287
 
                                      to_tree,
2288
 
                                      basis,
2289
 
                                      this_tree=self,
2290
 
                                      change_reporter=change_reporter)
2291
 
                self.set_last_revision(revision)
2292
2312
            finally:
2293
2313
                basis.unlock()
 
2314
 
 
2315
            # determine the branch point
 
2316
            graph = self.branch.repository.get_graph()
 
2317
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
 
2318
                                                last_rev)
 
2319
            base_tree = self.branch.repository.revision_tree(base_rev_id)
 
2320
 
 
2321
            nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
 
2322
                                             this_tree=self,
 
2323
                                             change_reporter=change_reporter)
 
2324
            self.set_last_revision(revision)
2294
2325
            # TODO - dedup parents list with things merged by pull ?
2295
2326
            # reuse the tree we've updated to to set the basis:
2296
2327
            parent_trees = [(revision, to_tree)]
2303
2334
            for parent in merges:
2304
2335
                parent_trees.append(
2305
2336
                    (parent, self.branch.repository.revision_tree(parent)))
2306
 
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
 
2337
            if not _mod_revision.is_null(old_tip):
2307
2338
                parent_trees.append(
2308
2339
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
2309
2340
            self.set_parent_trees(parent_trees)
2310
2341
            last_rev = parent_trees[0][0]
2311
 
        else:
2312
 
            # the working tree had the same last-revision as the master
2313
 
            # branch did. We may still have pivot local work from the local
2314
 
            # branch into old_tip:
2315
 
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2316
 
                self.add_parent_tree_id(old_tip)
2317
 
        if (old_tip is not None and not _mod_revision.is_null(old_tip)
2318
 
            and old_tip != last_rev):
2319
 
            # our last revision was not the prior branch last revision
2320
 
            # and we have converted that last revision to a pending merge.
2321
 
            # base is somewhere between the branch tip now
2322
 
            # and the now pending merge
2323
 
 
2324
 
            # Since we just modified the working tree and inventory, flush out
2325
 
            # the current state, before we modify it again.
2326
 
            # TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2327
 
            #       requires it only because TreeTransform directly munges the
2328
 
            #       inventory and calls tree._write_inventory(). Ultimately we
2329
 
            #       should be able to remove this extra flush.
2330
 
            self.flush()
2331
 
            graph = self.branch.repository.get_graph()
2332
 
            base_rev_id = graph.find_unique_lca(revision, old_tip)
2333
 
            base_tree = self.branch.repository.revision_tree(base_rev_id)
2334
 
            other_tree = self.branch.repository.revision_tree(old_tip)
2335
 
            result += merge.merge_inner(
2336
 
                                  self.branch,
2337
 
                                  other_tree,
2338
 
                                  base_tree,
2339
 
                                  this_tree=self,
2340
 
                                  change_reporter=change_reporter)
2341
 
        return result
 
2342
        return nb_conflicts
2342
2343
 
2343
2344
    def _write_hashcache_if_dirty(self):
2344
2345
        """Write out the hashcache if it is dirty."""
2655
2656
 
2656
2657
        In Format2 WorkingTrees we have a single lock for the branch and tree
2657
2658
        so lock_tree_write() degrades to lock_write().
 
2659
 
 
2660
        :return: An object with an unlock method which will release the lock
 
2661
            obtained.
2658
2662
        """
2659
2663
        self.branch.lock_write()
2660
2664
        try:
2661
 
            return self._control_files.lock_write()
 
2665
            self._control_files.lock_write()
 
2666
            return self
2662
2667
        except:
2663
2668
            self.branch.unlock()
2664
2669
            raise