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."""
29
from bzrlib.branch import Branch
30
from bzrlib.directory_service import directories
31
from bzrlib.osutils import pathjoin
32
from bzrlib.tests import (
34
TestCaseWithTransport,
36
from bzrlib.uncommit import uncommit
37
from bzrlib.workingtree import WorkingTree
40
class TestPull(TestCaseWithTransport):
42
def example_branch(self, path='.'):
43
tree = self.make_branch_and_tree(path)
44
self.build_tree_contents([
45
(pathjoin(path, 'hello'), 'foo'),
46
(pathjoin(path, 'goodbye'), 'baz')])
48
tree.commit(message='setup')
50
tree.commit(message='setup')
54
"""Pull changes from one branch to another."""
55
a_tree = self.example_branch('a')
57
self.run_bzr('pull', retcode=3)
58
self.run_bzr('missing', retcode=3)
59
self.run_bzr('missing .')
60
self.run_bzr('missing')
61
# this will work on windows because we check for the same branch
62
# in pull - if it fails, it is a regression
64
self.run_bzr('pull /', retcode=3)
65
if sys.platform not in ('win32', 'cygwin'):
69
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
74
b_tree.commit(message='blah', allow_pointless=True)
79
self.assertEqual(a.revision_history(), b.revision_history()[:-1])
82
self.run_bzr('pull ../b')
83
self.assertEqual(a.revision_history(), b.revision_history())
84
a_tree.commit(message='blah2', allow_pointless=True)
85
b_tree.commit(message='blah3', allow_pointless=True)
88
self.run_bzr('pull ../a', retcode=3)
90
b_tree.bzrdir.sprout('overwriteme')
91
os.chdir('overwriteme')
92
self.run_bzr('pull --overwrite ../a')
93
overwritten = Branch.open('.')
94
self.assertEqual(overwritten.revision_history(),
96
a_tree.merge_from_branch(b_tree.branch)
97
a_tree.commit(message="blah4", allow_pointless=True)
98
os.chdir('../b/subdir')
99
self.run_bzr('pull ../../a')
100
self.assertEqual(a.revision_history()[-1], b.revision_history()[-1])
101
sub_tree = WorkingTree.open_containing('.')[0]
102
sub_tree.commit(message="blah5", allow_pointless=True)
103
sub_tree.commit(message="blah6", allow_pointless=True)
105
self.run_bzr('pull ../a')
107
a_tree.commit(message="blah7", allow_pointless=True)
108
a_tree.merge_from_branch(b_tree.branch)
109
a_tree.commit(message="blah8", allow_pointless=True)
110
self.run_bzr('pull ../b')
111
self.run_bzr('pull ../b')
113
def test_pull_dash_d(self):
114
self.example_branch('a')
115
self.make_branch_and_tree('b')
116
self.make_branch_and_tree('c')
117
# pull into that branch
118
self.run_bzr('pull -d b a')
119
# pull into a branch specified by a url
120
c_url = urlutils.local_path_to_url('c')
121
self.assertStartsWith(c_url, 'file://')
122
self.run_bzr(['pull', '-d', c_url, 'a'])
124
def test_pull_revision(self):
125
"""Pull some changes from one branch to another."""
126
a_tree = self.example_branch('a')
127
self.build_tree_contents([
129
('a/goodbye2', 'baz')])
131
a_tree.commit(message="setup")
132
a_tree.add('goodbye2')
133
a_tree.commit(message="setup")
135
b_tree = a_tree.bzrdir.sprout('b',
136
revision_id=a_tree.branch.get_rev_id(1)).open_workingtree()
138
self.run_bzr('pull -r 2')
139
a = Branch.open('../a')
141
self.assertEqual(a.revno(),4)
142
self.assertEqual(b.revno(),2)
143
self.run_bzr('pull -r 3')
144
self.assertEqual(b.revno(),3)
145
self.run_bzr('pull -r 4')
146
self.assertEqual(a.revision_history(), b.revision_history())
148
def test_pull_tags(self):
149
"""Tags are updated by pull, and revisions named in those tags are
152
# Make a source, sprout a target off it
153
builder = self.make_branch_builder('source')
154
source = fixtures.build_branch_with_non_ancestral_rev(builder)
155
target_bzrdir = source.bzrdir.sprout('target')
156
source.tags.set_tag('tag-a', 'rev-2')
158
self.run_bzr('pull -d target source')
159
target = target_bzrdir.open_branch()
160
# The tag is present, and so is its revision.
161
self.assertEqual('rev-2', target.tags.lookup_tag('tag-a'))
162
target.repository.get_revision('rev-2')
164
def test_overwrite_uptodate(self):
165
# Make sure pull --overwrite overwrites
166
# even if the target branch has merged
167
# everything already.
168
a_tree = self.make_branch_and_tree('a')
169
self.build_tree_contents([('a/foo', 'original\n')])
171
a_tree.commit(message='initial commit')
173
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
175
self.build_tree_contents([('a/foo', 'changed\n')])
176
a_tree.commit(message='later change')
178
self.build_tree_contents([('a/foo', 'a third change')])
179
a_tree.commit(message='a third change')
181
rev_history_a = a_tree.branch.revision_history()
182
self.assertEqual(len(rev_history_a), 3)
184
b_tree.merge_from_branch(a_tree.branch)
185
b_tree.commit(message='merge')
187
self.assertEqual(len(b_tree.branch.revision_history()), 2)
190
self.run_bzr('pull --overwrite ../a')
191
rev_history_b = b_tree.branch.revision_history()
192
self.assertEqual(len(rev_history_b), 3)
194
self.assertEqual(rev_history_b, rev_history_a)
196
def test_overwrite_children(self):
197
# Make sure pull --overwrite sets the revision-history
198
# to be identical to the pull source, even if we have convergence
199
a_tree = self.make_branch_and_tree('a')
200
self.build_tree_contents([('a/foo', 'original\n')])
202
a_tree.commit(message='initial commit')
204
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
206
self.build_tree_contents([('a/foo', 'changed\n')])
207
a_tree.commit(message='later change')
209
self.build_tree_contents([('a/foo', 'a third change')])
210
a_tree.commit(message='a third change')
212
self.assertEqual(len(a_tree.branch.revision_history()), 3)
214
b_tree.merge_from_branch(a_tree.branch)
215
b_tree.commit(message='merge')
217
self.assertEqual(len(b_tree.branch.revision_history()), 2)
219
self.build_tree_contents([('a/foo', 'a fourth change\n')])
220
a_tree.commit(message='a fourth change')
222
rev_history_a = a_tree.branch.revision_history()
223
self.assertEqual(len(rev_history_a), 4)
225
# With convergence, we could just pull over the
226
# new change, but with --overwrite, we want to switch our history
228
self.run_bzr('pull --overwrite ../a')
229
rev_history_b = b_tree.branch.revision_history()
230
self.assertEqual(len(rev_history_b), 4)
232
self.assertEqual(rev_history_b, rev_history_a)
234
def test_pull_remember(self):
235
"""Pull changes from one branch to another and test parent location."""
236
transport = self.get_transport()
237
tree_a = self.make_branch_and_tree('branch_a')
238
branch_a = tree_a.branch
239
self.build_tree(['branch_a/a'])
241
tree_a.commit('commit a')
242
tree_b = branch_a.bzrdir.sprout('branch_b').open_workingtree()
243
branch_b = tree_b.branch
244
tree_c = branch_a.bzrdir.sprout('branch_c').open_workingtree()
245
branch_c = tree_c.branch
246
self.build_tree(['branch_a/b'])
248
tree_a.commit('commit b')
250
parent = branch_b.get_parent()
251
branch_b.set_parent(None)
252
self.assertEqual(None, branch_b.get_parent())
253
# test pull for failure without parent set
255
out = self.run_bzr('pull', retcode=3)
256
self.assertEqual(out,
257
('','bzr: ERROR: No pull location known or specified.\n'))
258
# test implicit --remember when no parent set, this pull conflicts
259
self.build_tree(['d'])
261
tree_b.commit('commit d')
262
out = self.run_bzr('pull ../branch_a', retcode=3)
263
self.assertEqual(out,
264
('','bzr: ERROR: These branches have diverged.'
265
' Use the missing command to see how.\n'
266
'Use the merge command to reconcile them.\n'))
267
self.assertEqual(branch_b.get_parent(), parent)
268
# test implicit --remember after resolving previous failure
269
uncommit(branch=branch_b, tree=tree_b)
270
transport.delete('branch_b/d')
272
self.assertEqual(branch_b.get_parent(), parent)
273
# test explicit --remember
274
self.run_bzr('pull ../branch_c --remember')
275
self.assertEqual(branch_b.get_parent(),
276
branch_c.bzrdir.root_transport.base)
278
def test_pull_bundle(self):
279
from bzrlib.testament import Testament
280
# Build up 2 trees and prepare for a pull
281
tree_a = self.make_branch_and_tree('branch_a')
282
f = open('branch_a/a', 'wb')
286
tree_a.commit('message')
288
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
290
# Make a change to 'a' that 'b' can pull
291
f = open('branch_a/a', 'wb')
294
tree_a.commit('message')
296
# Create the bundle for 'b' to pull
298
self.run_bzr('bundle ../branch_b -o ../bundle')
300
os.chdir('../branch_b')
301
out, err = self.run_bzr('pull ../bundle')
302
self.assertEqual(out,
303
'Now on revision 2.\n')
304
self.assertEqual(err,
305
' M a\nAll changes applied successfully.\n')
307
self.assertEqualDiff(tree_a.branch.revision_history(),
308
tree_b.branch.revision_history())
310
testament_a = Testament.from_revision(tree_a.branch.repository,
311
tree_a.get_parent_ids()[0])
312
testament_b = Testament.from_revision(tree_b.branch.repository,
313
tree_b.get_parent_ids()[0])
314
self.assertEqualDiff(testament_a.as_text(),
315
testament_b.as_text())
317
# it is legal to attempt to pull an already-merged bundle
318
out, err = self.run_bzr('pull ../bundle')
319
self.assertEqual(err, '')
320
self.assertEqual(out, 'No revisions to pull.\n')
322
def test_pull_verbose_no_files(self):
323
"""Pull --verbose should not list modified files"""
324
tree_a = self.make_branch_and_tree('tree_a')
325
self.build_tree(['tree_a/foo'])
328
tree_b = self.make_branch_and_tree('tree_b')
329
out = self.run_bzr('pull --verbose -d tree_b tree_a')[0]
330
self.assertContainsRe(out, 'bar')
331
self.assertNotContainsRe(out, 'added:')
332
self.assertNotContainsRe(out, 'foo')
334
def test_pull_quiet(self):
335
"""Check that bzr pull --quiet does not print anything"""
336
tree_a = self.make_branch_and_tree('tree_a')
337
self.build_tree(['tree_a/foo'])
339
revision_id = tree_a.commit('bar')
340
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
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)
345
self.build_tree(['tree_a/moo'])
347
revision_id = tree_a.commit('quack')
348
out, err = self.run_bzr('pull --quiet -d tree_b')
349
self.assertEqual(out, '')
350
self.assertEqual(err, '')
351
self.assertEqual(tree_b.last_revision(), revision_id)
353
def test_pull_from_directory_service(self):
354
source = self.make_branch_and_tree('source')
355
source.commit('commit 1')
356
target = source.bzrdir.sprout('target').open_workingtree()
357
source_last = source.commit('commit 2')
358
class FooService(object):
359
"""A directory service that always returns source"""
361
def look_up(self, name, url):
363
directories.register('foo:', FooService, 'Testing directory service')
364
self.addCleanup(directories.remove, 'foo:')
365
self.run_bzr('pull foo:bar -d target')
366
self.assertEqual(source_last, target.last_revision())
368
def test_pull_verbose_defaults_to_long(self):
369
tree = self.example_branch('source')
370
target = self.make_branch_and_tree('target')
371
out = self.run_bzr('pull -v source -d target')[0]
372
self.assertContainsRe(out,
373
r'revno: 1\ncommitter: .*\nbranch nick: source')
374
self.assertNotContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
376
def test_pull_verbose_uses_default_log(self):
377
tree = self.example_branch('source')
378
target = self.make_branch_and_tree('target')
379
target_config = target.branch.get_config()
380
target_config.set_user_option('log_format', 'short')
381
out = self.run_bzr('pull -v source -d target')[0]
382
self.assertContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
383
self.assertNotContainsRe(
384
out, r'revno: 1\ncommitter: .*\nbranch nick: source')
386
def test_pull_smart_stacked_streaming_acceptance(self):
387
"""'bzr pull -r 123' works on stacked, smart branches, even when the
388
revision specified by the revno is only present in the fallback
391
See <https://launchpad.net/bugs/380314>
393
self.setup_smart_server_with_call_log()
394
# Make a stacked-on branch with two commits so that the
395
# revision-history can't be determined just by looking at the parent
396
# field in the revision in the stacked repo.
397
parent = self.make_branch_and_tree('parent', format='1.9')
398
parent.commit(message='first commit')
399
parent.commit(message='second commit')
400
local = parent.bzrdir.sprout('local').open_workingtree()
401
local.commit(message='local commit')
402
local.branch.create_clone_on_transport(
403
self.get_transport('stacked'), stacked_on=self.get_url('parent'))
404
empty = self.make_branch_and_tree('empty', format='1.9')
405
self.reset_smart_call_log()
406
self.run_bzr(['pull', '-r', '1', self.get_url('stacked')],
408
# This figure represent the amount of work to perform this use case. It
409
# is entirely ok to reduce this number if a test fails due to rpc_count
410
# being too low. If rpc_count increases, more network roundtrips have
411
# become necessary for this use case. Please do not adjust this number
412
# upwards without agreement from bzr's network support maintainers.
413
self.assertLength(18, self.hpss_calls)
414
remote = Branch.open('stacked')
415
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
417
def test_pull_cross_format_warning(self):
418
"""You get a warning for probably slow cross-format pulls.
420
# this is assumed to be going through InterDifferingSerializer
421
from_tree = self.make_branch_and_tree('from', format='2a')
422
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
423
from_tree.commit(message='first commit')
424
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
425
self.assertContainsRe(err,
426
"(?m)Doing on-the-fly conversion")
428
def test_pull_cross_format_warning_no_IDS(self):
429
"""You get a warning for probably slow cross-format pulls.
431
# this simulates what would happen across the network, where
432
# interdifferingserializer is not active
434
debug.debug_flags.add('IDS_never')
435
# TestCase take care of restoring them
437
from_tree = self.make_branch_and_tree('from', format='2a')
438
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
439
from_tree.commit(message='first commit')
440
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
441
self.assertContainsRe(err,
442
"(?m)Doing on-the-fly conversion")
444
def test_pull_cross_format_from_network(self):
445
self.setup_smart_server_with_call_log()
446
from_tree = self.make_branch_and_tree('from', format='2a')
447
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
448
self.assertIsInstance(from_tree.branch, remote.RemoteBranch)
449
from_tree.commit(message='first commit')
450
out, err = self.run_bzr(['pull', '-d', 'to',
451
from_tree.branch.bzrdir.root_transport.base])
452
self.assertContainsRe(err,
453
"(?m)Doing on-the-fly conversion")
455
def test_pull_to_experimental_format_warning(self):
456
"""You get a warning for pulling into experimental formats.
458
from_tree = self.make_branch_and_tree('from', format='development-subtree')
459
to_tree = self.make_branch_and_tree('to', format='development-subtree')
460
from_tree.commit(message='first commit')
461
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
462
self.assertContainsRe(err,
463
"(?m)Fetching into experimental format")
465
def test_pull_cross_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='2a')
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_show_base(self):
476
"""bzr pull supports --show-base
478
see https://bugs.launchpad.net/bzr/+bug/202374"""
479
# create two trees with conflicts, setup conflict, check that
480
# conflicted file looks correct
481
a_tree = self.example_branch('a')
482
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
484
f = open(pathjoin('a', 'hello'),'wt')
489
f = open(pathjoin('b', 'hello'),'wt')
493
out,err=self.run_bzr(['pull','-d','b','a','--show-base'])
495
# check for message here
496
self.assertEqual(err,
497
' M hello\nText conflict in hello\n1 conflicts encountered.\n')
499
self.assertEqualDiff('<<<<<<< TREE\n'
500
'fie||||||| BASE-REVISION\n'
502
'fee>>>>>>> MERGE-SOURCE\n',
503
open(pathjoin('b', 'hello')).read())
505
def test_pull_show_base_working_tree_only(self):
506
"""--show-base only allowed if there's a working tree
508
see https://bugs.launchpad.net/bzr/+bug/202374"""
509
# create a branch, see that --show-base fails
510
self.make_branch('from')
511
self.make_branch('to')
512
out=self.run_bzr(['pull','-d','to','from','--show-base'],retcode=3)
513
self.assertEqual(out,
514
('','bzr: ERROR: Need working tree for --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', ''))