~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Patch Queue Manager
  • Date: 2011-09-22 14:12:18 UTC
  • mfrom: (6155.3.1 jam)
  • Revision ID: pqm@pqm.ubuntu.com-20110922141218-86s4uu6nqvourw4f
(jameinel) Cleanup comments bzrlib/smart/__init__.py (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
# -*- coding: utf-8 -*-
3
 
 
 
1
# Copyright (C) 2006-2011 Canonical Ltd
 
2
#
4
3
# This program is free software; you can redistribute it and/or modify
5
4
# it under the terms of the GNU General Public License as published by
6
5
# the Free Software Foundation; either version 2 of the License, or
7
6
# (at your option) any later version.
8
 
 
 
7
#
9
8
# This program is distributed in the hope that it will be useful,
10
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
11
# GNU General Public License for more details.
13
 
 
 
12
#
14
13
# You should have received a copy of the GNU General Public License
15
14
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
16
 
18
17
 
19
18
"""Black-box tests for bzr push."""
20
19
 
21
 
import os
22
 
 
23
 
import bzrlib
24
 
from bzrlib.branch import Branch
25
 
from bzrlib.bzrdir import BzrDirMetaFormat1
26
 
from bzrlib.osutils import abspath
27
 
from bzrlib.repository import RepositoryFormatKnit1
28
 
from bzrlib.tests.blackbox import ExternalBase
29
 
from bzrlib.uncommit import uncommit
30
 
from bzrlib.workingtree import WorkingTree
31
 
 
32
 
 
33
 
class TestPush(ExternalBase):
 
20
import re
 
21
 
 
22
from bzrlib import (
 
23
    branch,
 
24
    bzrdir,
 
25
    errors,
 
26
    osutils,
 
27
    tests,
 
28
    transport,
 
29
    uncommit,
 
30
    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')
34
58
 
35
59
    def test_push_remember(self):
36
60
        """Push changes from one branch to another and test push location."""
52
76
        tree_b.commit('commit c')
53
77
        # initial push location must be empty
54
78
        self.assertEqual(None, branch_b.get_push_location())
 
79
 
55
80
        # test push for failure without push location set
56
 
        os.chdir('branch_a')
57
 
        out = self.runbzr('push', retcode=3)
 
81
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
58
82
        self.assertEquals(out,
59
83
                ('','bzr: ERROR: No push location known or specified.\n'))
 
84
 
 
85
        # 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)
 
89
        self.assertEquals(
 
90
                ('', 'bzr: ERROR: No push location known or specified.\n'),
 
91
                out)
 
92
 
60
93
        # test implicit --remember when no push location set, push fails
61
 
        out = self.runbzr('push ../branch_b', retcode=3)
 
94
        out = self.run_bzr('push ../branch_b',
 
95
                           working_dir='branch_a', retcode=3)
62
96
        self.assertEquals(out,
63
97
                ('','bzr: ERROR: These branches have diverged.  '
64
 
                    'Try a merge then push with overwrite.\n'))
65
 
        self.assertEquals(abspath(branch_a.get_push_location()),
66
 
                          abspath(branch_b.bzrdir.root_transport.base))
 
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))
 
101
 
67
102
        # test implicit --remember after resolving previous failure
68
 
        uncommit(branch=branch_b, tree=tree_b)
 
103
        uncommit.uncommit(branch=branch_b, tree=tree_b)
69
104
        transport.delete('branch_b/c')
70
 
        self.runbzr('push')
71
 
        self.assertEquals(abspath(branch_a.get_push_location()),
72
 
                          abspath(branch_b.bzrdir.root_transport.base))
 
105
        out, err = self.run_bzr('push', working_dir='branch_a')
 
106
        path = branch_a.get_push_location()
 
107
        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))
 
112
        self.assertEqual(path,
 
113
                         branch_b.bzrdir.root_transport.base)
73
114
        # test explicit --remember
74
 
        self.runbzr('push ../branch_c --remember')
75
 
        self.assertEquals(abspath(branch_a.get_push_location()),
76
 
                          abspath(branch_c.bzrdir.root_transport.base))
77
 
    
 
115
        self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
 
116
        self.assertEquals(branch_a.get_push_location(),
 
117
                          branch_c.bzrdir.root_transport.base)
 
118
 
78
119
    def test_push_without_tree(self):
79
120
        # bzr push from a branch that does not have a checkout should work.
80
121
        b = self.make_branch('.')
81
 
        out, err = self.run_bzr('push', 'pushed-location')
 
122
        out, err = self.run_bzr('push pushed-location')
82
123
        self.assertEqual('', out)
83
 
        self.assertEqual('0 revision(s) pushed.\n', err)
84
 
        b2 = bzrlib.branch.Branch.open('pushed-location')
 
124
        self.assertEqual('Created new branch.\n', err)
 
125
        b2 = branch.Branch.open('pushed-location')
85
126
        self.assertEndsWith(b2.base, 'pushed-location/')
86
127
 
 
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
 
87
139
    def test_push_new_branch_revision_count(self):
88
 
        # bzr push of a branch with revisions to a new location 
89
 
        # should print the number of revisions equal to the length of the 
 
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
90
142
        # local branch.
91
143
        t = self.make_branch_and_tree('tree')
92
144
        self.build_tree(['tree/file'])
93
145
        t.add('file')
94
146
        t.commit('commit 1')
95
 
        os.chdir('tree')
96
 
        out, err = self.run_bzr('push', 'pushed-to')
97
 
        os.chdir('..')
98
 
        self.assertEqual('', out)
99
 
        self.assertEqual('1 revision(s) pushed.\n', err)
 
