~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-06-28 07:08:27 UTC
  • mfrom: (2553.1.3 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20070628070827-h5s313dg5tnag9vj
(robertc) Show the names of commit hooks during commit.

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
47
48
import errno
48
49
import itertools
49
50
import operator
65
66
    ignores,
66
67
    merge,
67
68
    osutils,
68
 
    revision as _mod_revision,
69
69
    revisiontree,
70
70
    repository,
71
71
    textui,
460
460
        return file(self.abspath(filename), 'rb')
461
461
 
462
462
    @needs_read_lock
463
 
    def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
 
463
    def annotate_iter(self, file_id):
464
464
        """See Tree.annotate_iter
465
465
 
466
466
        This implementation will use the basis tree implementation if possible.
493
493
                    continue
494
494
                old.append(list(tree.annotate_iter(file_id)))
495
495
            return annotate.reannotate(old, self.get_file(file_id).readlines(),
496
 
                                       default_revision)
 
496
                                       CURRENT_REVISION)
497
497
        finally:
498
498
            basis.unlock()
499
499
 
500
 
    def _get_ancestors(self, default_revision):
501
 
        ancestors = set([default_revision])
502
 
        for parent_id in self.get_parent_ids():
503
 
            ancestors.update(self.branch.repository.get_ancestry(
504
 
                             parent_id, topo_sorted=False))
505
 
        return ancestors
506
 
 
507
500
    def get_parent_ids(self):
508
501
        """See Tree.get_parent_ids.
509
502
        
510
503
        This implementation reads the pending merges list and last_revision
511
504
        value and uses that to decide what the parents list should be.
512
505
        """
513
 
        last_rev = _mod_revision.ensure_null(self._last_revision())
514
 
        if _mod_revision.NULL_REVISION == last_rev:
 
506
        last_rev = self._last_revision()
 
507
        if last_rev is None:
515
508
            parents = []
516
509
        else:
517
510
            parents = [last_rev]
623
616
        # should probably put it back with the previous ID.
624
617
        # the read and write working inventory should not occur in this 
625
618
        # function - they should be part of lock_write and unlock.
626
 
        inv = self.inventory
 
619
        inv = self.read_working_inventory()
627
620
        for f, file_id, kind in zip(files, ids, kinds):
628
621
            assert kind is not None
629
622
            if file_id is None:
631
624
            else:
632
625
                file_id = osutils.safe_file_id(file_id)
633
626
                inv.add_path(f, kind=kind, file_id=file_id)
634
 
            self._inventory_is_modified = True
 
627
        self._write_inventory(inv)
635
628
 
636
629
    @needs_tree_write_lock
637
630
    def _gather_kinds(self, files, kinds):
742
735
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
743
736
        self._check_parents_for_ghosts(revision_ids,
744
737
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
745
 
        for revision_id in revision_ids:
746
 
            _mod_revision.check_not_reserved_id(revision_id)
747
738
 
748
739
        if len(revision_ids) > 0:
749
740
            self.set_last_revision(revision_ids[0])
750
741
        else:
751
 
            self.set_last_revision(_mod_revision.NULL_REVISION)
 
742
            self.set_last_revision(None)
752
743
 
753
744
        self._set_merges_from_parent_ids(revision_ids)
754
745
 
756
747
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
757
748
        """See MutableTree.set_parent_trees."""
758
749
        parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
759
 
        for revision_id in parent_ids:
760
 
            _mod_revision.check_not_reserved_id(revision_id)
761
750
 
762
751
        self._check_parents_for_ghosts(parent_ids,
763
752
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
764
753
 
765
754
        if len(parent_ids) == 0:
766
 
            leftmost_parent_id = _mod_revision.NULL_REVISION
 
755
            leftmost_parent_id = None
767
756
            leftmost_parent_tree = None
768
757
        else:
769
758
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
819
808
            # local alterations
820
809
            merger.check_basis(check_clean=True, require_commits=False)
821
810
            if to_revision is None:
822
 
                to_revision = _mod_revision.ensure_null(branch.last_revision())
 
811
                to_revision = branch.last_revision()
823
812
            else:
824
813
                to_revision = osutils.safe_revision_id(to_revision)
825
814
            merger.other_rev_id = to_revision
826
 
            if _mod_revision.is_null(merger.other_rev_id):
 
815
            if merger.other_rev_id is None:
827
816
                raise errors.NoCommits(branch)
828
817
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
829
818
            merger.other_basis = merger.other_rev_id
1697
1686
        This is used to allow WorkingTree3 instances to not affect branch
1698
1687
        when their last revision is set.
1699
1688
        """
1700
 
        if _mod_revision.is_null(new_revision):
 
1689
        if new_revision is None:
1701
1690
            self.branch.set_revision_history([])
1702
1691
            return False
1703
1692
        try:
1774
1763
    @needs_tree_write_lock
1775
1764
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
1776
1765
        force=False):
