~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_push.py

  • Committer: Ian Clatworthy
  • Date: 2007-08-13 14:33:10 UTC
  • mto: (2733.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 2734.
  • Revision ID: ian.clatworthy@internode.on.net-20070813143310-twhj4la0qnupvze8
Added Quick Start Summary

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
18
"""Black-box tests for bzr push."""
19
19
 
20
 
import re
 
20
import os
21
21
 
22
22
from bzrlib import (
23
 
    branch,
24
 
    bzrdir,
25
23
    errors,
26
 
    osutils,
27
 
    tests,
28
 
    transport,
29
 
    uncommit,
30
24
    urlutils,
31
 
    workingtree
32
 
    )
33
 
from bzrlib.repofmt import knitrepo
34
 
from bzrlib.tests import (
35
 
    blackbox,
36
 
    http_server,
37
 
    scenarios,
38
 
    test_foreign,
39
 
    test_server,
40
 
    )
41
 
from bzrlib.transport import memory
42
 
 
43
 
 
44
 
load_tests = scenarios.load_tests_apply_scenarios
45
 
 
46
 
 
47
 
class TestPush(tests.TestCaseWithTransport):
48
 
 
49
 
    def test_push_error_on_vfs_http(self):
50
 
        """ pushing a branch to a HTTP server fails cleanly. """
51
 
        # the trunk is published on a web server
52
 
        self.transport_readonly_server = http_server.HttpServer
53
 
        self.make_branch('source')
54
 
        public_url = self.get_readonly_url('target')
55
 
        self.run_bzr_error(['http does not support mkdir'],
56
 
                           ['push', public_url],
57
 
                           working_dir='source')
 
25
    )
 
26
from bzrlib.branch import Branch
 
27
from bzrlib.bzrdir import BzrDirMetaFormat1
 
28
from bzrlib.osutils import abspath
 
29
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
 
30
from bzrlib.tests.blackbox import ExternalBase
 
31
from bzrlib.uncommit import uncommit
 
32
from bzrlib.urlutils import local_path_from_url
 
33
from bzrlib.workingtree import WorkingTree
 
34
 
 
35
 
 
36
class TestPush(ExternalBase):
58
37
 
59
38
    def test_push_remember(self):
60
39
        """Push changes from one branch to another and test push location."""
78
57
        self.assertEqual(None, branch_b.get_push_location())
79
58
 
80
59
        # test push for failure without push location set
81
 
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
 
60
        os.chdir('branch_a')
 
61
        out = self.run_bzr('push', retcode=3)
82
62
        self.assertEquals(out,
83
63
                ('','bzr: ERROR: No push location known or specified.\n'))
84
64
 
85
65
        # test not remembered if cannot actually push
86
 
        self.run_bzr('push path/which/doesnt/exist',
87
 
                     working_dir='branch_a', retcode=3)
88
 
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
 
66
        self.run_bzr('push ../path/which/doesnt/exist', retcode=3)
 
67
        out = self.run_bzr('push', retcode=3)
89
68
        self.assertEquals(
90
69
                ('', 'bzr: ERROR: No push location known or specified.\n'),
91
70
                out)
92
71
 
93
72
        # test implicit --remember when no push location set, push fails
94
 
        out = self.run_bzr('push ../branch_b',
95
 
                           working_dir='branch_a', retcode=3)
 
73
        out = self.run_bzr('push ../branch_b', retcode=3)
96
74
        self.assertEquals(out,
97
75
                ('','bzr: ERROR: These branches have diverged.  '
98
 
                 'See "bzr help diverged-branches" for more information.\n'))
99
 
        self.assertEquals(osutils.abspath(branch_a.get_push_location()),
100
 
                          osutils.abspath(branch_b.bzrdir.root_transport.base))
 
76
                    'Try using "merge" and then "push".\n'))
 
77
        self.assertEquals(abspath(branch_a.get_push_location()),
 
78
                          abspath(branch_b.bzrdir.root_transport.base))
101
79
 
102
80
        # test implicit --remember after resolving previous failure
103
 
        uncommit.uncommit(branch=branch_b, tree=tree_b)
 
81
        uncommit(branch=branch_b, tree=tree_b)
104
82
        transport.delete('branch_b/c')
105
 
        out, err = self.run_bzr('push', working_dir='branch_a')
 
83
        out, err = self.run_bzr('push')
106
84
        path = branch_a.get_push_location()
 
85
        self.assertEquals(out,
 
86
                          'Using saved location: %s\n' 
 
87
                          'Pushed up to revision 2.\n'
 
88
                          % local_path_from_url(path))
107
89
        self.assertEqual(err,
108
 
                         'Using saved push location: %s\n'
109
 
                         'All changes applied successfully.\n'
110
 
                         'Pushed up to revision 2.\n'
111
 
                         % urlutils.local_path_from_url(path))
 
90
                         'All changes applied successfully.\n')
112
91
        self.assertEqual(path,
113
92
                         branch_b.bzrdir.root_transport.base)
