~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Matthew Revell
  • Date: 2008-03-05 14:11:35 UTC
  • mto: This revision was merged to the branch mainline in revision 3263.
  • Revision ID: matthew.revell@canonical.com-20080305141135-96jf8j24b9crru6v
Added an admin-guide directory to doc/en and outlined headings for the admin guide in index.txt

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
18
"""Black-box tests for bzr push."""
19
19
 
 
20
import os
20
21
import re
21
22
 
22
23
from bzrlib import (
23
 
    branch,
24
 
    bzrdir,
25
24
    errors,
26
 
    osutils,
27
 
    tests,
28
 
    transport,
29
 
    uncommit,
30
25
    urlutils,
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')
 
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):
78
40
 
79
41
    def test_push_remember(self):
80
42
        """Push changes from one branch to another and test push location."""
98
60
        self.assertEqual(None, branch_b.get_push_location())
99
61
 
100
62
        # test push for failure without push location set
101
 
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
 
63
        os.chdir('branch_a')
 
64
        out = self.run_bzr('push', retcode=3)
102
65
        self.assertEquals(out,
103
66
                ('','bzr: ERROR: No push location known or specified.\n'))
104
67
 
105
68
        # test not remembered if cannot actually push
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)
 
69
        self.run_bzr('push ../path/which/doesnt/exist', retcode=3)
 
70
        out = self.run_bzr('push', retcode=3)
109
71
        self.assertEquals(
110
72
                ('', 'bzr: ERROR: No push location known or specified.\n'),
111
73
                out)
112
74
 
113
75
        # test implicit --remember when no push location set, push fails
114
 
        out = self.run_bzr('push ../branch_b',
115
 
                           working_dir='branch_a', retcode=3)
 
76
        out = self.run_bzr('push ../branch_b', retcode=3)
116
77
        self.assertEquals(out,
117
78
                ('','bzr: ERROR: These branches have diverged.  '
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
                    'Try using "merge" and then "push".\n'))
 
80
        self.assertEquals(abspath(branch_a.get_push_location()),
 
81
                          abspath(branch_b.bzrdir.root_transport.base))
121
82
 
122
83
        # test implicit --remember after resolving previous failure
123
 
        uncommit.uncommit(branch=branch_b, tree=tree_b)
 
84
        uncommit(branch=branch_b, tree=tree_b)
124
85
        transport.delete('branch_b/c')
125
 
        out, err = self.run_bzr('push', working_dir='branch_a')
 
86
        out, err = self.run_bzr('push')
126
87
        path = branch_a.get_push_location()
127
88
        self.assertEquals(out,
128
 
                          'Using saved push location: %s\n'
129
 
                          % urlutils.local_path_from_url(path))
 
89
                          'Using saved location: %s\n' 
 
90
                          'Pushed up to revision 2.\n'
 
91
                          % local_path_from_url(path))
130
92
        self.assertEqual(err,
131
 
                         'All changes applied successfully.\n'
132
 
                         'Pushed up to revision 2.\n')
 
93
                         'All changes applied successfully.\n')
133
94
        self.assertEqual(path,
134
95
                         branch_b.bzrdir.root_transport.base)
135
96
        # test explicit --remember
136
 
        self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
 
97
        self.run_bzr('push ../branch_c --remember')
137
98
        self.assertEquals(branch_a.get_push_location(),
138
99
                          branch_c.bzrdir.root_transport.base)
139
 
 
 
100
    
140
101
    def test_push_without_tree(self):
141
102
        # bzr push from a branch that does not have a checkout should work.
142
103
        b = self.make_branch('.')
143
104
        out, err = self.run_bzr('push pushed-location')
144
105
        self.assertEqual('', out)
145
106
        self.assertEqual('Created new branch.\n', err)
146
 
        b2 = branch.Branch.open('pushed-location')
 
107
        b2 = Branch.open('pushed-location')
147
108
        self.assertEndsWith(b2.base, 'pushed-location/')
148
109
 
149
110
    def test_push_new_branch_revision_count(self):
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
 
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 
152
113
        # local branch.
153
114
        t = self.make_branch_and_tree('tree')
154
115
        self.build_tree(['tree/file'])
155
116
        t.add('file')
156
117
        t.commit('commit 1')
157
 
        out, err = self.run_bzr('push -d tree pushed-to')
 
118
        os.chdir('tree')
 
119
        out, err = self.run_bzr('push pushed-to')
 
120
        os.chdir('..')
158
121
        self.assertEqual('', out)
159
122
        self.assertEqual('Created new branch.\n', err)
160
123
 
161
124
    def test_push_only_pushes_history(self):
162
125
        # Knit branches should only push the history for the current revision.