1777
 
        """Remove nominated files from the working inventory.
 
1766
        """Remove nominated files from the working inventor.
1778
1767
 
1779
1768
        :files: File paths relative to the basedir.
1780
1769
        :keep_files: If true, the files will also be kept.
1815
1804
                    recurse_directory_to_add_files(filename)
1816
1805
        files = [f for f in new_files]
1817
1806
 
1818
 
        if len(files) == 0:
1819
 
            return # nothing to do
1820
 
 
1821
1807
        # Sort needed to first handle directory content before the directory
1822
1808
        files.sort(reverse=True)
1823
1809
        if not keep_files and not force:
1824
 
            has_changed_files = len(unknown_files_in_directory) > 0
1825
 
            if not has_changed_files:
1826
 
                for (file_id, path, content_change, versioned, parent_id, name,
1827
 
                     kind, executable) in self._iter_changes(self.basis_tree(),
1828
 
                         include_unchanged=True, require_versioned=False,
1829
 
                         want_unversioned=True, specific_files=files):
1830
 
                    # check if it's unknown OR changed but not deleted:
1831
 
                    if (versioned == (False, False)
1832
 
                        or (content_change and kind[1] != None)):
1833
 
                        has_changed_files = True
1834
 
                        break
1835
 
 
1836
 
            if has_changed_files:
1837
 
                # make delta to show ALL applicable changes in error message.
1838
 
                tree_delta = self.changes_from(self.basis_tree(),
1839
 
                    specific_files=files)
1840
 
                for unknown_file in unknown_files_in_directory:
1841
 
                    tree_delta.unversioned.extend((unknown_file,))
 
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):
1842
1819
                raise errors.BzrRemoveChangedFilesError(tree_delta)
1843
1820
 
1844
1821
        # do this before any modifications
1994
1971
        """
1995
1972
        raise NotImplementedError(self.unlock)
1996
1973
 
1997
 
    def update(self, change_reporter=None):
 
1974
    def update(self):
1998
1975
        """Update a working tree along its branch.
1999
1976
 
2000
1977
        This will update the branch if its bound too, which means we have
2030
2007
                old_tip = self.branch.update()
2031
2008
            else:
2032
2009
                old_tip = None
2033
 
            return self._update_tree(old_tip, change_reporter)
 
2010
            return self._update_tree(old_tip)
2034
2011
        finally:
2035
2012
            self.unlock()
2036
2013
 
2037
2014
    @needs_tree_write_lock
2038
 
    def _update_tree(self, old_tip=None, change_reporter=None):
 
2015
    def _update_tree(self, old_tip=None):
