~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

Initial commit for russian version of documents.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
26
    )
33
 
from bzrlib.repofmt import knitrepo
34
 
from bzrlib.tests import http_server
35
 
from bzrlib.transport import memory
36
 
 
37
 
 
38
 
def load_tests(standard_tests, module, loader):
39
 
    """Multiply tests for the push command."""
40
 
    result = loader.suiteClass()
41
 
 
42
 
    # one for each king of change
43
 
    changes_tests, remaining_tests = tests.split_suite_by_condition(
44
 
        standard_tests, tests.condition_isinstance((
45
 
                TestPushStrictWithChanges,
46
 
                )))
47
 
    changes_scenarios = [
48
 
        ('uncommitted',
49
 
         dict(_changes_type= '_uncommitted_changes')),
50
 
        ('pending-merges',
51
 
         dict(_changes_type= '_pending_merges')),
52
 
        ('out-of-sync-trees',
53
 
         dict(_changes_type= '_out_of_sync_trees')),
54
 
        ]
55
 
    tests.multiply_tests(changes_tests, changes_scenarios, result)
56
 
    # No parametrization for the remaining tests
57
 
    result.addTests(remaining_tests)
58
 
 
59
 
    return result
60
 
 
61
 
 
62
 
class TestPush(tests.TestCaseWithTransport):
63
 
 
64
 
    def test_push_error_on_vfs_http(self):
65
 
        """ pushing a branch to a HTTP server fails cleanly. """
66
 
        # the trunk is published on a web server
67
 
        self.transport_readonly_server = http_server.HttpServer
68
 
        self.make_branch('source')
69
 
        public_url = self.get_readonly_url('target')
70
 
        self.run_bzr_error(['http does not support mkdir'],
71
 
                           ['push', public_url],
72
 
                           working_dir='source')
 
27
from bzrlib.branch import Branch
 
28
from bzrlib.bzrdir import BzrDirMetaFormat1
 
29
from bzrlib.osutils import abspath
 
30
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
 
31
from bzrlib.tests.blackbox import ExternalBase
 
32
from bzrlib.tests.http_server import HttpServer
 
33
from bzrlib.transport import register_transport, unregister_transport
 
34
from bzrlib.transport.memory import MemoryServer, MemoryTransport
 
35
from bzrlib.uncommit import uncommit
 
36
from bzrlib.urlutils import local_path_from_url
 
37
from bzrlib.workingtree import WorkingTree
 
38
 
 
39
 
 
40
class TestPush(ExternalBase):
73
41
 
74
42
    def test_push_remember(self):
75
43
        """Push changes from one branch to another and test push location."""
93
61
        self.assertEqual(None, branch_b.get_push_location())
94
62
 
95
63
        # test push for failure without push location set
96
 
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
 
64
        os.chdir('branch_a')
 
65
        out = self.run_bzr('push', retcode=3)
97
66
        self.assertEquals(out,
98
67
                ('','bzr: ERROR: No push location known or specified.\n'))
99
68
 
100
69
        # test not remembered if cannot actually push
101
 
        self.run_bzr('push path/which/doesnt/exist',
102
 
                     working_dir='branch_a', retcode=3)
103
 
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
 
70
        self.run_bzr('push ../path/which/doesnt/exist', retcode=3)
 
71
        out = self.run_bzr('push', retcode=3)
104
72
        self.assertEquals(
105
73
                ('', 'bzr: ERROR: No push location known or specified.\n'),
106
74
                out)
107
75
 
108
76
        # test implicit --remember when no push location set, push fails
109
 
        out = self.run_bzr('push ../branch_b',
110
 
                           working_dir='branch_a', retcode=3)
 
77
        out = self.run_bzr('push ../branch_b', retcode=3)
111
78
        self.assertEquals(out,
112
79
                ('','bzr: ERROR: These branches have diverged.  '
113
 
                 'See "bzr help diverged-branches" for more information.\n'))
114
 
        self.assertEquals(osutils.abspath(branch_a.get_push_location()),
115
 
                          osutils.abspath(branch_b.bzrdir.root_transport.base))
 
