~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: v.ladeuil+lp at free
  • Date: 2007-05-15 17:40:32 UTC
  • mto: (2485.8.44 bzr.connection.sharing)
  • mto: This revision was merged to the branch mainline in revision 2646.
  • Revision ID: v.ladeuil+lp@free.fr-20070515174032-qzdkangpv29l9e7g
Add a test that check that init connect only once. It fails.

* __init__.py:
(test_suite): Register the new test class.

* test_init.py: 
(InstrumentedTransport): A transport that can track connections.
(TransportHooks): Transport specific hooks.
(TestInit): Iniit command behavior tests.

* ftp.py:
(FtpTransport.__init__): Mark place that need fixing regarding
transport connection sharing

* builtins.py:
(cmd_init.run): Mark places that need fixing regarding transport
connection sharing.

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):
827
 
                raise errors.NoCommits(branch)
 
815
            if merger.other_rev_id is None:
 
816
                raise error.NoCommits(branch)
828
817
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
829
818
            merger.other_basis = merger.other_rev_id
830
819
            merger.other_tree = self.branch.repository.revision_tree(
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.
1786
1775
        if isinstance(files, basestring):
1787
1776
            files = [files]
1788
1777
 
1789
 
        inv_delta = []
 
1778
        inv = self.inventory
1790
1779
 
1791
1780
        new_files=set()
1792
1781
        unknown_files_in_directory=set()
1794
1783
        def recurse_directory_to_add_files(directory):
1795
1784
            # recurse directory and add all files
1796
1785
            # so we can check if they have changed.
1797
 
            for parent_info, file_infos in\
1798
 
                osutils.walkdirs(self.abspath(directory),
1799
 
                    directory):
1800
 
                for relpath, basename, kind, lstat, abspath in file_infos:
1801
 
                    if kind == 'file':
1802
 
                        if self.path2id(relpath): #is it versioned?
 
1786
            for contained_dir_info in self.walkdirs(directory):
 
1787
                for file_info in contained_dir_info[1]:
 
1788
                    if file_info[2] == 'file':
 
1789
                        relpath = self.relpath(file_info[0])
 
1790
                        if file_info[4]: #is it versioned?
1803
1791
                            new_files.add(relpath)
1804
1792
                        else:
1805
1793
                            unknown_files_in_directory.add(
1806
 
                                (relpath, None, kind))
 
1794
                                (relpath, None, file_info[2]))
1807
1795
 
1808
1796
        for filename in files:
1809
1797
            # Get file name into canonical form.
1810
 
            abspath = self.abspath(filename)
1811
 
            filename = self.relpath(abspath)
 
1798
            filename = self.relpath(self.abspath(filename))
1812
1799
            if len(filename) > 0:
1813
1800
                new_files.add(filename)
1814
 
                if osutils.isdir(abspath):
 
1801
                if osutils.isdir(filename) and len(os.listdir(filename)) > 0:
1815
1802
                    recurse_directory_to_add_files(filename)
1816
1803
        files = [f for f in new_files]
1817
1804
 
1818
 
        if len(files) == 0:
1819
 
            return # nothing to do
1820
 
 
1821
1805
        # Sort needed to first handle directory content before the directory
1822
1806
        files.sort(reverse=True)
1823
1807
        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,))
 
1808
            tree_delta = self.changes_from(self.basis_tree(),
 
1809
                specific_files=files)
 
1810
            for unknown_file in unknown_files_in_directory:
 
1811
                tree_delta.unversioned.extend((unknown_file,))
 
1812
            if bool(tree_delta.modified
 
1813
                    or tree_delta.added
 
1814
                    or tree_delta.renamed
 
1815
                    or tree_delta.kind_changed
 
1816
                    or tree_delta.unversioned):
1842
1817
                raise errors.BzrRemoveChangedFilesError(tree_delta)
1843
1818
 
1844
1819
        # do this before any modifications
1845
1820
        for f in files:
1846
 
            fid = self.path2id(f)
 
1821
            fid = inv.path2id(f)
1847
1822
            message=None
1848
1823
            if not fid:
1849
1824
                message="%s is not versioned." % (f,)
1854
1829
                        new_status = 'I'
1855
1830
                    else:
1856
1831
                        new_status = '?'
