~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-03-11 13:47:06 UTC
  • mfrom: (5051.3.16 use-branch-open)
  • Revision ID: pqm@pqm.ubuntu.com-20100311134706-kaerqhx3lf7xn6rh
(Jelmer) Pass colocated branch names further down the call stack.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007, 2008 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
21
20
import re
22
21
 
23
22
from bzrlib import (
 
23
    branch,
 
24
    bzrdir,
24
25
    errors,
 
26
    osutils,
 
27
    tests,
 
28
    transport,
 
29
    uncommit,
25
30
    urlutils,
26
 
    )
27
 
from bzrlib.branch import Branch
28
 
from bzrlib.bzrdir import BzrDirMetaFormat1
29
 
from bzrlib.osutils import abspath
30
 
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
31
 
from bzrlib.tests.blackbox import ExternalBase
32
 
from bzrlib.tests.http_server import HttpServer
33
 
from bzrlib.transport import register_transport, unregister_transport
34
 
from bzrlib.transport.memory import MemoryServer, MemoryTransport
35
 
from bzrlib.uncommit import uncommit
36
 
from bzrlib.urlutils import local_path_from_url
37
 
from bzrlib.workingtree import WorkingTree
38
 
 
39
 
 
40
 
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')
41
78
 
42
79
    def test_push_remember(self):
43
80
        """Push changes from one branch to another and test push location."""
61
98
        self.assertEqual(None, branch_b.get_push_location())
62
99
 
63
100
        # test push for failure without push location set
64
 
        os.chdir('branch_a')
65
 
        out = self.run_bzr('push', retcode=3)
 
101
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
66
102
        self.assertEquals(out,
67
103
                ('','bzr: ERROR: No push location known or specified.\n'))
68
104
 
69
105
        # test not remembered if cannot actually push
70
 
        self.run_bzr('push ../path/which/doesnt/exist', retcode=3)
71
 
        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)
72
109
        self.assertEquals(
73
110
                ('', 'bzr: ERROR: No push location known or specified.\n'),
74
111
                out)
75
112
 
76
113
        # test implicit --remember when no push location set, push fails
77
 
        out = self.run_bzr('push ../branch_b', retcode=3)
 
114
        out = self.run_bzr('push ../branch_b',
 
115
                           working_dir='branch_a', retcode=3)
78
116
        self.assertEquals(out,
79
117
                ('','bzr: ERROR: These branches have diverged.  '
80
 
                    'Try using "merge" and then "push".\n'))
81
 
        self.assertEquals(abspath(branch_a.get_push_location()),
82
 
                          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))
83
121
 
84
122
        # test implicit --remember after resolving previous failure
85
 
        uncommit(branch=branch_b, tree=tree_b)
 
123
        uncommit.uncommit(branch=branch_b, tree=tree_b)
86
124
        transport.delete('branch_b/c')
87
 
        out, err = self.run_bzr('push')
 
125
        out, err = self.run_bzr('push', working_dir='branch_a')
88
126
        path = branch_a.get_push_location()
89
127
        self.assertEquals(out,
90
 
                          'Using saved push location: %s\n' 
91
 
                          'Pushed up to revision 2.\n'
92
 
                          % local_path_from_url(path))
 
128
                          'Using saved push location: %s\n'
 
129
                          % urlutils.local_path_from_url(path))
93
130
        self.assertEqual(err,
94
 
                         'All changes applied successfully.\n')
 
131
                         'All changes applied successfully.\n'
 
132
                         'Pushed up to revision 2.\n')
95
133
        self.assertEqual(path,
96
134
                         branch_b.bzrdir.root_transport.base)
97
135
        # test explicit --remember
98
 
        self.run_bzr('push ../branch_c --remember')
 
136
        self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
99
137
        self.assertEquals(branch_a.get_push_location(),
100
138
                          branch_c.bzrdir.root_transport.base)
101
 
    
 
139
 
102
140
    def test_push_without_tree(self):
103
141
        # bzr push from a branch that does not have a checkout should work.
104
142
        b = self.make_branch('.')
105
143
        out, err = self.run_bzr('push pushed-location')
106
144
        self.assertEqual('', out)