80
                    'Try using "merge" and then "push".\n'))
 
81
        self.assertEquals(abspath(branch_a.get_push_location()),
 
82
                          abspath(branch_b.bzrdir.root_transport.base))
116
83
 
117
84
        # test implicit --remember after resolving previous failure
118
 
        uncommit.uncommit(branch=branch_b, tree=tree_b)
 
85
        uncommit(branch=branch_b, tree=tree_b)
119
86
        transport.delete('branch_b/c')
120
 
        out, err = self.run_bzr('push', working_dir='branch_a')
 
87
        out, err = self.run_bzr('push')
121
88
        path = branch_a.get_push_location()
122
89
        self.assertEquals(out,
123
 
                          'Using saved push location: %s\n'
124
 
                          % urlutils.local_path_from_url(path))
 
90
                          'Using saved location: %s\n' 
 
91
                          'Pushed up to revision 2.\n'
 
92
                          % local_path_from_url(path))
125
93
        self.assertEqual(err,
126
 
                         'All changes applied successfully.\n'
127
 
                         'Pushed up to revision 2.\n')
 
94
                         'All changes applied successfully.\n')
128
95
        self.assertEqual(path,
129
96
                         branch_b.bzrdir.root_transport.base)
130
97
        # test explicit --remember
131
 
        self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
 
98
        self.run_bzr('push ../branch_c --remember')
132
99
        self.assertEquals(branch_a.get_push_location(),
133
100
                          branch_c.bzrdir.root_transport.base)
134
 
 
 
101
    
135
102
    def test_push_without_tree(self):
136
103
        # bzr push from a branch that does not have a checkout should work.
137
104
        b = self.make_branch('.')
138
105
        out, err = self.run_bzr('push pushed-location')
139
106
        self.assertEqual('', out)
140
107
        self.assertEqual('Created new branch.\n', err)
141
 
        b2 = branch.Branch.open('pushed-location')
 
108
        b2 = Branch.open('pushed-location')
142
109
        self.assertEndsWith(b2.base, 'pushed-location/')
143
110
 
144
111
    def test_push_new_branch_revision_count(self):
145
 
        # bzr push of a branch with revisions to a new location
146
 
        # should print the number of revisions equal to the length of the
 
112
        # bzr push of a branch with revisions to a new location 
 
113
        # should print the number of revisions equal to the length of the 
147
114
        # local branch.
148
115
        t = self.make_branch_and_tree('tree')
149
116
        self.build_tree(['tree/file'])
150
117
        t.add('file')
151
118
        t.commit('commit 1')
152
 
        out, err = self.run_bzr('push -d tree pushed-to')
 
119
        os.chdir('tree')
 
120
        out, err = self.run_bzr('push pushed-to')
 
121
        os.chdir('..')
153
122
        self.assertEqual('', out)
154
123
        self.assertEqual('Created new branch.\n', err)
155
124
 
156
125
    def test_push_only_pushes_history(self):
157
126
        # Knit branches should only push the history for the current revision.
158
 
        format = bzrdir.BzrDirMetaFormat1()
159
 
        format.repository_format = knitrepo.RepositoryFormatKnit1()
 
127
        format = BzrDirMetaFormat1()
 
128
        format.repository_format = RepositoryFormatKnit1()
160
129
        shared_repo = self.make_repository('repo', format=format, shared=True)
161
130
        shared_repo.set_make_working_trees(True)
162
131
 
163
132
        def make_shared_tree(path):
164
133
            shared_repo.bzrdir.root_transport.mkdir(path)
165
134
            shared_repo.bzrdir.create_branch_convenience('repo/' + path)
166
 
            return workingtree.WorkingTree.open('repo/' + path)
 
135
            return WorkingTree.open('repo/' + path)
167
136
        tree_a = make_shared_tree('a')
168
137
        self.build_tree(['repo/a/file'])
169
138
        tree_a.add('file')
184
153
 
185
154
        # Now that we have a repository with shared files, make sure
