/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: Marius Kruger
  • Date: 2007-08-12 08:15:15 UTC
  • mfrom: (2695 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2979.
  • Revision ID: amanic@gmail.com-20070812081515-vgekipfhohcuj6rn
merge with bzr.dev

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
932
953
            transport = self.branch.bzrdir.root_transport
933
954
            for name in segments:
934
955
                transport = transport.clone(name)
935
 
                try:
936
 
                    transport.mkdir('.')
937
 
                except errors.FileExists:
938
 
                    pass
 
956
                transport.ensure_base()
939
957
            return transport
940
958
            
941
959
        sub_path = self.id2path(file_id)
942
960
        branch_transport = mkdirs(sub_path)
943
961
        if format is None:
944
962
            format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
945
 
        try:
946
 
            branch_transport.mkdir('.')
947
 
        except errors.FileExists:
948
 
            pass
 
963
        branch_transport.ensure_base()
949
964
        branch_bzrdir = format.initialize_on_transport(branch_transport)
950
965
        try:
951
966
            repo = branch_bzrdir.find_repository()
1692
1707
        This is used to allow WorkingTree3 instances to not affect branch
1693
1708
        when their last revision is set.
1694
1709
        """
1695
 
        if new_revision is None:
 
1710
        if _mod_revision.is_null(new_revision):
1696
1711
            self.branch.set_revision_history([])
1697
1712
            return False
1698
1713
        try:
1769
1784
    @needs_tree_write_lock
1770
1785
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
1771
1786
        force=False):
1772
 
        """Remove nominated files from the working inventor.
 
1787
        """Remove nominated files from the working inventory.
1773
1788
 
1774
1789
        :files: File paths relative to the basedir.
1775
1790
        :keep_files: If true, the files will also be kept.
1781
1796
        if isinstance(files, basestring):
1782
1797
            files = [files]
1783
1798
 
1784
 
        inv = self.inventory
 
1799
        inv_delta = []
1785
1800
 
1786
1801
        new_files=set()
1787
1802
        unknown_files_in_directory=set()
1789
1804
        def recurse_directory_to_add_files(directory):
1790
1805
            # recurse directory and add all files
1791
1806
            # so we can check if they have changed.
1792
 
            for contained_dir_info in self.walkdirs(directory):
1793
 
                for file_info in contained_dir_info[1]:
1794
 
                    if file_info[2] == 'file':
1795
 
                        relpath = self.relpath(file_info[0])
1796
 
                        if file_info[4]: #is it versioned?
 
1807
            for parent_info, file_infos in\
 
1808
                osutils.walkdirs(self.abspath(directory),
 
1809
                    directory):
 
1810
                for relpath, basename, kind, lstat, abspath in file_infos:
 
1811
                    if kind == 'file':
 
1812
                        if self.path2id(relpath): #is it versioned?
1797
1813
                            new_files.add(relpath)
1798
1814
                        else:
1799
1815
                            unknown_files_in_directory.add(
1800
 
                                (relpath, None, file_info[2]))
 
1816
                                (relpath, None, kind))
1801
1817
 
1802
1818
        for filename in files:
1803
1819
            # Get file name into canonical form.
1804
 
            filename = self.relpath(self.abspath(filename))
 
1820
            abspath = self.abspath(filename)
 
1821
            filename = self.relpath(abspath)
1805
1822
            if len(filename) > 0:
1806
1823
                new_files.add(filename)
1807
 
                if osutils.isdir(filename) and len(os.listdir(filename)) > 0:
 
1824
                if osutils.isdir(abspath):
1808
1825
                    recurse_directory_to_add_files(filename)
1809
1826
        files = [f for f in new_files]
1810
1827
 
 
1828
        if len(files) == 0:
 
1829
            return # nothing to do
 
1830
 
1811
1831
        # Sort needed to first handle directory content before the directory
1812
1832
        files.sort(reverse=True)
1813
1833
        if not keep_files and not force:
1814
 
            tree_delta = self.changes_from(self.basis_tree(),
1815
 
                specific_files=files)
1816
 
            for unknown_file in unknown_files_in_directory:
1817
 
                tree_delta.unversioned.extend((unknown_file,))
1818
 
            if bool(tree_delta.modified
1819
 
                    or tree_delta.added
1820
 
                    or tree_delta.renamed
1821
 
                    or tree_delta.kind_changed
1822
 
                    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,))
1823
1852
                raise errors.BzrRemoveChangedFilesError(tree_delta)
1824
1853
 
1825
1854
        # do this before any modifications
1826
1855
        for f in files:
1827
 
            fid = inv.path2id(f)
 
1856
            fid = self.path2id(f)
1828
1857
            message=None
1829
1858
            if not fid:
1830
1859
                message="%s is not versioned." % (f,)
1835
1864
                        new_status = 'I'
1836
1865
                    else:
1837
1866
                        new_status = '?'
1838
 
                    textui.show_status(new_status, inv[fid].kind, f,
 
1867
                    textui.show_status(new_status, self.kind(fid), f,
1839
1868
                                       to_file=to_file)
1840
1869
                # unversion file
1841
 
                del inv[fid]
 
1870
                inv_delta.append((f, None, fid, None))
1842
1871
                message="removed %s" % (f,)
1843
1872
 
1844
1873
            if not keep_files:
1858
1887
            # print only one message (if any) per file.
1859
1888
            if message is not None:
1860
1889
                note(message)
1861
 
        self._write_inventory(inv)
 
1890
        self.apply_inventory_delta(inv_delta)
1862
1891
 
1863
1892
    @needs_tree_write_lock
1864
1893
    def revert(self, filenames, old_tree=None, backups=True, 
1975
2004
        """
1976
2005
        raise NotImplementedError(self.unlock)
1977
2006
 
1978
 
    def update(self):
 
2007
    def update(self, change_reporter=None):
1979
2008
        """Update a working tree along its branch.
1980
2009
 
1981
2010
        This will update the branch if its bound too, which means we have
2011
2040
                old_tip = self.branch.update()
2012
2041
            else:
2013
2042
                old_tip = None
2014
 
            return self._update_tree(old_tip)
 
2043
            return self._update_tree(old_tip, change_reporter)
2015
2044
        finally:
2016
2045
            self.unlock()
2017
2046
 
2018
2047
    @needs_tree_write_lock
2019
 
    def _update_tree(self, old_tip=None):
 
2048
    def _update_tree(self, old_tip=None, change_reporter=None):
2020
2049
        """Update a tree to the master branch.
2021
2050
 
2022
2051
        :param old_tip: if supplied, the previous tip revision the branch,
2036
2065
        try:
2037
2066
            last_rev = self.get_parent_ids()[0]
2038
2067
        except IndexError:
2039
 
            last_rev = None
2040
 
        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()):
