/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: 2007-08-20 05:53:39 UTC
  • mfrom: (2727 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2730.
  • Revision ID: mbp@sourcefrog.net-20070820055339-uzei7f7i7jo6tugg
merge trunk

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,
399
399
            # the basis tree is a ghost so return an empty tree.
400
400
            return self.branch.repository.revision_tree(None)
401
401
 
 
402
    def _cleanup(self):
 
403
        self._flush_ignore_list_cache()
 
404
 
402
405
    @staticmethod
403
406
    @deprecated_method(zero_eight)
404
407
    def create(branch, directory):
460
463
        return file(self.abspath(filename), 'rb')
461
464
 
462
465
    @needs_read_lock
463
 
    def annotate_iter(self, file_id):
 
466
    def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
464
467
        """See Tree.annotate_iter
465
468
 
466
469
        This implementation will use the basis tree implementation if possible.
493
496
                    continue
494
497
                old.append(list(tree.annotate_iter(file_id)))
495
498
            return annotate.reannotate(old, self.get_file(file_id).readlines(),
496
 
                                       CURRENT_REVISION)
 
499
                                       default_revision)
497
500
        finally:
498
501
            basis.unlock()
499
502
 
 
503
    def _get_ancestors(self, default_revision):
 
504
        ancestors = set([default_revision])
 
505
        for parent_id in self.get_parent_ids():
 
506
            ancestors.update(self.branch.repository.get_ancestry(
 
507
                             parent_id, topo_sorted=False))
 
508
        return ancestors
 
509
 
500
510
    def get_parent_ids(self):
501
511
        """See Tree.get_parent_ids.
502
512
        
503
513
        This implementation reads the pending merges list and last_revision
504
514
        value and uses that to decide what the parents list should be.
505
515
        """
506
 
        last_rev = self._last_revision()
507
 
        if last_rev is None:
 
516
        last_rev = _mod_revision.ensure_null(self._last_revision())
 
517
        if _mod_revision.NULL_REVISION == last_rev:
508
518
            parents = []
509
519
        else:
510
520
            parents = [last_rev]
616
626
        # should probably put it back with the previous ID.
617
627
        # the read and write working inventory should not occur in this 
618
628
        # function - they should be part of lock_write and unlock.
619
 
        inv = self.read_working_inventory()
 
629
        inv = self.inventory
620
630
        for f, file_id, kind in zip(files, ids, kinds):
621
631
            assert kind is not None
622
632
            if file_id is None:
624
634
            else:
625
635
                file_id = osutils.safe_file_id(file_id)
626
636
                inv.add_path(f, kind=kind, file_id=file_id)
627
 
        self._write_inventory(inv)
 
637
            self._inventory_is_modified = True
628
638
 
629
639
    @needs_tree_write_lock
630
640
    def _gather_kinds(self, files, kinds):
735
745
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
736
746
        self._check_parents_for_ghosts(revision_ids,
737
747
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
748
        for revision_id in revision_ids:
 
749
            _mod_revision.check_not_reserved_id(revision_id)
738
750
 
739
751
        if len(revision_ids) > 0:
740
752
            self.set_last_revision(revision_ids[0])
741
753
        else:
742
 
            self.set_last_revision(None)
 
754
            self.set_last_revision(_mod_revision.NULL_REVISION)
743
755
 
744
756
        self._set_merges_from_parent_ids(revision_ids)
745
757
 
747
759
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
748
760
        """See MutableTree.set_parent_trees."""
749
761
        parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
 
762
        for revision_id in parent_ids:
 
763
            _mod_revision.check_not_reserved_id(revision_id)
750
764
 
751
765
        self._check_parents_for_ghosts(parent_ids,
752
766
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
753
767
 
754
768
        if len(parent_ids) == 0:
755
 
            leftmost_parent_id = None
 
769
            leftmost_parent_id = _mod_revision.NULL_REVISION
756
770
            leftmost_parent_tree = None
757
771
        else:
758
772
            leftmost_parent_id, leftmost_parent_tree = parents_list[0]
789
803
        self._control_files.put(filename, my_file)
790
804
 
791
805
    @needs_write_lock # because merge pulls data into the branch.
792
 
    def merge_from_branch(self, branch, to_revision=None):
 
806
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
 
807
        merge_type=None):
793
808
        """Merge from a branch into this working tree.
794
809
 
795
810
        :param branch: The branch to merge from.
808
823
            # local alterations
809
824
            merger.check_basis(check_clean=True, require_commits=False)
810
825
            if to_revision is None:
811
 
                to_revision = branch.last_revision()
 
826
                to_revision = _mod_revision.ensure_null(branch.last_revision())
812
827
            else:
813
828
                to_revision = osutils.safe_revision_id(to_revision)
814
829
            merger.other_rev_id = to_revision
815
 
            if merger.other_rev_id is None:
816
 
                raise error.NoCommits(branch)
 
830
            if _mod_revision.is_null(merger.other_rev_id):
 
831
                raise errors.NoCommits(branch)
817
832
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
818
833
            merger.other_basis = merger.other_rev_id
819
834
            merger.other_tree = self.branch.repository.revision_tree(
820
835
                merger.other_rev_id)
821
836
            merger.other_branch = branch
822
837
            merger.pp.next_phase()
823
 
            merger.find_base()
 
838
            if from_revision is None:
 
839
                merger.find_base()
 
840
            else:
 
841
                merger.set_base_revision(from_revision, branch)
824
842
            if merger.base_rev_id == merger.other_rev_id:
825
843
                raise errors.PointlessMerge
826
844
            merger.backup_files = False
827
 
            merger.merge_type = Merge3Merger
 
845
            if merge_type is None:
 
846
                merger.merge_type = Merge3Merger
 
847
            else:
 
848
                merger.merge_type = merge_type
828
849
            merger.set_interesting_files(None)
829
850
            merger.show_base = False
830
851
            merger.reprocess = False
1550
1571
        if ignoreset is not None:
1551
1572
            return ignoreset
1552
1573
 
1553
 
        ignore_globs = set(bzrlib.DEFAULT_IGNORE)
 
1574
        ignore_globs = set()
1554
1575
        ignore_globs.update(ignores.get_runtime_ignores())
1555
1576
        ignore_globs.update(ignores.get_user_ignores())
1556
1577
        if self.has_filename(bzrlib.IGNORE_FILENAME):
1686
1707
        This is used to allow WorkingTree3 instances to not affect branch
1687
1708
        when their last revision is set.
1688
1709
        """
1689
 
        if new_revision is None:
 
1710
        if _mod_revision.is_null(new_revision):
1690
1711
            self.branch.set_revision_history([])
1691
1712
            return False
1692
1713
        try:
1763
1784
    @needs_tree_write_lock
1764
1785
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
1765
1786
        force=False):
1766
 
        """Remove nominated files from the working inventor.
 
1787
        """Remove nominated files from the working inventory.
1767
1788
 
1768
1789
        :files: File paths relative to the basedir.
1769
1790
        :keep_files: If true, the files will also be kept.
1804
1825
                    recurse_directory_to_add_files(filename)
1805
1826
        files = [f for f in new_files]
1806
1827
 
 
1828
        if len(files) == 0:
 
1829
            return # nothing to do
 
1830
 
1807
1831
        # Sort needed to first handle directory content before the directory
1808
1832
        files.sort(reverse=True)
1809
1833
        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):
 
1834
            has_changed_files = len(unknown_files_in_directory) > 0
 
1835
            if not has_changed_files:
 
1836
                for (file_id, path, content_change, versioned, parent_id, name,
 
1837
                     kind, executable) in self._iter_changes(self.basis_tree(),
 
1838
                         include_unchanged=True, require_versioned=False,
 
1839
                         want_unversioned=True, specific_files=files):
 
1840
                    # check if it's unknown OR changed but not deleted:
 
1841
                    if (versioned == (False, False)
 
1842
                        or (content_change and kind[1] != None)):
 
1843
                        has_changed_files = True
 
1844
                        break
 
1845
 
 
1846
            if has_changed_files:
 
1847
                # make delta to show ALL applicable changes in error message.
 
1848
                tree_delta = self.changes_from(self.basis_tree(),
 
1849
                    specific_files=files)
 
1850
                for unknown_file in unknown_files_in_directory:
 
1851
                    tree_delta.unversioned.extend((unknown_file,))
1819
1852
                raise errors.BzrRemoveChangedFilesError(tree_delta)
1820
1853
 
1821
1854
        # do this before any modifications
1971
2004
        """
1972
2005
        raise NotImplementedError(self.unlock)
1973
2006
 
1974
 
    def update(self):
 
2007
    def update(self, change_reporter=None):
1975
2008
        """Update a working tree along its branch.
1976
2009
 
1977
2010
        This will update the branch if its bound too, which means we have
2007
2040
                old_tip = self.branch.update()
2008
2041
            else:
2009
2042
                old_tip = None
2010
 
            return self._update_tree(old_tip)
 
2043
            return self._update_tree(old_tip, change_reporter)
2011
2044
        finally:
2012
2045
            self.unlock()
2013
2046
 
2014
2047
    @needs_tree_write_lock
2015
 
    def _update_tree(self, old_tip=None):
 
2048
    def _update_tree(self, old_tip=None, change_reporter=None):
2016
2049
        """Update a tree to the master branch.
2017
2050
 
2018
2051
        :param old_tip: if supplied, the previous tip revision the branch,
2032
2065
        try:
2033
2066
            last_rev = self.get_parent_ids()[0]
2034
2067
        except IndexError:
2035
 
            last_rev = None
2036
 
        if last_rev != self.branch.last_revision():
 
2068
            last_rev = _mod_revision.NULL_REVISION
 
2069
        if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
2037
2070
            # merge tree state up to new branch tip.
2038
2071
            basis = self.basis_tree()
2039
2072
            basis.lock_read()
2046
2079
                                      self.branch,
2047
2080
                                      to_tree,
2048
2081
                                      basis,
2049
 
                                      this_tree=self)
 