2039
2016
        """Update a tree to the master branch.
2040
2017
 
2041
2018
        :param old_tip: if supplied, the previous tip revision the branch,
2055
2032
        try:
2056
2033
            last_rev = self.get_parent_ids()[0]
2057
2034
        except IndexError:
2058
 
            last_rev = _mod_revision.NULL_REVISION
2059
 
        if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
 
2035
            last_rev = None
 
2036
        if last_rev != self.branch.last_revision():
2060
2037
            # merge tree state up to new branch tip.
2061
2038
            basis = self.basis_tree()
2062
2039
            basis.lock_read()
2069
2046
                                      self.branch,
2070
2047
                                      to_tree,
2071
2048
                                      basis,
2072
 
                                      this_tree=self,
2073
 
                                      change_reporter=change_reporter)
 
2049
                                      this_tree=self)
2074
2050
            finally:
2075
2051
                basis.unlock()
2076
2052
            # TODO - dedup parents list with things merged by pull ?
2085
2061
            for parent in merges:
2086
2062
                parent_trees.append(
2087
2063
                    (parent, self.branch.repository.revision_tree(parent)))
2088
 
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
 
2064
            if old_tip is not None:
2089
2065
                parent_trees.append(
2090
2066
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
2091
2067
            self.set_parent_trees(parent_trees)
2094
2070
            # the working tree had the same last-revision as the master
2095
2071
            # branch did. We may still have pivot local work from the local
2096
2072
            # branch into old_tip:
2097
 
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
 
2073
            if old_tip is not None:
2098
2074
                self.add_parent_tree_id(old_tip)
2099
 
        if (old_tip is not None and not _mod_revision.is_null(old_tip)
2100
 
            and old_tip != last_rev):
 
2075
        if old_tip and old_tip != last_rev:
2101
2076
            # our last revision was not the prior branch last revision
2102
2077
            # and we have converted that last revision to a pending merge.
2103
2078
            # base is somewhere between the branch tip now
2119
2094
                                  self.branch,
2120
2095
                                  other_tree,
2121
2096
                                  base_tree,
2122
 
                                  this_tree=self,
2123
 
                                  change_reporter=change_reporter)
 
2097
                                  this_tree=self)
2124
2098
        return result
2125
2099
 
2126
2100
    def _write_hashcache_if_dirty(self):
2619
2593
        if not isinstance(a_bzrdir.transport, LocalTransport):
2620
2594
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2621
2595
        branch = a_bzrdir.open_branch()
2622
 
        if revision_id is None:
2623
 
            revision_id = _mod_revision.ensure_null(branch.last_revision())
2624
 
        else:
 
2596
        if revision_id is not None:
2625
2597
            revision_id = osutils.safe_revision_id(revision_id)
2626
 
        branch.lock_write()
2627
 
        try:
2628
 
            branch.generate_revision_history(revision_id)
2629
 
        finally:
2630
 
            branch.unlock()
 
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()
2631
2609
        inv = Inventory()
2632
2610
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2633
2611
                         branch,
2635
2613
                         _internal=True,
2636
2614
                         _format=self,
2637
2615
                         _bzrdir=a_bzrdir)
2638
 
        basis_tree = branch.repository.revision_tree(revision_id)
 
2616
        basis_tree = branch.repository.revision_tree(revision)
2639
2617
        if basis_tree.inventory.root is not None:
2640
2618
            wt.set_root_id(basis_tree.inventory.root.file_id)
2641
2619
        # set the parent list and cache the basis tree.
2642
 
        if _mod_revision.is_null(revision_id):
2643
 
            parent_trees = []
2644
 
        else:
2645
 
            parent_trees = [(revision_id, basis_tree)]
2646
 
        wt.set_parent_trees(parent_trees)
 
2620
        wt.set_parent_trees([(revision, basis_tree)])
2647
2621
        transform.build_tree(basis_tree, wt)
2648
2622
        return wt
2649
2623
 
2720
2694
        control_files.put_utf8('format', self.get_format_string())
2721
2695
        branch = a_bzrdir.open_branch()
2722
2696
        if revision_id is None:
2723
 
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2697
            revision_id = branch.last_revision()
2724
2698
        else:
2725
2699
            revision_id = osutils.safe_revision_id(revision_id)
2726
2700
        # WorkingTree3 can handle an inventory which has a unique root id.
2798
2772
# and not independently creatable, so are not registered.
2799
2773
_legacy_formats = [WorkingTreeFormat2(),
2800
2774
                   ]
 
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