29
24
from bzrlib.branch import Branch
30
from bzrlib.directory_service import directories
31
from bzrlib.osutils import pathjoin
25
from bzrlib.osutils import abspath
32
26
from bzrlib.tests.blackbox import ExternalBase
33
27
from bzrlib.uncommit import uncommit
34
from bzrlib.workingtree import WorkingTree
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')
47
self.runbzr('pull', retcode=3)
48
self.runbzr('missing', retcode=3)
49
self.runbzr('missing .')
50
self.runbzr('missing')
58
51
# this will work on windows because we check for the same branch
59
52
# in pull - if it fails, it is a regression
61
self.run_bzr('pull /', retcode=3)
54
self.runbzr('pull /', retcode=3)
62
55
if sys.platform not in ('win32', 'cygwin'):
66
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
59
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)
63
self.runbzr('add subdir')
64
self.runbzr('commit -m blah --unchanged')
67
b = Branch.open('../b')
68
self.assertEquals(a.revision_history(), b.revision_history()[:-1])
69
self.runbzr('pull ../b')
70
self.assertEquals(a.revision_history(), b.revision_history())
71
self.runbzr('commit -m blah2 --unchanged')
73
self.runbzr('commit -m blah3 --unchanged')
85
self.run_bzr('pull ../a', retcode=3)
75
self.runbzr('pull ../a', retcode=3)
87
b_tree.bzrdir.sprout('overwriteme')
77
self.runbzr('branch b overwriteme')
88
78
os.chdir('overwriteme')
89
self.run_bzr('pull --overwrite ../a')
79
self.runbzr('pull --overwrite ../a')
90
80
overwritten = Branch.open('.')
91
81
self.assertEqual(overwritten.revision_history(),
92
82
a.revision_history())
93
a_tree.merge_from_branch(b_tree.branch)
94
a_tree.commit(message="blah4", allow_pointless=True)
84
self.runbzr('merge ../b')
85
self.runbzr('commit -m blah4 --unchanged')
95
86
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)
87
self.runbzr('pull ../../a')
88
self.assertEquals(a.revision_history()[-1], b.revision_history()[-1])
89
self.runbzr('commit -m blah5 --unchanged')
90
self.runbzr('commit -m blah6 --unchanged')
102
self.run_bzr('pull ../a')
92
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'])
94
self.runbzr('commit -m blah7 --unchanged')
95
self.runbzr('merge ../b')
96
self.runbzr('commit -m blah8 --unchanged')
97
self.runbzr('pull ../b')
98
self.runbzr('pull ../b')
121
100
def test_pull_revision(self):
122
101
"""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()
105
self.example_branch()
106
file('hello2', 'wt').write('foo')
107
self.runbzr('add hello2')
108
self.runbzr('commit -m setup hello2')
109
file('goodbye2', 'wt').write('baz')
110
self.runbzr('add goodbye2')
111
self.runbzr('commit -m setup goodbye2')
114
self.runbzr('branch -r 1 a b')
135
self.run_bzr('pull -r 2')
116
self.runbzr('pull -r 2')
136
117
a = Branch.open('../a')
137
118
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())
119
self.assertEquals(a.revno(),4)
120
self.assertEquals(b.revno(),2)
121
self.runbzr('pull -r 3')
122
self.assertEquals(b.revno(),3)
123
self.runbzr('pull -r 4')
124
self.assertEquals(a.revision_history(), b.revision_history())
146
127
def test_overwrite_uptodate(self):
147
128
# Make sure pull --overwrite overwrites
148
129
# even if the target branch has merged
149
130
# 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)
133
def get_rh(expected_len):
134
rh = self.capture('revision-history')
135
# Make sure we don't have trailing empty revisions
136
rh = rh.strip().split('\n')
137
self.assertEqual(len(rh), expected_len)
143
open('foo', 'wb').write('original\n')
145
bzr('commit', '-m', 'initial commit')
148
bzr('branch', 'a', 'b')
151
open('foo', 'wb').write('changed\n')
152
bzr('commit', '-m', 'later change')
154
open('foo', 'wb').write('another\n')
155
bzr('commit', '-m', 'a third change')
157
rev_history_a = get_rh(3)
161
bzr('commit', '-m', 'merge')
163
rev_history_b = get_rh(2)
165
bzr('pull', '--overwrite', '../a')
166
rev_history_b = get_rh(3)
176
168
self.assertEqual(rev_history_b, rev_history_a)
178
170
def test_overwrite_children(self):
179
171
# Make sure pull --overwrite sets the revision-history
180
172
# 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)
175
def get_rh(expected_len):
176
rh = self.capture('revision-history')
177
# Make sure we don't have trailing empty revisions
178
rh = rh.strip().split('\n')
179
self.assertEqual(len(rh), expected_len)
185
open('foo', 'wb').write('original\n')
187
bzr('commit', '-m', 'initial commit')
190
bzr('branch', 'a', 'b')
193
open('foo', 'wb').write('changed\n')
194
bzr('commit', '-m', 'later change')
196
open('foo', 'wb').write('another\n')
197
bzr('commit', '-m', 'a third change')
199
rev_history_a = get_rh(3)
203
bzr('commit', '-m', 'merge')
205
rev_history_b = get_rh(2)
208
open('foo', 'wb').write('a fourth change\n')
209
bzr('commit', '-m', 'a fourth change')
211
rev_history_a = get_rh(4)
207
213
# With convergence, we could just pull over the
208
214
# 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)
216
bzr('pull', '--overwrite', '../a')
217
rev_history_b = get_rh(4)
214
219
self.assertEqual(rev_history_b, rev_history_a)
234
239
self.assertEqual(None, branch_b.get_parent())
235
240
# test pull for failure without parent set
236
241
os.chdir('branch_b')
237
out = self.run_bzr('pull', retcode=3)
238
self.assertEqual(out,
242
out = self.runbzr('pull', retcode=3)
243
self.assertEquals(out,
239
244
('','bzr: ERROR: No pull location known or specified.\n'))
240
245
# test implicit --remember when no parent set, this pull conflicts
241
246
self.build_tree(['d'])
243
248
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
out = self.runbzr('pull ../branch_a', retcode=3)
250
self.assertEquals(out,
251
('','bzr: ERROR: These branches have diverged. Try merge.\n'))
252
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
250
253
# test implicit --remember after resolving previous failure
251
254
uncommit(branch=branch_b, tree=tree_b)
252
255
transport.delete('branch_b/d')
254
self.assertEqual(branch_b.get_parent(), parent)
257
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
255
258
# 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")
259
self.runbzr('pull ../branch_c --remember')
260
self.assertEquals(abspath(branch_b.get_parent()),
261
abspath(branch_c.bzrdir.root_transport.base))