2082
                                      this_tree=self,
 
2083
                                      change_reporter=change_reporter)
2050
2084
            finally:
2051
2085
                basis.unlock()
2052
2086
            # TODO - dedup parents list with things merged by pull ?
2061
2095
            for parent in merges:
2062
2096
                parent_trees.append(
2063
2097
                    (parent, self.branch.repository.revision_tree(parent)))
2064
 
            if old_tip is not None:
 
2098
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2065
2099
                parent_trees.append(
2066
2100
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
2067
2101
            self.set_parent_trees(parent_trees)
2070
2104
            # the working tree had the same last-revision as the master
2071
2105
            # branch did. We may still have pivot local work from the local
2072
2106
            # branch into old_tip:
2073
 
            if old_tip is not None:
 
2107
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2074
2108
                self.add_parent_tree_id(old_tip)
2075
 
        if old_tip and old_tip != last_rev:
 
2109
        if (old_tip is not None and not _mod_revision.is_null(old_tip)
 
2110
            and old_tip != last_rev):
2076
2111
            # our last revision was not the prior branch last revision
2077
2112
            # and we have converted that last revision to a pending merge.
2078
2113
            # base is somewhere between the branch tip now
2085
2120
            #       inventory and calls tree._write_inventory(). Ultimately we
2086
2121
            #       should be able to remove this extra flush.
2087
2122
            self.flush()
2088
 
            from bzrlib.revision import common_ancestor
2089
 
            try:
2090
 
                base_rev_id = common_ancestor(self.branch.last_revision(),
2091
 
                                              old_tip,
2092
 
                                              self.branch.repository)
2093
 
            except errors.NoCommonAncestor:
2094
 
                base_rev_id = None
 
2123
            graph = self.branch.repository.get_graph()
 
2124
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
 
2125
                                                old_tip)