107
145
        self.assertEqual('Created new branch.\n', err)
108
 
        b2 = Branch.open('pushed-location')
 
146
        b2 = branch.Branch.open('pushed-location')
109
147
        self.assertEndsWith(b2.base, 'pushed-location/')
110
148
 
111
149
    def test_push_new_branch_revision_count(self):
112
 
        # bzr push of a branch with revisions to a new location 
113
 
        # 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
114
152
        # local branch.
115
153
        t = self.make_branch_and_tree('tree')
116
154
        self.build_tree(['tree/file'])
117
155
        t.add('file')
118
156
        t.commit('commit 1')
119
 
        os.chdir('tree')
120
 
        out, err = self.run_bzr('push pushed-to')
121
 
        os.chdir('..')
 
157
        out, err = self.run_bzr('push -d tree pushed-to')
122
158
        self.assertEqual('', out)
123
159
        self.assertEqual('Created new branch.\n', err)
124
160
 
125
161
    def test_push_only_pushes_history(self):
126
162
        # Knit branches should only push the history for the current revision.
127
 
        format = BzrDirMetaFormat1()
128
 
        format.repository_format = RepositoryFormatKnit1()
 
163
        format = bzrdir.BzrDirMetaFormat1()
 
164
        format.repository_format = knitrepo.RepositoryFormatKnit1()
129
165
        shared_repo = self.make_repository('repo', format=format, shared=True)
130
166
        shared_repo.set_make_working_trees(True)
131
167
 
132
168
        def make_shared_tree(path):
133
169
            shared_repo.bzrdir.root_transport.mkdir(path)
134
170
            shared_repo.bzrdir.create_branch_convenience('repo/' + path)
135
 
            return WorkingTree.open('repo/' + path)
 
171
            return workingtree.WorkingTree.open('repo/' + path)
136
172
        tree_a = make_shared_tree('a')
137
173
        self.build_tree(['repo/a/file'])
138
174
        tree_a.add('file')
153
189
 
154
190
        # Now that we have a repository with shared files, make sure
155
191
        # that things aren't copied out by a 'push'
156
 
        os.chdir('repo/b')
157
 
        self.run_bzr('push ../../push-b')
158
 
        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')
159
194
        pushed_repo = pushed_tree.branch.repository
160
195
        self.assertFalse(pushed_repo.has_revision('a-1'))
161
196
        self.assertFalse(pushed_repo.has_revision('a-2'))
163
198
 
164
199
    def test_push_funky_id(self):
165
200
        t = self.make_branch_and_tree('tree')
166
 
        os.chdir('tree')
167
 
        self.build_tree(['filename'])
 
201
        self.build_tree(['tree/filename'])
168
202
        t.add('filename', 'funky-chars<>%&;"\'')
169
203
        t.commit('commit filename')
170
 
        self.run_bzr('push ../new-tree')
 
204
        self.run_bzr('push -d tree new-tree')
171
205
 
172
206
    def test_push_dash_d(self):
173
207
        t = self.make_branch_and_tree('from')
175
209
                message='first commit')
176
210
        self.run_bzr('push -d from to-one')
177
211
        self.failUnlessExists('to-one')