186
155
        # that things aren't copied out by a 'push'
187
 
        self.run_bzr('push ../../push-b', working_dir='repo/b')
188
 
        pushed_tree = workingtree.WorkingTree.open('push-b')
 
156
        os.chdir('repo/b')
 
157
        self.run_bzr('push ../../push-b')
 
158
        pushed_tree = WorkingTree.open('../../push-b')
189
159
        pushed_repo = pushed_tree.branch.repository
190
160
        self.assertFalse(pushed_repo.has_revision('a-1'))
191
161
        self.assertFalse(pushed_repo.has_revision('a-2'))
193
163
 
194
164
    def test_push_funky_id(self):
195
165
        t = self.make_branch_and_tree('tree')
196
 
        self.build_tree(['tree/filename'])
 
166
        os.chdir('tree')
 
167
        self.build_tree(['filename'])
197
168
        t.add('filename', 'funky-chars<>%&;"\'')
198
169
        t.commit('commit filename')
199
 
        self.run_bzr('push -d tree new-tree')
 
170
        self.run_bzr('push ../new-tree')
200
171
 
201
172
    def test_push_dash_d(self):
202
173
        t = self.make_branch_and_tree('from')
204
175
                message='first commit')
205
176
        self.run_bzr('push -d from to-one')
206
177
        self.failUnlessExists('to-one')
207
 
        self.run_bzr('push -d %s %s'
 
178
        self.run_bzr('push -d %s %s' 
208
179
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
209
180
        self.failUnlessExists('to-two')
210
181
 
211
 
    def test_push_smart_non_stacked_streaming_acceptance(self):
212
 
        self.setup_smart_server_with_call_log()
213
 
        t = self.make_branch_and_tree('from')
214
 
        t.commit(allow_pointless=True, message='first commit')
215
 
        self.reset_smart_call_log()
216
 
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
217
 
        # This figure represent the amount of work to perform this use case. It
218
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
219
 
        # being too low. If rpc_count increases, more network roundtrips have
220
 
        # become necessary for this use case. Please do not adjust this number
221
 
        # upwards without agreement from bzr's network support maintainers.
222
 
        self.assertLength(9, self.hpss_calls)
223
 
 
224
 
    def test_push_smart_stacked_streaming_acceptance(self):
225
 
        self.setup_smart_server_with_call_log()
226
 
        parent = self.make_branch_and_tree('parent', format='1.9')
227
 
        parent.commit(message='first commit')
228
 
        local = parent.bzrdir.sprout('local').open_workingtree()
229
 
        local.commit(message='local commit')
230
 
        self.reset_smart_call_log()
231
 
        self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
232
 
            self.get_url('public')], working_dir='local')
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(14, self.hpss_calls)
239
 
        remote = branch.Branch.open('public')
240
 
        self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
241
 
 
242
 
    def test_push_smart_tags_streaming_acceptance(self):
243
 
        self.setup_smart_server_with_call_log()
244
 
        t = self.make_branch_and_tree('from')
245
 
        rev_id = t.commit(allow_pointless=True, message='first commit')
246
 
        t.branch.tags.set_tag('new-tag', rev_id)
247
 
        self.reset_smart_call_log()
248
 
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
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(11, self.hpss_calls)
255
 
 
256
 
    def test_push_smart_with_default_stacking_url_path_segment(self):
257
 
        # If the default stacked-on location is a path element then branches
258
 
        # we push there over the smart server are stacked and their
259
 
        # stacked_on_url is that exact path segment. Added to nail bug 385132.
260
 
        self.setup_smart_server_with_call_log()
261
 
        self.make_branch('stack-on', format='1.9')
262
 
        self.make_bzrdir('.').get_config().set_default_stack_on(
263
 
            '/stack-on')
264
 
        self.make_branch('from', format='1.9')
265
 
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
266
 
        b = branch.Branch.open(self.get_url('to'))
267
 
        self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
268
 
 
269
 
    def test_push_smart_with_default_stacking_relative_path(self):
270
 
        # If the default stacked-on location is a relative path then branches
