~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Robert Collins
  • Date: 2006-06-09 07:45:35 UTC
  • mto: (1755.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 1756.
  • Revision ID: robertc@robertcollins.net-20060609074535-3002a0209179b35c
Fixup '== None' usage in inventory.py.

Show diffs side-by-side

added added

removed removed

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