114
93
        # test explicit --remember
115
 
        self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
 
94
        self.run_bzr('push ../branch_c --remember')
116
95
        self.assertEquals(branch_a.get_push_location(),
117
96
                          branch_c.bzrdir.root_transport.base)
118
 
 
 
97
    
119
98
    def test_push_without_tree(self):
120
99
        # bzr push from a branch that does not have a checkout should work.
121
100
        b = self.make_branch('.')
122
101
        out, err = self.run_bzr('push pushed-location')
123
102
        self.assertEqual('', out)
124
103
        self.assertEqual('Created new branch.\n', err)
125
 
        b2 = branch.Branch.open('pushed-location')
 
104
        b2 = Branch.open('pushed-location')
126
105
        self.assertEndsWith(b2.base, 'pushed-location/')
127
106
 
128
 
    def test_push_no_tree(self):
129
 
        # bzr push --no-tree of a branch with working trees
130
 
        b = self.make_branch_and_tree('push-from')
131
 
        self.build_tree(['push-from/file'])
132
 
        b.add('file')
133
 
        b.commit('commit 1')
134
 
        out, err = self.run_bzr('push --no-tree -d push-from push-to')
135
 
        self.assertEqual('', out)
136
 
        self.assertEqual('Created new branch.\n', err)
137
 
        self.assertPathDoesNotExist('push-to/file')
138
 
 
139
107
    def test_push_new_branch_revision_count(self):
140
 
        # bzr push of a branch with revisions to a new location
141
 
        # should print the number of revisions equal to the length of the
 
108
        # bzr push of a branch with revisions to a new location 
 
109
        # should print the number of revisions equal to the length of the 
142
110
        # local branch.
143
111
        t = self.make_branch_and_tree('tree')
144
112
        self.build_tree(['tree/file'])
145
113
        t.add('file')
146
114
        t.commit('commit 1')
147
 
        out, err = self.run_bzr('push -d tree pushed-to')
 
115
        os.chdir('tree')
 
116
        out, err = self.run_bzr('push pushed-to')
 
117
        os.chdir('..')
148
118
        self.assertEqual('', out)
149
119
        self.assertEqual('Created new branch.\n', err)
150
120
 
151
 
    def test_push_quiet(self):
152
 
        # test that using -q makes output quiet
153
 
        t = self.make_branch_and_tree('tree')
154
 
        self.build_tree(['tree/file'])
155
 
        t.add('file')
156
 
        t.commit('commit 1')
157
 
        self.run_bzr('push -d tree pushed-to')
158
 
        path = t.branch.get_push_location()
159
 
        out, err = self.run_bzr('push', working_dir="tree")
160
 
        self.assertEqual('Using saved push location: %s\n'
161
 
                         'No new revisions or tags to push.\n' %
162
 
                         urlutils.local_path_from_url(path), err)
163
 
        out, err = self.run_bzr('push -q', working_dir="tree")
164
 
        self.assertEqual('', out)
165
 
        self.assertEqual('', err)
166
 
 
167
121
    def test_push_only_pushes_history(self):
168
122
        # Knit branches should only push the history for the current revision.
169
 
        format = bzrdir.BzrDirMetaFormat1()
170
 
        format.repository_format = knitrepo.RepositoryFormatKnit1()
 
123
        format = BzrDirMetaFormat1()
 
124
        format.repository_format = RepositoryFormatKnit1()
171
125
        shared_repo = self.make_repository('repo', format=format, shared=True)
172
126
        shared_repo.set_make_working_trees(True)
173
127
 
174
128
        def make_shared_tree(path):
175
129
            shared_repo.bzrdir.root_transport.mkdir(path)
176
130
            shared_repo.bzrdir.create_branch_convenience('repo/' + path)
177
 
            return workingtree.WorkingTree.open('repo/' + path)
 
131
            return WorkingTree.open('repo/' + path)
178
132
        tree_a = make_shared_tree('a')
179
133
        self.build_tree(['repo/a/file'])
180
134
        tree_a.add('file')
195
149
 
196
150
        # Now that we have a repository with shared files, make sure
197
151
        # that things aren't copied out by a 'push'
198
 
        self.run_bzr('push ../../push-b', working_dir='repo/b')
199
 
        pushed_tree = workingtree.WorkingTree.open('push-b')
 
152
        os.chdir('repo/b')
 
153
        self.run_bzr('push ../../push-b')
 
154
        pushed_tree = WorkingTree.open('../../push-b')
200
155
        pushed_repo = pushed_tree.branch.repository
201
156
        self.assertFalse(pushed_repo.has_revision('a-1'))
202
157
        self.assertFalse(pushed_repo.has_revision('a-2'))
204
159
 
205
160
    def test_push_funky_id(self):
206
161
        t = self.make_branch_and_tree('tree')
207
 
        self.build_tree(['tree/filename'])
 
162
        os.chdir('tree')
 
163
        self.build_tree(['filename'])
208
164
        t.add('filename', 'funky-chars<>%&;"\'')
