/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

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

  • Committer: Gustav Hartvigsson
  • Date: 2021-01-09 21:36:27 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20210109213627-h1xwcutzy9m7a99b
Added 'Case Preserving Working Tree Use Cases' from Canonical Wiki

* Addod a page from the Canonical Bazaar wiki
  with information on the scmeatics of case
  perserving filesystems an a case insensitive
  filesystem works.
  
  * Needs re-work, but this will do as it is the
    same inforamoton as what was on the linked
    page in the currint documentation.

Show diffs side-by-side

added added

removed removed

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