~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
21
from bzrlib.branch import Branch
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
22
from bzrlib import errors
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
23
from bzrlib.memorytree import MemoryTree
24
from bzrlib.revision import NULL_REVISION
25
from bzrlib.tests.branch_implementations.test_branch import TestCaseWithBranch
26
27
28
class TestPull(TestCaseWithBranch):
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
29
30
    def test_pull_convergence_simple(self):
31
        # when revisions are pulled, the left-most accessible parents must 
32
        # become the revision-history.
33
        parent = self.make_branch_and_tree('parent')
34
        parent.commit('1st post', rev_id='P1', allow_pointless=True)
35
        mine = parent.bzrdir.sprout('mine').open_workingtree()
36
        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.
37
        parent.merge_from_branch(mine.branch)
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
38
        parent.commit('merge my change', rev_id='P2')
39
        mine.pull(parent.branch)
40
        self.assertEqual(['P1', 'P2'], mine.branch.revision_history())
41
42
    def test_pull_merged_indirect(self):
43
        # it should be possible to do a pull from one branch into another
44
        # when the tip of the target was merged into the source branch
45
        # via a third branch - so its buried in the ancestry and is not
46
        # directly accessible.
47
        parent = self.make_branch_and_tree('parent')
48
        parent.commit('1st post', rev_id='P1', allow_pointless=True)
49
        mine = parent.bzrdir.sprout('mine').open_workingtree()
50
        mine.commit('my change', rev_id='M1', allow_pointless=True)
51
        other = parent.bzrdir.sprout('other').open_workingtree()
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
52
        other.merge_from_branch(mine.branch)
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
53
        other.commit('merge my change', rev_id='O2')
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
54
        parent.merge_from_branch(other.branch)
1649.1.1 by Robert Collins
* 'pull' and 'push' now normalise the revision history, so that any two
55
        parent.commit('merge other', rev_id='P2')
56
        mine.pull(parent.branch)
57
        self.assertEqual(['P1', 'P2'], mine.branch.revision_history())
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
58
59
    def test_pull_updates_checkout_and_master(self):
60
        """Pulling into a checkout updates the checkout and the master branch"""
61
        master_tree = self.make_branch_and_tree('master')
62
        rev1 = master_tree.commit('master')
63
        checkout = master_tree.branch.create_checkout('checkout')
64
65
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
66
        rev2 = other.commit('other commit')
67
        # now pull, which should update both checkout and master.
68
        checkout.branch.pull(other.branch)
69
        self.assertEqual([rev1, rev2], checkout.branch.revision_history())
70
        self.assertEqual([rev1, rev2], master_tree.branch.revision_history())
71
72
    def test_pull_raises_specific_error_on_master_connection_error(self):
73
        master_tree = self.make_branch_and_tree('master')
74
        checkout = master_tree.branch.create_checkout('checkout')
75
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
76
        # move the branch out of the way on disk to cause a connection
77
        # error.
78
        os.rename('master', 'master_gone')
79
        # try to pull, which should raise a BoundBranchConnectionFailure.
80
        self.assertRaises(errors.BoundBranchConnectionFailure,
81
                checkout.branch.pull, other.branch)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
82
1551.10.23 by Aaron Bentley
Update test case per review
83
    def test_pull_overwrite(self):
84
        tree_a = self.make_branch_and_tree('tree_a')
85
        tree_a.commit('message 1')
86
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
87
        tree_a.commit('message 2', rev_id='rev2a')
88
        tree_b.commit('message 2', rev_id='rev2b')
89
        self.assertRaises(errors.DivergedBranches, tree_a.pull, tree_b.branch)
90
        tree_a.branch.pull(tree_a.branch, overwrite=True,
91
                           stop_revision='rev2b')
92
        self.assertEqual('rev2b', tree_a.branch.last_revision())
93
        self.assertEqual(tree_b.branch.revision_history(),
94
                         tree_a.branch.revision_history())
95
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
96
97
class TestPullHook(TestCaseWithBranch):
98
99
    def setUp(self):
100
        self.hook_calls = []
101
        TestCaseWithBranch.setUp(self)
102
2297.1.1 by Martin Pool
Pull now returns a PullResult rather than just an integer.
103
    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
104
        """Capture post pull hook calls to self.hook_calls.
105
        
106
        The call is logged, as is some state of the two branches.
107
        """
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
108
        if result.local_branch:
109
            local_locked = result.local_branch.is_locked()
110
            local_base = result.local_branch.base
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
111
        else:
112
            local_locked = None
113
            local_base = None
114
        self.hook_calls.append(
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
115
            ('post_pull', result.source_branch, local_base,
116
             result.master_branch.base, result.old_revno,
2297.1.1 by Martin Pool
Pull now returns a PullResult rather than just an integer.
117
             result.old_revid,
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
118
             result.new_revno, result.new_revid,
119
             result.source_branch.is_locked(), local_locked,
120
             result.master_branch.is_locked()))
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
121
122
    def test_post_pull_empty_history(self):
123
        target = self.make_branch('target')
124
        source = self.make_branch('source')
125
        Branch.hooks.install_hook('post_pull', self.capture_post_pull_hook)
126
        target.pull(source)
127
        # with nothing there we should still get a notification, and
128
        # have both branches locked at the notification time.
129
        self.assertEqual([
130
            ('post_pull', source, None, target.base, 0, NULL_REVISION,
131
             0, NULL_REVISION, True, None, True)
132
            ],
133
            self.hook_calls)
134
135
    def test_post_pull_bound_branch(self):
136
        # pulling to a bound branch should pass in the master branch to the
137
        # hook, allowing the correct number of emails to be sent, while still
138
        # allowing hooks that want to modify the target to do so to both 
139
        # instances.
140
        target = self.make_branch('target')
141
        local = self.make_branch('local')
142
        try:
143
            local.bind(target)
144
        except errors.UpgradeRequired:
145
            # cant bind this format, the test is irrelevant.
146
            return
147
        source = self.make_branch('source')
148
        Branch.hooks.install_hook('post_pull', self.capture_post_pull_hook)
149
        local.pull(source)
150
        # with nothing there we should still get a notification, and
151
        # have both branches locked at the notification time.
152
        self.assertEqual([
153
            ('post_pull', source, local.base, target.base, 0, NULL_REVISION,
154
             0, NULL_REVISION, True, True, True)
155
            ],
156
            self.hook_calls)
157
158
    def test_post_pull_nonempty_history(self):
159
        target = self.make_branch_and_memory_tree('target')
160
        target.lock_write()
161
        target.add('')
162
        rev1 = target.commit('rev 1')
163
        target.unlock()
164
        sourcedir = target.bzrdir.clone(self.get_url('source'))
165
        source = MemoryTree.create_on_branch(sourcedir.open_branch())
166
        rev2 = source.commit('rev 2')
167
        Branch.hooks.install_hook('post_pull', self.capture_post_pull_hook)
168
        target.branch.pull(source.branch)
169
        # with nothing there we should still get a notification, and
170
        # have both branches locked at the notification time.
171
        self.assertEqual([
172
            ('post_pull', source.branch, None, target.branch.base, 1, rev1,
173
             2, rev2, True, None, True)
174
            ],
175
            self.hook_calls)