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."""
23
from bzrlib.branch import Branch
24
from bzrlib.directory_service import directories
25
from bzrlib.osutils import pathjoin
26
from bzrlib.tests.blackbox import ExternalBase
27
from bzrlib.uncommit import uncommit
28
from bzrlib.workingtree import WorkingTree
29
from bzrlib import urlutils
32
class TestPull(ExternalBase):
34
def example_branch(self, path='.'):
35
tree = self.make_branch_and_tree(path)
36
self.build_tree_contents([
37
(pathjoin(path, 'hello'), 'foo'),
38
(pathjoin(path, 'goodbye'), 'baz')])
40
tree.commit(message='setup')
42
tree.commit(message='setup')
46
"""Pull changes from one branch to another."""
47
a_tree = self.example_branch('a')
49
self.run_bzr('pull', retcode=3)
50
self.run_bzr('missing', retcode=3)
51
self.run_bzr('missing .')
52
self.run_bzr('missing')
53
# this will work on windows because we check for the same branch
54
# in pull - if it fails, it is a regression
56
self.run_bzr('pull /', retcode=3)
57
if sys.platform not in ('win32', 'cygwin'):
61
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
66
b_tree.commit(message='blah', allow_pointless=True)
71
self.assertEqual(a.revision_history(), b.revision_history()[:-1])
74
self.run_bzr('pull ../b')
75
self.assertEqual(a.revision_history(), b.revision_history())
76
a_tree.commit(message='blah2', allow_pointless=True)
77
b_tree.commit(message='blah3', allow_pointless=True)
80
self.run_bzr('pull ../a', retcode=3)
82
b_tree.bzrdir.sprout('overwriteme')
83
os.chdir('overwriteme')
84
self.run_bzr('pull --overwrite ../a')
85
overwritten = Branch.open('.')
86
self.assertEqual(overwritten.revision_history(),
88
a_tree.merge_from_branch(b_tree.branch)
89
a_tree.commit(message="blah4", allow_pointless=True)
90
os.chdir('../b/subdir')
91
self.run_bzr('pull ../../a')
92
self.assertEqual(a.revision_history()[-1], b.revision_history()[-1])
93
sub_tree = WorkingTree.open_containing('.')[0]
94
sub_tree.commit(message="blah5", allow_pointless=True)
95
sub_tree.commit(message="blah6", allow_pointless=True)
97
self.run_bzr('pull ../a')
99
a_tree.commit(message="blah7", allow_pointless=True)
100
a_tree.merge_from_branch(b_tree.branch)
101
a_tree.commit(message="blah8", allow_pointless=True)
102
self.run_bzr('pull ../b')
103
self.run_bzr('pull ../b')
105
def test_pull_dash_d(self):
106
self.example_branch('a')
107
self.make_branch_and_tree('b')
108
self.make_branch_and_tree('c')
109
# pull into that branch
110
self.run_bzr('pull -d b a')
111
# pull into a branch specified by a url
112
c_url = urlutils.local_path_to_url('c')
113
self.assertStartsWith(c_url, 'file://')
114
self.run_bzr(['pull', '-d', c_url, 'a'])
116
def test_pull_revision(self):
117
"""Pull some changes from one branch to another."""
118
a_tree = self.example_branch('a')
119
self.build_tree_contents([
121
('a/goodbye2', 'baz')])
123
a_tree.commit(message="setup")
124
a_tree.add('goodbye2')
125
a_tree.commit(message="setup")
127
b_tree = a_tree.bzrdir.sprout('b',
128
revision_id=a_tree.branch.get_rev_id(1)).open_workingtree()
130
self.run_bzr('pull -r 2')
131
a = Branch.open('../a')
133
self.assertEqual(a.revno(),4)
134
self.assertEqual(b.revno(),2)
135
self.run_bzr('pull -r 3')
136
self.assertEqual(b.revno(),3)
137
self.run_bzr('pull -r 4')
138
self.assertEqual(a.revision_history(), b.revision_history())
141
def test_overwrite_uptodate(self):
142
# Make sure pull --overwrite overwrites
143
# even if the target branch has merged
144
# everything already.
145
a_tree = self.make_branch_and_tree('a')
146
self.build_tree_contents([('a/foo', 'original\n')])
148
a_tree.commit(message='initial commit')
150
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
152
self.build_tree_contents([('a/foo', 'changed\n')])
153
a_tree.commit(message='later change')
155
self.build_tree_contents([('a/foo', 'a third change')])
156
a_tree.commit(message='a third change')
158
rev_history_a = a_tree.branch.revision_history()
159
self.assertEqual(len(rev_history_a), 3)
161
b_tree.merge_from_branch(a_tree.branch)
162
b_tree.commit(message='merge')
164
self.assertEqual(len(b_tree.branch.revision_history()), 2)
167
self.run_bzr('pull --overwrite ../a')
168
rev_history_b = b_tree.branch.revision_history()
169
self.assertEqual(len(rev_history_b), 3)
171
self.assertEqual(rev_history_b, rev_history_a)
173
def test_overwrite_children(self):
174
# Make sure pull --overwrite sets the revision-history
175
# to be identical to the pull source, even if we have convergence
176
a_tree = self.make_branch_and_tree('a')
177
self.build_tree_contents([('a/foo', 'original\n')])
179
a_tree.commit(message='initial commit')
181
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
183
self.build_tree_contents([('a/foo', 'changed\n')])
184
a_tree.commit(message='later change')
186
self.build_tree_contents([('a/foo', 'a third change')])
187
a_tree.commit(message='a third change')
189
self.assertEqual(len(a_tree.branch.revision_history()), 3)
191
b_tree.merge_from_branch(a_tree.branch)
192
b_tree.commit(message='merge')
194
self.assertEqual(len(b_tree.branch.revision_history()), 2)
196
self.build_tree_contents([('a/foo', 'a fourth change\n')])
197
a_tree.commit(message='a fourth change')
199
rev_history_a = a_tree.branch.revision_history()
200
self.assertEqual(len(rev_history_a), 4)
202
# With convergence, we could just pull over the
203
# new change, but with --overwrite, we want to switch our history
205
self.run_bzr('pull --overwrite ../a')
206
rev_history_b = b_tree.branch.revision_history()
207
self.assertEqual(len(rev_history_b), 4)
209
self.assertEqual(rev_history_b, rev_history_a)
211
def test_pull_remember(self):
212
"""Pull changes from one branch to another and test parent location."""
213
transport = self.get_transport()
214
tree_a = self.make_branch_and_tree('branch_a')
215
branch_a = tree_a.branch
216
self.build_tree(['branch_a/a'])
218
tree_a.commit('commit a')
219
tree_b = branch_a.bzrdir.sprout('branch_b').open_workingtree()
220
branch_b = tree_b.branch
221
tree_c = branch_a.bzrdir.sprout('branch_c').open_workingtree()
222
branch_c = tree_c.branch
223
self.build_tree(['branch_a/b'])
225
tree_a.commit('commit b')
227
parent = branch_b.get_parent()
228
branch_b.set_parent(None)
229
self.assertEqual(None, branch_b.get_parent())
230
# test pull for failure without parent set
232
out = self.run_bzr('pull', retcode=3)
233
self.assertEqual(out,
234
('','bzr: ERROR: No pull location known or specified.\n'))
235
# test implicit --remember when no parent set, this pull conflicts
236
self.build_tree(['d'])
238
tree_b.commit('commit d')
239
out = self.run_bzr('pull ../branch_a', retcode=3)
240
self.assertEqual(out,
241
('','bzr: ERROR: These branches have diverged.'
242
' Use the missing command to see how.\n'
243
'Use the merge command to reconcile them.\n'))
244
self.assertEqual(branch_b.get_parent(), parent)
245
# test implicit --remember after resolving previous failure
246
uncommit(branch=branch_b, tree=tree_b)
247
transport.delete('branch_b/d')
249
self.assertEqual(branch_b.get_parent(), parent)
250
# test explicit --remember
251
self.run_bzr('pull ../branch_c --remember')
252
self.assertEqual(branch_b.get_parent(),
253
branch_c.bzrdir.root_transport.base)
255
def test_pull_bundle(self):
256
from bzrlib.testament import Testament
257
# Build up 2 trees and prepare for a pull
258
tree_a = self.make_branch_and_tree('branch_a')
259
f = open('branch_a/a', 'wb')
263
tree_a.commit('message')
265
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
267
# Make a change to 'a' that 'b' can pull
268
f = open('branch_a/a', 'wb')
271
tree_a.commit('message')
273
# Create the bundle for 'b' to pull
275
self.run_bzr('bundle ../branch_b -o ../bundle')
277
os.chdir('../branch_b')
278
out, err = self.run_bzr('pull ../bundle')
279
self.assertEqual(out,
280
'Now on revision 2.\n')
281
self.assertEqual(err,
282
' M a\nAll changes applied successfully.\n')
284
self.assertEqualDiff(tree_a.branch.revision_history(),
285
tree_b.branch.revision_history())
287
testament_a = Testament.from_revision(tree_a.branch.repository,
288
tree_a.get_parent_ids()[0])
289
testament_b = Testament.from_revision(tree_b.branch.repository,
290
tree_b.get_parent_ids()[0])
291
self.assertEqualDiff(testament_a.as_text(),
292
testament_b.as_text())
294
# it is legal to attempt to pull an already-merged bundle
295
out, err = self.run_bzr('pull ../bundle')
296
self.assertEqual(err, '')
297
self.assertEqual(out, 'No revisions to pull.\n')
299
def test_pull_verbose_no_files(self):
300
"""Pull --verbose should not list modified files"""
301
tree_a = self.make_branch_and_tree('tree_a')
302
self.build_tree(['tree_a/foo'])
305
tree_b = self.make_branch_and_tree('tree_b')
306
out = self.run_bzr('pull --verbose -d tree_b tree_a')[0]
307
self.assertContainsRe(out, 'bar')
308
self.assertNotContainsRe(out, 'added:')
309
self.assertNotContainsRe(out, 'foo')
311
def test_pull_quiet(self):
312
"""Check that bzr pull --quiet does not print anything"""
313
tree_a = self.make_branch_and_tree('tree_a')
314
self.build_tree(['tree_a/foo'])
316
revision_id = tree_a.commit('bar')
317
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
318
out, err = self.run_bzr('pull --quiet -d tree_b')
319
self.assertEqual(out, '')
320
self.assertEqual(err, '')
321
self.assertEqual(tree_b.last_revision(), revision_id)
322
self.build_tree(['tree_a/moo'])
324
revision_id = tree_a.commit('quack')
325
out, err = self.run_bzr('pull --quiet -d tree_b')
326
self.assertEqual(out, '')
327
self.assertEqual(err, '')
328
self.assertEqual(tree_b.last_revision(), revision_id)
330
def test_pull_from_directory_service(self):
331
source = self.make_branch_and_tree('source')
332
source.commit('commit 1')
333
target = source.bzrdir.sprout('target').open_workingtree()
334
source_last = source.commit('commit 2')
335
class FooService(object):
336
"""A directory service that always returns source"""
338
def look_up(self, name, url):
340
directories.register('foo:', FooService, 'Testing directory service')
341
self.addCleanup(lambda: directories.remove('foo:'))
342
self.run_bzr('pull foo:bar -d target')
343
self.assertEqual(source_last, target.last_revision())
345
def test_pull_verbose_defaults_to_long(self):
346
tree = self.example_branch('source')
347
target = self.make_branch_and_tree('target')
348
out = self.run_bzr('pull -v source -d target')[0]
349
self.assertContainsRe(out,
350
r'revno: 1\ncommitter: .*\nbranch nick: source')
351
self.assertNotContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
353
def test_pull_verbose_uses_default_log(self):
354
tree = self.example_branch('source')
355
target = self.make_branch_and_tree('target')
356
target_config = target.branch.get_config()
357
target_config.set_user_option('log_format', 'short')
358
out = self.run_bzr('pull -v source -d target')[0]
359
self.assertContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
360
self.assertNotContainsRe(
361
out, r'revno: 1\ncommitter: .*\nbranch nick: source')
363
def test_pull_smart_stacked_streaming_acceptance(self):
364
"""'bzr pull -r 123' works on stacked, smart branches, even when the
365
revision specified by the revno is only present in the fallback
368
See <https://launchpad.net/bugs/380314>
370
self.setup_smart_server_with_call_log()
371
# Make a stacked-on branch with two commits so that the
372
# revision-history can't be determined just by looking at the parent
373
# field in the revision in the stacked repo.
374
parent = self.make_branch_and_tree('parent', format='1.9')
375
parent.commit(message='first commit')
376
parent.commit(message='second commit')
377
local = parent.bzrdir.sprout('local').open_workingtree()
378
local.commit(message='local commit')
379
local.branch.create_clone_on_transport(
380
self.get_transport('stacked'), stacked_on=self.get_url('parent'))
381
empty = self.make_branch_and_tree('empty', format='1.9')
382
self.reset_smart_call_log()
383
self.run_bzr(['pull', '-r', '1', self.get_url('stacked')],
385
# This figure represent the amount of work to perform this use case. It
386
# is entirely ok to reduce this number if a test fails due to rpc_count
387
# being too low. If rpc_count increases, more network roundtrips have
388
# become necessary for this use case. Please do not adjust this number
389
# upwards without agreement from bzr's network support maintainers.
390
self.assertLength(18, self.hpss_calls)
391
remote = Branch.open('stacked')
392
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
394
def test_pull_cross_format_warning(self):
395
"""You get a warning for probably slow cross-format pulls.
398
from_tree = self.make_branch_and_tree('from', format='2a')
399
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
400
from_tree.commit(message='first commit')
401
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
402
self.assertContainsRe(err,
403
"(?m)Fetching between repositories with different formats.*")