~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: 2006-07-12 12:36:57 UTC
  • mfrom: (1732.3.4 bzr.revnoX)
  • Revision ID: pqm@pqm.ubuntu.com-20060712123657-365eeb32b69308bf
(matthieu) revno:x:url revision spec

Show diffs side-by-side

added added

removed removed

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