~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Martin Pool
  • Date: 2005-04-28 07:24:55 UTC
  • Revision ID: mbp@sourcefrog.net-20050428072453-7b99afa993a1e549
todo

Show diffs side-by-side

added added

removed removed

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