~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Ian Clatworthy
  • Date: 2007-12-11 02:07:30 UTC
  • mto: (3119.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 3120.
  • Revision ID: ian.clatworthy@internode.on.net-20071211020730-sdj4kj794dw0628e
make help topics more discoverable

Show diffs side-by-side

added added

removed removed

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