~bzr-pqm/bzr/bzr.dev

2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
1
# Copyright (C) 2004, 2005, 2007 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for branch.pull behaviour."""
18
19
import os
20
2477.1.2 by Martin Pool
Rename push/pull back to 'run_hooks' (jameinel)
21
from bzrlib.branch import Branch, BzrBranchFormat5
22
from bzrlib.bzrdir import BzrDir
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
23
from bzrlib import errors
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
24
from bzrlib.memorytree import MemoryTree
25
from bzrlib.revision import NULL_REVISION
2477.1.2 by Martin Pool
Rename push/pull back to 'run_hooks' (jameinel)
26
from bzrlib.tests import TestSkipped
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
27
from bzrlib.tests.branch_implementations.test_branch import TestCaseWithBranch
28
29
30
class TestPull(TestCaseWithBranch):
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
31
32
    def test_pull_convergence_simple(self):
33
        # when revisions are pulled, the left-most accessible parents must 
34
        # become the revision-history.
35
        parent = self.make_branch_and_tree('parent')
36
        parent.commit('1st post', rev_id='P1', allow_pointless=True)
37
        mine = parent.bzrdir.sprout('mine').open_workingtree()
38
        mine.commit('my change', rev_id='M1', allow_pointless=True)
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
39
        parent.merge_from_branch(mine.branch)
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
40
        parent.commit('merge my change', rev_id='P2')
41
        mine.pull(parent.branch)
42
        self.assertEqual(['P1', 'P2'], mine.branch.revision_history())
43
44
    def test_pull_merged_indirect(self):
45
        # it should be possible to do a pull from one branch into another
46
        # when the tip of the target was merged into the source branch
47
        # via a third branch - so its buried in the ancestry and is not
48
        # directly accessible.
49
        parent = self.make_branch_and_tree('parent')
50
        parent.commit('1st post', rev_id='P1', allow_pointless=True)
51
        mine = parent.bzrdir.sprout('mine').open_workingtree()
52
        mine.commit('my change', rev_id='M1', allow_pointless=True)
53
        other = parent.bzrdir.sprout('other').open_workingtree()
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
54
        other.merge_from_branch(mine.branch)
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
55
        other.commit('merge my change', rev_id='O2')
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
56
        parent.merge_from_branch(other.branch)
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
57
        parent.commit('merge other', rev_id='P2')
58
        mine.pull(parent.branch)
59
        self.assertEqual(['P1', 'P2'], mine.branch.revision_history())
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
60
61
    def test_pull_updates_checkout_and_master(self):
62
        """Pulling into a checkout updates the checkout and the master branch"""
63
        master_tree = self.make_branch_and_tree('master')
64
        rev1 = master_tree.commit('master')
65
        checkout = master_tree.branch.create_checkout('checkout')
66
67
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
68
        rev2 = other.commit('other commit')
69
        # now pull, which should update both checkout and master.
70
        checkout.branch.pull(other.branch)
71
        self.assertEqual([rev1, rev2], checkout.branch.revision_history())
72
        self.assertEqual([rev1, rev2], master_tree.branch.revision_history())
73
74
    def test_pull_raises_specific_error_on_master_connection_error(self):
75
        master_tree = self.make_branch_and_tree('master')
76
        checkout = master_tree.branch.create_checkout('checkout')
77
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
78
        # move the branch out of the way on disk to cause a connection
79
        # error.
80
        os.rename('master', 'master_gone')
81
        # try to pull, which should raise a BoundBranchConnectionFailure.
82
        self.assertRaises(errors.BoundBranchConnectionFailure,
83
                checkout.branch.pull, other.branch)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
84
1551.10.23 by Aaron Bentley
Update test case per review
85
    def test_pull_overwrite(self):
86
        tree_a = self.make_branch_and_tree('tree_a')
87
        tree_a.commit('message 1')
88
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
89
        tree_a.commit('message 2', rev_id='rev2a')
90
        tree_b.commit('message 2', rev_id='rev2b')
91
        self.assertRaises(errors.DivergedBranches, tree_a.pull, tree_b.branch)
