~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: John Arbash Meinel
  • Date: 2007-07-13 02:23:34 UTC
  • mfrom: (2592 +trunk) (2612 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2614.
  • Revision ID: john@arbash-meinel.com-20070713022334-qb6ewgo6v4251yd9
[merge] bzr.dev 2612

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
lazy_import(globals(), """
45
45
from bisect import bisect_left
46
46
import collections
47
 
from copy import deepcopy
48
47
import errno
49
48
import itertools
50
49
import operator
66
65
    ignores,
67
66
    merge,
68
67
    osutils,
 
68
    revision as _mod_revision,
69
69
    revisiontree,
70
70
    repository,
71
71
    textui,
503
503
        This implementation reads the pending merges list and last_revision
504
504
        value and uses that to decide what the parents list should be.
505
505
        """
506
 
        last_rev = self._last_revision()
507
 
        if last_rev is None:
 
506
        last_rev = _mod_revision.ensure_null(self._last_revision())
 
507
        if _mod_revision.NULL_REVISION == last_rev:
508
508
            parents = []
509
509
        else:
510
510
            parents = [last_rev]
616
616
        # should probably put it back with the previous ID.
617
617
        # the read and write working inventory should not occur in this 
618
618
        # function - they should be part of lock_write and unlock.
619
 
        inv = self.read_working_inventory()
 
619
        inv = self.inventory
620
620
        for f, file_id, kind in zip(files, ids, kinds):
621
621
            assert kind is not None
622
622
            if file_id is None:
624
624
            else:
625
625
                file_id = osutils.safe_file_id(file_id)
626
626
                inv.add_path(f, kind=kind, file_id=file_id)
627
 
        self._write_inventory(inv)
 
627
            self._inventory_is_modified = True
628
628
 
629
629
    @needs_tree_write_lock
630
630
    def _gather_kinds(self, files, kinds):
735
735
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
736
736
        self._check_parents_for_ghosts(revision_ids,
737
737
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
738
        for revision_id in revision_ids:
 
739
            _mod_revision.check_not_reserved_id(revision_id)
738
740
 
739
741
        if len(revision_ids) > 0:
740
742
            self.set_last_revision(revision_ids[0])
741
743
        else:
742
 
            self.set_last_revision(None)
 
744
            self.set_last_revision(_mod_revision.NULL_REVISION)
743
745
 
744
746
        self._set_merges_from_parent_ids(revision_ids)
745
747
 
747
749
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
748
750
        """See MutableTree.set_parent_trees."""
749
751
        parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
 
752
        for revision_id in parent_ids:
 
753
            _mod_revision.check_not_reserved_id(revision_id)
750
754
 
751
755
        self._check_parents_for_ghosts(parent_ids,
752
756
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
753
757
 
754
758
        if len(parent_ids) == 0:
755
 
            leftmost_parent_id = None
 
759
            leftmost_parent_id = _mod_revision.NULL_REVISION
756
760
            leftmost_parent_tree = None
757
761
        else:
758
762
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
808
812
            # local alterations
809
813
            merger.check_basis(check_clean=True, require_commits=False)
810
814
            if to_revision is None:
811
 
                to_revision = branch.last_revision()
 
815
                to_revision = _mod_revision.ensure_null(branch.last_revision())
812
816
            else:
813
817
                to_revision = osutils.safe_revision_id(to_revision)
814
818
            merger.other_rev_id = to_revision
815
 
            if merger.other_rev_id is None:
 
819
            if _mod_revision.is_null(merger.other_rev_id):
816
820
                raise errors.NoCommits(branch)
817
821
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
818
822
            merger.other_basis = merger.other_rev_id
1686
1690
        This is used to allow WorkingTree3 instances to not affect branch
1687
1691
        when their last revision is set.
1688
1692
        """
1689
 
        if new_revision is None:
 
1693
        if _mod_revision.is_null(new_revision):
1690
1694
            self.branch.set_revision_history([])
1691
1695
            return False
1692
1696
        try:
1763
1767
    @needs_tree_write_lock
1764
1768
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
1765
1769
        force=False):
1766
 
        """Remove nominated files from the working inventor.
 
1770
        """Remove nominated files from the working inventory.
1767
1771
 
1768
1772
        :files: File paths relative to the basedir.
1769
1773
        :keep_files: If true, the files will also be kept.
1804
1808
                    recurse_directory_to_add_files(filename)
1805
1809
        files = [f for f in new_files]
1806
1810
 
 
1811
        if len(files) == 0:
 
1812
            return # nothing to do
 
1813
 
1807
1814
        # Sort needed to first handle directory content before the directory
1808
1815
        files.sort(reverse=True)
1809
1816
        if not keep_files and not force:
1810
 
            tree_delta = self.changes_from(self.basis_tree(),
1811
 
                specific_files=files)
1812
 
            for unknown_file in unknown_files_in_directory:
1813
 
                tree_delta.unversioned.extend((unknown_file,))
1814
 
            if bool(tree_delta.modified
1815
 
                    or tree_delta.added
1816
 
                    or tree_delta.renamed
1817
 
                    or tree_delta.kind_changed
1818
 
                    or tree_delta.unversioned):
 
1817
            has_changed_files = len(unknown_files_in_directory) > 0
 
1818
            if not has_changed_files:
 
1819
                for (file_id, path, content_change, versioned, parent_id, name,
 
1820
                     kind, executable) in self._iter_changes(self.basis_tree(),
 
1821
                         include_unchanged=True, require_versioned=False,
 
1822
                         want_unversioned=True, specific_files=files):
 
1823
                    # check if it's unknown OR changed but not deleted:
 
1824
                    if (versioned == (False, False)
 
1825
                        or (content_change and kind[1] != None)):
 
1826
                        has_changed_files = True
 
1827
                        break
 
1828
 
 
1829
            if has_changed_files:
 
1830
                # make delta to show ALL applicable changes in error message.
 
1831
                tree_delta = self.changes_from(self.basis_tree(),
 
1832
                    specific_files=files)
 
1833
                for unknown_file in unknown_files_in_directory:
 
1834
                    tree_delta.unversioned.extend((unknown_file,))
1819
1835
                raise errors.BzrRemoveChangedFilesError(tree_delta)
1820
1836
 
1821
1837
        # do this before any modifications
1971
1987
        """
1972
1988
        raise NotImplementedError(self.unlock)
1973
1989
 
1974
 
    def update(self):
 
1990
    def update(self, change_reporter=None):
1975
1991
        """Update a working tree along its branch.
1976
1992
 
1977
1993
        This will update the branch if its bound too, which means we have
2007
2023
                old_tip = self.branch.update()
2008
2024
            else:
2009
2025
                old_tip = None
2010
 
            return self._update_tree(old_tip)
 
2026
            return self._update_tree(old_tip, change_reporter)
2011
2027
        finally:
2012
2028
            self.unlock()
2013
2029
 
2014
2030
    @needs_tree_write_lock
2015
 
    def _update_tree(self, old_tip=None):
 
2031
    def _update_tree(self, old_tip=None, change_reporter=None):
2016
2032
        """Update a tree to the master branch.
2017
2033
 
2018
2034
        :param old_tip: if supplied, the previous tip revision the branch,
2032
2048
        try:
2033
2049
            last_rev = self.get_parent_ids()[0]
2034
2050
        except IndexError:
2035
 
            last_rev = None
2036
 
        if last_rev != self.branch.last_revision():
 
2051
            last_rev = _mod_revision.NULL_REVISION
 
2052
        if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
2037
2053
            # merge tree state up to new branch tip.
2038
2054
            basis = self.basis_tree()
2039
2055
            basis.lock_read()
2046
2062
                                      self.branch,
2047
2063
                                      to_tree,
2048
2064
                                      basis,
2049
 
                                      this_tree=self)
 
2065
                                      this_tree=self,
 
2066
                                      change_reporter=change_reporter)
2050
2067
            finally:
2051
2068
                basis.unlock()
2052
2069
            # TODO - dedup parents list with things merged by pull ?
2061
2078
            for parent in merges:
2062
2079
                parent_trees.append(
2063
2080
                    (parent, self.branch.repository.revision_tree(parent)))
2064
 
            if old_tip is not None:
 
2081
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2065
2082
                parent_trees.append(
2066
2083
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
2067
2084
            self.set_parent_trees(parent_trees)
2070
2087
            # the working tree had the same last-revision as the master
2071
2088
            # branch did. We may still have pivot local work from the local
2072
2089
            # branch into old_tip:
2073
 
            if old_tip is not None:
 
2090
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2074
2091
                self.add_parent_tree_id(old_tip)
2075
 
        if old_tip and old_tip != last_rev:
 
2092
        if (old_tip is not None and not _mod_revision.is_null(old_tip)
 
2093
            and old_tip != last_rev):
2076
2094
            # our last revision was not the prior branch last revision
2077
2095
            # and we have converted that last revision to a pending merge.
2078
2096
            # base is somewhere between the branch tip now
2094
2112
                                  self.branch,
2095
2113
                                  other_tree,
2096
2114
                                  base_tree,
2097
 
                                  this_tree=self)
 
2115
                                  this_tree=self,
 
2116
                                  change_reporter=change_reporter)
2098
2117
        return result
2099
2118
 
2100
2119
    def _write_hashcache_if_dirty(self):
2593
2612
        if not isinstance(a_bzrdir.transport, LocalTransport):
2594
2613
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2595
2614
        branch = a_bzrdir.open_branch()
2596
 
        if revision_id is not None:
 
2615
        if revision_id is None:
 
2616
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2617
        else:
2597
2618
            revision_id = osutils.safe_revision_id(revision_id)
2598
 
            branch.lock_write()
2599
 
            try:
2600
 
                revision_history = branch.revision_history()
2601
 
                try:
2602
 
                    position = revision_history.index(revision_id)
2603
 
                except ValueError:
2604
 
                    raise errors.NoSuchRevision(branch, revision_id)
2605
 
                branch.set_revision_history(revision_history[:position + 1])
2606
 
            finally:
2607
 
                branch.unlock()
2608
 
        revision = branch.last_revision()
 
2619
        branch.lock_write()
 
2620
        try:
 
2621
            branch.generate_revision_history(revision_id)
 
2622
        finally:
 
2623
            branch.unlock()
2609
2624
        inv = Inventory()
2610
2625
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2611
2626
                         branch,
2613
2628
                         _internal=True,
2614
2629
                         _format=self,
2615
2630
                         _bzrdir=a_bzrdir)