163
 
        format = bzrdir.BzrDirMetaFormat1()
164
 
        format.repository_format = knitrepo.RepositoryFormatKnit1()
 
126
        format = BzrDirMetaFormat1()
 
127
        format.repository_format = RepositoryFormatKnit1()
165
128
        shared_repo = self.make_repository('repo', format=format, shared=True)
166
129
        shared_repo.set_make_working_trees(True)
167
130
 
168
131
        def make_shared_tree(path):
169
132
            shared_repo.bzrdir.root_transport.mkdir(path)
170
133
            shared_repo.bzrdir.create_branch_convenience('repo/' + path)
171
 
            return workingtree.WorkingTree.open('repo/' + path)
 
134
            return WorkingTree.open('repo/' + path)
172
135
        tree_a = make_shared_tree('a')
173
136
        self.build_tree(['repo/a/file'])
174
137
        tree_a.add('file')
189
152
 
190
153
        # Now that we have a repository with shared files, make sure
191
154
        # that things aren't copied out by a 'push'
192
 
        self.run_bzr('push ../../push-b', working_dir='repo/b')
193
 
        pushed_tree = workingtree.WorkingTree.open('push-b')
 
155
        os.chdir('repo/b')
 
156
        self.run_bzr('push ../../push-b')
 
157
        pushed_tree = WorkingTree.open('../../push-b')
194
158
        pushed_repo = pushed_tree.branch.repository
195
159
        self.assertFalse(pushed_repo.has_revision('a-1'))
196
160
        self.assertFalse(pushed_repo.has_revision('a-2'))
198
162
 
199
163
    def test_push_funky_id(self):
200
164
        t = self.make_branch_and_tree('tree')
201
 
        self.build_tree(['tree/filename'])
 
165
        os.chdir('tree')
 
166
        self.build_tree(['filename'])
202
167
        t.add('filename', 'funky-chars<>%&;"\'')
203
168
        t.commit('commit filename')
204
 
        self.run_bzr('push -d tree new-tree')
 
169
        self.run_bzr('push ../new-tree')
205
170
 
206
171
    def test_push_dash_d(self):
207
172
        t = self.make_branch_and_tree('from')
209
174
                message='first commit')
210
175
        self.run_bzr('push -d from to-one')
211
176
        self.failUnlessExists('to-one')
212
 
        self.run_bzr('push -d %s %s'
 
177
        self.run_bzr('push -d %s %s' 
213
178
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
214
179
        self.failUnlessExists('to-two')
215
180
 
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
 
 
302
181
    def create_simple_tree(self):
303
182
        tree = self.make_branch_and_tree('tree')
304
183
        self.build_tree(['tree/a'])
315
194
                           working_dir='tree')
316
195
        self.run_bzr('push ../new/tree --create-prefix',
317
196
                     working_dir='tree')
318
 
        new_tree = workingtree.WorkingTree.open('new/tree')
 
197
        new_tree = WorkingTree.open('new/tree')
319
198
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
320
199
        self.failUnlessExists('new/tree/a')
321
200
 
335
214
        self.run_bzr('push --use-existing-dir ../target',
336
215
                     working_dir='tree')
337
216
 
338
 
        new_tree = workingtree.WorkingTree.open('target')
 
217
        new_tree = WorkingTree.open('target')
339
218
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
340
219
        # The push should have created target/a
341
220
        self.failUnlessExists('target/a')
342
221
 
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
 
 
354
222
    def test_push_onto_repo(self):
355
223
        """We should be able to 'bzr push' into an existing bzrdir."""
356
224
        tree = self.create_simple_tree()
362
230
        # Pushing onto an existing bzrdir will create a repository and
363
231
        # branch as needed, but will only create a working tree if there was
364
232
        # no BzrDir before.
365
 
        self.assertRaises(errors.NoWorkingTree,
366
 
                          workingtree.WorkingTree.open, 'repo')
367
 
        new_branch = branch.Branch.open('repo')
 
233
        self.assertRaises(errors.NoWorkingTree, WorkingTree.open, 'repo')
 
234
        new_branch = Branch.open('repo')
368
235
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
369
236
 
370
237
    def test_push_onto_just_bzrdir(self):
381
248
                'push ../dir',
382
249
                working_dir='tree')
383
250
 
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)
 
251
 
 
252
class RedirectingMemoryTransport(MemoryTransport):
 
253
 
 
254
    def mkdir(self, path, mode=None):
 
255
        path = self.abspath(path)[len(self._scheme):]
 
256
        if path == '/source':
 
257
            raise errors.RedirectRequested(
 
258
                path, self._scheme + '/target', is_permanent=True)
 
259
        elif path == '/infinite-loop':
 
