~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: 2008-03-16 16:58:03 UTC
  • mfrom: (3224.3.1 news-typo)
  • Revision ID: pqm@pqm.ubuntu.com-20080316165803-tisoc9mpob9z544o
(Matt Nordhoff) Trivial NEWS typo fix

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