271
 
        # we push there over the smart server are stacked and their
272
 
        # stacked_on_url is a relative path. Added to nail bug 385132.
273
 
        self.setup_smart_server_with_call_log()
274
 
        self.make_branch('stack-on', format='1.9')
275
 
        self.make_bzrdir('.').get_config().set_default_stack_on('stack-on')
276
 
        self.make_branch('from', format='1.9')
277
 
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
278
 
        b = branch.Branch.open(self.get_url('to'))
279
 
        self.assertEqual('../stack-on', b.get_stacked_on_url())
280
 
 
281
182
    def create_simple_tree(self):
282
183
        tree = self.make_branch_and_tree('tree')
283
184
        self.build_tree(['tree/a'])
294
195
                           working_dir='tree')
295
196
        self.run_bzr('push ../new/tree --create-prefix',
296
197
                     working_dir='tree')
297
 
        new_tree = workingtree.WorkingTree.open('new/tree')
 
198
        new_tree = WorkingTree.open('new/tree')
298
199
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
299
200
        self.failUnlessExists('new/tree/a')
300
201
 
314
215
        self.run_bzr('push --use-existing-dir ../target',
315
216
                     working_dir='tree')
316
217
 
317
 
        new_tree = workingtree.WorkingTree.open('target')
 
218
        new_tree = WorkingTree.open('target')
318
219
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
319
220
        # The push should have created target/a
320
221
        self.failUnlessExists('target/a')
330
231
        # Pushing onto an existing bzrdir will create a repository and
331
232
        # branch as needed, but will only create a working tree if there was
332
233
        # no BzrDir before.
333
 
        self.assertRaises(errors.NoWorkingTree,
334
 
                          workingtree.WorkingTree.open, 'repo')
335
 
        new_branch = branch.Branch.open('repo')
 
234
        self.assertRaises(errors.NoWorkingTree, WorkingTree.open, 'repo')
 
235
        new_branch = Branch.open('repo')
336
236
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
337
237
 
338
238
    def test_push_onto_just_bzrdir(self):
357
257
 
358
258
        self.run_bzr('push -r1 ../to', working_dir='from')
359
259
 
360
 
        tree_to = workingtree.WorkingTree.open('to')
 
260
        tree_to = WorkingTree.open('to')
361
261
        repo_to = tree_to.branch.repository
362
262
        self.assertTrue(repo_to.has_revision('from-1'))
363
263
        self.assertFalse(repo_to.has_revision('from-2'))
364
264
        self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
365
265
 
366
266
        self.run_bzr_error(
367
 
            ['bzr: ERROR: bzr push --revision '
368
 
             'takes exactly one revision identifier\n'],
 
267
            "bzr: ERROR: bzr push --revision takes one value.\n",
369
268
            'push -r0..2 ../to', working_dir='from')
370
269
 
371
270
    def create_trunk_and_feature_branch(self):
372
271
        # We have a mainline
373
272
        trunk_tree = self.make_branch_and_tree('target',
374
 
            format='1.9')
 
273
            format='development')
375
274
        trunk_tree.commit('mainline')
376
275
        # and a branch from it
377
276
        branch_tree = self.make_branch_and_tree('branch',
378
 
            format='1.9')
 
277
            format='development')
379
278
        branch_tree.pull(trunk_tree.branch)
380
279
        branch_tree.branch.set_parent(trunk_tree.branch.base)
381
280
        # with some work on it
384
283
 
385
284
    def assertPublished(self, branch_revid, stacked_on):
386
285
        """Assert that the branch 'published' has been published correctly."""
387
 
        published_branch = branch.Branch.open('published')
 
286
        published_branch = Branch.open('published')
388
287
        # The published branch refers to the mainline
389
288
        self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
390
289
        # and the branch's work was pushed
412
311
        self.assertEqual('', out)
413
312
        self.assertEqual('Created new stacked branch referring to %s.\n' %
414
313
            trunk_tree.branch.base, err)
415
 
        self.assertPublished(branch_tree.last_revision(),
416
 
                             trunk_tree.branch.base)
 