2041
2070
            # merge tree state up to new branch tip.
2042
2071
            basis = self.basis_tree()
2043
2072
            basis.lock_read()
2050
2079
                                      self.branch,
2051
2080
                                      to_tree,
2052
2081
                                      basis,
2053
 
                                      this_tree=self)
 
2082
                                      this_tree=self,
 
2083
                                      change_reporter=change_reporter)
2054
2084
            finally:
2055
2085
                basis.unlock()
2056
2086
            # TODO - dedup parents list with things merged by pull ?
2065
2095
            for parent in merges:
2066
2096
                parent_trees.append(
2067
2097
                    (parent, self.branch.repository.revision_tree(parent)))
2068
 
            if old_tip is not None:
 
2098
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2069
2099
                parent_trees.append(
2070
2100
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
2071
2101
            self.set_parent_trees(parent_trees)
2074
2104
            # the working tree had the same last-revision as the master
2075
2105
            # branch did. We may still have pivot local work from the local
2076
2106
            # branch into old_tip:
2077
 
            if old_tip is not None:
 
2107
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2078
2108
                self.add_parent_tree_id(old_tip)
2079
 
        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):
2080
2111
            # our last revision was not the prior branch last revision
2081
2112
            # and we have converted that last revision to a pending merge.
2082
2113
            # base is somewhere between the branch tip now
2089
2120
            #       inventory and calls tree._write_inventory(). Ultimately we
2090
2121
            #       should be able to remove this extra flush.
2091
2122
            self.flush()
2092
 
            from bzrlib.revision import common_ancestor
2093
 
            try:
2094
 
                base_rev_id = common_ancestor(self.branch.last_revision(),
2095
 
                                              old_tip,
2096
 
                                              self.branch.repository)
2097
 
            except errors.NoCommonAncestor:
2098
 
                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)
2099
2126
            base_tree = self.branch.repository.revision_tree(base_rev_id)
2100
2127
            other_tree = self.branch.repository.revision_tree(old_tip)
2101
2128
            result += merge.merge_inner(
2102
2129
                                  self.branch,
2103
2130
                                  other_tree,
2104
2131
                                  base_tree,
2105
 
                                  this_tree=self)
 
2132
                                  this_tree=self,
 
2133
                                  change_reporter=change_reporter)
2106
2134
        return result
2107
2135
 
2108
2136
    def _write_hashcache_if_dirty(self):
2382
2410
            raise
2383
2411
 
2384
2412
    def unlock(self):
 
2413
        # do non-implementation specific cleanup
 
2414
        self._cleanup()
 
2415
 
2385
2416
        # we share control files:
2386
2417
        if self._control_files._lock_count == 3:
2387
2418
            # _inventory_is_modified is always False during a read lock.