2095
2126
            base_tree = self.branch.repository.revision_tree(base_rev_id)
2096
2127
            other_tree = self.branch.repository.revision_tree(old_tip)
2097
2128
            result += merge.merge_inner(
2098
2129
                                  self.branch,
2099
2130
                                  other_tree,
2100
2131
                                  base_tree,
2101
 
                                  this_tree=self)
 
2132
                                  this_tree=self,
 
2133
                                  change_reporter=change_reporter)
2102
2134
        return result
2103
2135
 
2104
2136
    def _write_hashcache_if_dirty(self):
2377
2409
            raise
2378
2410
 
2379
2411
    def unlock(self):
 
2412
        # do non-implementation specific cleanup
 
2413
        self._cleanup()
 
2414
 
2380
2415
        # we share control files:
2381
2416
        if self._control_files._lock_count == 3:
2382
2417
            # _inventory_is_modified is always False during a read lock.
2448
2483
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2449
2484
 
2450
2485
    def unlock(self):
 
2486
        # do non-implementation specific cleanup
 
2487
        self._cleanup()
2451
2488
        if self._control_files._lock_count == 1:
2452
2489
            # _inventory_is_modified is always False during a read lock.
2453
2490
            if self._inventory_is_modified:
2597
2634
        if not isinstance(a_bzrdir.transport, LocalTransport):