314
        self.assertPublished(branch_tree.last_revision(), trunk_tree.branch.base)
417
315
 
418
316
    def test_push_new_branch_stacked_uses_parent_public(self):
419
317
        """Pushing a new branch with --stacked creates a stacked branch."""
420
318
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
421
319
        # the trunk is published on a web server
422
 
        self.transport_readonly_server = http_server.HttpServer
423
 
        trunk_public = self.make_branch('public_trunk', format='1.9')
 
320
        self.transport_readonly_server = HttpServer
 
321
        trunk_public = self.make_branch('public_trunk', format='development')
424
322
        trunk_public.pull(trunk_tree.branch)
425
323
        trunk_public_url = self.get_readonly_url('public_trunk')
426
324
        trunk_tree.branch.set_public_branch(trunk_public_url)
435
333
 
436
334
    def test_push_new_branch_stacked_no_parent(self):
437
335
        """Pushing with --stacked and no parent branch errors."""
438
 
        branch = self.make_branch_and_tree('branch', format='1.9')
 
336
        branch = self.make_branch_and_tree('branch', format='development')
439
337
        # now we do a stacked push, which should fail as the place to refer too
440
338
        # cannot be determined.
441
339
        out, err = self.run_bzr_error(
445
343
        self.assertFalse(self.get_transport('published').has('.'))
446
344
 
447
345
    def test_push_notifies_default_stacking(self):
448
 
        self.make_branch('stack_on', format='1.6')
 
346
        self.make_branch('stack_on', format='development1')
449
347
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
450
 
        self.make_branch('from', format='1.6')
 
348
        self.make_branch('from', format='development1')
451
349
        out, err = self.run_bzr('push -d from to')
452
350
        self.assertContainsRe(err,
453
351
                              'Using default stacking branch stack_on at .*')
454
352
 
455
 
    def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
456
 
        self.make_branch('stack_on', format='1.6')
457
 
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
458
 
        self.make_branch('from', format='pack-0.92')
459
 
        out, err = self.run_bzr('push -d from to')
460
 
        b = branch.Branch.open('to')
461
 
        self.assertEqual('../stack_on', b.get_stacked_on_url())
462
 
 
463
 
    def test_push_does_not_change_format_with_default_if_target_cannot(self):
464
 
        self.make_branch('stack_on', format='pack-0.92')
465
 
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
466
 
        self.make_branch('from', format='pack-0.92')
467
 
        out, err = self.run_bzr('push -d from to')
468
 
        b = branch.Branch.open('to')
469
 
        self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
470
 
 
471
 
    def test_push_doesnt_create_broken_branch(self):
472
 
        """Pushing a new standalone branch works even when there's a default
473
 
        stacking policy at the destination.
474
 
 
475
 
        The new branch will preserve the repo format (even if it isn't the
476
 
        default for the branch), and will be stacked when the repo format
477
 
        allows (which means that the branch format isn't necessarly preserved).
478
 
        """
479
 
        self.make_repository('repo', shared=True, format='1.6')
480
 
        builder = self.make_branch_builder('repo/local', format='pack-0.92')
481
 
        builder.start_series()
482
 
        builder.build_snapshot('rev-1', None, [
483
 
            ('add', ('', 'root-id', 'directory', '')),
484
 
            ('add', ('filename', 'f-id', 'file', 'content\n'))])
485
 
        builder.build_snapshot('rev-2', ['rev-1'], [])
486
 
        builder.build_snapshot('rev-3', ['rev-2'],
487
 
            [('modify', ('f-id', 'new-content\n'))])
488
 
        builder.finish_series()
489
 
        branch = builder.get_branch()
490
 
        # Push rev-1 to "trunk", so that we can stack on it.
491
 
        self.run_bzr('push -d repo/local trunk -r 1')
492
 
        # Set a default stacking policy so that new branches will automatically
493
 
        # stack on trunk.
494
 
        self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
495
 
        # Push rev-2 to a new branch "remote".  It will be stacked on "trunk".
496
 
        out, err = self.run_bzr('push -d repo/local remote -r 2')
497
 
        self.assertContainsRe(
498
 
            err, 'Using default stacking branch trunk at .*')
499
 
        # Push rev-3 onto "remote".  If "remote" not stacked and is missing the
500
 
        # fulltext record for f-id @ rev-1, then this will fail.
501
 
        out, err = self.run_bzr('push -d repo/local remote -r 3')
502
 
 
503
 
    def test_push_verbose_shows_log(self):
504
 
        tree = self.make_branch_and_tree('source')
505
 
        tree.commit('rev1')
506
 
        out, err = self.run_bzr('push -v -d source target')
507
 
        # initial push contains log
508
 
        self.assertContainsRe(out, 'rev1')
509
 
        tree.commit('rev2')
510
 
        out, err = self.run_bzr('push -v -d source target')
511
 
        # subsequent push contains log
512
 
        self.assertContainsRe(out, 'rev2')
513
 
        # subsequent log is accurate
514
 
        self.assertNotContainsRe(out, 'rev1')
515
 
 
516
 
    def test_push_from_subdir(self):
517
 
        t = self.make_branch_and_tree('tree')
518
 
        self.build_tree(['tree/dir/', 'tree/dir/file'])
519
 
        t.add('dir', 'dir/file')
520
 
        t.commit('r1')
521
 
        out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
522
 
        self.assertEqual('', out)
523
 
        self.assertEqual('Created new branch.\n', err)
524
 
 
525
 
 
526
 
class RedirectingMemoryTransport(memory.MemoryTransport):
527
 
 
528
 
    def mkdir(self, relpath, mode=None):
529
 
        if self._cwd == '/source/':
530
 
            raise errors.RedirectRequested(self.abspath(relpath),
531
 
                                           self.abspath('../target'),
532
 
                                           is_permanent=True)
533
 
        elif self._cwd == '/infinite-loop/':
534
 
            raise errors.RedirectRequested(self.abspath(relpath),
535
 
                                           self.abspath('../infinite-loop'),
536
 
                                           is_permanent=True)
 
353
 
 
354
class RedirectingMemoryTransport(MemoryTransport):
 
355
 
 
356
    def mkdir(self, path, mode=None):
 
357
        path = self.abspath(path)[len(self._scheme):]
 
358
        if path == '/source':
 
359
            raise errors.RedirectRequested(
 
360
                path, self._scheme + '/target', is_permanent=True)
 
361
        elif path == '/infinite-loop':
 
362
            raise errors.RedirectRequested(
 
363
                path, self._scheme + '/infinite-loop', is_permanent=True)
537
364
        else:
538
365
            return super(RedirectingMemoryTransport, self).mkdir(
539
 
                relpath, mode)
540
 
 
541
 
    def get(self, relpath):
542
 
        if self.clone(relpath)._cwd == '/infinite-loop/':
543
 
            raise errors.RedirectRequested(self.abspath(relpath),
544
 
                                           self.abspath('../infinite-loop'),
545
 
                                           is_permanent=True)
546
 
        else:
547
 
            return super(RedirectingMemoryTransport, self).get(relpath)
548
 
 
549
 
    def _redirected_to(self, source, target):
550
 
        # We do accept redirections
551
 
        return transport.get_transport(target)
552
 
 
553
 
 
554
 
class RedirectingMemoryServer(memory.MemoryServer):
 
366
                path, mode)
 
