/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4211.1.5 by Jelmer Vernooij
Fix copyright year, number of columns used.
1
# Copyright (C) 2004, 2005, 2007, 2009 Canonical Ltd
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
2
#
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.
7
#
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.
12
#
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
"""Tests for branch.push behaviour."""
18
19
from cStringIO import StringIO
20
import os
21
22
from bzrlib import (
23
    branch,
24
    builtins,
25
    bzrdir,
4332.3.35 by Robert Collins
Fix failing tests.
26
    check,
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
27
    debug,
28
    errors,
29
    push,
30
    repository,
31
    tests,
32
    )
33
from bzrlib.branch import Branch
34
from bzrlib.bzrdir import BzrDir
35
from bzrlib.memorytree import MemoryTree
36
from bzrlib.revision import NULL_REVISION
37
from bzrlib.smart import client, server
38
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
39
from bzrlib.tests.per_interbranch import (
40
    TestCaseWithInterBranch,
41
    )
42
from bzrlib.transport import get_transport
43
from bzrlib.transport.local import LocalURLServer
44
45
4211.1.6 by Jelmer Vernooij
Review from Ian.
46
# These tests are based on similar tests in 
4523.1.1 by Martin Pool
Rename tests.branch_implementations to per_branch
47
# bzrlib.tests.per_branch.test_push.
4211.1.6 by Jelmer Vernooij
Review from Ian.
48
49
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
50
class TestPush(TestCaseWithInterBranch):
51
52
    def test_push_convergence_simple(self):
53
        # when revisions are pushed, the left-most accessible parents must
54
        # become the revision-history.
55
        mine = self.make_from_branch_and_tree('mine')
56
        mine.commit('1st post', rev_id='P1', allow_pointless=True)
57
        other = self.sprout_to(mine.bzrdir, 'other').open_workingtree()
58
        other.commit('my change', rev_id='M1', allow_pointless=True)
59
        mine.merge_from_branch(other.branch)
60
        mine.commit('merge my change', rev_id='P2')
61
        result = mine.branch.push(other.branch)
62
        self.assertEqual(['P1', 'P2'], other.branch.revision_history())
63
        # result object contains some structured data
64
        self.assertEqual(result.old_revid, 'M1')
65
        self.assertEqual(result.new_revid, 'P2')
66
        # and it can be treated as an integer for compatibility
67
        self.assertEqual(int(result), 0)
68
69
    def test_push_merged_indirect(self):
70
        # it should be possible to do a push from one branch into another
71
        # when the tip of the target was merged into the source branch
72
        # via a third branch - so its buried in the ancestry and is not
73
        # directly accessible.
74
        mine = self.make_from_branch_and_tree('mine')
75
        mine.commit('1st post', rev_id='P1', allow_pointless=True)
76
        target = self.sprout_to(mine.bzrdir, 'target').open_workingtree()
77
        target.commit('my change', rev_id='M1', allow_pointless=True)
78
        other = self.sprout_to(mine.bzrdir, 'other').open_workingtree()
79
        other.merge_from_branch(target.branch)
80
        other.commit('merge my change', rev_id='O2')
81
        mine.merge_from_branch(other.branch)
82
        mine.commit('merge other', rev_id='P2')
83
        mine.branch.push(target.branch)
84
        self.assertEqual(['P1', 'P2'], target.branch.revision_history())
85
86
    def test_push_to_checkout_updates_master(self):
87
        """Pushing into a checkout updates the checkout and the master branch"""
88
        master_tree = self.make_to_branch_and_tree('master')
89
        checkout = self.make_to_branch_and_tree('checkout')
90
        try:
91
            checkout.branch.bind(master_tree.branch)
92
        except errors.UpgradeRequired:
93
            # cant bind this format, the test is irrelevant.
94
            return
95
        rev1 = checkout.commit('master')
96
4211.1.5 by Jelmer Vernooij
Fix copyright year, number of columns used.
97
        other_bzrdir = self.sprout_from(master_tree.branch.bzrdir, 'other')
98
        other = other_bzrdir.open_workingtree()
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
99
        rev2 = other.commit('other commit')
100
        # now push, which should update both checkout and master.
101
        other.branch.push(checkout.branch)
102
        self.assertEqual([rev1, rev2], checkout.branch.revision_history())
