1
# Copyright (C) 2005, 2006, 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
37
28
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')
30
def example_branch(test):
32
file('hello', 'wt').write('foo')
33
test.runbzr('add hello')
34
test.runbzr('commit -m setup hello')
35
file('goodbye', 'wt').write('baz')
36
test.runbzr('add goodbye')
37
test.runbzr('commit -m setup goodbye')
50
39
def test_pull(self):
51
40
"""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'):
45
self.runbzr('pull', retcode=3)
46
self.runbzr('missing', retcode=3)
47
self.runbzr('missing .')
48
self.runbzr('missing')
49
if sys.platform not in ('win32', 'cygwin'):
50
# This is equivalent to doing "bzr pull ."
51
# Which means that bzr creates 2 branches grabbing
52
# the same location, and tries to pull.
53
# However, 2 branches mean 2 locks on the same file
54
# which ultimately implies a deadlock.
55
# (non windows platforms allow multiple locks on the
56
# same file by the same calling process)
58
self.runbzr('pull /', retcode=3)
59
if sys.platform not in ('win32', 'cygwin'):
66
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
63
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)
67
self.runbzr('add subdir')
68
self.runbzr('commit -m blah --unchanged')
71
b = Branch.open('../b')
72
self.assertEquals(a.revision_history(), b.revision_history()[:-1])
73
self.runbzr('pull ../b')
74
self.assertEquals(a.revision_history(), b.revision_history())
75
self.runbzr('commit -m blah2 --unchanged')
77
self.runbzr('commit -m blah3 --unchanged')
85
self.run_bzr('pull ../a', retcode=3)
79
self.runbzr('pull ../a', retcode=3)
87
b_tree.bzrdir.sprout('overwriteme')
81
self.runbzr('branch b overwriteme')
88
82
os.chdir('overwriteme')
89
self.run_bzr('pull --overwrite ../a')
83
self.runbzr('pull --overwrite ../a')
90
84
overwritten = Branch.open('.')
91
85
self.assertEqual(overwritten.revision_history(),
92
86
a.revision_history())
93
a_tree.merge_from_branch(b_tree.branch)
94
a_tree.commit(message="blah4", allow_pointless=True)
88
self.runbzr('merge ../b')
89
self.runbzr('commit -m blah4 --unchanged')
95
90
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)
91
self.runbzr('pull ../../a')
92
self.assertEquals(a.revision_history()[-1], b.revision_history()[-1])
93
self.runbzr('commit -m blah5 --unchanged')
94
self.runbzr('commit -m blah6 --unchanged')
102
self.run_bzr('pull ../a')
96
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'])
121
def test_pull_revision(self):
122
"""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()
135
self.run_bzr('pull -r 2')
136
a = Branch.open('../a')
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())
98
self.runbzr('commit -m blah7 --unchanged')
99
self.runbzr('merge ../b')
100
self.runbzr('commit -m blah8 --unchanged')
101
self.runbzr('pull ../b')
102
self.runbzr('pull ../b')
146
104
def test_overwrite_uptodate(self):
147
105
# Make sure pull --overwrite overwrites
148
106
# even if the target branch has merged
149
107
# 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)
110
def get_rh(expected_len):
111
rh = self.capture('revision-history')
112
# Make sure we don't have trailing empty revisions
113
rh = rh.strip().split('\n')
114
self.assertEqual(len(rh), expected_len)
120
open('foo', 'wb').write('original\n')
122
bzr('commit', '-m', 'initial commit')
125
bzr('branch', 'a', 'b')
128
open('foo', 'wb').write('changed\n')
129
bzr('commit', '-m', 'later change')
131
open('foo', 'wb').write('another\n')
132
bzr('commit', '-m', 'a third change')
134
rev_history_a = get_rh(3)
138
bzr('commit', '-m', 'merge')
140
rev_history_b = get_rh(2)
142
bzr('pull', '--overwrite', '../a')
143
rev_history_b = get_rh(3)
176
145
self.assertEqual(rev_history_b, rev_history_a)
178
147
def test_overwrite_children(self):
179
148
# Make sure pull --overwrite sets the revision-history
180
149
# 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)
152
def get_rh(expected_len):
153
rh = self.capture('revision-history')
154
# Make sure we don't have trailing empty revisions
155
rh = rh.strip().split('\n')
156
self.assertEqual(len(rh), expected_len)
162
open('foo', 'wb').write('original\n')
164
bzr('commit', '-m', 'initial commit')
167
bzr('branch', 'a', 'b')
170
open('foo', 'wb').write('changed\n')
171
bzr('commit', '-m', 'later change')
173
open('foo', 'wb').write('another\n')
174
bzr('commit', '-m', 'a third change')
176
rev_history_a = get_rh(3)
180
bzr('commit', '-m', 'merge')
182
rev_history_b = get_rh(2)
185
open('foo', 'wb').write('a fourth change\n')
186
bzr('commit', '-m', 'a fourth change')
188
rev_history_a = get_rh(4)
207
190
# With convergence, we could just pull over the
208
191
# 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)
193
bzr('pull', '--overwrite', '../a')
194
rev_history_b = get_rh(4)
214
196
self.assertEqual(rev_history_b, rev_history_a)
216
def test_pull_remember(self):
217
"""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
parent = branch_b.get_parent()
233
branch_b.set_parent(None)
234
self.assertEqual(None, branch_b.get_parent())
235
# test pull for failure without parent set
237
out = self.run_bzr('pull', retcode=3)
238
self.assertEqual(out,
239
('','bzr: ERROR: No pull location known or specified.\n'))
240
# 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)
250
# 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)
255
# 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")