367
 
 
368
 
 
369
class RedirectingMemoryServer(MemoryServer):
555
370
 
556
371
    def setUp(self):
557
372
        self._dirs = {'/': None}
558
373
        self._files = {}
559
374
        self._locks = {}
560
375
        self._scheme = 'redirecting-memory+%s:///' % id(self)
561
 
        transport.register_transport(self._scheme, self._memory_factory)
 
376
        register_transport(self._scheme, self._memory_factory)
562
377
 
563
378
    def _memory_factory(self, url):
564
379
        result = RedirectingMemoryTransport(url)
568
383
        return result
569
384
 
570
385
    def tearDown(self):
571
 
        transport.unregister_transport(self._scheme, self._memory_factory)
572
 
 
573
 
 
574
 
class TestPushRedirect(tests.TestCaseWithTransport):
 
386
        unregister_transport(self._scheme, self._memory_factory)
 
387
 
 
388
 
 
389
class TestPushRedirect(ExternalBase):
575
390
 
576
391
    def setUp(self):
577
 
        tests.TestCaseWithTransport.setUp(self)
 
392
        ExternalBase.setUp(self)
578
393
        self.memory_server = RedirectingMemoryServer()
579
394
        self.memory_server.setUp()
580
395
        self.addCleanup(self.memory_server.tearDown)