1857
 
                    textui.show_status(new_status, self.kind(fid), f,
 
1832
                    textui.show_status(new_status, inv[fid].kind, f,
1858
1833
                                       to_file=to_file)
1859
1834
                # unversion file
1860
 
                inv_delta.append((f, None, fid, None))
 
1835
                del inv[fid]
1861
1836
                message="removed %s" % (f,)
1862
1837
 
1863
1838
            if not keep_files:
1877
1852
            # print only one message (if any) per file.
1878
1853
            if message is not None:
1879
1854
                note(message)
1880
 
        self.apply_inventory_delta(inv_delta)
 
1855
        self._write_inventory(inv)
1881
1856
 
1882
1857
    @needs_tree_write_lock
1883
1858
    def revert(self, filenames, old_tree=None, backups=True, 
1994
1969
        """
1995
1970
        raise NotImplementedError(self.unlock)
1996
1971
 
1997
 
    def update(self, change_reporter=None):
 
1972
    def update(self):
1998
1973
        """Update a working tree along its branch.
1999
1974
 
2000
1975
        This will update the branch if its bound too, which means we have
2030
2005
                old_tip = self.branch.update()
2031
2006
            else:
2032
2007
                old_tip = None
2033
 
            return self._update_tree(old_tip, change_reporter)
 
2008
            return self._update_tree(old_tip)
2034
2009
        finally:
2035
2010
            self.unlock()
2036
2011
 
2037
2012
    @needs_tree_write_lock
2038
 
    def _update_tree(self, old_tip=None, change_reporter=None):
 
2013
    def _update_tree(self, old_tip=None):
2039
2014
        """Update a tree to the master branch.
2040
2015
 
2041
2016
        :param old_tip: if supplied, the previous tip revision the branch,
2055
2030
        try:
2056
2031
            last_rev = self.get_parent_ids()[0]
2057
2032
        except IndexError:
2058
 
            last_rev = _mod_revision.NULL_REVISION
2059
 
        if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
 
2033
            last_rev = None
 
2034
        if last_rev != self.branch.last_revision():
2060
2035
            # merge tree state up to new branch tip.
2061
2036
            basis = self.basis_tree()
2062
2037
            basis.lock_read()
2069
2044
                                      self.branch,
2070
2045
                                      to_tree,
2071
2046
                                      basis,
2072
 
                                      this_tree=self,
2073
 
                                      change_reporter=change_reporter)
 
2047
                                      this_tree=self)
2074
2048
            finally:
2075
2049
                basis.unlock()
2076
2050
            # TODO - dedup parents list with things merged by pull ?
2085
2059
            for parent in merges:
2086
2060
                parent_trees.append(
2087
2061
                    (parent, self.branch.repository.revision_tree(parent)))
2088
 
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
 
2062
            if old_tip is not None:
2089
2063
                parent_trees.append(
2090
2064
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
2091
2065
            self.set_parent_trees(parent_trees)
2094
2068
            # the working tree had the same last-revision as the master
2095
2069
            # branch did. We may still have pivot local work from the local
2096
2070
            # branch into old_tip:
2097
 
            if (old_tip is not None and not _mod_revision.is_null(old_tip)):
 
2071
            if old_tip is not None:
2098
2072
                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):
 
2073
        if old_tip and old_tip != last_rev:
2101
2074
            # our last revision was not the prior branch last revision
2102
2075
            # and we have converted that last revision to a pending merge.
2103
2076
            # base is somewhere between the branch tip now
2110
2083
            #       inventory and calls tree._write_inventory(). Ultimately we
2111
2084
            #       should be able to remove this extra flush.
2112
2085
            self.flush()
2113
 
            graph = self.branch.repository.get_graph()
2114
 
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2115
 
                                                old_tip)
 
2086
            from bzrlib.revision import common_ancestor
 
2087
            try:
 
2088
                base_rev_id = common_ancestor(self.branch.last_revision(),
 
2089
                                              old_tip,
 
2090
                                              self.branch.repository)
 
2091
            except errors.NoCommonAncestor:
 
2092
                base_rev_id = None
2116
2093
            base_tree = self.branch.repository.revision_tree(base_rev_id)
2117
2094
            other_tree = self.branch.repository.revision_tree(old_tip)
2118
2095
            result += merge.merge_inner(
2119
2096
                                  self.branch,
2120
2097
                                  other_tree,
2121
2098
                                  base_tree,
2122
 
                                  this_tree=self,
2123
 
                                  change_reporter=change_reporter)
 
2099
                                  this_tree=self)
2124
2100
        return result
2125
2101
 
2126
2102
    def _write_hashcache_if_dirty(self):
2619
2595
        if not isinstance(a_bzrdir.transport, LocalTransport):
2620
2596
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
2621
2597
        branch = a_bzrdir.open_branch()
2622
 
        if revision_id is None:
2623
 
            revision_id = _mod_revision.ensure_null(branch.last_revision())
2624
 
        else:
 
2598
        if revision_id is not None:
2625
2599
            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()
 
2600
            branch.lock_write()
 
2601
            try:
 
2602
                revision_history = branch.revision_history()
 
2603
                try:
 
2604
                    position = revision_history.index(revision_id)
 
2605
                except ValueError:
 
2606
                    raise errors.NoSuchRevision(branch, revision_id)
 
2607
                branch.set_revision_history(revision_history[:position + 1])
 
2608
            finally:
 
2609
                branch.unlock()
 
2610
        revision = branch.last_revision()
2631
2611
        inv = Inventory()
2632
2612
        wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2633
2613
                         branch,
2635
2615
                         _internal=True,
2636
2616
                         _format=self,
2637
2617
                         _bzrdir=a_bzrdir)
2638
 
        basis_tree = branch.repository.revision_tree(revision_id)
 
2618
        basis_tree = branch.repository.revision_tree(revision)
2639
2619
        if basis_tree.inventory.root is not None:
2640
2620
            wt.set_root_id(basis_tree.inventory.root.file_id)
2641
2621
        # 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)
 
2622
        wt.set_parent_trees([(revision, basis_tree)])
2647
2623
        transform.build_tree(basis_tree, wt)
2648
2624
        return wt
2649
2625
 
2720
2696
        control_files.put_utf8('format', self.get_format_string())
2721
2697
        branch = a_bzrdir.open_branch()
2722
2698
        if revision_id is None:
2723
 
            revision_id = _mod_revision.ensure_null(branch.last_revision())
 
2699
            revision_id = branch.last_revision()
2724
2700
        else:
2725
2701
            revision_id = osutils.safe_revision_id(revision_id)
2726
2702
        # WorkingTree3 can handle an inventory which has a unique root id.
2798
2774
# and not independently creatable, so are not registered.
2799
2775
_legacy_formats = [WorkingTreeFormat2(),
2800
2776
                   ]
 
2777
 
 
2778
 
 
2779
class WorkingTreeTestProviderAdapter(object):
 
2780
    """A tool to generate a suite testing multiple workingtree formats at once.
 
2781
 
 
2782
    This is done by copying the test once for each transport and injecting
 
2783
    the transport_server, transport_readonly_server, and workingtree_format
 
2784
    classes into each copy. Each copy is also given a new id() to make it
 
2785
    easy to identify.
 
2786
    """
 
2787
 
 
2788
    def __init__(self, transport_server, transport_readonly_server, formats):
 
2789
        self._transport_server = transport_server
 
2790
        self._transport_readonly_server = transport_readonly_server
 
2791
        self._formats = formats
 
2792
    
 
2793
    def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
 
2794
        """Clone test for adaption."""
 
2795
        new_test = deepcopy(test)
 
2796
        new_test.transport_server = self._transport_server
 
2797
        new_test.transport_readonly_server = self._transport_readonly_server
 
2798
        new_test.bzrdir_format = bzrdir_format
 
2799
        new_test.workingtree_format = workingtree_format
 
2800
        def make_new_test_id():
 
2801
            new_id = "%s(%s)" % (test.id(), variation)
 
2802
            return lambda: new_id
 
2803
        new_test.id = make_new_test_id()
 
2804
        return new_test
 
2805
    
 
2806
    def adapt(self, test):
 
2807
        from bzrlib.tests import TestSuite
 
2808
        result = TestSuite()
 
2809
        for workingtree_format, bzrdir_format in self._formats:
 
2810
            new_test = self._clone_test(
 
2811
                test,
 
2812
                bzrdir_format,
 
2813
                workingtree_format, workingtree_format.__class__.__name__)
 
2814
            result.addTest(new_test)
 
2815
        return result