209
165
        t.commit('commit filename')
210
 
        self.run_bzr('push -d tree new-tree')
 
166
        self.run_bzr('push ../new-tree')
211
167
 
212
168
    def test_push_dash_d(self):
213
169
        t = self.make_branch_and_tree('from')
214
170
        t.commit(allow_pointless=True,
215
171
                message='first commit')
216
172
        self.run_bzr('push -d from to-one')
217
 
        self.assertPathExists('to-one')
218
 
        self.run_bzr('push -d %s %s'
 
173
        self.failUnlessExists('to-one')
 
174
        self.run_bzr('push -d %s %s' 
219
175
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
220
 
        self.assertPathExists('to-two')
221
 
 
222
 
    def test_push_repository_no_branch_doesnt_fetch_all_revs(self):
223
 
        # See https://bugs.launchpad.net/bzr/+bug/465517
224
 
        target_repo = self.make_repository('target')
225
 
        source = self.make_branch_builder('source')
226
 
        source.start_series()
227
 
        source.build_snapshot('A', None, [
228
 
            ('add', ('', 'root-id', 'directory', None))])
229
 
        source.build_snapshot('B', ['A'], [])
230
 
        source.build_snapshot('C', ['A'], [])
231
 
        source.finish_series()
232
 
        self.run_bzr('push target -d source')
233
 
        self.addCleanup(target_repo.lock_read().unlock)
234
 
        # We should have pushed 'C', but not 'B', since it isn't in the
235
 
        # ancestry
236
 
        self.assertEqual([('A',), ('C',)], sorted(target_repo.revisions.keys()))
237
 
 
238
 
    def test_push_smart_non_stacked_streaming_acceptance(self):
239
 
        self.setup_smart_server_with_call_log()
240
 
        t = self.make_branch_and_tree('from')
241
 
        t.commit(allow_pointless=True, message='first commit')
242
 
        self.reset_smart_call_log()
243
 
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
244
 
        # This figure represent the amount of work to perform this use case. It
245
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
246
 
        # being too low. If rpc_count increases, more network roundtrips have
247
 
        # become necessary for this use case. Please do not adjust this number
248
 
        # upwards without agreement from bzr's network support maintainers.
249
 
        self.assertLength(9, self.hpss_calls)
250
 
 
251
 
    def test_push_smart_stacked_streaming_acceptance(self):
252
 
        self.setup_smart_server_with_call_log()
253
 
        parent = self.make_branch_and_tree('parent', format='1.9')
254
 
        parent.commit(message='first commit')
255
 
        local = parent.bzrdir.sprout('local').open_workingtree()
256
 
        local.commit(message='local commit')
257
 
        self.reset_smart_call_log()
258
 
        self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
259
 
            self.get_url('public')], working_dir='local')
260
 
        # This figure represent the amount of work to perform this use case. It
261
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
262
 
        # being too low. If rpc_count increases, more network roundtrips have
263
 
        # become necessary for this use case. Please do not adjust this number
264
 
        # upwards without agreement from bzr's network support maintainers.
265
 
        self.assertLength(13, self.hpss_calls)
266
 
        remote = branch.Branch.open('public')
267
 
        self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
268
 
 
269
 
    def test_push_smart_tags_streaming_acceptance(self):
270
 
        self.setup_smart_server_with_call_log()
271
 
        t = self.make_branch_and_tree('from')
272
 
        rev_id = t.commit(allow_pointless=True, message='first commit')
273
 
        t.branch.tags.set_tag('new-tag', rev_id)
274
 
        self.reset_smart_call_log()
275
 
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
276
 
        # This figure represent the amount of work to perform this use case. It
277
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
278
 
        # being too low. If rpc_count increases, more network roundtrips have
279
 
        # become necessary for this use case. Please do not adjust this number
280
 
        # upwards without agreement from bzr's network support maintainers.
281
 
        self.assertLength(11, self.hpss_calls)
282
 
 
283
 
    def test_push_smart_incremental_acceptance(self):
284
 
        self.setup_smart_server_with_call_log()
285
 
        t = self.make_branch_and_tree('from')
286
 
        rev_id1 = t.commit(allow_pointless=True, message='first commit')
287
 
        rev_id2 = t.commit(allow_pointless=True, message='second commit')
288
 
        self.run_bzr(
289
 
            ['push', self.get_url('to-one'), '-r1'], working_dir='from')
290
 
        self.reset_smart_call_log()
291
 
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
292
 
        # This figure represent the amount of work to perform this use case. It
293
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
294
 
        # being too low. If rpc_count increases, more network roundtrips have
295
 
        # become necessary for this use case. Please do not adjust this number
296
 
        # upwards without agreement from bzr's network support maintainers.
297
 
        self.assertLength(11, self.hpss_calls)
298
 
 
299
 
    def test_push_smart_with_default_stacking_url_path_segment(self):
300
 
        # If the default stacked-on location is a path element then branches
301
 
        # we push there over the smart server are stacked and their