103
        self.assertEqual([rev1, rev2], master_tree.branch.revision_history())
104
105
    def test_push_raises_specific_error_on_master_connection_error(self):
106
        master_tree = self.make_to_branch_and_tree('master')
107
        checkout = self.make_to_branch_and_tree('checkout')
108
        try:
109
            checkout.branch.bind(master_tree.branch)
110
        except errors.UpgradeRequired:
111
            # cant bind this format, the test is irrelevant.
112
            return
4211.1.5 by Jelmer Vernooij
Fix copyright year, number of columns used.
113
        other_bzrdir = self.sprout_from(master_tree.branch.bzrdir, 'other')
114
        other = other_bzrdir.open_workingtree()
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
115
        # move the branch out of the way on disk to cause a connection
116
        # error.
117
        os.rename('master', 'master_gone')
118
        # try to push, which should raise a BoundBranchConnectionFailure.
119
        self.assertRaises(errors.BoundBranchConnectionFailure,
120
                other.branch.push, checkout.branch)
121
122
    def test_push_uses_read_lock(self):
123
        """Push should only need a read lock on the source side."""
124
        source = self.make_from_branch_and_tree('source')
125
        target = self.make_to_branch('target')
126
127
        self.build_tree(['source/a'])
128
        source.add(['a'])
129
        source.commit('a')
130
131
        source.branch.lock_read()
132
        try:
133
            target.lock_write()
134
            try:
135
                source.branch.push(target, stop_revision=source.last_revision())
136
            finally:
137
                target.unlock()
138
        finally:
139
            source.branch.unlock()
140
141
    def test_push_within_repository(self):
142
        """Push from one branch to another inside the same repository."""
143
        try:
144
            repo = self.make_repository('repo', shared=True)
145
        except (errors.IncompatibleFormat, errors.UninitializableFormat):
146
            # This Branch format cannot create shared repositories
147
            return
148
        # This is a little bit trickier because make_branch_and_tree will not
149
        # re-use a shared repository.
150
        try:
151
            a_branch = self.make_from_branch('repo/tree')
152
        except (errors.UninitializableFormat):
153
            # Cannot create these branches
154
            return
155
        try:
156
            tree = a_branch.bzrdir.create_workingtree()
157
        except errors.NotLocalUrl:
158
            if self.vfs_transport_factory is LocalURLServer:
159
                # the branch is colocated on disk, we cannot create a checkout.
160
                # hopefully callers will expect this.
4211.1.5 by Jelmer Vernooij
Fix copyright year, number of columns used.
161
                local_controldir = bzrdir.BzrDir.open(self.get_vfs_only_url('repo/tree'))
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
162
                tree = local_controldir.create_workingtree()
163
            else:
164
                tree = a_branch.create_checkout('repo/tree', lightweight=True)
165
        self.build_tree(['repo/tree/a'])
166
        tree.add(['a'])
167
        tree.commit('a')
168
169
        to_branch = self.make_to_branch('repo/branch')
170
        tree.branch.push(to_branch)
171
172
        self.assertEqual(tree.branch.last_revision(),
173
                         to_branch.last_revision())
174
175
    def test_push_overwrite_of_non_tip_with_stop_revision(self):
176
        """Combining the stop_revision and overwrite options works.
177
178
        This was <https://bugs.launchpad.net/bzr/+bug/234229>.
179
        """
180
        source = self.make_from_branch_and_tree('source')
181
        target = self.make_to_branch('target')
182
183
        source.commit('1st commit')
184
        source.branch.push(target)
185
        source.commit('2nd commit', rev_id='rev-2')
186
        source.commit('3rd commit')
187
188
        source.branch.push(target, stop_revision='rev-2', overwrite=True)
189
        self.assertEqual('rev-2', target.last_revision())
190
191
    def test_push_with_default_stacking_does_not_create_broken_branch(self):
192
        """Pushing a new standalone branch works even when there's a default
193
        stacking policy at the destination.
194
195
        The new branch will preserve the repo format (even if it isn't the
196
        default for the branch), and will be stacked when the repo format
197
        allows (which means that the branch format isn't necessarly preserved).
198
        """
199
        if isinstance(self.branch_format_from, branch.BzrBranchFormat4):
200
            raise tests.TestNotApplicable('Not a metadir format.')
201
        if isinstance(self.branch_format_from, branch.BranchReferenceFormat):
