1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
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.
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.
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
18
"""Black-box tests for brz pull."""
32
from breezy.bzr import (
36
from breezy.directory_service import directories
37
from breezy.tests import (
43
class TestPull(tests.TestCaseWithTransport):
45
def example_branch(self, path='.'):
46
tree = self.make_branch_and_tree(path)
47
self.build_tree_contents([
48
(osutils.pathjoin(path, 'hello'), 'foo'),
49
(osutils.pathjoin(path, 'goodbye'), 'baz')])
51
tree.commit(message='setup')
53
tree.commit(message='setup')
57
"""Pull changes from one branch to another."""
58
a_tree = self.example_branch('a')
59
base_rev = a_tree.branch.last_revision()
60
self.run_bzr('pull', retcode=3, working_dir='a')
61
self.run_bzr('missing', retcode=3, working_dir='a')
62
self.run_bzr('missing .', working_dir='a')
63
self.run_bzr('missing', working_dir='a')
64
# this will work on windows because we check for the same branch
65
# in pull - if it fails, it is a regression
66
self.run_bzr('pull', working_dir='a')
67
self.run_bzr('pull /', retcode=3, working_dir='a')
68
if sys.platform not in ('win32', 'cygwin'):
69
self.run_bzr('pull', working_dir='a')
71
b_tree = a_tree.controldir.sprout('b').open_workingtree()
72
self.run_bzr('pull', working_dir='b')
75
new_rev = b_tree.commit(message='blah', allow_pointless=True)
77
a = branch.Branch.open('a')
78
b = branch.Branch.open('b')
79
self.assertEqual(a.last_revision(), base_rev)
80
self.assertEqual(b.last_revision(), new_rev)
82
self.run_bzr('pull ../b', working_dir='a')
83
self.assertEqual(a.last_revision(), b.last_revision())
84
a_tree.commit(message='blah2', allow_pointless=True)
85
b_tree.commit(message='blah3', allow_pointless=True)
87
self.run_bzr('pull ../a', retcode=3, working_dir='b')
88
b_tree.controldir.sprout('overwriteme')
89
self.run_bzr('pull --overwrite ../a', working_dir='overwriteme')
90
overwritten = branch.Branch.open('overwriteme')
91
self.assertEqual(overwritten.last_revision(),
93
a_tree.merge_from_branch(b_tree.branch)
94
a_tree.commit(message="blah4", allow_pointless=True)
96
self.run_bzr('pull ../../a', working_dir='b/subdir')
97
self.assertEqual(a.last_revision(), b.last_revision())
98
sub_tree = workingtree.WorkingTree.open_containing('b/subdir')[0]
99
sub_tree.commit(message="blah5", allow_pointless=True)
100
sub_tree.commit(message="blah6", allow_pointless=True)
101
self.run_bzr('pull ../a', working_dir='b')
102
a_tree.commit(message="blah7", allow_pointless=True)
103
a_tree.merge_from_branch(b_tree.branch)
104
a_tree.commit(message="blah8", allow_pointless=True)
105
self.run_bzr('pull ../b', working_dir='a')
106
self.run_bzr('pull ../b', working_dir='a')
108
def test_pull_dash_d(self):
109
self.example_branch('a')
110
self.make_branch_and_tree('b')
111
self.make_branch_and_tree('c')
112
# pull into that branch
113
self.run_bzr('pull -d b a')
114
# pull into a branch specified by a url
115
c_url = urlutils.local_path_to_url('c')
116
self.assertStartsWith(c_url, 'file://')
117
self.run_bzr(['pull', '-d', c_url, 'a'])
119
def test_pull_revision(self):
120
"""Pull some changes from one branch to another."""
121
a_tree = self.example_branch('a')
122
self.build_tree_contents([
124
('a/goodbye2', 'baz')])
126
a_tree.commit(message="setup")
127
a_tree.add('goodbye2')
128
a_tree.commit(message="setup")
130
b_tree = a_tree.controldir.sprout('b',
131
revision_id=a_tree.branch.get_rev_id(1)).open_workingtree()
132
self.run_bzr('pull -r 2', working_dir='b')
133
a = branch.Branch.open('a')
134
b = branch.Branch.open('b')
135
self.assertEqual(a.revno(),4)
136
self.assertEqual(b.revno(),2)
137
self.run_bzr('pull -r 3', working_dir='b')
138
self.assertEqual(b.revno(),3)
139
self.run_bzr('pull -r 4', working_dir='b')
140
self.assertEqual(a.last_revision(), b.last_revision())
142
def test_pull_tags(self):
143
"""Tags are updated by pull, and revisions named in those tags are
146
# Make a source, sprout a target off it
147
builder = self.make_branch_builder('source')
148
source = fixtures.build_branch_with_non_ancestral_rev(builder)
149
source.get_config_stack().set('branch.fetch_tags', True)
150
target_bzrdir = source.controldir.sprout('target')
151
source.tags.set_tag('tag-a', 'rev-2')
153
self.run_bzr('pull -d target source')
154
target = target_bzrdir.open_branch()
155
# The tag is present, and so is its revision.
156
self.assertEqual('rev-2', target.tags.lookup_tag('tag-a'))
157
target.repository.get_revision('rev-2')
159
def test_overwrite_uptodate(self):
160
# Make sure pull --overwrite overwrites
161
# even if the target branch has merged
162
# everything already.
163
a_tree = self.make_branch_and_tree('a')
164
self.build_tree_contents([('a/foo', 'original\n')])
166
a_tree.commit(message='initial commit')
168
b_tree = a_tree.controldir.sprout('b').open_workingtree()
170
self.build_tree_contents([('a/foo', 'changed\n')])
171
a_tree.commit(message='later change')
173
self.build_tree_contents([('a/foo', 'a third change')])
174
a_tree.commit(message='a third change')
176
self.assertEqual(a_tree.branch.last_revision_info()[0], 3)
178
b_tree.merge_from_branch(a_tree.branch)
179
b_tree.commit(message='merge')
181
self.assertEqual(b_tree.branch.last_revision_info()[0], 2)
183
self.run_bzr('pull --overwrite ../a', working_dir='b')
184
(last_revinfo_b) = b_tree.branch.last_revision_info()
185
self.assertEqual(last_revinfo_b[0], 3)
186
self.assertEqual(last_revinfo_b[1], a_tree.branch.last_revision())
188
def test_overwrite_children(self):
189
# Make sure pull --overwrite sets the revision-history
190
# to be identical to the pull source, even if we have convergence
191
a_tree = self.make_branch_and_tree('a')
192
self.build_tree_contents([('a/foo', 'original\n')])
194
a_tree.commit(message='initial commit')
196
b_tree = a_tree.controldir.sprout('b').open_workingtree()
198
self.build_tree_contents([('a/foo', 'changed\n')])
199
a_tree.commit(message='later change')
201
self.build_tree_contents([('a/foo', 'a third change')])
202
a_tree.commit(message='a third change')
204
self.assertEqual(a_tree.branch.last_revision_info()[0], 3)
206
b_tree.merge_from_branch(a_tree.branch)
207
b_tree.commit(message='merge')
209
self.assertEqual(b_tree.branch.last_revision_info()[0], 2)
211
self.build_tree_contents([('a/foo', 'a fourth change\n')])
212
a_tree.commit(message='a fourth change')
214
rev_info_a = a_tree.branch.last_revision_info()
215
self.assertEqual(rev_info_a[0], 4)
217
# With convergence, we could just pull over the
218
# new change, but with --overwrite, we want to switch our history
219
self.run_bzr('pull --overwrite ../a', working_dir='b')
220
rev_info_b = b_tree.branch.last_revision_info()
221
self.assertEqual(rev_info_b[0], 4)
222
self.assertEqual(rev_info_b, rev_info_a)
224
def test_pull_remember(self):
225
"""Pull changes from one branch to another and test parent location."""
226
t = self.get_transport()
227
tree_a = self.make_branch_and_tree('branch_a')
228
branch_a = tree_a.branch
229
self.build_tree(['branch_a/a'])
231
tree_a.commit('commit a')
232
tree_b = branch_a.controldir.sprout('branch_b').open_workingtree()
233
branch_b = tree_b.branch
234
tree_c = branch_a.controldir.sprout('branch_c').open_workingtree()
235
branch_c = tree_c.branch
236
self.build_tree(['branch_a/b'])
238
tree_a.commit('commit b')
240
parent = branch_b.get_parent()
241
branch_b = branch.Branch.open('branch_b')
242
branch_b.set_parent(None)
243
self.assertEqual(None, branch_b.get_parent())
244
# test pull for failure without parent set
245
out = self.run_bzr('pull', retcode=3, working_dir='branch_b')
246
self.assertEqual(out,
247
('','brz: ERROR: No pull location known or specified.\n'))
248
# test implicit --remember when no parent set, this pull conflicts
249
self.build_tree(['branch_b/d'])
251
tree_b.commit('commit d')
252
out = self.run_bzr('pull ../branch_a', retcode=3,
253
working_dir='branch_b')
254
self.assertEqual(out,
255
('','brz: ERROR: These branches have diverged.'
256
' Use the missing command to see how.\n'
257
'Use the merge command to reconcile them.\n'))
258
tree_b = tree_b.controldir.open_workingtree()
259
branch_b = tree_b.branch
260
self.assertEqual(parent, branch_b.get_parent())
261
# test implicit --remember after resolving previous failure
262
uncommit.uncommit(branch=branch_b, tree=tree_b)
263
t.delete('branch_b/d')
264
self.run_bzr('pull', working_dir='branch_b')
265
# Refresh the branch object as 'pull' modified it
266
branch_b = branch_b.controldir.open_branch()
267
self.assertEqual(branch_b.get_parent(), parent)
268
# test explicit --remember
269
self.run_bzr('pull ../branch_c --remember', working_dir='branch_b')
270
# Refresh the branch object as 'pull' modified it
271
branch_b = branch_b.controldir.open_branch()
272
self.assertEqual(branch_c.controldir.root_transport.base,
273
branch_b.get_parent())
275
def test_pull_bundle(self):
276
from breezy.testament import Testament
277
# Build up 2 trees and prepare for a pull
278
tree_a = self.make_branch_and_tree('branch_a')
279
with open('branch_a/a', 'wb') as f:
282
tree_a.commit('message')
284
tree_b = tree_a.controldir.sprout('branch_b').open_workingtree()
286
# Make a change to 'a' that 'b' can pull
287
with open('branch_a/a', 'wb') as f:
289
tree_a.commit('message')
291
# Create the bundle for 'b' to pull
292
self.run_bzr('bundle ../branch_b -o ../bundle', working_dir='branch_a')
294
out, err = self.run_bzr('pull ../bundle', working_dir='branch_b')
295
self.assertEqual(out,
296
'Now on revision 2.\n')
297
self.assertEqual(err,
298
' M a\nAll changes applied successfully.\n')
300
self.assertEqualDiff(tree_a.branch.last_revision(),
301
tree_b.branch.last_revision())
303
testament_a = Testament.from_revision(tree_a.branch.repository,
304
tree_a.get_parent_ids()[0])
305
testament_b = Testament.from_revision(tree_b.branch.repository,
306
tree_b.get_parent_ids()[0])
307
self.assertEqualDiff(testament_a.as_text(),
308
testament_b.as_text())
310
# it is legal to attempt to pull an already-merged bundle
311
out, err = self.run_bzr('pull ../bundle', working_dir='branch_b')
312
self.assertEqual(err, '')
313
self.assertEqual(out, 'No revisions or tags to pull.\n')
315
def test_pull_verbose_no_files(self):
316
"""Pull --verbose should not list modified files"""
317
tree_a = self.make_branch_and_tree('tree_a')
318
self.build_tree(['tree_a/foo'])
321
tree_b = self.make_branch_and_tree('tree_b')
322
out = self.run_bzr('pull --verbose -d tree_b tree_a')[0]
323
self.assertContainsRe(out, 'bar')
324
self.assertNotContainsRe(out, 'added:')
325
self.assertNotContainsRe(out, 'foo')
327
def test_pull_quiet(self):
328
"""Check that brz pull --quiet does not print anything"""
329
tree_a = self.make_branch_and_tree('tree_a')
330
self.build_tree(['tree_a/foo'])
332
revision_id = tree_a.commit('bar')
333
tree_b = tree_a.controldir.sprout('tree_b').open_workingtree()
334
out, err = self.run_bzr('pull --quiet -d tree_b')
335
self.assertEqual(out, '')
336
self.assertEqual(err, '')
337
self.assertEqual(tree_b.last_revision(), revision_id)
338
self.build_tree(['tree_a/moo'])
340
revision_id = tree_a.commit('quack')
341
out, err = self.run_bzr('pull --quiet -d tree_b')
342
self.assertEqual(out, '')
343
self.assertEqual(err, '')
344
self.assertEqual(tree_b.last_revision(), revision_id)
346
def test_pull_from_directory_service(self):
347
source = self.make_branch_and_tree('source')
348
source.commit('commit 1')
349
target = source.controldir.sprout('target').open_workingtree()
350
source_last = source.commit('commit 2')
351
class FooService(object):
352
"""A directory service that always returns source"""
354
def look_up(self, name, url):
356
directories.register('foo:', FooService, 'Testing directory service')
357
self.addCleanup(directories.remove, 'foo:')
358
self.run_bzr('pull foo:bar -d target')
359
self.assertEqual(source_last, target.last_revision())
361
def test_pull_verbose_defaults_to_long(self):
362
tree = self.example_branch('source')
363
target = self.make_branch_and_tree('target')
364
out = self.run_bzr('pull -v source -d target')[0]
365
self.assertContainsRe(out,
366
r'revno: 1\ncommitter: .*\nbranch nick: source')
367
self.assertNotContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
369
def test_pull_verbose_uses_default_log(self):
370
tree = self.example_branch('source')
371
target = self.make_branch_and_tree('target')
372
target.branch.get_config_stack().set('log_format', 'short')
373
out = self.run_bzr('pull -v source -d target')[0]
374
self.assertContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
375
self.assertNotContainsRe(
376
out, r'revno: 1\ncommitter: .*\nbranch nick: source')
378
def test_pull_smart_bound_branch(self):
379
self.setup_smart_server_with_call_log()
380
parent = self.make_branch_and_tree('parent')
381
parent.commit(message='first commit')
382
child = parent.controldir.sprout('child').open_workingtree()
383
child.commit(message='second commit')
384
checkout = parent.branch.create_checkout('checkout')
385
self.run_bzr(['pull', self.get_url('child')], working_dir='checkout')
387
def test_pull_smart_stacked_streaming_acceptance(self):
388
"""'brz pull -r 123' works on stacked, smart branches, even when the
389
revision specified by the revno is only present in the fallback
392
See <https://launchpad.net/bugs/380314>
394
self.setup_smart_server_with_call_log()
395
# Make a stacked-on branch with two commits so that the
396
# revision-history can't be determined just by looking at the parent
397
# field in the revision in the stacked repo.
398
parent = self.make_branch_and_tree('parent', format='1.9')
399
parent.commit(message='first commit')
400
parent.commit(message='second commit')
401
local = parent.controldir.sprout('local').open_workingtree()
402
local.commit(message='local commit')
403
local.branch.create_clone_on_transport(
404
self.get_transport('stacked'), stacked_on=self.get_url('parent'))
405
empty = self.make_branch_and_tree('empty', format='1.9')
406
self.reset_smart_call_log()
407
self.run_bzr(['pull', '-r', '1', self.get_url('stacked')],
409
# This figure represent the amount of work to perform this use case. It
410
# is entirely ok to reduce this number if a test fails due to rpc_count
411
# being too low. If rpc_count increases, more network roundtrips have
412
# become necessary for this use case. Please do not adjust this number
413
# upwards without agreement from bzr's network support maintainers.
414
self.assertLength(19, self.hpss_calls)
415
self.assertLength(1, self.hpss_connections)
416
remote = branch.Branch.open('stacked')
417
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
419
def test_pull_cross_format_warning(self):
420
"""You get a warning for probably slow cross-format pulls.
422
# this is assumed to be going through InterDifferingSerializer
423
from_tree = self.make_branch_and_tree('from', format='2a')
424
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
425
from_tree.commit(message='first commit')
426
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
427
self.assertContainsRe(err,
428
"(?m)Doing on-the-fly conversion")
430
def test_pull_cross_format_warning_no_IDS(self):
431
"""You get a warning for probably slow cross-format pulls.
433
# this simulates what would happen across the network, where
434
# interdifferingserializer is not active
436
debug.debug_flags.add('IDS_never')
437
# TestCase take care of restoring them
439
from_tree = self.make_branch_and_tree('from', format='2a')
440
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
441
from_tree.commit(message='first commit')
442
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
443
self.assertContainsRe(err,
444
"(?m)Doing on-the-fly conversion")
446
def test_pull_cross_format_from_network(self):
447
self.setup_smart_server_with_call_log()
448
from_tree = self.make_branch_and_tree('from', format='2a')
449
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
450
self.assertIsInstance(from_tree.branch, remote.RemoteBranch)
451
from_tree.commit(message='first commit')
452
out, err = self.run_bzr(['pull', '-d', 'to',
453
from_tree.branch.controldir.root_transport.base])
454
self.assertContainsRe(err,
455
"(?m)Doing on-the-fly conversion")
457
def test_pull_to_experimental_format_warning(self):
458
"""You get a warning for pulling into experimental formats.
460
from_tree = self.make_branch_and_tree('from', format='development-subtree')
461
to_tree = self.make_branch_and_tree('to', format='development-subtree')
462
from_tree.commit(message='first commit')
463
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
464
self.assertContainsRe(err,
465
"(?m)Fetching into experimental format")
467
def test_pull_cross_to_experimental_format_warning(self):
468
"""You get a warning for pulling into experimental formats.
470
from_tree = self.make_branch_and_tree('from', format='2a')
471
to_tree = self.make_branch_and_tree('to', format='development-subtree')
472
from_tree.commit(message='first commit')
473
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
474
self.assertContainsRe(err,
475
"(?m)Fetching into experimental format")
477
def test_pull_show_base(self):
478
"""brz pull supports --show-base
480
see https://bugs.launchpad.net/bzr/+bug/202374"""
481
# create two trees with conflicts, setup conflict, check that
482
# conflicted file looks correct
483
a_tree = self.example_branch('a')
484
b_tree = a_tree.controldir.sprout('b').open_workingtree()
486
with open(osutils.pathjoin('a', 'hello'),'wt') as f:
490
with open(osutils.pathjoin('b', 'hello'),'wt') as f:
493
out,err=self.run_bzr(['pull','-d','b','a','--show-base'])
495
# check for message here
498
' M hello\nText conflict in hello\n1 conflicts encountered.\n')
500
self.assertEqualDiff('<<<<<<< TREE\n'
501
'fie||||||| BASE-REVISION\n'
503
'fee>>>>>>> MERGE-SOURCE\n',
504
open(osutils.pathjoin('b', 'hello')).read())
506
def test_pull_warns_about_show_base_when_no_working_tree(self):
507
"""--show-base is useless if there's no working tree
509
see https://bugs.launchpad.net/bzr/+bug/1022160"""
510
self.make_branch('from')
511
self.make_branch('to')
512
out = self.run_bzr(['pull','-d','to','from','--show-base'])
513
self.assertEqual(out, ('No revisions or tags to pull.\n',
514
'No working tree, ignoring --show-base\n'))
516
def test_pull_tag_conflicts(self):
517
"""pulling tags with conflicts will change the exit code"""
518
# create a branch, see that --show-base fails
519
from_tree = self.make_branch_and_tree('from')
520
from_tree.branch.tags.set_tag("mytag", "somerevid")
521
to_tree = self.make_branch_and_tree('to')
522
to_tree.branch.tags.set_tag("mytag", "anotherrevid")
523
out = self.run_bzr(['pull','-d','to','from'],retcode=1)
524
self.assertEqual(out,
525
('No revisions to pull.\nConflicting tags:\n mytag\n', ''))
527
def test_pull_tag_notification(self):
528
"""pulling tags with conflicts will change the exit code"""
529
# create a branch, see that --show-base fails
530
from_tree = self.make_branch_and_tree('from')
531
from_tree.branch.tags.set_tag("mytag", "somerevid")
532
to_tree = self.make_branch_and_tree('to')
533
out = self.run_bzr(['pull', '-d', 'to', 'from'])
534
self.assertEqual(out,
535
('1 tag(s) updated.\n', ''))
537
def test_overwrite_tags(self):
538
"""--overwrite-tags only overwrites tags, not revisions."""
539
from_tree = self.make_branch_and_tree('from')
540
from_tree.branch.tags.set_tag("mytag", "somerevid")
541
to_tree = self.make_branch_and_tree('to')
542
to_tree.branch.tags.set_tag("mytag", "anotherrevid")
543
revid1 = to_tree.commit('my commit')
544
out = self.run_bzr(['pull', '-d', 'to', 'from'], retcode=1)
545
self.assertEqual(out,
546
('No revisions to pull.\nConflicting tags:\n mytag\n', ''))
547
out = self.run_bzr(['pull', '-d', 'to', '--overwrite-tags', 'from'])
548
self.assertEqual(out, ('1 tag(s) updated.\n', ''))
550
self.assertEqual(to_tree.branch.tags.lookup_tag('mytag'),
552
self.assertEqual(to_tree.branch.last_revision(), revid1)
554
def test_pull_tag_overwrite(self):
555
"""pulling tags with --overwrite only reports changed tags."""
556
# create a branch, see that --show-base fails
557
from_tree = self.make_branch_and_tree('from')
558
from_tree.branch.tags.set_tag("mytag", "somerevid")
559
to_tree = self.make_branch_and_tree('to')
560
to_tree.branch.tags.set_tag("mytag", "somerevid")
561
out = self.run_bzr(['pull', '--overwrite', '-d', 'to', 'from'])
562
self.assertEqual(out,
563
('No revisions or tags to pull.\n', ''))
566
class TestPullOutput(script.TestCaseWithTransportAndScript):
568
def test_pull_log_format(self):
571
Created a standalone tree (format: 2a)
576
$ brz commit -m 'we need some foo'
577
2>Committing to:...trunk/
579
2>Committed revision 1.
582
Created a standalone tree (format: 2a)
584
$ brz pull -v ../trunk -Olog_format=line
587
1: jrandom@example.com ...we need some foo
589
2>All changes applied successfully.