302
 
        # stacked_on_url is that exact path segment. Added to nail bug 385132.
303
 
        self.setup_smart_server_with_call_log()
304
 
        self.make_branch('stack-on', format='1.9')
305
 
        self.make_bzrdir('.').get_config().set_default_stack_on(
306
 
            '/stack-on')
307
 
        self.make_branch('from', format='1.9')
308
 
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
309
 
        b = branch.Branch.open(self.get_url('to'))
310
 
        self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
311
 
 
312
 
    def test_push_smart_with_default_stacking_relative_path(self):
313
 
        # If the default stacked-on location is a relative path then branches
314
 
        # we push there over the smart server are stacked and their
315
 
        # stacked_on_url is a relative path. Added to nail bug 385132.
316
 
        self.setup_smart_server_with_call_log()
317
 
        self.make_branch('stack-on', format='1.9')
318
 
        self.make_bzrdir('.').get_config().set_default_stack_on('stack-on')
319
 
        self.make_branch('from', format='1.9')
320
 
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
321
 
        b = branch.Branch.open(self.get_url('to'))
322
 
        self.assertEqual('../stack-on', b.get_stacked_on_url())
 
176
        self.failUnlessExists('to-two')
323
177
 
324
178
    def create_simple_tree(self):
325
179
        tree = self.make_branch_and_tree('tree')
337
191
                           working_dir='tree')
338
192
        self.run_bzr('push ../new/tree --create-prefix',
339
193
                     working_dir='tree')
340
 
        new_tree = workingtree.WorkingTree.open('new/tree')
 
194
        new_tree = WorkingTree.open('new/tree')
341
195
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
342
 
        self.assertPathExists('new/tree/a')
 
196
        self.failUnlessExists('new/tree/a')
343
197
 
344
198
    def test_push_use_existing(self):
345
199
        """'bzr push --use-existing-dir' can push into an existing dir.
357
211
        self.run_bzr('push --use-existing-dir ../target',
358
212
                     working_dir='tree')
359
213
 
360
 
        new_tree = workingtree.WorkingTree.open('target')
 
214
        new_tree = WorkingTree.open('target')
361
215
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
362
216
        # The push should have created target/a
363
 
        self.assertPathExists('target/a')
364
 
 
365
 
    def test_push_use_existing_into_empty_bzrdir(self):
366
 
        """'bzr push --use-existing-dir' into a dir with an empty .bzr dir
367
 
        fails.
