/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

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

  • Committer: Martin
  • Date: 2017-06-18 10:15:11 UTC
  • mto: This revision was merged to the branch mainline in revision 6715.
  • Revision ID: gzlist@googlemail.com-20170618101511-fri1mouxt1hc09r8
Make _simple_set tests pass on py3 and with random hash

Show diffs side-by-side

added added

removed removed

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