260
            raise errors.RedirectRequested(
 
261
                path, self._scheme + '/infinite-loop', is_permanent=True)
569
262
        else:
570
263
            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):
 
264
                path, mode)
 
265
 
 
266
 
 
267
class RedirectingMemoryServer(MemoryServer):
 
268
 
 
269
    def setUp(self):
589
270
        self._dirs = {'/': None}
590
271
        self._files = {}
591
272
        self._locks = {}
592
273
        self._scheme = 'redirecting-memory+%s:///' % id(self)
593
 
        transport.register_transport(self._scheme, self._memory_factory)
 
274
        register_transport(self._scheme, self._memory_factory)
594
275
 
595
276
    def _memory_factory(self, url):
596
277
        result = RedirectingMemoryTransport(url)
599
280
        result._locks = self._locks
600
281
        return result
601
282
 
602
 
    def stop_server(self):
603
 
        transport.unregister_transport(self._scheme, self._memory_factory)
604
 
 
605
 
 
606
 
class TestPushRedirect(tests.TestCaseWithTransport):
 
283
    def tearDown(self):
 
284
        unregister_transport(self._scheme, self._memory_factory)
 
285
 
 
286
 
 
287
class TestPushRedirect(ExternalBase):
607
288
 
608
289
    def setUp(self):
609
 
        tests.TestCaseWithTransport.setUp(self)
 
290
        ExternalBase.setUp(self)
610
291
        self.memory_server = RedirectingMemoryServer()
611
 
        self.start_server(self.memory_server)
 
292
        self.memory_server.setUp()
 
293
        self.addCleanup(self.memory_server.tearDown)
 
294
 
612
295
        # Make the branch and tree that we'll be pushing.
613
296
        t = self.make_branch_and_tree('tree')
614
297
        self.build_tree(['tree/file'])
621
304
        This is added primarily to handle lp:/ URI support, so that users can
622
305
        push to new branches by specifying lp:/ URIs.
623
306
        """
 
307
        os.chdir('tree')
624
308
        destination_url = self.memory_server.get_url() + 'source'
625
 
        self.run_bzr(['push', '-d', 'tree', destination_url])
 
309
        self.run_bzr('push %s' % destination_url)
 
310
        os.chdir('..')
626
311
 
627
 
        local_revision = branch.Branch.open('tree').last_revision()
628
 
        remote_revision = branch.Branch.open(
 
312
        local_revision = Branch.open('tree').last_revision()
 
313
        remote_revision = Branch.open(
629
314
            self.memory_server.get_url() + 'target').last_revision()
630
315
        self.assertEqual(remote_revision, local_revision)
631
316
 
633
318
        """Push fails gracefully if the mkdir generates a large number of
634
319
        redirects.