368
 
        """
369
 
        tree = self.create_simple_tree()
370
 
        self.build_tree(['target/', 'target/.bzr/'])
371
 
        self.run_bzr_error(
372
 
            ['Target directory ../target already contains a .bzr directory, '
373
 
             'but it is not valid.'],
374
 
            'push ../target --use-existing-dir', working_dir='tree')
 
217
        self.failUnlessExists('target/a')
375
218
 
376
219
    def test_push_onto_repo(self):
377
220
        """We should be able to 'bzr push' into an existing bzrdir."""
384
227
        # Pushing onto an existing bzrdir will create a repository and
385
228
        # branch as needed, but will only create a working tree if there was
386
229
        # no BzrDir before.
387
 
        self.assertRaises(errors.NoWorkingTree,
388
 
                          workingtree.WorkingTree.open, 'repo')
389
 
        new_branch = branch.Branch.open('repo')
 
230
        self.assertRaises(errors.NoWorkingTree, WorkingTree.open, 'repo')
 
231
        new_branch = Branch.open('repo')
390
232
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
391
233
 
392
234
    def test_push_onto_just_bzrdir(self):
402
244
        self.run_bzr_error(['At ../dir you have a valid .bzr control'],
403
245
                'push ../dir',
404
246
                working_dir='tree')
405
 
 
406
 
    def test_push_with_revisionspec(self):
407
 
        """We should be able to push a revision older than the tip."""
408
 
        tree_from = self.make_branch_and_tree('from')
409
 
        tree_from.commit("One.", rev_id="from-1")
410
 
        tree_from.commit("Two.", rev_id="from-2")
411
 
 
412
 
        self.run_bzr('push -r1 ../to', working_dir='from')
413
 
 
414
 
        tree_to = workingtree.WorkingTree.open('to')
415
 
        repo_to = tree_to.branch.repository
416
 
        self.assertTrue(repo_to.has_revision('from-1'))
417
 
        self.assertFalse(repo_to.has_revision('from-2'))
418
 
        self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
419
 
 
420
 
        self.run_bzr_error(
421
 
            ['bzr: ERROR: bzr push --revision '
422
 
             'takes exactly one revision identifier\n'],
423
 
            'push -r0..2 ../to', working_dir='from')
424
 
 
425
 
    def create_trunk_and_feature_branch(self):
426
 
        # We have a mainline
427
 
        trunk_tree = self.make_branch_and_tree('target',
428
 
            format='1.9')
429
 
        trunk_tree.commit('mainline')
430
 
        # and a branch from it
431
 
        branch_tree = self.make_branch_and_tree('branch',
432
 
            format='1.9')
433
 
        branch_tree.pull(trunk_tree.branch)
434
 
        branch_tree.branch.set_parent(trunk_tree.branch.base)
435
 
        # with some work on it
436
 
        branch_tree.commit('moar work plz')
437
 
        return trunk_tree, branch_tree
438
 
 
439
 
    def assertPublished(self, branch_revid, stacked_on):
440
 
        """Assert that the branch 'published' has been published correctly."""
441
 
        published_branch = branch.Branch.open('published')
442
 
        # The published branch refers to the mainline
443
 
        self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
444
 
        # and the branch's work was pushed
445
 
        self.assertTrue(published_branch.repository.has_revision(branch_revid))
446
 
 
447
 
    def test_push_new_branch_stacked_on(self):
448
 
        """Pushing a new branch with --stacked-on creates a stacked branch."""
449
 
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
450
 
        # we publish branch_tree with a reference to the mainline.
451
 
        out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
452
 
            self.get_url('published')], working_dir='branch')
453
 
        self.assertEqual('', out)
454
 
        self.assertEqual('Created new stacked branch referring to %s.\n' %
455
 
            trunk_tree.branch.base, err)
456
 
        self.assertPublished(branch_tree.last_revision(),
457
 
            trunk_tree.branch.base)
458
 
 
459
 
    def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
460
 
        """When the parent has no public url the parent is used as-is."""
461
 
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
462
 
        # now we do a stacked push, which should determine the public location
463
 
        # for us.
464
 
        out, err = self.run_bzr(['push', '--stacked',
465
 
            self.get_url('published')], working_dir='branch')
466
 
        self.assertEqual('', out)
467
 
        self.assertEqual('Created new stacked branch referring to %s.\n' %
468
 
            trunk_tree.branch.base, err)
469
 
        self.assertPublished(branch_tree.last_revision(),
470
 
                             trunk_tree.branch.base)
471
 
 
472
 
    def test_push_new_branch_stacked_uses_parent_public(self):
473
 
        """Pushing a new branch with --stacked creates a stacked branch."""
474
 
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
475
 
        # the trunk is published on a web server
476
 
        self.transport_readonly_server = http_server.HttpServer
477
 
        trunk_public = self.make_branch('public_trunk', format='1.9')
478
 
        trunk_public.pull(trunk_tree.branch)
479
 
        trunk_public_url = self.get_readonly_url('public_trunk')
480
 
        trunk_tree.branch.set_public_branch(trunk_public_url)
481
 
        # now we do a stacked push, which should determine the public location
482
 
        # for us.
483
 
        out, err = self.run_bzr(['push', '--stacked',
484
 
            self.get_url('published')], working_dir='branch')
485
 
        self.assertEqual('', out)
486
 
        self.assertEqual('Created new stacked branch referring to %s.\n' %
487
 
            trunk_public_url, err)
488
 
        self.assertPublished(branch_tree.last_revision(), trunk_public_url)
489
 
 
490
 
    def test_push_new_branch_stacked_no_parent(self):
491
 
        """Pushing with --stacked and no parent branch errors."""
492
 
        branch = self.make_branch_and_tree('branch', format='1.9')
493
 
        # now we do a stacked push, which should fail as the place to refer too
494
 
        # cannot be determined.
495
 
        out, err = self.run_bzr_error(
496
 
            ['Could not determine branch to refer to\\.'], ['push', '--stacked',
497
 
            self.get_url('published')], working_dir='branch')
498
 
        self.assertEqual('', out)
499
 
        self.assertFalse(self.get_transport('published').has('.'))
500
 
 
501
 
    def test_push_notifies_default_stacking(self):
502
 
        self.make_branch('stack_on', format='1.6')
503
 
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
504
 
        self.make_branch('from', format='1.6')
505
 
        out, err = self.run_bzr('push -d from to')
506
 
        self.assertContainsRe(err,
507
 
                              'Using default stacking branch stack_on at .*')
508
 
 
509
 
    def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
510
 
        self.make_branch('stack_on', format='1.6')
511
 
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
512
 
        self.make_branch('from', format='pack-0.92')
513
 
        out, err = self.run_bzr('push -d from to')
514
 
        b = branch.Branch.open('to')
515
 
        self.assertEqual('../stack_on', b.get_stacked_on_url())
516
 
 
517
 
    def test_push_does_not_change_format_with_default_if_target_cannot(self):
518
 
        self.make_branch('stack_on', format='pack-0.92')
519
 
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
520
 
        self.make_branch('from', format='pack-0.92')
521
 
        out, err = self.run_bzr('push -d from to')
522
 
        b = branch.Branch.open('to')
523
 
        self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
524
 
 
525
 
    def test_push_doesnt_create_broken_branch(self):
526
 
        """Pushing a new standalone branch works even when there's a default
527
 
        stacking policy at the destination.
528
 
 
529
 
        The new branch will preserve the repo format (even if it isn't the
530
 
        default for the branch), and will be stacked when the repo format