147
        out, err = self.run_bzr('push -d tree pushed-to')
 
148
        self.assertEqual('', out)
 
149
        self.assertEqual('Created new branch.\n', err)
 
150
 
 
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)
100
166
 
101
167
    def test_push_only_pushes_history(self):
102
168
        # Knit branches should only push the history for the current revision.
103
 
        format = BzrDirMetaFormat1()
104
 
        format.repository_format = RepositoryFormatKnit1()
 
169
        format = bzrdir.BzrDirMetaFormat1()
 
170
        format.repository_format = knitrepo.RepositoryFormatKnit1()
105
171
        shared_repo = self.make_repository('repo', format=format, shared=True)
106
172
        shared_repo.set_make_working_trees(True)
107
173
 
108
174
        def make_shared_tree(path):
109
175
            shared_repo.bzrdir.root_transport.mkdir(path)
110
176
            shared_repo.bzrdir.create_branch_convenience('repo/' + path)
111
 
            return WorkingTree.open('repo/' + path)
 
177
            return workingtree.WorkingTree.open('repo/' + path)
112
178
        tree_a = make_shared_tree('a')
113
179
        self.build_tree(['repo/a/file'])
114
180
        tree_a.add('file')
129
195
 
130
196
        # Now that we have a repository with shared files, make sure
131
197
        # that things aren't copied out by a 'push'
132
 
        os.chdir('repo/b')
133
 
        self.run_bzr('push', '../../push-b')
134
 
        pushed_tree = WorkingTree.open('../../push-b')
 
198
        self.run_bzr('push ../../push-b', working_dir='repo/b')
 
199
        pushed_tree = workingtree.WorkingTree.open('push-b')
135
200
        pushed_repo = pushed_tree.branch.repository
136
201
        self.assertFalse(pushed_repo.has_revision('a-1'))
137
202
        self.assertFalse(pushed_repo.has_revision('a-2'))
138
203
        self.assertTrue(pushed_repo.has_revision('b-1'))
139
204
 
 
205
    def test_push_funky_id(self):
 
206
        t = self.make_branch_and_tree('tree')
 
207
        self.build_tree(['tree/filename'])
 
208
        t.add('filename', 'funky-chars<>%&;"\'')
 
209
        t.commit('commit filename')
 
210
        self.run_bzr('push -d tree new-tree')
 
211
 
 
212
    def test_push_dash_d(self):
 
213
        t = self.make_branch_and_tree('from')
 
214
        t.commit(allow_pointless=True,
 
215
                message='first commit')
 
216
        self.run_bzr('push -d from to-one')
 
217
        self.assertPathExists('to-one')
 
218
        self.run_bzr('push -d %s %s'
 
219
            % 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())
 
323
 
 
324
    def create_simple_tree(self):
 
325
        tree = self.make_branch_and_tree('tree')
 
326
        self.build_tree(['tree/a'])
 
327
        tree.add(['a'], ['a-id'])
 
328
        tree.commit('one', rev_id='r1')
 
329
        return tree
 
330
 
 
331
    def test_push_create_prefix(self):
 
332
        """'bzr push --create-prefix' will create leading directories."""
 
333
        tree = self.create_simple_tree()
 
334
 
 
335
        self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
 
336
                           'push ../new/tree',
 
337
                           working_dir='tree')
 
338
        self.run_bzr('push ../new/tree --create-prefix',
 
339
                     working_dir='tree')
 
340
        new_tree = workingtree.WorkingTree.open('new/tree')
 
341
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
 
342
        self.assertPathExists('new/tree/a')
 
343
 
 
344
    def test_push_use_existing(self):
 
345
        """'bzr push --use-existing-dir' can push into an existing dir.
 
346
 
 
347
        By default, 'bzr push' will not use an existing, non-versioned dir.
 
348
        """
 
349
        tree = self.create_simple_tree()
 
350
        self.build_tree(['target/'])
 
351
 
 
352
        self.run_bzr_error(['Target directory ../target already exists',
 
353
                            'Supply --use-existing-dir',
 
354
                           ],
 
355
                           'push ../target', working_dir='tree')
 
356
 
 
357
        self.run_bzr('push --use-existing-dir ../target',
 
358
                     working_dir='tree')
 
359
 
 
360
        new_tree = workingtree.WorkingTree.open('target')
 
361
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
 
362
        # 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')
 
375
 
 
376
    def test_push_onto_repo(self):
 
377
        """We should be able to 'bzr push' into an existing bzrdir."""
 
378
        tree = self.create_simple_tree()
 
379
        repo = self.make_repository('repo', shared=True)
 
380
 
 
381
        self.run_bzr('push ../repo',
 
382
                     working_dir='tree')
 
383
 
 
384
        # Pushing onto an existing bzrdir will create a repository and
 
385
        # branch as needed, but will only create a working tree if there was
 
386
        # no BzrDir before.
 
387
        self.assertRaises(errors.NoWorkingTree,
 
388
                          workingtree.WorkingTree.open, 'repo')
 
389
        new_branch = branch.Branch.open('repo')
 
390
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
 
391
 
 
392
    def test_push_onto_just_bzrdir(self):
 
393
        """We don't handle when the target is just a bzrdir.
 
394
 
 
395
        Because you shouldn't be able to create *just* a bzrdir in the wild.
 
396
        """
 
397
        # TODO: jam 20070109 Maybe it would be better to create the repository
 
398
        #       if at this point
 
399
        tree = self.create_simple_tree()
 
400
        a_bzrdir = self.make_bzrdir('dir')
 
401
 
 
402
        self.run_bzr_error(['At ../dir you have a valid .bzr control'],
 
403
                'push ../dir',
 
404
                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")