~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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
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
4523.1.1 by Martin Pool
Rename tests.branch_implementations to per_branch
26
from bzrlib.tests.per_branch.test_branch import TestCaseWithBranch
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
27
28
29
class TestPull(TestCaseWithBranch):
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
30
31
    def test_pull_convergence_simple(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
32
        # when revisions are pulled, the left-most accessible parents must
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
33
        # become the revision-history.
34
        parent = self.make_branch_and_tree('parent')
35
        parent.commit('1st post', rev_id='P1', allow_pointless=True)
36
        mine = parent.bzrdir.sprout('mine').open_workingtree()
37
        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.
38
        parent.merge_from_branch(mine.branch)
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
39
        parent.commit('merge my change', rev_id='P2')
40
        mine.pull(parent.branch)
41
        self.assertEqual(['P1', 'P2'], mine.branch.revision_history())
42
43
    def test_pull_merged_indirect(self):
44
        # it should be possible to do a pull from one branch into another
45
        # when the tip of the target was merged into the source branch
46
        # via a third branch - so its buried in the ancestry and is not
47
        # directly accessible.
48
        parent = self.make_branch_and_tree('parent')
49
        parent.commit('1st post', rev_id='P1', allow_pointless=True)
50
        mine = parent.bzrdir.sprout('mine').open_workingtree()
51
        mine.commit('my change', rev_id='M1', allow_pointless=True)
52
        other = parent.bzrdir.sprout('other').open_workingtree()
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
53
        other.merge_from_branch(mine.branch)
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
54
        other.commit('merge my change', rev_id='O2')
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
55
        parent.merge_from_branch(other.branch)
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
56
        parent.commit('merge other', rev_id='P2')
57
        mine.pull(parent.branch)
58
        self.assertEqual(['P1', 'P2'], mine.branch.revision_history())
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
59
60
    def test_pull_updates_checkout_and_master(self):
61
        """Pulling into a checkout updates the checkout and the master branch"""
62
        master_tree = self.make_branch_and_tree('master')
63
        rev1 = master_tree.commit('master')
64
        checkout = master_tree.branch.create_checkout('checkout')
65
66
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
67
        rev2 = other.commit('other commit')
68
        # now pull, which should update both checkout and master.
69
        checkout.branch.pull(other.branch)
70
        self.assertEqual([rev1, rev2], checkout.branch.revision_history())
71
        self.assertEqual([rev1, rev2], master_tree.branch.revision_history())
72
4056.6.3 by Gary van der Merwe
Add local args to pull methods, and add more tests
73
    def test_pull_local_updates_checkout_only(self):
74
        """Pulling --local into a checkout updates the checkout and not the
75
        master branch"""
76
        master_tree = self.make_branch_and_tree('master')
77
        rev1 = master_tree.commit('master')
78
        checkout = master_tree.branch.create_checkout('checkout')
79
80
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
81
        rev2 = other.commit('other commit')
4335.1.1 by Ian Clatworthy
(igc) pull --local (Gary van der Merwe)
82
        # now pull local, which should update checkout but not master.
4056.6.3 by Gary van der Merwe
Add local args to pull methods, and add more tests
83
        checkout.branch.pull(other.branch, local = True)
84
        self.assertEqual([rev1, rev2], checkout.branch.revision_history())
85
        self.assertEqual([rev1], master_tree.branch.revision_history())
86
87
    def test_pull_local_raises_LocalRequiresBoundBranch_on_unbound(self):
88
        """Pulling --local into a branch that is not bound should fail."""
89
        master_tree = self.make_branch_and_tree('branch')
90
        rev1 = master_tree.commit('master')
91
92
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
93
        rev2 = other.commit('other commit')
94
        # now pull --local, which should raise LocalRequiresBoundBranch error.
95
        self.assertRaises(errors.LocalRequiresBoundBranch,
96
                          master_tree.branch.pull, other.branch, local = True)
97
        self.assertEqual([rev1], master_tree.branch.revision_history())
98
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
99
    def test_pull_raises_specific_error_on_master_connection_error(self):
100
        master_tree = self.make_branch_and_tree('master')
101
        checkout = master_tree.branch.create_checkout('checkout')
102
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
103
        # move the branch out of the way on disk to cause a connection
104
        # error.
105
        os.rename('master', 'master_gone')
106
        # try to pull, which should raise a BoundBranchConnectionFailure.
107
        self.assertRaises(errors.BoundBranchConnectionFailure,
108
                checkout.branch.pull, other.branch)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
109
3482.1.1 by John Arbash Meinel
Fix bug #238149, RemoteBranch.pull needs to return the _real_branch's pull result.
110
    def test_pull_returns_result(self):
111
        parent = self.make_branch_and_tree('parent')
112
        parent.commit('1st post', rev_id='P1')
113
        mine = parent.bzrdir.sprout('mine').open_workingtree()
114
        mine.commit('my change', rev_id='M1')
115
        result = parent.branch.pull(mine.branch)
116
        self.assertIsNot(None, result)
117
        self.assertIs(mine.branch, result.source_branch)
118
        self.assertIs(parent.branch, result.target_branch)
119
        self.assertIs(parent.branch, result.master_branch)
120
        self.assertIs(None, result.local_branch)
121
        self.assertEqual(1, result.old_revno)
122
        self.assertEqual('P1', result.old_revid)
123
        self.assertEqual(2, result.new_revno)
124
        self.assertEqual('M1', result.new_revid)
125
        self.assertEqual(None, result.tag_conflicts)
126
1551.10.23 by Aaron Bentley
Update test case per review
127
    def test_pull_overwrite(self):
128
        tree_a = self.make_branch_and_tree('tree_a')
129
        tree_a.commit('message 1')
130
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
131
        tree_a.commit('message 2', rev_id='rev2a')
132
        tree_b.commit('message 2', rev_id='rev2b')
133
        self.assertRaises(errors.DivergedBranches, tree_a.pull, tree_b.branch)
3052.5.2 by John Arbash Meinel
Use a Graph.heads() check to determine if the ancestries are compatible.
134
        self.assertRaises(errors.DivergedBranches,
135
                          tree_a.branch.pull, tree_b.branch,
136
                          overwrite=False, stop_revision='rev2b')
137
        # It should not have updated the branch tip, but it should have fetched
138
        # the revision
139
        self.assertEqual('rev2a', tree_a.branch.last_revision())
140
        self.assertTrue(tree_a.branch.repository.has_revision('rev2b'))
2975.1.1 by Robert Collins
Minor fixes for foreign format friendliness.
141
        tree_a.branch.pull(tree_b.branch, overwrite=True,
1551.10.23 by Aaron Bentley
Update test case per review
142
                           stop_revision='rev2b')
143
        self.assertEqual('rev2b', tree_a.branch.last_revision())
144
        self.assertEqual(tree_b.branch.revision_history(),
145
                         tree_a.branch.revision_history())
146
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
147
148
class TestPullHook(TestCaseWithBranch):
149
150
    def setUp(self):
151
        self.hook_calls = []
152
        TestCaseWithBranch.setUp(self)
153
2297.1.1 by Martin Pool
Pull now returns a PullResult rather than just an integer.
154
    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
155
        """Capture post pull hook calls to self.hook_calls.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
156
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
157
        The call is logged, as is some state of the two branches.
158
        """
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
159
        if result.local_branch:
160
            local_locked = result.local_branch.is_locked()
161
            local_base = result.local_branch.base
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
162
        else:
163
            local_locked = None
164
            local_base = None
165
        self.hook_calls.append(
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
166
            ('post_pull', result.source_branch, local_base,
167
             result.master_branch.base, result.old_revno,
2297.1.1 by Martin Pool
Pull now returns a PullResult rather than just an integer.
168
             result.old_revid,
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
169
             result.new_revno, result.new_revid,
170
             result.source_branch.is_locked(), local_locked,
171
             result.master_branch.is_locked()))
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
172
173
    def test_post_pull_empty_history(self):
174
        target = self.make_branch('target')
175
        source = self.make_branch('source')
3256.2.17 by Daniel Watkins
Updated uses of Hooks.install_hook to Hooks.install_named_hook in tests.branch_implementation.test_pull.
176
        Branch.hooks.install_named_hook('post_pull',
177
            self.capture_post_pull_hook, None)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
178
        target.pull(source)
179
        # with nothing there we should still get a notification, and
180
        # have both branches locked at the notification time.
181
        self.assertEqual([
182
            ('post_pull', source, None, target.base, 0, NULL_REVISION,
183
             0, NULL_REVISION, True, None, True)
184
            ],
185
            self.hook_calls)
186
187
    def test_post_pull_bound_branch(self):
188
        # pulling to a bound branch should pass in the master branch to the
189
        # hook, allowing the correct number of emails to be sent, while still
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
190
        # allowing hooks that want to modify the target to do so to both
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
191
        # instances.
192
        target = self.make_branch('target')
193
        local = self.make_branch('local')
194
        try:
195
            local.bind(target)
196
        except errors.UpgradeRequired:
2477.1.2 by Martin Pool
Rename push/pull back to 'run_hooks' (jameinel)
197
            # We can't bind this format to itself- typically it is the local
198
            # branch that doesn't support binding.  As of May 2007
199
            # remotebranches can't be bound.  Let's instead make a new local
200
            # branch of the default type, which does allow binding.
201
            # See https://bugs.launchpad.net/bzr/+bug/112020
202
            local = BzrDir.create_branch_convenience('local2')
203
            local.bind(target)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
204
        source = self.make_branch('source')
3256.2.17 by Daniel Watkins
Updated uses of Hooks.install_hook to Hooks.install_named_hook in tests.branch_implementation.test_pull.
205
        Branch.hooks.install_named_hook('post_pull',
206
            self.capture_post_pull_hook, None)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
207
        local.pull(source)
208
        # with nothing there we should still get a notification, and
209
        # have both branches locked at the notification time.
210
        self.assertEqual([
211
            ('post_pull', source, local.base, target.base, 0, NULL_REVISION,
212
             0, NULL_REVISION, True, True, True)
213
            ],
214
            self.hook_calls)
215
216
    def test_post_pull_nonempty_history(self):
217
        target = self.make_branch_and_memory_tree('target')
218
        target.lock_write()
219
        target.add('')
220
        rev1 = target.commit('rev 1')
221
        target.unlock()
222
        sourcedir = target.bzrdir.clone(self.get_url('source'))
223
        source = MemoryTree.create_on_branch(sourcedir.open_branch())
224
        rev2 = source.commit('rev 2')
3256.2.17 by Daniel Watkins
Updated uses of Hooks.install_hook to Hooks.install_named_hook in tests.branch_implementation.test_pull.
225
        Branch.hooks.install_named_hook('post_pull',
226
            self.capture_post_pull_hook, None)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
227
        target.branch.pull(source.branch)
228
        # with nothing there we should still get a notification, and
229
        # have both branches locked at the notification time.
230
        self.assertEqual([
231
            ('post_pull', source.branch, None, target.branch.base, 1, rev1,
232
             2, rev2, True, None, True)
233
            ],
234
            self.hook_calls)