~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-07-07 21:30:06 UTC
  • mfrom: (5333.1.2 better_pyqt_include)
  • Revision ID: pqm@pqm.ubuntu.com-20100707213006-lriphkkbzwwrl7ne
(jameinel) Use a better list of PyQt includes and excludes. (Gary van der
 Merwe)

Show diffs side-by-side

added added

removed removed

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