~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

(jelmer) Support upgrading between the 2a and development-colo formats.
 (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

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