202
            # This test could in principle apply to BranchReferenceFormat, but
203
            # make_branch_builder doesn't support it.
204
            raise tests.TestSkipped(
205
                "BranchBuilder can't make reference branches.")
206
        # Make a branch called "local" in a stackable repository
207
        # The branch has 3 revisions:
208
        #   - rev-1, adds a file
209
        #   - rev-2, no changes
210
        #   - rev-3, modifies the file.
211
        repo = self.make_repository('repo', shared=True, format='1.6')
212
        builder = self.make_from_branch_builder('repo/local')
213
        builder.start_series()
214
        builder.build_snapshot('rev-1', None, [
215
            ('add', ('', 'root-id', 'directory', '')),
216
            ('add', ('filename', 'f-id', 'file', 'content\n'))])
217
        builder.build_snapshot('rev-2', ['rev-1'], [])
218
        builder.build_snapshot('rev-3', ['rev-2'],
219
            [('modify', ('f-id', 'new-content\n'))])
220
        builder.finish_series()
221
        trunk = builder.get_branch()
222
        # Sprout rev-1 to "trunk", so that we can stack on it.
223
        trunk.bzrdir.sprout(self.get_url('trunk'), revision_id='rev-1')
224
        # Set a default stacking policy so that new branches will automatically
225
        # stack on trunk.
226
        self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
227
        # Push rev-2 to a new branch "remote".  It will be stacked on "trunk".
228
        output = StringIO()
229
        push._show_push_branch(trunk, 'rev-2', self.get_url('remote'), output)
230
        # Push rev-3 onto "remote".  If "remote" not stacked and is missing the
231
        # fulltext record for f-id @ rev-1, then this will fail.
232
        remote_branch = Branch.open(self.get_url('remote'))
233
        trunk.push(remote_branch)
4332.3.35 by Robert Collins
Fix failing tests.
234
        check.check_dwim(remote_branch.base, False, True, True)
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
235
236
    def test_no_get_parent_map_after_insert_stream(self):
237
        # Effort test for bug 331823
238
        self.setup_smart_server_with_call_log()
239
        # Make a local branch with four revisions.  Four revisions because:
240
        # one to push, one there for _walk_to_common_revisions to find, one we
241
        # don't want to access, one for luck :)
242
        if isinstance(self.branch_format_from, branch.BranchReferenceFormat):
243
            # This test could in principle apply to BranchReferenceFormat, but
244
            # make_branch_builder doesn't support it.
245
            raise tests.TestSkipped(
246
                "BranchBuilder can't make reference branches.")
247
        try:
248
            builder = self.make_from_branch_builder('local')
249
        except (errors.TransportNotPossible, errors.UninitializableFormat):
250
            raise tests.TestNotApplicable('format not directly constructable')
251
        builder.start_series()
252
        builder.build_snapshot('first', None, [
253
            ('add', ('', 'root-id', 'directory', ''))])
254
        builder.build_snapshot('second', ['first'], [])
255
        builder.build_snapshot('third', ['second'], [])
256
        builder.build_snapshot('fourth', ['third'], [])
257
        builder.finish_series()
258
        local = builder.get_branch()
259
        local = branch.Branch.open(self.get_vfs_only_url('local'))
260
        # Initial push of three revisions
261
        remote_bzrdir = local.bzrdir.sprout(
262
            self.get_url('remote'), revision_id='third')
263
        remote = remote_bzrdir.open_branch()
264
        # Push fourth revision
265
        self.reset_smart_call_log()
266
        self.disableOptimisticGetParentMap()
267
        self.assertFalse(local.is_locked())
268
        local.push(remote)
269
        hpss_call_names = [item.call.method for item in self.hpss_calls]
270
        self.assertTrue('Repository.insert_stream' in hpss_call_names)
271
        insert_stream_idx = hpss_call_names.index('Repository.insert_stream')
272
        calls_after_insert_stream = hpss_call_names[insert_stream_idx:]
273
        # After inserting the stream the client has no reason to query the
274
        # remote graph any further.
275
        self.assertEqual(
276
            ['Repository.insert_stream', 'Repository.insert_stream', 'get',
277
             'Branch.set_last_revision_info', 'Branch.unlock'],
278
            calls_after_insert_stream)
279
280
    def disableOptimisticGetParentMap(self):