2616
 
        basis_tree = branch.repository.revision_tree(revision)
 
2631
        basis_tree = branch.repository.revision_tree(revision_id)
2617
2632
        if basis_tree.inventory.root is not None:
2618
2633
            wt.set_root_id(basis_tree.inventory.root.file_id)
2619
2634
        # set the parent list and cache the basis tree.
2620
 
        wt.set_parent_trees([(revision, basis_tree)])
 
2635
        if _mod_revision.is_null(revision_id):
 
2636
            parent_trees = []
 
2637
        else:
 
2638
            parent_trees = [(revision_id, basis_tree)]
 
2639
        wt.set_parent_trees(parent_trees)
2621
2640
        transform.build_tree(basis_tree, wt)
2622
2641
        return wt
2623
2642
 
2694
2713
        control_files.put_utf8('format', self.get_format_string())
2695
2714
        branch = a_bzrdir.open_branch()
2696
2715
        if revision_id is None:
2697
 
            revision_id = branch.last_revision()
 
2716
            revision_id = _mod_revision.ensure_null(branch.last_revision())
2698
2717
        else:
2699
2718
            revision_id = osutils.safe_revision_id(revision_id)
2700
2719
        # WorkingTree3 can handle an inventory which has a unique root id.
2772
2791
# and not independently creatable, so are not registered.
2773
2792
_legacy_formats = [WorkingTreeFormat2(),
2774
2793
                   ]