178
 
        self.run_bzr('push -d %s %s' 
 
212
        self.run_bzr('push -d %s %s'
179
213
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
180
214
        self.failUnlessExists('to-two')
181
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
 
182
302
    def create_simple_tree(self):
183
303
        tree = self.make_branch_and_tree('tree')
184
304
        self.build_tree(['tree/a'])
195
315
                           working_dir='tree')
196
316
        self.run_bzr('push ../new/tree --create-prefix',
197
317
                     working_dir='tree')
198
 
        new_tree = WorkingTree.open('new/tree')
 
318
        new_tree = workingtree.WorkingTree.open('new/tree')
199
319
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
200
320
        self.failUnlessExists('new/tree/a')
201
321
 
215
335
        self.run_bzr('push --use-existing-dir ../target',
216
336
                     working_dir='tree')
217
337
 
218
 
        new_tree = WorkingTree.open('target')
 
338
        new_tree = workingtree.WorkingTree.open('target')
219
339
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
220
340
        # The push should have created target/a
221
341
        self.failUnlessExists('target/a')
222
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
 
223
354
    def test_push_onto_repo(self):
224
355
        """We should be able to 'bzr push' into an existing bzrdir."""
225
356
        tree = self.create_simple_tree()
231
362
        # Pushing onto an existing bzrdir will create a repository and
232
363
        # branch as needed, but will only create a working tree if there was
233
364
        # no BzrDir before.
234
 
        self.assertRaises(errors.NoWorkingTree, WorkingTree.open, 'repo')
235
 
        new_branch = Branch.open('repo')
 
365
        self.assertRaises(errors.NoWorkingTree,
 
366
                          workingtree.WorkingTree.open, 'repo')
 
367
        new_branch = branch.Branch.open('repo')
236
368
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
237
369
 
238
370
    def test_push_onto_just_bzrdir(self):
257
389
 
258
390
        self.run_bzr('push -r1 ../to', working_dir='from')
259
391
 
260
 
        tree_to = WorkingTree.open('to')
 
392
        tree_to = workingtree.WorkingTree.open('to')
261
393
        repo_to = tree_to.branch.repository
262
394
        self.assertTrue(repo_to.has_revision('from-1'))
263
395
        self.assertFalse(repo_to.has_revision('from-2'))
264
396
        self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
265
397
 
266
398
        self.run_bzr_error(
267
 
            "bzr: ERROR: bzr push --revision takes one value.\n",
 
399
            ['bzr: ERROR: bzr push --revision '
 
400
             'takes exactly one revision identifier\n'],
268
401
            'push -r0..2 ../to', working_dir='from')
269
402
 
270
403
    def create_trunk_and_feature_branch(self):
271
404
        # We have a mainline
272
405
        trunk_tree = self.make_branch_and_tree('target',
273
 
            format='development')
 
406
            format='1.9')
274
407
        trunk_tree.commit('mainline')
275
408
        # and a branch from it
276
409
        branch_tree = self.make_branch_and_tree('branch',
277
 
            format='development')
 
410
            format='1.9')
278
411
        branch_tree.pull(trunk_tree.branch)
279
412
        branch_tree.branch.set_parent(trunk_tree.branch.base)
280
413
        # with some work on it
283
416
 
284
417
    def assertPublished(self, branch_revid, stacked_on):
285
418
        """Assert that the branch 'published' has been published correctly."""
286
 
        published_branch = Branch.open('published')
 
419
        published_branch = branch.Branch.open('published')
287
420
        # The published branch refers to the mainline
288
421
        self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
289
422
        # and the branch's work was pushed
311
444
        self.assertEqual('', out)
312
445
        self.assertEqual('Created new stacked branch referring to %s.\n' %
313
446
            trunk_tree.branch.base, err)
314
 
        self.assertPublished(branch_tree.last_revision(), trunk_tree.branch.base)
 
447
        self.assertPublished(branch_tree.last_revision(),
 
448
                             trunk_tree.branch.base)
315
449
 
316
450
    def test_push_new_branch_stacked_uses_parent_public(self):
317
451
        """Pushing a new branch with --stacked creates a stacked branch."""
318
452
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
319
453
        # the trunk is published on a web server
320
 
        self.transport_readonly_server = HttpServer
321
 
        trunk_public = self.make_branch('public_trunk', format='development')
 
454
        self.transport_readonly_server = http_server.HttpServer
 
455
        trunk_public = self.make_branch('public_trunk', format='1.9')
322
456
        trunk_public.pull(trunk_tree.branch)
323
457
        trunk_public_url = self.get_readonly_url('public_trunk')
324
458
        trunk_tree.branch.set_public_branch(trunk_public_url)
333
467
 
334
468
    def test_push_new_branch_stacked_no_parent(self):
335
469
        """Pushing with --stacked and no parent branch errors."""
336
 
        branch = self.make_branch_and_tree('branch', format='development')
 
470
        branch = self.make_branch_and_tree('branch', format='1.9')
337
471
        # now we do a stacked push, which should fail as the place to refer too
338
472
        # cannot be determined.