92
        tree_a.branch.pull(tree_a.branch, overwrite=True,
93
                           stop_revision='rev2b')
94
        self.assertEqual('rev2b', tree_a.branch.last_revision())
95
        self.assertEqual(tree_b.branch.revision_history(),
96
                         tree_a.branch.revision_history())
97
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
98
99
class TestPullHook(TestCaseWithBranch):
100
101
    def setUp(self):
102
        self.hook_calls = []
103
        TestCaseWithBranch.setUp(self)
104
2297.1.1 by Martin Pool
Pull now returns a PullResult rather than just an integer.
105
    def capture_post_pull_hook(self, result):
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
106
        """Capture post pull hook calls to self.hook_calls.
107
        
108
        The call is logged, as is some state of the two branches.
109
        """
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
110
        if result.local_branch:
111
            local_locked = result.local_branch.is_locked()
112
            local_base = result.local_branch.base
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
113
        else:
114
            local_locked = None
115
            local_base = None
116
        self.hook_calls.append(
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
117
            ('post_pull', result.source_branch, local_base,
118
             result.master_branch.base, result.old_revno,
2297.1.1 by Martin Pool
Pull now returns a PullResult rather than just an integer.
119
             result.old_revid,
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
120
             result.new_revno, result.new_revid,
121
             result.source_branch.is_locked(), local_locked,
122
             result.master_branch.is_locked()))
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
123
124
    def test_post_pull_empty_history(self):
125
        target = self.make_branch('target')
126
        source = self.make_branch('source')
127
        Branch.hooks.install_hook('post_pull', self.capture_post_pull_hook)
128
        target.pull(source)
129
        # with nothing there we should still get a notification, and
130
        # have both branches locked at the notification time.
131
        self.assertEqual([
132
            ('post_pull', source, None, target.base, 0, NULL_REVISION,
133
             0, NULL_REVISION, True, None, True)
134
            ],
135
            self.hook_calls)
136
137
    def test_post_pull_bound_branch(self):
138
        # pulling to a bound branch should pass in the master branch to the
139
        # hook, allowing the correct number of emails to be sent, while still
140
        # allowing hooks that want to modify the target to do so to both 
141
        # instances.
142
        target = self.make_branch('target')
143
        local = self.make_branch('local')
144
        try:
145
            local.bind(target)
146
        except errors.UpgradeRequired:
2477.1.2 by Martin Pool
Rename push/pull back to 'run_hooks' (jameinel)
147
            # We can't bind this format to itself- typically it is the local
148
            # branch that doesn't support binding.  As of May 2007
149
            # remotebranches can't be bound.  Let's instead make a new local
150
            # branch of the default type, which does allow binding.
151
            # See https://bugs.launchpad.net/bzr/+bug/112020
152
            local = BzrDir.create_branch_convenience('local2')
153
            local.bind(target)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
154
        source = self.make_branch('source')
155
        Branch.hooks.install_hook('post_pull', self.capture_post_pull_hook)
156
        local.pull(source)
157
        # with nothing there we should still get a notification, and
158
        # have both branches locked at the notification time.
159
        self.assertEqual([
160
            ('post_pull', source, local.base, target.base, 0, NULL_REVISION,
161
             0, NULL_REVISION, True, True, True)
162
            ],
163
            self.hook_calls)
164
165
    def test_post_pull_nonempty_history(self):
166
        target = self.make_branch_and_memory_tree('target')
167
        target.lock_write()
168
        target.add('')
169
        rev1 = target.commit('rev 1')
170
        target.unlock()
171
        sourcedir = target.bzrdir.clone(self.get_url('source'))
172
        source = MemoryTree.create_on_branch(sourcedir.open_branch())
173
        rev2 = source.commit('rev 2')
174
        Branch.hooks.install_hook('post_pull', self.capture_post_pull_hook)
175
        target.branch.pull(source.branch)
176
        # with nothing there we should still get a notification, and
177
        # have both branches locked at the notification time.
178
        self.assertEqual([
179
            ('post_pull', source.branch, None, target.branch.base, 1, rev1,
180
             2, rev2, True, None, True)
181
            ],
182
            self.hook_calls)