2453
2484
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2454
2485
 
2455
2486
    def unlock(self):
 
2487
        # do non-implementation specific cleanup
 
2488
        self._cleanup()
2456
2489
        if self._control_files._lock_count == 1:
2457
2490
            # _inventory_is_modified is always False during a read lock.
2458
2491
            if self._inventory_is_modified:
2602
2635
        if not isinstance(a_bzrdir.transport, LocalTransport):
2603
2636
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2604
2637
        branch = a_bzrdir.open_branch()
2605
 
        if revision_id is not None:
 
2638
        if revision_id is None:
 
2639
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2640
        else:
2606
2641
            revision_id = osutils.safe_revision_id(revision_id)
2607
 
            branch.lock_write()
2608
 
            try:
2609
 
                revision_history = branch.revision_history()
2610
 
                try:
2611
 
                    position = revision_history.index(revision_id)
2612
 
                except ValueError:
2613
 
                    raise errors.NoSuchRevision(branch, revision_id)
2614
 
                branch.set_revision_history(revision_history[:position + 1])
2615
 
            finally:
2616
 
                branch.unlock()
2617
 
        revision = branch.last_revision()
 
2642
        branch.lock_write()
 
2643
        try:
 
2644
            branch.generate_revision_history(revision_id)
 
2645
        finally:
 
2646
            branch.unlock()
2618
2647
        inv = Inventory()
2619
2648
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2620
2649
                         branch,
2622
2651
                         _internal=True,
2623
2652
                         _format=self,
2624
2653
                         _bzrdir=a_bzrdir)
2625
 
        basis_tree = branch.repository.revision_tree(revision)
 
2654
        basis_tree = branch.repository.revision_tree(revision_id)
2626
2655
        if basis_tree.inventory.root is not None:
2627
2656
            wt.set_root_id(basis_tree.inventory.root.file_id)
2628
2657
        # set the parent list and cache the basis tree.
2629
 
        wt.set_parent_trees([(revision, basis_tree)])
 
2658
        if _mod_revision.is_null(revision_id):
 
2659
            parent_trees = []
 
2660
        else:
 
2661
            parent_trees = [(revision_id, basis_tree)]
 
2662
        wt.set_parent_trees(parent_trees)
2630
2663
        transform.build_tree(basis_tree, wt)
2631
2664
        return wt
2632
2665
 
2703
2736
        control_files.put_utf8('format', self.get_format_string())
2704
2737
        branch = a_bzrdir.open_branch()
2705
2738
        if revision_id is None:
2706
 
            revision_id = branch.last_revision()
 
2739
            revision_id = _mod_revision.ensure_null(branch.last_revision())
2707
2740
        else:
2708
2741
            revision_id = osutils.safe_revision_id(revision_id)
2709
2742
        # WorkingTree3 can handle an inventory which has a unique root id.
2781
2814
# and not independently creatable, so are not registered.
2782
2815
_legacy_formats = [WorkingTreeFormat2(),
2783
2816
                   ]
2784
 
 
2785
 
 
2786
 
class WorkingTreeTestProviderAdapter(object):
2787
 
    """A tool to generate a suite testing multiple workingtree formats at once.
2788
 
 
2789
 
    This is done by copying the test once for each transport and injecting
2790
 
    the transport_server, transport_readonly_server, and workingtree_format
2791
 
    classes into each copy. Each copy is also given a new id() to make it
2792
 
    easy to identify.
2793
 
    """
2794
 
 
2795
 
    def __init__(self, transport_server, transport_readonly_server, formats):
2796
 
        self._transport_server = transport_server
2797
 
        self._transport_readonly_server = transport_readonly_server
2798
 
        self._formats = formats
2799
 
    
2800
 
    def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2801
 
        """Clone test for adaption."""
2802
 
        new_test = deepcopy(test)
2803
 
        new_test.transport_server = self._transport_server
2804
 
        new_test.transport_readonly_server = self._transport_readonly_server
2805
 
        new_test.bzrdir_format = bzrdir_format
2806
 
        new_test.workingtree_format = workingtree_format
2807
 
        def make_new_test_id():
2808
 
            new_id = "%s(%s)" % (test.id(), variation)
2809
 
            return lambda: new_id
2810
 
        new_test.id = make_new_test_id()
2811
 
        return new_test
2812
 
    
2813
 
    def adapt(self, test):
2814
 
        from bzrlib.tests import TestSuite
2815
 
        result = TestSuite()
2816
 
        for workingtree_format, bzrdir_format in self._formats:
2817
 
            new_test = self._clone_test(
2818
 
                test,
2819
 
                bzrdir_format,
2820
 
                workingtree_format, workingtree_format.__class__.__name__)
2821
 
            result.addTest(new_test)
2822
 
        return result