2775
 
 
2776
 
 
2777
 
class WorkingTreeTestProviderAdapter(object):
2778
 
    """A tool to generate a suite testing multiple workingtree formats at once.
2779
 
 
2780
 
    This is done by copying the test once for each transport and injecting
2781
 
    the transport_server, transport_readonly_server, and workingtree_format
2782
 
    classes into each copy. Each copy is also given a new id() to make it
2783
 
    easy to identify.
2784
 
    """
2785
 
 
2786
 
    def __init__(self, transport_server, transport_readonly_server, formats):
2787
 
        self._transport_server = transport_server
2788
 
        self._transport_readonly_server = transport_readonly_server
2789
 
        self._formats = formats
2790
 
    
2791
 
    def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2792
 
        """Clone test for adaption."""
2793
 
        new_test = deepcopy(test)
2794
 
        new_test.transport_server = self._transport_server
2795
 
        new_test.transport_readonly_server = self._transport_readonly_server
2796
 
        new_test.bzrdir_format = bzrdir_format
2797
 
        new_test.workingtree_format = workingtree_format
2798
 
        def make_new_test_id():
2799
 
            new_id = "%s(%s)" % (test.id(), variation)
2800
 
            return lambda: new_id
2801
 
        new_test.id = make_new_test_id()
2802
 
        return new_test
2803
 
    
2804
 
    def adapt(self, test):
2805
 
        from bzrlib.tests import TestSuite
2806
 
        result = TestSuite()
2807
 
        for workingtree_format, bzrdir_format in self._formats:
2808
 
            new_test = self._clone_test(
2809
 
                test,
2810
 
                bzrdir_format,
2811
 
                workingtree_format, workingtree_format.__class__.__name__)
2812
 
            result.addTest(new_test)
2813
 
        return result