1
# Copyright (C) 2005-2010 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 bzr pull."""
30
from bzrlib.branch import Branch
31
from bzrlib.directory_service import directories
32
from bzrlib.osutils import pathjoin
33
from bzrlib.tests import (
37
from bzrlib.uncommit import uncommit
38
from bzrlib.workingtree import WorkingTree
41
class TestPull(tests.TestCaseWithTransport):
43
def example_branch(self, path='.'):
44
tree = self.make_branch_and_tree(path)
45
self.build_tree_contents([
46
(pathjoin(path, 'hello'), 'foo'),
47
(pathjoin(path, 'goodbye'), 'baz')])
49
tree.commit(message='setup')
51
tree.commit(message='setup')
55
"""Pull changes from one branch to another."""
56
a_tree = self.example_branch('a')
57
base_rev = a_tree.branch.last_revision()
59
self.run_bzr('pull', retcode=3)
60
self.run_bzr('missing', retcode=3)
61
self.run_bzr('missing .')
62
self.run_bzr('missing')
63
# this will work on windows because we check for the same branch
64
# in pull - if it fails, it is a regression
66
self.run_bzr('pull /', retcode=3)
67
if sys.platform not in ('win32', 'cygwin'):
71
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
76
new_rev = b_tree.commit(message='blah', allow_pointless=True)
81
self.assertEqual(a.last_revision(), base_rev)
82
self.assertEqual(b.last_revision(), new_rev)
85
self.run_bzr('pull ../b')
86
self.assertEqual(a.last_revision(), b.last_revision())
87
a_tree.commit(message='blah2', allow_pointless=True)
88
b_tree.commit(message='blah3', allow_pointless=True)
91
self.run_bzr('pull ../a', retcode=3)
93
b_tree.bzrdir.sprout('overwriteme')
94
os.chdir('overwriteme')
95
self.run_bzr('pull --overwrite ../a')
96
overwritten = Branch.open('.')
97
self.assertEqual(overwritten.last_revision(),
99
a_tree.merge_from_branch(b_tree.branch)
100
a_tree.commit(message="blah4", allow_pointless=True)
101
os.chdir('../b/subdir')
102
self.run_bzr('pull ../../a')
103
self.assertEqual(a.last_revision(), b.last_revision())
104
sub_tree = WorkingTree.open_containing('.')[0]
105
sub_tree.commit(message="blah5", allow_pointless=True)
106
sub_tree.commit(message="blah6", allow_pointless=True)
108
self.run_bzr('pull ../a')
110
a_tree.commit(message="blah7", allow_pointless=True)
111
a_tree.merge_from_branch(b_tree.branch)
112
a_tree.commit(message="blah8", allow_pointless=True)
113
self.run_bzr('pull ../b')
114
self.run_bzr('pull ../b')
116
def test_pull_dash_d(self):
117
self.example_branch('a')
118
self.make_branch_and_tree('b')
119
self.make_branch_and_tree('c')
120
# pull into that branch
121
self.run_bzr('pull -d b a')
122
# pull into a branch specified by a url
123
c_url = urlutils.local_path_to_url('c')
124
self.assertStartsWith(c_url, 'file://')
125
self.run_bzr(['pull', '-d', c_url, 'a'])
127
def test_pull_revision(self):
128
"""Pull some changes from one branch to another."""
129
a_tree = self.example_branch('a')
130
self.build_tree_contents([
132
('a/goodbye2', 'baz')])
134
a_tree.commit(message="setup")
135
a_tree.add('goodbye2')
136
a_tree.commit(message="setup")
138
b_tree = a_tree.bzrdir.sprout('b',
139
revision_id=a_tree.branch.get_rev_id(1)).open_workingtree()
141
self.run_bzr('pull -r 2')
142
a = Branch.open('../a')
144
self.assertEqual(a.revno(),4)
145
self.assertEqual(b.revno(),2)
146
self.run_bzr('pull -r 3')
147
self.assertEqual(b.revno(),3)
148
self.run_bzr('pull -r 4')
149
self.assertEqual(a.last_revision(), b.last_revision())
151
def test_pull_tags(self):
152
"""Tags are updated by pull, and revisions named in those tags are
155
# Make a source, sprout a target off it
156
builder = self.make_branch_builder('source')
157
source = fixtures.build_branch_with_non_ancestral_rev(builder)
158
source.get_config().set_user_option('branch.fetch_tags', 'True')
159
target_bzrdir = source.bzrdir.sprout('target')
160
source.tags.set_tag('tag-a', 'rev-2')
162
self.run_bzr('pull -d target source')
163
target = target_bzrdir.open_branch()
164
# The tag is present, and so is its revision.
165
self.assertEqual('rev-2', target.tags.lookup_tag('tag-a'))
166
target.repository.get_revision('rev-2')
168
def test_overwrite_uptodate(self):
169
# Make sure pull --overwrite overwrites
170
# even if the target branch has merged
171
# everything already.
172
a_tree = self.make_branch_and_tree('a')
173
self.build_tree_contents([('a/foo', 'original\n')])
175
a_tree.commit(message='initial commit')
177
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
179
self.build_tree_contents([('a/foo', 'changed\n')])
180
a_tree.commit(message='later change')
182
self.build_tree_contents([('a/foo', 'a third change')])
183
a_tree.commit(message='a third change')
185
self.assertEqual(a_tree.branch.last_revision_info()[0], 3)
187
b_tree.merge_from_branch(a_tree.branch)
188
b_tree.commit(message='merge')
190
self.assertEqual(b_tree.branch.last_revision_info()[0], 2)
193
self.run_bzr('pull --overwrite ../a')
194
(last_revinfo_b) = b_tree.branch.last_revision_info()
195
self.assertEqual(last_revinfo_b[0], 3)
196
self.assertEqual(last_revinfo_b[1], a_tree.branch.last_revision())
198
def test_overwrite_children(self):
199
# Make sure pull --overwrite sets the revision-history
200
# to be identical to the pull source, even if we have convergence
201
a_tree = self.make_branch_and_tree('a')
202
self.build_tree_contents([('a/foo', 'original\n')])
204
a_tree.commit(message='initial commit')
206
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
208
self.build_tree_contents([('a/foo', 'changed\n')])
209
a_tree.commit(message='later change')
211
self.build_tree_contents([('a/foo', 'a third change')])
212
a_tree.commit(message='a third change')
214
self.assertEqual(a_tree.branch.last_revision_info()[0], 3)
216
b_tree.merge_from_branch(a_tree.branch)
217
b_tree.commit(message='merge')
219
self.assertEqual(b_tree.branch.last_revision_info()[0], 2)
221
self.build_tree_contents([('a/foo', 'a fourth change\n')])
222
a_tree.commit(message='a fourth change')
224
rev_info_a = a_tree.branch.last_revision_info()
225
self.assertEqual(rev_info_a[0], 4)
227
# With convergence, we could just pull over the
228
# new change, but with --overwrite, we want to switch our history
230
self.run_bzr('pull --overwrite ../a')
231
rev_info_b = b_tree.branch.last_revision_info()
232
self.assertEqual(rev_info_b[0], 4)
233
self.assertEqual(rev_info_b, rev_info_a)
235
def test_pull_remember(self):
236
"""Pull changes from one branch to another and test parent location."""
237
transport = self.get_transport()
238
tree_a = self.make_branch_and_tree('branch_a')
239
branch_a = tree_a.branch
240
self.build_tree(['branch_a/a'])
242
tree_a.commit('commit a')
243
tree_b = branch_a.bzrdir.sprout('branch_b').open_workingtree()
244
branch_b = tree_b.branch
245
tree_c = branch_a.bzrdir.sprout('branch_c').open_workingtree()
246
branch_c = tree_c.branch
247
self.build_tree(['branch_a/b'])
249
tree_a.commit('commit b')
251
parent = branch_b.get_parent()
252
branch_b.set_parent(None)
253
self.assertEqual(None, branch_b.get_parent())
254
# test pull for failure without parent set
256
out = self.run_bzr('pull', retcode=3)
257
self.assertEqual(out,
258
('','bzr: ERROR: No pull location known or specified.\n'))
259
# test implicit --remember when no parent set, this pull conflicts
260
self.build_tree(['d'])
262
tree_b.commit('commit d')
263
out = self.run_bzr('pull ../branch_a', retcode=3)
264
self.assertEqual(out,
265
('','bzr: ERROR: These branches have diverged.'
266
' Use the missing command to see how.\n'
267
'Use the merge command to reconcile them.\n'))
268
self.assertEqual(branch_b.get_parent(), parent)
269
# test implicit --remember after resolving previous failure
270
uncommit(branch=branch_b, tree=tree_b)
271
transport.delete('branch_b/d')
273
self.assertEqual(branch_b.get_parent(), parent)
274
# test explicit --remember
275
self.run_bzr('pull ../branch_c --remember')
276
self.assertEqual(branch_b.get_parent(),
277
branch_c.bzrdir.root_transport.base)
279
def test_pull_bundle(self):
280
from bzrlib.testament import Testament
281
# Build up 2 trees and prepare for a pull
282
tree_a = self.make_branch_and_tree('branch_a')
283
f = open('branch_a/a', 'wb')
287
tree_a.commit('message')
289
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
291
# Make a change to 'a' that 'b' can pull
292
f = open('branch_a/a', 'wb')
295
tree_a.commit('message')
297
# Create the bundle for 'b' to pull
299
self.run_bzr('bundle ../branch_b -o ../bundle')
301
os.chdir('../branch_b')
302
out, err = self.run_bzr('pull ../bundle')
303
self.assertEqual(out,
304
'Now on revision 2.\n')
305
self.assertEqual(err,
306
' M a\nAll changes applied successfully.\n')
308
self.assertEqualDiff(tree_a.branch.last_revision(),
309
tree_b.branch.last_revision())
311
testament_a = Testament.from_revision(tree_a.branch.repository,
312
tree_a.get_parent_ids()[0])
313
testament_b = Testament.from_revision(tree_b.branch.repository,
314
tree_b.get_parent_ids()[0])
315
self.assertEqualDiff(testament_a.as_text(),
316
testament_b.as_text())
318
# it is legal to attempt to pull an already-merged bundle
319
out, err = self.run_bzr('pull ../bundle')
320
self.assertEqual(err, '')
321
self.assertEqual(out, 'No revisions or tags to pull.\n')
323
def test_pull_verbose_no_files(self):
324
"""Pull --verbose should not list modified files"""
325
tree_a = self.make_branch_and_tree('tree_a')
326
self.build_tree(['tree_a/foo'])
329
tree_b = self.make_branch_and_tree('tree_b')
330
out = self.run_bzr('pull --verbose -d tree_b tree_a')[0]
331
self.assertContainsRe(out, 'bar')
332
self.assertNotContainsRe(out, 'added:')
333
self.assertNotContainsRe(out, 'foo')
335
def test_pull_quiet(self):
336
"""Check that bzr pull --quiet does not print anything"""
337
tree_a = self.make_branch_and_tree('tree_a')
338
self.build_tree(['tree_a/foo'])
340
revision_id = tree_a.commit('bar')
341
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
342
out, err = self.run_bzr('pull --quiet -d tree_b')
343
self.assertEqual(out, '')
344
self.assertEqual(err, '')
345
self.assertEqual(tree_b.last_revision(), revision_id)
346
self.build_tree(['tree_a/moo'])
348
revision_id = tree_a.commit('quack')
349
out, err = self.run_bzr('pull --quiet -d tree_b')
350
self.assertEqual(out, '')
351
self.assertEqual(err, '')
352
self.assertEqual(tree_b.last_revision(), revision_id)
354
def test_pull_from_directory_service(self):
355
source = self.make_branch_and_tree('source')
356
source.commit('commit 1')
357
target = source.bzrdir.sprout('target').open_workingtree()
358
source_last = source.commit('commit 2')
359
class FooService(object):
360
"""A directory service that always returns source"""
362
def look_up(self, name, url):
364
directories.register('foo:', FooService, 'Testing directory service')
365
self.addCleanup(directories.remove, 'foo:')
366
self.run_bzr('pull foo:bar -d target')
367
self.assertEqual(source_last, target.last_revision())
369
def test_pull_verbose_defaults_to_long(self):
370
tree = self.example_branch('source')
371
target = self.make_branch_and_tree('target')
372
out = self.run_bzr('pull -v source -d target')[0]
373
self.assertContainsRe(out,
374
r'revno: 1\ncommitter: .*\nbranch nick: source')
375
self.assertNotContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
377
def test_pull_verbose_uses_default_log(self):
378
tree = self.example_branch('source')
379
target = self.make_branch_and_tree('target')
380
target_config = target.branch.get_config()
381
target_config.set_user_option('log_format', 'short')
382
out = self.run_bzr('pull -v source -d target')[0]
383
self.assertContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
384
self.assertNotContainsRe(
385
out, r'revno: 1\ncommitter: .*\nbranch nick: source')
387
def test_pull_smart_bound_branch(self):
388
self.setup_smart_server_with_call_log()
389
parent = self.make_branch_and_tree('parent')
390
parent.commit(message='first commit')
391
child = parent.bzrdir.sprout('child').open_workingtree()
392
child.commit(message='second commit')
393
checkout = parent.branch.create_checkout('checkout')
394
self.run_bzr(['pull', self.get_url('child')], working_dir='checkout')
396
def test_pull_smart_stacked_streaming_acceptance(self):
397
"""'bzr pull -r 123' works on stacked, smart branches, even when the
398
revision specified by the revno is only present in the fallback
401
See <https://launchpad.net/bugs/380314>
403
self.setup_smart_server_with_call_log()
404
# Make a stacked-on branch with two commits so that the
405
# revision-history can't be determined just by looking at the parent
406
# field in the revision in the stacked repo.
407
parent = self.make_branch_and_tree('parent', format='1.9')
408
parent.commit(message='first commit')
409
parent.commit(message='second commit')
410
local = parent.bzrdir.sprout('local').open_workingtree()
411
local.commit(message='local commit')
412
local.branch.create_clone_on_transport(
413
self.get_transport('stacked'), stacked_on=self.get_url('parent'))
414
empty = self.make_branch_and_tree('empty', format='1.9')
415
self.reset_smart_call_log()
416
self.run_bzr(['pull', '-r', '1', self.get_url('stacked')],
418
# This figure represent the amount of work to perform this use case. It
419
# is entirely ok to reduce this number if a test fails due to rpc_count
420
# being too low. If rpc_count increases, more network roundtrips have
421
# become necessary for this use case. Please do not adjust this number
422
# upwards without agreement from bzr's network support maintainers.
423
self.assertLength(19, self.hpss_calls)
424
remote = Branch.open('stacked')
425
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
427
def test_pull_cross_format_warning(self):
428
"""You get a warning for probably slow cross-format pulls.
430
# this is assumed to be going through InterDifferingSerializer
431
from_tree = self.make_branch_and_tree('from', format='2a')
432
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
433
from_tree.commit(message='first commit')
434
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
435
self.assertContainsRe(err,
436
"(?m)Doing on-the-fly conversion")
438
def test_pull_cross_format_warning_no_IDS(self):
439
"""You get a warning for probably slow cross-format pulls.
441
# this simulates what would happen across the network, where
442
# interdifferingserializer is not active
444
debug.debug_flags.add('IDS_never')
445
# TestCase take care of restoring them
447
from_tree = self.make_branch_and_tree('from', format='2a')
448
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
449
from_tree.commit(message='first commit')
450
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
451
self.assertContainsRe(err,
452
"(?m)Doing on-the-fly conversion")
454
def test_pull_cross_format_from_network(self):
455
self.setup_smart_server_with_call_log()
456
from_tree = self.make_branch_and_tree('from', format='2a')
457
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
458
self.assertIsInstance(from_tree.branch, remote.RemoteBranch)
459
from_tree.commit(message='first commit')
460
out, err = self.run_bzr(['pull', '-d', 'to',
461
from_tree.branch.bzrdir.root_transport.base])
462
self.assertContainsRe(err,
463
"(?m)Doing on-the-fly conversion")
465
def test_pull_to_experimental_format_warning(self):
466
"""You get a warning for pulling into experimental formats.
468
from_tree = self.make_branch_and_tree('from', format='development-subtree')
469
to_tree = self.make_branch_and_tree('to', format='development-subtree')
470
from_tree.commit(message='first commit')
471
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
472
self.assertContainsRe(err,
473
"(?m)Fetching into experimental format")
475
def test_pull_cross_to_experimental_format_warning(self):
476
"""You get a warning for pulling into experimental formats.
478
from_tree = self.make_branch_and_tree('from', format='2a')
479
to_tree = self.make_branch_and_tree('to', format='development-subtree')
480
from_tree.commit(message='first commit')
481
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
482
self.assertContainsRe(err,
483
"(?m)Fetching into experimental format")
485
def test_pull_show_base(self):
486
"""bzr pull supports --show-base
488
see https://bugs.launchpad.net/bzr/+bug/202374"""
489
# create two trees with conflicts, setup conflict, check that
490
# conflicted file looks correct
491
a_tree = self.example_branch('a')
492
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
494
f = open(pathjoin('a', 'hello'),'wt')
499
f = open(pathjoin('b', 'hello'),'wt')
503
out,err=self.run_bzr(['pull','-d','b','a','--show-base'])
505
# check for message here
506
self.assertEqual(err,
507
' M hello\nText conflict in hello\n1 conflicts encountered.\n')
509
self.assertEqualDiff('<<<<<<< TREE\n'
510
'fie||||||| BASE-REVISION\n'
512
'fee>>>>>>> MERGE-SOURCE\n',
513
open(pathjoin('b', 'hello')).read())
515
def test_pull_show_base_working_tree_only(self):
516
"""--show-base only allowed if there's a working tree
518
see https://bugs.launchpad.net/bzr/+bug/202374"""
519
# create a branch, see that --show-base fails
520
self.make_branch('from')
521
self.make_branch('to')
522
out=self.run_bzr(['pull','-d','to','from','--show-base'],retcode=3)
523
self.assertEqual(out,
524
('','bzr: ERROR: Need working tree for --show-base.\n'))
526
def test_pull_tag_conflicts(self):
527
"""pulling tags with conflicts will change the exit code"""
528
# create a branch, see that --show-base fails
529
from_tree = self.make_branch_and_tree('from')
530
from_tree.branch.tags.set_tag("mytag", "somerevid")
531
to_tree = self.make_branch_and_tree('to')
532
to_tree.branch.tags.set_tag("mytag", "anotherrevid")
533
out = self.run_bzr(['pull','-d','to','from'],retcode=1)
534
self.assertEqual(out,
535
('No revisions to pull.\nConflicting tags:\n mytag\n', ''))
537
def test_pull_tag_notification(self):
538
"""pulling tags with conflicts will change the exit code"""
539
# create a branch, see that --show-base fails
540
from_tree = self.make_branch_and_tree('from')
541
from_tree.branch.tags.set_tag("mytag", "somerevid")
542
to_tree = self.make_branch_and_tree('to')
543
out = self.run_bzr(['pull', '-d', 'to', 'from'])
544
self.assertEqual(out,
545
('1 tag(s) updated.\n', ''))
547
def test_pull_tag_overwrite(self):
548
"""pulling tags with --overwrite only reports changed tags."""
549
# create a branch, see that --show-base fails
550
from_tree = self.make_branch_and_tree('from')
551
from_tree.branch.tags.set_tag("mytag", "somerevid")
552
to_tree = self.make_branch_and_tree('to')
553
to_tree.branch.tags.set_tag("mytag", "somerevid")
554
out = self.run_bzr(['pull', '--overwrite', '-d', 'to', 'from'])
555
self.assertEqual(out,
556
('No revisions or tags to pull.\n', ''))
559
class TestPullOutput(script.TestCaseWithTransportAndScript):
561
def test_pull_log_format(self):
564
Created a standalone tree (format: 2a)
569
$ bzr commit -m 'we need some foo'
570
2>Committing to:...trunk/
572
2>Committed revision 1.
575
Created a standalone tree (format: 2a)
577
$ bzr pull -v ../trunk -Olog_format=line
580
1: jrandom@example.com ...we need some foo
582
2>All changes applied successfully.