281
        # Tweak some class variables to stop remote get_parent_map calls asking
282
        # for or receiving more data than the caller asked for.
283
        old_flag = SmartServerRepositoryGetParentMap.no_extra_results
284
        inter_class = repository.InterRepository
285
        old_batch_size = inter_class._walk_to_common_revisions_batch_size
286
        inter_class._walk_to_common_revisions_batch_size = 1
287
        SmartServerRepositoryGetParentMap.no_extra_results = True
288
        def reset_values():
289
            SmartServerRepositoryGetParentMap.no_extra_results = old_flag
290
            inter_class._walk_to_common_revisions_batch_size = old_batch_size
291
        self.addCleanup(reset_values)
292
293
294
class TestPushHook(TestCaseWithInterBranch):
295
296
    def setUp(self):
297
        self.hook_calls = []
298
        TestCaseWithInterBranch.setUp(self)
299
300
    def capture_post_push_hook(self, result):
301
        """Capture post push hook calls to self.hook_calls.
302
303
        The call is logged, as is some state of the two branches.
304
        """
305
        if result.local_branch:
306
            local_locked = result.local_branch.is_locked()
307
            local_base = result.local_branch.base
308
        else:
309
            local_locked = None
310
            local_base = None
311
        self.hook_calls.append(
312
            ('post_push', result.source_branch, local_base,
313
             result.master_branch.base,
314
             result.old_revno, result.old_revid,
315
             result.new_revno, result.new_revid,
316
             result.source_branch.is_locked(), local_locked,
317
             result.master_branch.is_locked()))
318
319
    def test_post_push_empty_history(self):
320
        target = self.make_to_branch('target')
321
        source = self.make_from_branch('source')
322
        Branch.hooks.install_named_hook('post_push',
323
                                        self.capture_post_push_hook, None)
324
        source.push(target)
325
        # with nothing there we should still get a notification, and
326
        # have both branches locked at the notification time.
327
        self.assertEqual([
328
            ('post_push', source, None, target.base, 0, NULL_REVISION,
329
             0, NULL_REVISION, True, None, True)
330
            ],
331
            self.hook_calls)
332
333
    def test_post_push_bound_branch(self):
334
        # pushing to a bound branch should pass in the master branch to the
335
        # hook, allowing the correct number of emails to be sent, while still
336
        # allowing hooks that want to modify the target to do so to both
337
        # instances.
338
        target = self.make_to_branch('target')
339
        local = self.make_from_branch('local')
340
        try:
341
            local.bind(target)
342
        except errors.UpgradeRequired:
343
            # We can't bind this format to itself- typically it is the local
344
            # branch that doesn't support binding.  As of May 2007
345
            # remotebranches can't be bound.  Let's instead make a new local
346
            # branch of the default type, which does allow binding.
347
            # See https://bugs.launchpad.net/bzr/+bug/112020
348
            local = BzrDir.create_branch_convenience('local2')
349
            local.bind(target)
350
        source = self.make_from_branch('source')
351
        Branch.hooks.install_named_hook('post_push',
352
                                        self.capture_post_push_hook, None)
353
        source.push(local)
354
        # with nothing there we should still get a notification, and
355
        # have both branches locked at the notification time.
356
        self.assertEqual([
357
            ('post_push', source, local.base, target.base, 0, NULL_REVISION,
358
             0, NULL_REVISION, True, True, True)
359
            ],
360
            self.hook_calls)
361
362
    def test_post_push_nonempty_history(self):
363
        target = self.make_to_branch_and_tree('target')
364
        target.lock_write()
365
        target.add('')
366
        rev1 = target.commit('rev 1')
367
        target.unlock()
368
        sourcedir = target.bzrdir.clone(self.get_url('source'))
369
        source = MemoryTree.create_on_branch(sourcedir.open_branch())
370
        rev2 = source.commit('rev 2')
371
        Branch.hooks.install_named_hook('post_push',
372
                                        self.capture_post_push_hook, None)
373
        source.branch.push(target.branch)
374
        # with nothing there we should still get a notification, and
375
        # have both branches locked at the notification time.
376
        self.assertEqual([
377
            ('post_push', source.branch, None, target.branch.base, 1, rev1,
378
             2, rev2, True, None, True)
379
            ],
380
            self.hook_calls)