1
# Copyright (C) 2005-2010 Canonical Ltd
1
# Copyright (C) 2005 by Canonical Ltd
2
# -*- coding: utf-8 -*-
3
4
# This program is free software; you can redistribute it and/or modify
4
5
# it under the terms of the GNU General Public License as published by
5
6
# the Free Software Foundation; either version 2 of the License, or
6
7
# (at your option) any later version.
8
9
# This program is distributed in the hope that it will be useful,
9
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
12
# GNU General Public License for more details.
13
14
# You should have received a copy of the GNU General Public License
14
15
# 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."""
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
"""Black-box tests for bzr pull.
29
25
from bzrlib.branch import Branch
30
from bzrlib.directory_service import directories
31
from bzrlib.osutils import pathjoin
32
26
from bzrlib.tests.blackbox import ExternalBase
33
from bzrlib.uncommit import uncommit
34
from bzrlib.workingtree import WorkingTree
27
from bzrlib.osutils import abspath
37
30
class TestPull(ExternalBase):
39
def example_branch(self, path='.'):
40
tree = self.make_branch_and_tree(path)
41
self.build_tree_contents([
42
(pathjoin(path, 'hello'), 'foo'),
43
(pathjoin(path, 'goodbye'), 'baz')])
45
tree.commit(message='setup')
47
tree.commit(message='setup')
32
def example_branch(test):
34
file('hello', 'wt').write('foo')
35
test.runbzr('add hello')
36
test.runbzr('commit -m setup hello')
37
file('goodbye', 'wt').write('baz')
38
test.runbzr('add goodbye')
39
test.runbzr('commit -m setup goodbye')
50
41
def test_pull(self):
51
42
"""Pull changes from one branch to another."""
52
a_tree = self.example_branch('a')
54
self.run_bzr('pull', retcode=3)
55
self.run_bzr('missing', retcode=3)
56
self.run_bzr('missing .')
57
self.run_bzr('missing')
58
# this will work on windows because we check for the same branch
59
# in pull - if it fails, it is a regression
61
self.run_bzr('pull /', retcode=3)
62
if sys.platform not in ('win32', 'cygwin'):
47
self.runbzr('pull', retcode=3)
48
self.runbzr('missing', retcode=3)
49
self.runbzr('missing .')
50
self.runbzr('missing')
51
if sys.platform not in ('win32', 'cygwin'):
52
# This is equivalent to doing "bzr pull ."
53
# Which means that bzr creates 2 branches grabbing
54
# the same location, and tries to pull.
55
# However, 2 branches mean 2 locks on the same file
56
# which ultimately implies a deadlock.
57
# (non windows platforms allow multiple locks on the
58
# same file by the same calling process)
60
self.runbzr('pull /', retcode=3)
61
if sys.platform not in ('win32', 'cygwin'):
66
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
65
self.runbzr('branch a b')
71
b_tree.commit(message='blah', allow_pointless=True)
76
self.assertEqual(a.revision_history(), b.revision_history()[:-1])
79
self.run_bzr('pull ../b')
80
self.assertEqual(a.revision_history(), b.revision_history())
81
a_tree.commit(message='blah2', allow_pointless=True)
82
b_tree.commit(message='blah3', allow_pointless=True)
69
self.runbzr('add subdir')
70
self.runbzr('commit -m blah --unchanged')
73
b = Branch.open('../b')
74
self.assertEquals(a.revision_history(), b.revision_history()[:-1])
75
self.runbzr('pull ../b')
76
self.assertEquals(a.revision_history(), b.revision_history())
77
self.runbzr('commit -m blah2 --unchanged')
79
self.runbzr('commit -m blah3 --unchanged')
85
self.run_bzr('pull ../a', retcode=3)
81
self.runbzr('pull ../a', retcode=3)
87
b_tree.bzrdir.sprout('overwriteme')
83
self.runbzr('branch b overwriteme')
88
84
os.chdir('overwriteme')
89
self.run_bzr('pull --overwrite ../a')
85
self.runbzr('pull --overwrite ../a')
90
86
overwritten = Branch.open('.')
91
87
self.assertEqual(overwritten.revision_history(),
92
88
a.revision_history())
93
a_tree.merge_from_branch(b_tree.branch)
94
a_tree.commit(message="blah4", allow_pointless=True)
90
self.runbzr('merge ../b')
91
self.runbzr('commit -m blah4 --unchanged')
95
92
os.chdir('../b/subdir')
96
self.run_bzr('pull ../../a')
97
self.assertEqual(a.revision_history()[-1], b.revision_history()[-1])
98
sub_tree = WorkingTree.open_containing('.')[0]
99
sub_tree.commit(message="blah5", allow_pointless=True)
100
sub_tree.commit(message="blah6", allow_pointless=True)
93
self.runbzr('pull ../../a')
94
self.assertEquals(a.revision_history()[-1], b.revision_history()[-1])
95
self.runbzr('commit -m blah5 --unchanged')
96
self.runbzr('commit -m blah6 --unchanged')
102
self.run_bzr('pull ../a')
98
self.runbzr('pull ../a')
104
a_tree.commit(message="blah7", allow_pointless=True)
105
a_tree.merge_from_branch(b_tree.branch)
106
a_tree.commit(message="blah8", allow_pointless=True)
107
self.run_bzr('pull ../b')
108
self.run_bzr('pull ../b')
110
def test_pull_dash_d(self):
111
self.example_branch('a')
112
self.make_branch_and_tree('b')
113
self.make_branch_and_tree('c')
114
# pull into that branch
115
self.run_bzr('pull -d b a')
116
# pull into a branch specified by a url
117
c_url = urlutils.local_path_to_url('c')
118
self.assertStartsWith(c_url, 'file://')
119
self.run_bzr(['pull', '-d', c_url, 'a'])
100
self.runbzr('commit -m blah7 --unchanged')
101
self.runbzr('merge ../b')
102
self.runbzr('commit -m blah8 --unchanged')
103
self.runbzr('pull ../b')
104
self.runbzr('pull ../b')
121
106
def test_pull_revision(self):
122
107
"""Pull some changes from one branch to another."""
123
a_tree = self.example_branch('a')
124
self.build_tree_contents([
126
('a/goodbye2', 'baz')])
128
a_tree.commit(message="setup")
129
a_tree.add('goodbye2')
130
a_tree.commit(message="setup")
132
b_tree = a_tree.bzrdir.sprout('b',
133
revision_id=a_tree.branch.get_rev_id(1)).open_workingtree()
111
self.example_branch()
112
file('hello2', 'wt').write('foo')
113
self.runbzr('add hello2')
114
self.runbzr('commit -m setup hello2')
115
file('goodbye2', 'wt').write('baz')
116
self.runbzr('add goodbye2')
117
self.runbzr('commit -m setup goodbye2')
120
self.runbzr('branch -r 1 a b')
135
self.run_bzr('pull -r 2')
122
self.runbzr('pull -r 2')
136
123
a = Branch.open('../a')
137
124
b = Branch.open('.')
138
self.assertEqual(a.revno(),4)
139
self.assertEqual(b.revno(),2)
140
self.run_bzr('pull -r 3')
141
self.assertEqual(b.revno(),3)
142
self.run_bzr('pull -r 4')
143
self.assertEqual(a.revision_history(), b.revision_history())
125
self.assertEquals(a.revno(),4)
126
self.assertEquals(b.revno(),2)
127
self.runbzr('pull -r 3')
128
self.assertEquals(b.revno(),3)
129
self.runbzr('pull -r 4')
130
self.assertEquals(a.revision_history(), b.revision_history())
146
133
def test_overwrite_uptodate(self):
147
134
# Make sure pull --overwrite overwrites
148
135
# even if the target branch has merged
149
136
# everything already.
150
a_tree = self.make_branch_and_tree('a')
151
self.build_tree_contents([('a/foo', 'original\n')])
153
a_tree.commit(message='initial commit')
155
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
157
self.build_tree_contents([('a/foo', 'changed\n')])
158
a_tree.commit(message='later change')
160
self.build_tree_contents([('a/foo', 'a third change')])
161
a_tree.commit(message='a third change')
163
rev_history_a = a_tree.branch.revision_history()
164
self.assertEqual(len(rev_history_a), 3)
166
b_tree.merge_from_branch(a_tree.branch)
167
b_tree.commit(message='merge')
169
self.assertEqual(len(b_tree.branch.revision_history()), 2)
172
self.run_bzr('pull --overwrite ../a')
173
rev_history_b = b_tree.branch.revision_history()
174
self.assertEqual(len(rev_history_b), 3)
139
def get_rh(expected_len):
140
rh = self.capture('revision-history')
141
# Make sure we don't have trailing empty revisions
142
rh = rh.strip().split('\n')
143
self.assertEqual(len(rh), expected_len)
149
open('foo', 'wb').write('original\n')
151
bzr('commit', '-m', 'initial commit')
154
bzr('branch', 'a', 'b')
157
open('foo', 'wb').write('changed\n')
158
bzr('commit', '-m', 'later change')
160
open('foo', 'wb').write('another\n')
161
bzr('commit', '-m', 'a third change')
163
rev_history_a = get_rh(3)
167
bzr('commit', '-m', 'merge')
169
rev_history_b = get_rh(2)
171
bzr('pull', '--overwrite', '../a')
172
rev_history_b = get_rh(3)
176
174
self.assertEqual(rev_history_b, rev_history_a)
178
176
def test_overwrite_children(self):
179
177
# Make sure pull --overwrite sets the revision-history
180
178
# to be identical to the pull source, even if we have convergence
181
a_tree = self.make_branch_and_tree('a')
182
self.build_tree_contents([('a/foo', 'original\n')])
184
a_tree.commit(message='initial commit')
186
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
188
self.build_tree_contents([('a/foo', 'changed\n')])
189
a_tree.commit(message='later change')
191
self.build_tree_contents([('a/foo', 'a third change')])
192
a_tree.commit(message='a third change')
194
self.assertEqual(len(a_tree.branch.revision_history()), 3)
196
b_tree.merge_from_branch(a_tree.branch)
197
b_tree.commit(message='merge')
199
self.assertEqual(len(b_tree.branch.revision_history()), 2)
201
self.build_tree_contents([('a/foo', 'a fourth change\n')])
202
a_tree.commit(message='a fourth change')
204
rev_history_a = a_tree.branch.revision_history()
205
self.assertEqual(len(rev_history_a), 4)
181
def get_rh(expected_len):
182
rh = self.capture('revision-history')
183
# Make sure we don't have trailing empty revisions
184
rh = rh.strip().split('\n')
185
self.assertEqual(len(rh), expected_len)
191
open('foo', 'wb').write('original\n')
193
bzr('commit', '-m', 'initial commit')
196
bzr('branch', 'a', 'b')
199
open('foo', 'wb').write('changed\n')
200
bzr('commit', '-m', 'later change')
202
open('foo', 'wb').write('another\n')
203
bzr('commit', '-m', 'a third change')
205
rev_history_a = get_rh(3)
209
bzr('commit', '-m', 'merge')
211
rev_history_b = get_rh(2)
214
open('foo', 'wb').write('a fourth change\n')
215
bzr('commit', '-m', 'a fourth change')
217
rev_history_a = get_rh(4)
207
219
# With convergence, we could just pull over the
208
220
# new change, but with --overwrite, we want to switch our history
210
self.run_bzr('pull --overwrite ../a')
211
rev_history_b = b_tree.branch.revision_history()
212
self.assertEqual(len(rev_history_b), 4)
222
bzr('pull', '--overwrite', '../a')
223
rev_history_b = get_rh(4)
214
225
self.assertEqual(rev_history_b, rev_history_a)
216
227
def test_pull_remember(self):
217
228
"""Pull changes from one branch to another and test parent location."""
218
transport = self.get_transport()
219
tree_a = self.make_branch_and_tree('branch_a')
220
branch_a = tree_a.branch
221
self.build_tree(['branch_a/a'])
223
tree_a.commit('commit a')
224
tree_b = branch_a.bzrdir.sprout('branch_b').open_workingtree()
225
branch_b = tree_b.branch
226
tree_c = branch_a.bzrdir.sprout('branch_c').open_workingtree()
227
branch_c = tree_c.branch
228
self.build_tree(['branch_a/b'])
230
tree_a.commit('commit b')
232
self.example_branch()
233
self.runbzr('branch . ../b')
234
self.runbzr('branch . ../c')
235
file('bottles', 'wt').write('99 bottles of beer on the wall')
236
self.runbzr('add bottles')
237
self.runbzr('commit -m 99_bottles')
240
parent = b.get_parent()
232
parent = branch_b.get_parent()
233
branch_b.set_parent(None)
234
self.assertEqual(None, branch_b.get_parent())
243
self.assertEqual(None, b.get_parent())
235
244
# test pull for failure without parent set
237
out = self.run_bzr('pull', retcode=3)
238
self.assertEqual(out,
245
out = self.runbzr('pull', retcode=3)
246
self.assertEquals(out,
239
247
('','bzr: ERROR: No pull location known or specified.\n'))
240
248
# test implicit --remember when no parent set, this pull conflicts
241
self.build_tree(['d'])
243
tree_b.commit('commit d')
244
out = self.run_bzr('pull ../branch_a', retcode=3)
245
self.assertEqual(out,
246
('','bzr: ERROR: These branches have diverged.'
247
' Use the missing command to see how.\n'
248
'Use the merge command to reconcile them.\n'))
249
self.assertEqual(branch_b.get_parent(), parent)
249
file('bottles', 'wt').write('98 bottles of beer on the wall')
250
self.runbzr('add bottles')
251
self.runbzr('commit -m 98_bottles')
252
out = self.runbzr('pull ../a', retcode=3)
253
self.assertEquals(out,
254
('','bzr: ERROR: These branches have diverged. Try merge.\n'))
255
self.assertEquals(abspath(b.get_parent()), abspath(parent))
250
256
# test implicit --remember after resolving previous failure
251
uncommit(branch=branch_b, tree=tree_b)
252
transport.delete('branch_b/d')
254
self.assertEqual(branch_b.get_parent(), parent)
257
self.runbzr('uncommit --force')
259
self.assertEquals(abspath(b.get_parent()), abspath(parent))
255
260
# test explicit --remember
256
self.run_bzr('pull ../branch_c --remember')
257
self.assertEqual(branch_b.get_parent(),
258
branch_c.bzrdir.root_transport.base)
260
def test_pull_bundle(self):
261
from bzrlib.testament import Testament
262
# Build up 2 trees and prepare for a pull
263
tree_a = self.make_branch_and_tree('branch_a')
264
f = open('branch_a/a', 'wb')
268
tree_a.commit('message')
270
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
272
# Make a change to 'a' that 'b' can pull
273
f = open('branch_a/a', 'wb')
276
tree_a.commit('message')
278
# Create the bundle for 'b' to pull
280
self.run_bzr('bundle ../branch_b -o ../bundle')
282
os.chdir('../branch_b')
283
out, err = self.run_bzr('pull ../bundle')
284
self.assertEqual(out,
285
'Now on revision 2.\n')
286
self.assertEqual(err,
287
' M a\nAll changes applied successfully.\n')
289
self.assertEqualDiff(tree_a.branch.revision_history(),
290
tree_b.branch.revision_history())
292
testament_a = Testament.from_revision(tree_a.branch.repository,
293
tree_a.get_parent_ids()[0])
294
testament_b = Testament.from_revision(tree_b.branch.repository,
295
tree_b.get_parent_ids()[0])
296
self.assertEqualDiff(testament_a.as_text(),
297
testament_b.as_text())
299
# it is legal to attempt to pull an already-merged bundle
300
out, err = self.run_bzr('pull ../bundle')
301
self.assertEqual(err, '')
302
self.assertEqual(out, 'No revisions to pull.\n')
304
def test_pull_verbose_no_files(self):
305
"""Pull --verbose should not list modified files"""
306
tree_a = self.make_branch_and_tree('tree_a')
307
self.build_tree(['tree_a/foo'])
310
tree_b = self.make_branch_and_tree('tree_b')
311
out = self.run_bzr('pull --verbose -d tree_b tree_a')[0]
312
self.assertContainsRe(out, 'bar')
313
self.assertNotContainsRe(out, 'added:')
314
self.assertNotContainsRe(out, 'foo')
316
def test_pull_quiet(self):
317
"""Check that bzr pull --quiet does not print anything"""
318
tree_a = self.make_branch_and_tree('tree_a')
319
self.build_tree(['tree_a/foo'])
321
revision_id = tree_a.commit('bar')
322
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
323
out, err = self.run_bzr('pull --quiet -d tree_b')
324
self.assertEqual(out, '')
325
self.assertEqual(err, '')
326
self.assertEqual(tree_b.last_revision(), revision_id)
327
self.build_tree(['tree_a/moo'])
329
revision_id = tree_a.commit('quack')
330
out, err = self.run_bzr('pull --quiet -d tree_b')
331
self.assertEqual(out, '')
332
self.assertEqual(err, '')
333
self.assertEqual(tree_b.last_revision(), revision_id)
335
def test_pull_from_directory_service(self):
336
source = self.make_branch_and_tree('source')
337
source.commit('commit 1')
338
target = source.bzrdir.sprout('target').open_workingtree()
339
source_last = source.commit('commit 2')
340
class FooService(object):
341
"""A directory service that always returns source"""
343
def look_up(self, name, url):
345
directories.register('foo:', FooService, 'Testing directory service')
346
self.addCleanup(directories.remove, 'foo:')
347
self.run_bzr('pull foo:bar -d target')
348
self.assertEqual(source_last, target.last_revision())
350
def test_pull_verbose_defaults_to_long(self):
351
tree = self.example_branch('source')
352
target = self.make_branch_and_tree('target')
353
out = self.run_bzr('pull -v source -d target')[0]
354
self.assertContainsRe(out,
355
r'revno: 1\ncommitter: .*\nbranch nick: source')
356
self.assertNotContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
358
def test_pull_verbose_uses_default_log(self):
359
tree = self.example_branch('source')
360
target = self.make_branch_and_tree('target')
361
target_config = target.branch.get_config()
362
target_config.set_user_option('log_format', 'short')
363
out = self.run_bzr('pull -v source -d target')[0]
364
self.assertContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
365
self.assertNotContainsRe(
366
out, r'revno: 1\ncommitter: .*\nbranch nick: source')
368
def test_pull_smart_stacked_streaming_acceptance(self):
369
"""'bzr pull -r 123' works on stacked, smart branches, even when the
370
revision specified by the revno is only present in the fallback
373
See <https://launchpad.net/bugs/380314>
375
self.setup_smart_server_with_call_log()
376
# Make a stacked-on branch with two commits so that the
377
# revision-history can't be determined just by looking at the parent
378
# field in the revision in the stacked repo.
379
parent = self.make_branch_and_tree('parent', format='1.9')
380
parent.commit(message='first commit')
381
parent.commit(message='second commit')
382
local = parent.bzrdir.sprout('local').open_workingtree()
383
local.commit(message='local commit')
384
local.branch.create_clone_on_transport(
385
self.get_transport('stacked'), stacked_on=self.get_url('parent'))
386
empty = self.make_branch_and_tree('empty', format='1.9')
387
self.reset_smart_call_log()
388
self.run_bzr(['pull', '-r', '1', self.get_url('stacked')],
390
# This figure represent the amount of work to perform this use case. It
391
# is entirely ok to reduce this number if a test fails due to rpc_count
392
# being too low. If rpc_count increases, more network roundtrips have
393
# become necessary for this use case. Please do not adjust this number
394
# upwards without agreement from bzr's network support maintainers.
395
self.assertLength(18, self.hpss_calls)
396
remote = Branch.open('stacked')
397
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
399
def test_pull_cross_format_warning(self):
400
"""You get a warning for probably slow cross-format pulls.
402
# this is assumed to be going through InterDifferingSerializer
403
from_tree = self.make_branch_and_tree('from', format='2a')
404
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
405
from_tree.commit(message='first commit')
406
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
407
self.assertContainsRe(err,
408
"(?m)Doing on-the-fly conversion")
410
def test_pull_cross_format_warning_no_IDS(self):
411
"""You get a warning for probably slow cross-format pulls.
413
# this simulates what would happen across the network, where
414
# interdifferingserializer is not active
416
debug.debug_flags.add('IDS_never')
417
# TestCase take care of restoring them
419
from_tree = self.make_branch_and_tree('from', format='2a')
420
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
421
from_tree.commit(message='first commit')
422
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
423
self.assertContainsRe(err,
424
"(?m)Doing on-the-fly conversion")
426
def test_pull_cross_format_from_network(self):
427
self.setup_smart_server_with_call_log()
428
from_tree = self.make_branch_and_tree('from', format='2a')
429
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
430
self.assertIsInstance(from_tree.branch, remote.RemoteBranch)
431
from_tree.commit(message='first commit')
432
out, err = self.run_bzr(['pull', '-d', 'to',
433
from_tree.branch.bzrdir.root_transport.base])
434
self.assertContainsRe(err,
435
"(?m)Doing on-the-fly conversion")
437
def test_pull_to_experimental_format_warning(self):
438
"""You get a warning for pulling into experimental formats.
440
from_tree = self.make_branch_and_tree('from', format='development-subtree')
441
to_tree = self.make_branch_and_tree('to', format='development-subtree')
442
from_tree.commit(message='first commit')
443
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
444
self.assertContainsRe(err,
445
"(?m)Fetching into experimental format")
447
def test_pull_cross_to_experimental_format_warning(self):
448
"""You get a warning for pulling into experimental formats.
450
from_tree = self.make_branch_and_tree('from', format='2a')
451
to_tree = self.make_branch_and_tree('to', format='development-subtree')
452
from_tree.commit(message='first commit')
453
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
454
self.assertContainsRe(err,
455
"(?m)Fetching into experimental format")
261
self.runbzr('pull ../c --remember')
262
self.assertEquals(abspath(b.get_parent()), abspath('../c'))