339
473
        out, err = self.run_bzr_error(
343
477
        self.assertFalse(self.get_transport('published').has('.'))
344
478
 
345
479
    def test_push_notifies_default_stacking(self):
346
 
        self.make_branch('stack_on', format='development1')
 
480
        self.make_branch('stack_on', format='1.6')
347
481
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
348
 
        self.make_branch('from', format='development1')
 
482
        self.make_branch('from', format='1.6')
349
483
        out, err = self.run_bzr('push -d from to')
350
484
        self.assertContainsRe(err,
351
485
                              'Using default stacking branch stack_on at .*')
352
486
 
353
 
 
354
 
class RedirectingMemoryTransport(MemoryTransport):
355
 
 
356
 
    def mkdir(self, path, mode=None):
357
 
        path = self.abspath(path)[len(self._scheme):]
358
 
        if path == '/source':
359
 
            raise errors.RedirectRequested(
360
 
                path, self._scheme + '/target', is_permanent=True)
361
 
        elif path == '/infinite-loop':
362
 
            raise errors.RedirectRequested(
363
 
                path, self._scheme + '/infinite-loop', is_permanent=True)
 
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)
364
569
        else:
365
570
            return super(RedirectingMemoryTransport, self).mkdir(
366
 
                path, mode)
367
 
 
368
 
 
369
 
class RedirectingMemoryServer(MemoryServer):
370
 
 
371
 
    def setUp(self):
 
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):
372
589
        self._dirs = {'/': None}
373
590
        self._files = {}
374
591
        self._locks = {}
375
592
        self._scheme = 'redirecting-memory+%s:///' % id(self)
376
 
        register_transport(self._scheme, self._memory_factory)
 
593
        transport.register_transport(self._scheme, self._memory_factory)
377
594
 
378
595
    def _memory_factory(self, url):
379
596
        result = RedirectingMemoryTransport(url)
382
599
        result._locks = self._locks
383
600
        return result
384
601
 
385
 
    def tearDown(self):
386
 
        unregister_transport(self._scheme, self._memory_factory)
387
 
 
388
 
 
389
 
class TestPushRedirect(ExternalBase):
 
602
    def stop_server(self):
 
603
        transport.unregister_transport(self._scheme, self._memory_factory)
 
604
 
 
605
 
 
606
class TestPushRedirect(tests.TestCaseWithTransport):
390
607
 
391
608
    def setUp(self):
392
 
        ExternalBase.setUp(self)
 
609
        tests.TestCaseWithTransport.setUp(self)
393
610
        self.memory_server = RedirectingMemoryServer()
394
 
        self.memory_server.setUp()
395
 
        self.addCleanup(self.memory_server.tearDown)
396
 
 
 
611
        self.start_server(self.memory_server)
397
612
        # Make the branch and tree that we'll be pushing.
398
613
        t = self.make_branch_and_tree('tree')
399
614
        self.build_tree(['tree/file'])
406
621
        This is added primarily to handle lp:/ URI support, so that users can
407
622
        push to new branches by specifying lp:/ URIs.
408
623
        """
409
 
        os.chdir('tree')
410
624
        destination_url = self.memory_server.get_url() + 'source'
411
 
        self.run_bzr('push %s' % destination_url)
412
 
        os.chdir('..')
 
625
        self.run_bzr(['push', '-d', 'tree', destination_url])
413
626
 
414
 
        local_revision = Branch.open('tree').last_revision()
415
 
        remote_revision = Branch.open(
 
627
        local_revision = branch.Branch.open('tree').last_revision()
 
628
        remote_revision = branch.Branch.open(
416
629
            self.memory_server.get_url() + 'target').last_revision()
417
630
        self.assertEqual(remote_revision, local_revision)
418
631
 
420
633
        """Push fails gracefully if the mkdir generates a large number of
421
634
        redirects.
422
635
        """
423
 
        os.chdir('tree')
424
636
        destination_url = self.memory_server.get_url() + 'infinite-loop'
425
637
        out, err = self.run_bzr_error(
426
638
            ['Too many redirections trying to make %s\\.\n'
427
639
             % re.escape(destination_url)],
428
 
            'push %s' % destination_url, retcode=3)
429
 
        os.chdir('..')
 
640
            ['push', '-d', 'tree', destination_url], retcode=3)
430
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_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")