531
 
        allows (which means that the branch format isn't necessarly preserved).
532
 
        """
533
 
        self.make_repository('repo', shared=True, format='1.6')
534
 
        builder = self.make_branch_builder('repo/local', format='pack-0.92')
535
 
        builder.start_series()
536
 
        builder.build_snapshot('rev-1', None, [
537
 
            ('add', ('', 'root-id', 'directory', '')),
538
 
            ('add', ('filename', 'f-id', 'file', 'content\n'))])
539
 
        builder.build_snapshot('rev-2', ['rev-1'], [])
540
 
        builder.build_snapshot('rev-3', ['rev-2'],
541
 
            [('modify', ('f-id', 'new-content\n'))])
542
 
        builder.finish_series()
543
 
        branch = builder.get_branch()
544
 
        # Push rev-1 to "trunk", so that we can stack on it.
545
 
        self.run_bzr('push -d repo/local trunk -r 1')
546
 
        # Set a default stacking policy so that new branches will automatically
547
 
        # stack on trunk.
548
 
        self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
549
 
        # Push rev-2 to a new branch "remote".  It will be stacked on "trunk".
550
 
        out, err = self.run_bzr('push -d repo/local remote -r 2')
551
 
        self.assertContainsRe(
552
 
            err, 'Using default stacking branch trunk at .*')
553
 
        # Push rev-3 onto "remote".  If "remote" not stacked and is missing the
554
 
        # fulltext record for f-id @ rev-1, then this will fail.
555
 
        out, err = self.run_bzr('push -d repo/local remote -r 3')
556
 
 
557
 
    def test_push_verbose_shows_log(self):
558
 
        tree = self.make_branch_and_tree('source')
559
 
        tree.commit('rev1')
560
 
        out, err = self.run_bzr('push -v -d source target')
561
 
        # initial push contains log
562
 
        self.assertContainsRe(out, 'rev1')
563
 
        tree.commit('rev2')
564
 
        out, err = self.run_bzr('push -v -d source target')
565
 
        # subsequent push contains log
566
 
        self.assertContainsRe(out, 'rev2')
567
 
        # subsequent log is accurate
568
 
        self.assertNotContainsRe(out, 'rev1')
569
 
 
570
 
    def test_push_from_subdir(self):
571
 
        t = self.make_branch_and_tree('tree')
572
 
        self.build_tree(['tree/dir/', 'tree/dir/file'])
573
 
        t.add('dir', 'dir/file')
574
 
        t.commit('r1')
575
 
        out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
576
 
        self.assertEqual('', out)
577
 
        self.assertEqual('Created new branch.\n', err)
578
 
 
579
 
 
580
 
class RedirectingMemoryTransport(memory.MemoryTransport):
581
 
 
582
 
    def mkdir(self, relpath, mode=None):
583
 
        if self._cwd == '/source/':
584
 
            raise errors.RedirectRequested(self.abspath(relpath),
585
 
                                           self.abspath('../target'),
586
 
                                           is_permanent=True)
587
 
        elif self._cwd == '/infinite-loop/':
588
 
            raise errors.RedirectRequested(self.abspath(relpath),
589
 
                                           self.abspath('../infinite-loop'),
590
 
                                           is_permanent=True)
591
 
        else:
592
 
            return super(RedirectingMemoryTransport, self).mkdir(
593
 
                relpath, mode)
594
 
 
595
 
    def get(self, relpath):
596
 
        if self.clone(relpath)._cwd == '/infinite-loop/':
597
 
            raise errors.RedirectRequested(self.abspath(relpath),
598
 
                                           self.abspath('../infinite-loop'),
599
 
                                           is_permanent=True)
600
 
        else:
601
 
            return super(RedirectingMemoryTransport, self).get(relpath)
602
 
 
603
 
    def _redirected_to(self, source, target):
604
 
        # We do accept redirections
605
 
        return transport.get_transport(target)
606
 
 
607
 
 
608
 
class RedirectingMemoryServer(memory.MemoryServer):
609
 
 
610
 
    def start_server(self):
611
 
        self._dirs = {'/': None}
612
 
        self._files = {}
613
 
        self._locks = {}
614
 
        self._scheme = 'redirecting-memory+%s:///' % id(self)
615
 
        transport.register_transport(self._scheme, self._memory_factory)
616
 
 
617
 
    def _memory_factory(self, url):
618
 
        result = RedirectingMemoryTransport(url)
619
 
        result._dirs = self._dirs
620
 
        result._files = self._files
621
 
        result._locks = self._locks
622
 
        return result
623
 
 
624
 
    def stop_server(self):
625
 
        transport.unregister_transport(self._scheme, self._memory_factory)
626
 
 
627
 
 
628
 
class TestPushRedirect(tests.TestCaseWithTransport):
629
 
 
630
 
    def setUp(self):
631
 
        tests.TestCaseWithTransport.setUp(self)
632
 
        self.memory_server = RedirectingMemoryServer()
633
 
        self.start_server(self.memory_server)
634
 
        # Make the branch and tree that we'll be pushing.
635
 
        t = self.make_branch_and_tree('tree')
636
 
        self.build_tree(['tree/file'])
637
 
        t.add('file')
638
 
        t.commit('commit 1')
639
 
 
640
 
    def test_push_redirects_on_mkdir(self):
641
 
        """If the push requires a mkdir, push respects redirect requests.
642
 
 
643
 
        This is added primarily to handle lp:/ URI support, so that users can
644
 
        push to new branches by specifying lp:/ URIs.
645
 
        """
646
 
        destination_url = self.memory_server.get_url() + 'source'
647
 
        self.run_bzr(['push', '-d', 'tree', destination_url])
648
 
 
649
 
        local_revision = branch.Branch.open('tree').last_revision()
650
 
        remote_revision = branch.Branch.open(
651
 
            self.memory_server.get_url() + 'target').last_revision()
652
 
        self.assertEqual(remote_revision, local_revision)
653
 
 
654
 
    def test_push_gracefully_handles_too_many_redirects(self):
655
 
        """Push fails gracefully if the mkdir generates a large number of
656
 
        redirects.
657
 
        """
658
 
        destination_url = self.memory_server.get_url() + 'infinite-loop'
659
 
        out, err = self.run_bzr_error(
660
 
            ['Too many redirections trying to make %s\\.\n'
661
 
             % re.escape(destination_url)],
662
 
            ['push', '-d', 'tree', destination_url], retcode=3)
663
 
        self.assertEqual('', out)
664
 
 
665
 
 
666
 
class TestPushStrictMixin(object):
667
 
 
668
 
    def make_local_branch_and_tree(self):
669
 
        self.tree = self.make_branch_and_tree('local')
670
 
        self.build_tree_contents([('local/file', 'initial')])
671
 
        self.tree.add('file')
672
 
        self.tree.commit('adding file', rev_id='added')
673
 
        self.build_tree_contents([('local/file', 'modified')])
674
 
        self.tree.commit('modify file', rev_id='modified')
675
 
 
676
 
    def set_config_push_strict(self, value):
677
 
        # set config var (any of bazaar.conf, locations.conf, branch.conf
678
 
        # should do)
679
 
        conf = self.tree.branch.get_config_stack()
680
 
        conf.set('push_strict', value)
681
 
 
682
 
    _default_command = ['push', '../to']
683
 
    _default_wd = 'local'
684
 
    _default_errors = ['Working tree ".*/local/" has uncommitted '
685
 
                       'changes \(See bzr status\)\.',]
686
 
    _default_additional_error = 'Use --no-strict to force the push.\n'
687
 
    _default_additional_warning = 'Uncommitted changes will not be pushed.'
688
 
 
689
 
 
690
 
    def assertPushFails(self, args):
691
 
        out, err = self.run_bzr_error(self._default_errors,
692
 
                                      self._default_command + args,
693
 
                                      working_dir=self._default_wd, retcode=3)
694
 
        self.assertContainsRe(err, self._default_additional_error)
695
 
 
696
 
    def assertPushSucceeds(self, args, with_warning=False, revid_to_push=None):
697
 
        if with_warning:
698
 
            error_regexes = self._default_errors
699
 
        else:
700
 
            error_regexes = []
701
 
        out, err = self.run_bzr(self._default_command + args,
702
 
                                working_dir=self._default_wd,
703
 
                                error_regexes=error_regexes)
704
 
        if with_warning:
705
 
            self.assertContainsRe(err, self._default_additional_warning)
706
 
        else:
707
 
            self.assertNotContainsRe(err, self._default_additional_warning)
708
 
        branch_from = branch.Branch.open(self._default_wd)
709
 
        if revid_to_push is None:
710
 
            revid_to_push = branch_from.last_revision()
711
 
        branch_to = branch.Branch.open('to')
712
 
        repo_to = branch_to.repository
713
 
        self.assertTrue(repo_to.has_revision(revid_to_push))
714
 
        self.assertEqual(revid_to_push, branch_to.last_revision())
715
 
 
716
 
 
717
 
 
718
 
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
719
 
                                   TestPushStrictMixin):
720
 
 
721
 
    def setUp(self):
722
 
        super(TestPushStrictWithoutChanges, self).setUp()
723
 
        self.make_local_branch_and_tree()
724
 
 
725
 
    def test_push_default(self):
726
 
        self.assertPushSucceeds([])
727
 
 
728
 
    def test_push_strict(self):
729
 
        self.assertPushSucceeds(['--strict'])
730
 
 
731
 
    def test_push_no_strict(self):
732
 
        self.assertPushSucceeds(['--no-strict'])
733
 
 
734
 
    def test_push_config_var_strict(self):
735
 
        self.set_config_push_strict('true')
736
 
        self.assertPushSucceeds([])
737
 
 
738
 
    def test_push_config_var_no_strict(self):
739
 
        self.set_config_push_strict('false')
740
 
        self.assertPushSucceeds([])
741
 
 
742
 
 
743
 
strict_push_change_scenarios = [
744
 
    ('uncommitted',
745
 
        dict(_changes_type= '_uncommitted_changes')),
746
 
    ('pending-merges',
747
 
        dict(_changes_type= '_pending_merges')),
748
 
    ('out-of-sync-trees',
749
 
        dict(_changes_type= '_out_of_sync_trees')),
750
 
    ]
751
 
 
752
 
 
753
 
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
754
 
                                TestPushStrictMixin):
755
 
 
756
 
    scenarios = strict_push_change_scenarios 
757
 
    _changes_type = None # Set by load_tests
758
 
 
759
 
    def setUp(self):
760
 
        super(TestPushStrictWithChanges, self).setUp()
761
 
        # Apply the changes defined in load_tests: one of _uncommitted_changes,
762
 
        # _pending_merges or _out_of_sync_trees
763
 
        getattr(self, self._changes_type)()
764
 
 
765
 
    def _uncommitted_changes(self):
766
 
        self.make_local_branch_and_tree()
767
 
        # Make a change without committing it
768
 
        self.build_tree_contents([('local/file', 'in progress')])
769
 
 
770
 
    def _pending_merges(self):
771
 
        self.make_local_branch_and_tree()
772
 
        # Create 'other' branch containing a new file
773
 
        other_bzrdir = self.tree.bzrdir.sprout('other')
774
 
        other_tree = other_bzrdir.open_workingtree()
775
 
        self.build_tree_contents([('other/other-file', 'other')])
776
 
        other_tree.add('other-file')
777
 
        other_tree.commit('other commit', rev_id='other')
778
 
        # Merge and revert, leaving a pending merge
779
 
        self.tree.merge_from_branch(other_tree.branch)
780
 
        self.tree.revert(filenames=['other-file'], backups=False)
781
 
 
782
 
    def _out_of_sync_trees(self):
783
 
        self.make_local_branch_and_tree()
784
 
        self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
785
 
        # Make a change and commit it
786
 
        self.build_tree_contents([('local/file', 'modified in local')])
787
 
        self.tree.commit('modify file', rev_id='modified-in-local')
788
 
        # Exercise commands from the checkout directory
789
 
        self._default_wd = 'checkout'
790
 
        self._default_errors = ["Working tree is out of date, please run"
791
 
                                " 'bzr update'\.",]
792
 
 
793
 
    def test_push_default(self):
794
 
        self.assertPushSucceeds([], with_warning=True)
795
 
 
796
 
    def test_push_with_revision(self):
797
 
        self.assertPushSucceeds(['-r', 'revid:added'], revid_to_push='added')
798
 
 
799
 
    def test_push_no_strict(self):
800
 
        self.assertPushSucceeds(['--no-strict'])
801
 
 
802
 
    def test_push_strict_with_changes(self):
803
 
        self.assertPushFails(['--strict'])
804
 
 
805
 
    def test_push_respect_config_var_strict(self):
806
 
        self.set_config_push_strict('true')
807
 
        self.assertPushFails([])
808
 
 
809
 
    def test_push_bogus_config_var_ignored(self):
810
 
        self.set_config_push_strict("I don't want you to be strict")
811
 
        self.assertPushSucceeds([], with_warning=True)
812
 
 
813
 
    def test_push_no_strict_command_line_override_config(self):
814
 
        self.set_config_push_strict('yES')
815
 
        self.assertPushFails([])
816
 
        self.assertPushSucceeds(['--no-strict'])
817
 
 
818
 
    def test_push_strict_command_line_override_config(self):
819
 
        self.set_config_push_strict('oFF')
820
 
        self.assertPushFails(['--strict'])
821
 
        self.assertPushSucceeds([])
822
 
 
823
 
 
824
 
class TestPushForeign(tests.TestCaseWithTransport):
825
 
 
826
 
    def setUp(self):
827
 
        super(TestPushForeign, self).setUp()
828
 
        test_foreign.register_dummy_foreign_for_test(self)
829
 
 
830
 
    def make_dummy_builder(self, relpath):
831
 
        builder = self.make_branch_builder(
832
 
            relpath, format=test_foreign.DummyForeignVcsDirFormat())
833
 
        builder.build_snapshot('revid', None,
834
 
            [('add', ('', 'TREE_ROOT', 'directory', None)),
835
 
             ('add', ('foo', 'fooid', 'file', 'bar'))])
836
 
        return builder
837
 
 
838
 
    def test_no_roundtripping(self):
839
 
        target_branch = self.make_dummy_builder('dp').get_branch()
840
 
        source_tree = self.make_branch_and_tree("dc")
841
 
        output, error = self.run_bzr("push -d dc dp", retcode=3)
842
 
        self.assertEquals("", output)
843
 
        self.assertEquals(error, "bzr: ERROR: It is not possible to losslessly"
844
 
            " push to dummy. You may want to use dpush instead.\n")