635
320
        """
 
321
        os.chdir('tree')
636
322
        destination_url = self.memory_server.get_url() + 'infinite-loop'
637
323
        out, err = self.run_bzr_error(
638
324
            ['Too many redirections trying to make %s\\.\n'
639
325
             % re.escape(destination_url)],
640
 
            ['push', '-d', 'tree', destination_url], retcode=3)
 
326
            'push %s' % destination_url, retcode=3)
 
327
        os.chdir('..')
641
328
        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_pushed_revid = 'modified'
665
 
 
666
 
    def assertPushFails(self, args):
667
 
        self.run_bzr_error(self._default_errors, self._default_command + args,
668
 
                           working_dir=self._default_wd, retcode=3)
669
 
 
670
 
    def assertPushSucceeds(self, args, pushed_revid=None):
671
 
        self.run_bzr(self._default_command + args,
672
 
                     working_dir=self._default_wd)
673
 
        if pushed_revid is None:
674
 
            pushed_revid = self._default_pushed_revid
675
 
        tree_to = workingtree.WorkingTree.open('to')
676
 
        repo_to = tree_to.branch.repository
677
 
        self.assertTrue(repo_to.has_revision(pushed_revid))
678
 
        self.assertEqual(tree_to.branch.last_revision_info()[1], pushed_revid)
679
 
 
680
 
 
681
 
 
682
 
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
683
 
                                   TestPushStrictMixin):
684
 
 
685
 
    def setUp(self):
686
 
        super(TestPushStrictWithoutChanges, self).setUp()
687
 
        self.make_local_branch_and_tree()
688
 
 
689
 
    def test_push_default(self):
690
 
        self.assertPushSucceeds([])
691
 
 
692
 
    def test_push_strict(self):
693
 
        self.assertPushSucceeds(['--strict'])
694
 
 
695
 
    def test_push_no_strict(self):
696
 
        self.assertPushSucceeds(['--no-strict'])
697
 
 
698
 
    def test_push_config_var_strict(self):
699
 
        self.set_config_push_strict('true')
700
 
        self.assertPushSucceeds([])
701
 
 
702
 
    def test_push_config_var_no_strict(self):
703
 
        self.set_config_push_strict('false')
704
 
        self.assertPushSucceeds([])
705
 
 
706
 
 
707
 
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
708
 
                                TestPushStrictMixin):
709
 
 
710
 
    _changes_type = None # Set by load_tests
711
 
 
712
 
    def setUp(self):
713
 
        super(TestPushStrictWithChanges, self).setUp()
714
 
        # Apply the changes defined in load_tests: one of _uncommitted_changes,
715
 
        # _pending_merges or _out_of_sync_trees
716
 
        getattr(self, self._changes_type)()
717
 
 
718
 
    def _uncommitted_changes(self):
719
 
        self.make_local_branch_and_tree()
720
 
        # Make a change without committing it
721
 
        self.build_tree_contents([('local/file', 'in progress')])
722
 
 
723
 
    def _pending_merges(self):
724
 
        self.make_local_branch_and_tree()
725
 
        # Create 'other' branch containing a new file
726
 
        other_bzrdir = self.tree.bzrdir.sprout('other')
727
 
        other_tree = other_bzrdir.open_workingtree()
728
 
        self.build_tree_contents([('other/other-file', 'other')])
729
 
        other_tree.add('other-file')
730
 
        other_tree.commit('other commit', rev_id='other')
731
 
        # Merge and revert, leaving a pending merge
732
 
        self.tree.merge_from_branch(other_tree.branch)
733
 
        self.tree.revert(filenames=['other-file'], backups=False)
734
 
 
735
 
    def _out_of_sync_trees(self):
736
 
        self.make_local_branch_and_tree()
737
 
        self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
738
 
        # Make a change and commit it
739
 
        self.build_tree_contents([('local/file', 'modified in local')])
740
 
        self.tree.commit('modify file', rev_id='modified-in-local')
741
 
        # Exercise commands from the checkout directory
742
 
        self._default_wd = 'checkout'
743
 
        self._default_errors = ["Working tree is out of date, please run"
744
 
                                " 'bzr update'\.",]
745
 
        self._default_pushed_revid = 'modified-in-local'
746
 
 
747
 
    def test_push_default(self):
748
 
        self.assertPushFails([])
749
 
 
750
 
    def test_push_with_revision(self):
751
 
        self.assertPushSucceeds(['-r', 'revid:added'], pushed_revid='added')
752
 
 
753
 
    def test_push_no_strict(self):
754
 
        self.assertPushSucceeds(['--no-strict'])
755
 
 
756
 
    def test_push_strict_with_changes(self):
757
 
        self.assertPushFails(['--strict'])
758
 
 
759
 
    def test_push_respect_config_var_strict(self):
760
 
        self.set_config_push_strict('true')
761
 
        self.assertPushFails([])
762
 
 
763
 
    def test_push_bogus_config_var_ignored(self):
764
 
        self.set_config_push_strict("I don't want you to be strict")
765
 
        self.assertPushFails([])
766
 
 
767
 
    def test_push_no_strict_command_line_override_config(self):
768
 
        self.set_config_push_strict('yES')
769
 
        self.assertPushFails([])
770
 
        self.assertPushSucceeds(['--no-strict'])
771
 
 
772
 
    def test_push_strict_command_line_override_config(self):
773
 
        self.set_config_push_strict('oFF')
774
 
        self.assertPushFails(['--strict'])
775
 
        self.assertPushSucceeds([])
776
 
 
777
 
 
778
 
class TestPushForeign(blackbox.ExternalBase):
779
 
 
780
 
    def setUp(self):
781
 
        super(TestPushForeign, self).setUp()
782
 
        test_foreign.register_dummy_foreign_for_test(self)
783
 
 
784
 
    def make_dummy_builder(self, relpath):
785
 
        builder = self.make_branch_builder(
786
 
            relpath, format=test_foreign.DummyForeignVcsDirFormat())
787
 
        builder.build_snapshot('revid', None,
788
 
            [('add', ('', 'TREE_ROOT', 'directory', None)),
789
 
             ('add', ('foo', 'fooid', 'file', 'bar'))])
790
 
        return builder
791
 
 
792
 
    def test_no_roundtripping(self):
793
 
        target_branch = self.make_dummy_builder('dp').get_branch()
794
 
        source_tree = self.make_branch_and_tree("dc")
795
 
        output, error = self.run_bzr("push -d dc dp", retcode=3)
796
 
        self.assertEquals("", output)
797
 
        self.assertEquals(error, "bzr: ERROR: It is not possible to losslessly"
798
 
            " push to dummy. You may want to use dpush instead.\n")