2598
2635
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2599
2636
        branch = a_bzrdir.open_branch()
2600
 
        if revision_id is not None:
 
2637
        if revision_id is None:
 
2638
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2639
        else:
2601
2640
            revision_id = osutils.safe_revision_id(revision_id)
2602
 
            branch.lock_write()
2603
 
            try:
2604
 
                revision_history = branch.revision_history()
2605
 
                try:
2606
 
                    position = revision_history.index(revision_id)
2607
 
                except ValueError:
2608
 
                    raise errors.NoSuchRevision(branch, revision_id)
2609
 
                branch.set_revision_history(revision_history[:position + 1])
2610
 
            finally:
2611
 
                branch.unlock()
2612
 
        revision = branch.last_revision()
 
2641
        branch.lock_write()
 
2642
        try:
 
2643
            branch.generate_revision_history(revision_id)
 
2644
        finally:
 
2645
            branch.unlock()
2613
2646
        inv = Inventory()
2614
2647
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2615
2648
                         branch,
2617
2650
                         _internal=True,
2618
2651
                         _format=self,
2619
2652
                         _bzrdir=a_bzrdir)
2620
 
        basis_tree = branch.repository.revision_tree(revision)
 
2653
        basis_tree = branch.repository.revision_tree(revision_id)
2621
2654
        if basis_tree.inventory.root is not None:
2622
2655
            wt.set_root_id(basis_tree.inventory.root.file_id)
2623
2656
        # set the parent list and cache the basis tree.
2624
 
        wt.set_parent_trees([(revision, basis_tree)])
 
2657
        if _mod_revision.is_null(revision_id):
 
2658
            parent_trees = []
 
2659
        else:
 
2660
            parent_trees = [(revision_id, basis_tree)]
 
2661
        wt.set_parent_trees(parent_trees)
2625
2662
        transform.build_tree(basis_tree, wt)
2626
2663
        return wt
2627
2664
 
2698
2735
        control_files.put_utf8('format', self.get_format_string())
2699
2736
        branch = a_bzrdir.open_branch()
2700
2737
        if revision_id is None:
2701
 
            revision_id = branch.last_revision()
 
2738
            revision_id = _mod_revision.ensure_null(branch.last_revision())
2702
2739
        else:
2703
2740
            revision_id = osutils.safe_revision_id(revision_id)
2704
2741
        # WorkingTree3 can handle an inventory which has a unique root id.
2776
2813
# and not independently creatable, so are not registered.
2777
2814
_legacy_formats = [WorkingTreeFormat2(),
2778
2815
                   ]
2779
 
 
2780
 
 
2781
 
class WorkingTreeTestProviderAdapter(object):
2782
 
    """A tool to generate a suite testing multiple workingtree formats at once.
2783
 
 
2784
 
    This is done by copying the test once for each transport and injecting
2785
 
    the transport_server, transport_readonly_server, and workingtree_format
2786
 
    classes into each copy. Each copy is also given a new id() to make it
2787
 
    easy to identify.
2788
 
    """
2789
 
 
2790
 
    def __init__(self, transport_server, transport_readonly_server, formats):
2791
 
        self._transport_server = transport_server
2792
 
        self._transport_readonly_server = transport_readonly_server
2793
 
        self._formats = formats
2794
 
    
2795
 
    def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2796
 
        """Clone test for adaption."""
2797
 
        new_test = deepcopy(test)
2798
 
        new_test.transport_server = self._transport_server
2799
 
        new_test.transport_readonly_server = self._transport_readonly_server
2800
 
        new_test.bzrdir_format = bzrdir_format
2801
 
        new_test.workingtree_format = workingtree_format
2802
 
        def make_new_test_id():
2803
 
            new_id = "%s(%s)" % (test.id(), variation)
2804
 
            return lambda: new_id
2805
 
        new_test.id = make_new_test_id()
2806
 
        return new_test
2807
 
    
2808
 
    def adapt(self, test):
2809
 
        from bzrlib.tests import TestSuite
2810
 
        result = TestSuite()
2811
 
        for workingtree_format, bzrdir_format in self._formats:
2812
 
            new_test = self._clone_test(
2813
 
                test,
2814
 
                bzrdir_format,
2815
 
                workingtree_format, workingtree_format.__class__.__name__)
2816
 
            result.addTest(new_test)
2817
 
        return result