591
406
        This is added primarily to handle lp:/ URI support, so that users can
592
407
        push to new branches by specifying lp:/ URIs.
593
408
        """
 
409
        os.chdir('tree')
594
410
        destination_url = self.memory_server.get_url() + 'source'
595
 
        self.run_bzr(['push', '-d', 'tree', destination_url])
 
411
        self.run_bzr('push %s' % destination_url)
 
412
        os.chdir('..')
596
413
 
597
 
        local_revision = branch.Branch.open('tree').last_revision()
598
 
        remote_revision = branch.Branch.open(
 
414
        local_revision = Branch.open('tree').last_revision()
 
415
        remote_revision = Branch.open(
599
416
            self.memory_server.get_url() + 'target').last_revision()
600
417
        self.assertEqual(remote_revision, local_revision)
601
418
 
603
420
        """Push fails gracefully if the mkdir generates a large number of
604
421
        redirects.
605
422
        """
 
423
        os.chdir('tree')
606
424
        destination_url = self.memory_server.get_url() + 'infinite-loop'
607
425
        out, err = self.run_bzr_error(
608
426
            ['Too many redirections trying to make %s\\.\n'
609
427
             % re.escape(destination_url)],
610
 
            ['push', '-d', 'tree', destination_url], retcode=3)
 
428
            'push %s' % destination_url, retcode=3)
 
429
        os.chdir('..')
611
430
        self.assertEqual('', out)
612
 
 
613
 
 
614
 
class TestPushStrictMixin(object):
615
 
 
616
 
    def make_local_branch_and_tree(self):
617
 
        self.tree = self.make_branch_and_tree('local')
618
 
        self.build_tree_contents([('local/file', 'initial')])
619
 
        self.tree.add('file')
620
 
        self.tree.commit('adding file', rev_id='added')
621
 
        self.build_tree_contents([('local/file', 'modified')])
622
 
        self.tree.commit('modify file', rev_id='modified')
623
 
 
624
 
    def set_config_push_strict(self, value):
625
 
        # set config var (any of bazaar.conf, locations.conf, branch.conf
626
 
        # should do)
627
 
        conf = self.tree.branch.get_config()
628
 
        conf.set_user_option('push_strict', value)
629
 
 
630
 
    _default_command = ['push', '../to']
631
 
    _default_wd = 'local'
632
 
    _default_errors = ['Working tree ".*/local/" has uncommitted '
633
 
                       'changes \(See bzr status\)\.',]
634
 
    _default_pushed_revid = 'modified'
635
 
 
636
 
    def assertPushFails(self, args):
637
 
        self.run_bzr_error(self._default_errors, self._default_command + args,
638
 
                           working_dir=self._default_wd, retcode=3)
639
 
 
640
 
    def assertPushSucceeds(self, args, pushed_revid=None):
641
 
        self.run_bzr(self._default_command + args,
642
 
                     working_dir=self._default_wd)
643
 
        if pushed_revid is None:
644
 
            pushed_revid = self._default_pushed_revid
645
 
        tree_to = workingtree.WorkingTree.open('to')
646
 
        repo_to = tree_to.branch.repository
647
 
        self.assertTrue(repo_to.has_revision(pushed_revid))
648
 
        self.assertEqual(tree_to.branch.last_revision_info()[1], pushed_revid)
649
 
 
650
 
 
651
 
 
652
 
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
653
 
                                   TestPushStrictMixin):
654
 
 
655
 
    def setUp(self):
656
 
        super(TestPushStrictWithoutChanges, self).setUp()
657
 
        self.make_local_branch_and_tree()
658
 
 
659
 
    def test_push_default(self):
660
 
        self.assertPushSucceeds([])
661
 
 
662
 
    def test_push_strict(self):
663
 
        self.assertPushSucceeds(['--strict'])
664
 
 
665
 
    def test_push_no_strict(self):
666
 
        self.assertPushSucceeds(['--no-strict'])
667
 
 
668
 
    def test_push_config_var_strict(self):
669
 
        self.set_config_push_strict('true')
670
 
        self.assertPushSucceeds([])
671
 
 
672
 
    def test_push_config_var_no_strict(self):
673
 
        self.set_config_push_strict('false')
674
 
        self.assertPushSucceeds([])
675
 
 
676
 
 
677
 
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
678
 
                                TestPushStrictMixin):
679
 
 
680
 
    _changes_type = None # Set by load_tests
681
 
 
682
 
    def setUp(self):
683
 
        super(TestPushStrictWithChanges, self).setUp()
684
 
        getattr(self, self._changes_type)()
685
 
 
686
 
    def _uncommitted_changes(self):
687
 
        self.make_local_branch_and_tree()
688
 
        # Make a change without committing it
689
 
        self.build_tree_contents([('local/file', 'in progress')])
690
 
 
691
 
    def _pending_merges(self):
692
 
        self.make_local_branch_and_tree()
693
 
        # Create 'other' branch containing a new file
694
 
        other_bzrdir = self.tree.bzrdir.sprout('other')
695
 
        other_tree = other_bzrdir.open_workingtree()
696
 
        self.build_tree_contents([('other/other-file', 'other')])
697
 
        other_tree.add('other-file')
698
 
        other_tree.commit('other commit', rev_id='other')
699
 
        # Merge and revert, leaving a pending merge
700
 
        self.tree.merge_from_branch(other_tree.branch)
701
 
        self.tree.revert(filenames=['other-file'], backups=False)
702
 
 
703
 
    def _out_of_sync_trees(self):
704
 
        self.make_local_branch_and_tree()
705
 
        self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
706
 
        # Make a change and commit it
707
 
        self.build_tree_contents([('local/file', 'modified in local')])
708
 
        self.tree.commit('modify file', rev_id='modified-in-local')
709
 
        # Exercise commands from the checkout directory
710
 
        self._default_wd = 'checkout'
711
 
        self._default_errors = ["Working tree is out of date, please run"
712
 
                                " 'bzr update'\.",]
713
 
        self._default_pushed_revid = 'modified-in-local'
714
 
 
715
 
    def test_push_default(self):
716
 
        self.assertPushFails([])
717
 
 
718
 
    def test_push_with_revision(self):
719
 
        self.assertPushSucceeds(['-r', 'revid:added'], pushed_revid='added')
720
 
 
721
 
    def test_push_no_strict(self):
722
 
        self.assertPushSucceeds(['--no-strict'])
723
 
 
724
 
    def test_push_strict_with_changes(self):
725
 
        self.assertPushFails(['--strict'])
726
 
 
727
 
    def test_push_respect_config_var_strict(self):
728
 
        self.set_config_push_strict('true')
729
 
        self.assertPushFails([])
730
 
 
731
 
    def test_push_bogus_config_var_ignored(self):
732
 
        self.set_config_push_strict("I don't want you to be strict")
733
 
        self.assertPushFails([])
734
 
 
735
 
    def test_push_no_strict_command_line_override_config(self):
736
 
        self.set_config_push_strict('yES')
737
 
        self.assertPushFails([])
738
 
        self.assertPushSucceeds(['--no-strict'])
739
 
 
740
 
    def test_push_strict_command_line_override_config(self):
741
 
        self.set_config_push_strict('oFF')
742
 
        self.assertPushFails(['--strict'])
743
 
        self.assertPushSucceeds([])