1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
|
# Copyright (C) 2005-2010 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"""Black-box tests for bzr pull."""
import os
import sys
from bzrlib import (
debug,
remote,
tests,
urlutils,
)
from bzrlib.branch import Branch
from bzrlib.directory_service import directories
from bzrlib.osutils import pathjoin
from bzrlib.tests import (
fixtures,
script,
)
from bzrlib.uncommit import uncommit
from bzrlib.workingtree import WorkingTree
class TestPull(tests.TestCaseWithTransport):
def example_branch(self, path='.'):
tree = self.make_branch_and_tree(path)
self.build_tree_contents([
(pathjoin(path, 'hello'), 'foo'),
(pathjoin(path, 'goodbye'), 'baz')])
tree.add('hello')
tree.commit(message='setup')
tree.add('goodbye')
tree.commit(message='setup')
return tree
def test_pull(self):
"""Pull changes from one branch to another."""
a_tree = self.example_branch('a')
base_rev = a_tree.branch.last_revision()
os.chdir('a')
self.run_bzr('pull', retcode=3)
self.run_bzr('missing', retcode=3)
self.run_bzr('missing .')
self.run_bzr('missing')
# this will work on windows because we check for the same branch
# in pull - if it fails, it is a regression
self.run_bzr('pull')
self.run_bzr('pull /', retcode=3)
if sys.platform not in ('win32', 'cygwin'):
self.run_bzr('pull')
os.chdir('..')
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
os.chdir('b')
self.run_bzr('pull')
os.mkdir('subdir')
b_tree.add('subdir')
new_rev = b_tree.commit(message='blah', allow_pointless=True)
os.chdir('..')
a = Branch.open('a')
b = Branch.open('b')
self.assertEqual(a.last_revision(), base_rev)
self.assertEqual(b.last_revision(), new_rev)
os.chdir('a')
self.run_bzr('pull ../b')
self.assertEqual(a.last_revision(), b.last_revision())
a_tree.commit(message='blah2', allow_pointless=True)
b_tree.commit(message='blah3', allow_pointless=True)
# no overwrite
os.chdir('../b')
self.run_bzr('pull ../a', retcode=3)
os.chdir('..')
b_tree.bzrdir.sprout('overwriteme')
os.chdir('overwriteme')
self.run_bzr('pull --overwrite ../a')
overwritten = Branch.open('.')
self.assertEqual(overwritten.last_revision(),
a.last_revision())
a_tree.merge_from_branch(b_tree.branch)
a_tree.commit(message="blah4", allow_pointless=True)
os.chdir('../b/subdir')
self.run_bzr('pull ../../a')
self.assertEqual(a.last_revision(), b.last_revision())
sub_tree = WorkingTree.open_containing('.')[0]
sub_tree.commit(message="blah5", allow_pointless=True)
sub_tree.commit(message="blah6", allow_pointless=True)
os.chdir('..')
self.run_bzr('pull ../a')
os.chdir('../a')
a_tree.commit(message="blah7", allow_pointless=True)
a_tree.merge_from_branch(b_tree.branch)
a_tree.commit(message="blah8", allow_pointless=True)
self.run_bzr('pull ../b')
self.run_bzr('pull ../b')
def test_pull_dash_d(self):
self.example_branch('a')
self.make_branch_and_tree('b')
self.make_branch_and_tree('c')
# pull into that branch
self.run_bzr('pull -d b a')
# pull into a branch specified by a url
c_url = urlutils.local_path_to_url('c')
self.assertStartsWith(c_url, 'file://')
self.run_bzr(['pull', '-d', c_url, 'a'])
def test_pull_revision(self):
"""Pull some changes from one branch to another."""
a_tree = self.example_branch('a')
self.build_tree_contents([
('a/hello2', 'foo'),
('a/goodbye2', 'baz')])
a_tree.add('hello2')
a_tree.commit(message="setup")
a_tree.add('goodbye2')
a_tree.commit(message="setup")
b_tree = a_tree.bzrdir.sprout('b',
revision_id=a_tree.branch.get_rev_id(1)).open_workingtree()
os.chdir('b')
self.run_bzr('pull -r 2')
a = Branch.open('../a')
b = Branch.open('.')
self.assertEqual(a.revno(),4)
self.assertEqual(b.revno(),2)
self.run_bzr('pull -r 3')
self.assertEqual(b.revno(),3)
self.run_bzr('pull -r 4')
self.assertEqual(a.last_revision(), b.last_revision())
def test_pull_tags(self):
"""Tags are updated by pull, and revisions named in those tags are
fetched.
"""
# Make a source, sprout a target off it
builder = self.make_branch_builder('source')
source = fixtures.build_branch_with_non_ancestral_rev(builder)
source.get_config().set_user_option('branch.fetch_tags', 'True')
target_bzrdir = source.bzrdir.sprout('target')
source.tags.set_tag('tag-a', 'rev-2')
# Pull from source
self.run_bzr('pull -d target source')
target = target_bzrdir.open_branch()
# The tag is present, and so is its revision.
self.assertEqual('rev-2', target.tags.lookup_tag('tag-a'))
target.repository.get_revision('rev-2')
def test_overwrite_uptodate(self):
# Make sure pull --overwrite overwrites
# even if the target branch has merged
# everything already.
a_tree = self.make_branch_and_tree('a')
self.build_tree_contents([('a/foo', 'original\n')])
a_tree.add('foo')
a_tree.commit(message='initial commit')
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
self.build_tree_contents([('a/foo', 'changed\n')])
a_tree.commit(message='later change')
self.build_tree_contents([('a/foo', 'a third change')])
a_tree.commit(message='a third change')
self.assertEqual(a_tree.branch.last_revision_info()[0], 3)
b_tree.merge_from_branch(a_tree.branch)
b_tree.commit(message='merge')
self.assertEqual(b_tree.branch.last_revision_info()[0], 2)
os.chdir('b')
self.run_bzr('pull --overwrite ../a')
(last_revinfo_b) = b_tree.branch.last_revision_info()
self.assertEqual(last_revinfo_b[0], 3)
self.assertEqual(last_revinfo_b[1], a_tree.branch.last_revision())
def test_overwrite_children(self):
# Make sure pull --overwrite sets the revision-history
# to be identical to the pull source, even if we have convergence
a_tree = self.make_branch_and_tree('a')
self.build_tree_contents([('a/foo', 'original\n')])
a_tree.add('foo')
a_tree.commit(message='initial commit')
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
self.build_tree_contents([('a/foo', 'changed\n')])
a_tree.commit(message='later change')
self.build_tree_contents([('a/foo', 'a third change')])
a_tree.commit(message='a third change')
self.assertEqual(a_tree.branch.last_revision_info()[0], 3)
b_tree.merge_from_branch(a_tree.branch)
b_tree.commit(message='merge')
self.assertEqual(b_tree.branch.last_revision_info()[0], 2)
self.build_tree_contents([('a/foo', 'a fourth change\n')])
a_tree.commit(message='a fourth change')
rev_info_a = a_tree.branch.last_revision_info()
self.assertEqual(rev_info_a[0], 4)
# With convergence, we could just pull over the
# new change, but with --overwrite, we want to switch our history
os.chdir('b')
self.run_bzr('pull --overwrite ../a')
rev_info_b = b_tree.branch.last_revision_info()
self.assertEqual(rev_info_b[0], 4)
self.assertEqual(rev_info_b, rev_info_a)
def test_pull_remember(self):
"""Pull changes from one branch to another and test parent location."""
transport = self.get_transport()
tree_a = self.make_branch_and_tree('branch_a')
branch_a = tree_a.branch
self.build_tree(['branch_a/a'])
tree_a.add('a')
tree_a.commit('commit a')
tree_b = branch_a.bzrdir.sprout('branch_b').open_workingtree()
branch_b = tree_b.branch
tree_c = branch_a.bzrdir.sprout('branch_c').open_workingtree()
branch_c = tree_c.branch
self.build_tree(['branch_a/b'])
tree_a.add('b')
tree_a.commit('commit b')
# reset parent
parent = branch_b.get_parent()
branch_b.set_parent(None)
self.assertEqual(None, branch_b.get_parent())
# test pull for failure without parent set
os.chdir('branch_b')
out = self.run_bzr('pull', retcode=3)
self.assertEqual(out,
('','bzr: ERROR: No pull location known or specified.\n'))
# test implicit --remember when no parent set, this pull conflicts
self.build_tree(['d'])
tree_b.add('d')
tree_b.commit('commit d')
out = self.run_bzr('pull ../branch_a', retcode=3)
self.assertEqual(out,
('','bzr: ERROR: These branches have diverged.'
' Use the missing command to see how.\n'
'Use the merge command to reconcile them.\n'))
self.assertEqual(branch_b.get_parent(), parent)
# test implicit --remember after resolving previous failure
uncommit(branch=branch_b, tree=tree_b)
transport.delete('branch_b/d')
self.run_bzr('pull')
self.assertEqual(branch_b.get_parent(), parent)
# test explicit --remember
self.run_bzr('pull ../branch_c --remember')
self.assertEqual(branch_b.get_parent(),
branch_c.bzrdir.root_transport.base)
def test_pull_bundle(self):
from bzrlib.testament import Testament
# Build up 2 trees and prepare for a pull
tree_a = self.make_branch_and_tree('branch_a')
f = open('branch_a/a', 'wb')
f.write('hello')
f.close()
tree_a.add('a')
tree_a.commit('message')
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
# Make a change to 'a' that 'b' can pull
f = open('branch_a/a', 'wb')
f.write('hey there')
f.close()
tree_a.commit('message')
# Create the bundle for 'b' to pull
os.chdir('branch_a')
self.run_bzr('bundle ../branch_b -o ../bundle')
os.chdir('../branch_b')
out, err = self.run_bzr('pull ../bundle')
self.assertEqual(out,
'Now on revision 2.\n')
self.assertEqual(err,
' M a\nAll changes applied successfully.\n')
self.assertEqualDiff(tree_a.branch.last_revision(),
tree_b.branch.last_revision())
testament_a = Testament.from_revision(tree_a.branch.repository,
tree_a.get_parent_ids()[0])
testament_b = Testament.from_revision(tree_b.branch.repository,
tree_b.get_parent_ids()[0])
self.assertEqualDiff(testament_a.as_text(),
testament_b.as_text())
# it is legal to attempt to pull an already-merged bundle
out, err = self.run_bzr('pull ../bundle')
self.assertEqual(err, '')
self.assertEqual(out, 'No revisions or tags to pull.\n')
def test_pull_verbose_no_files(self):
"""Pull --verbose should not list modified files"""
tree_a = self.make_branch_and_tree('tree_a')
self.build_tree(['tree_a/foo'])
tree_a.add('foo')
tree_a.commit('bar')
tree_b = self.make_branch_and_tree('tree_b')
out = self.run_bzr('pull --verbose -d tree_b tree_a')[0]
self.assertContainsRe(out, 'bar')
self.assertNotContainsRe(out, 'added:')
self.assertNotContainsRe(out, 'foo')
def test_pull_quiet(self):
"""Check that bzr pull --quiet does not print anything"""
tree_a = self.make_branch_and_tree('tree_a')
self.build_tree(['tree_a/foo'])
tree_a.add('foo')
revision_id = tree_a.commit('bar')
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
out, err = self.run_bzr('pull --quiet -d tree_b')
self.assertEqual(out, '')
self.assertEqual(err, '')
self.assertEqual(tree_b.last_revision(), revision_id)
self.build_tree(['tree_a/moo'])
tree_a.add('moo')
revision_id = tree_a.commit('quack')
out, err = self.run_bzr('pull --quiet -d tree_b')
self.assertEqual(out, '')
self.assertEqual(err, '')
self.assertEqual(tree_b.last_revision(), revision_id)
def test_pull_from_directory_service(self):
source = self.make_branch_and_tree('source')
source.commit('commit 1')
target = source.bzrdir.sprout('target').open_workingtree()
source_last = source.commit('commit 2')
class FooService(object):
"""A directory service that always returns source"""
def look_up(self, name, url):
return 'source'
directories.register('foo:', FooService, 'Testing directory service')
self.addCleanup(directories.remove, 'foo:')
self.run_bzr('pull foo:bar -d target')
self.assertEqual(source_last, target.last_revision())
def test_pull_verbose_defaults_to_long(self):
tree = self.example_branch('source')
target = self.make_branch_and_tree('target')
out = self.run_bzr('pull -v source -d target')[0]
self.assertContainsRe(out,
r'revno: 1\ncommitter: .*\nbranch nick: source')
self.assertNotContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
def test_pull_verbose_uses_default_log(self):
tree = self.example_branch('source')
target = self.make_branch_and_tree('target')
target_config = target.branch.get_config()
target_config.set_user_option('log_format', 'short')
out = self.run_bzr('pull -v source -d target')[0]
self.assertContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
self.assertNotContainsRe(
out, r'revno: 1\ncommitter: .*\nbranch nick: source')
def test_pull_smart_bound_branch(self):
self.setup_smart_server_with_call_log()
parent = self.make_branch_and_tree('parent')
parent.commit(message='first commit')
child = parent.bzrdir.sprout('child').open_workingtree()
child.commit(message='second commit')
checkout = parent.branch.create_checkout('checkout')
self.run_bzr(['pull', self.get_url('child')], working_dir='checkout')
def test_pull_smart_stacked_streaming_acceptance(self):
"""'bzr pull -r 123' works on stacked, smart branches, even when the
revision specified by the revno is only present in the fallback
repository.
See <https://launchpad.net/bugs/380314>
"""
self.setup_smart_server_with_call_log()
# Make a stacked-on branch with two commits so that the
# revision-history can't be determined just by looking at the parent
# field in the revision in the stacked repo.
parent = self.make_branch_and_tree('parent', format='1.9')
parent.commit(message='first commit')
parent.commit(message='second commit')
local = parent.bzrdir.sprout('local').open_workingtree()
local.commit(message='local commit')
local.branch.create_clone_on_transport(
self.get_transport('stacked'), stacked_on=self.get_url('parent'))
empty = self.make_branch_and_tree('empty', format='1.9')
self.reset_smart_call_log()
self.run_bzr(['pull', '-r', '1', self.get_url('stacked')],
working_dir='empty')
# This figure represent the amount of work to perform this use case. It
# is entirely ok to reduce this number if a test fails due to rpc_count
# being too low. If rpc_count increases, more network roundtrips have
# become necessary for this use case. Please do not adjust this number
# upwards without agreement from bzr's network support maintainers.
self.assertLength(19, self.hpss_calls)
remote = Branch.open('stacked')
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
def test_pull_cross_format_warning(self):
"""You get a warning for probably slow cross-format pulls.
"""
# this is assumed to be going through InterDifferingSerializer
from_tree = self.make_branch_and_tree('from', format='2a')
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
from_tree.commit(message='first commit')
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
self.assertContainsRe(err,
"(?m)Doing on-the-fly conversion")
def test_pull_cross_format_warning_no_IDS(self):
"""You get a warning for probably slow cross-format pulls.
"""
# this simulates what would happen across the network, where
# interdifferingserializer is not active
debug.debug_flags.add('IDS_never')
# TestCase take care of restoring them
from_tree = self.make_branch_and_tree('from', format='2a')
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
from_tree.commit(message='first commit')
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
self.assertContainsRe(err,
"(?m)Doing on-the-fly conversion")
def test_pull_cross_format_from_network(self):
self.setup_smart_server_with_call_log()
from_tree = self.make_branch_and_tree('from', format='2a')
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
self.assertIsInstance(from_tree.branch, remote.RemoteBranch)
from_tree.commit(message='first commit')
out, err = self.run_bzr(['pull', '-d', 'to',
from_tree.branch.bzrdir.root_transport.base])
self.assertContainsRe(err,
"(?m)Doing on-the-fly conversion")
def test_pull_to_experimental_format_warning(self):
"""You get a warning for pulling into experimental formats.
"""
from_tree = self.make_branch_and_tree('from', format='development-subtree')
to_tree = self.make_branch_and_tree('to', format='development-subtree')
from_tree.commit(message='first commit')
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
self.assertContainsRe(err,
"(?m)Fetching into experimental format")
def test_pull_cross_to_experimental_format_warning(self):
"""You get a warning for pulling into experimental formats.
"""
from_tree = self.make_branch_and_tree('from', format='2a')
to_tree = self.make_branch_and_tree('to', format='development-subtree')
from_tree.commit(message='first commit')
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
self.assertContainsRe(err,
"(?m)Fetching into experimental format")
def test_pull_show_base(self):
"""bzr pull supports --show-base
see https://bugs.launchpad.net/bzr/+bug/202374"""
# create two trees with conflicts, setup conflict, check that
# conflicted file looks correct
a_tree = self.example_branch('a')
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
f = open(pathjoin('a', 'hello'),'wt')
f.write('fee')
f.close()
a_tree.commit('fee')
f = open(pathjoin('b', 'hello'),'wt')
f.write('fie')
f.close()
out,err=self.run_bzr(['pull','-d','b','a','--show-base'])
# check for message here
self.assertEqual(err,
' M hello\nText conflict in hello\n1 conflicts encountered.\n')
self.assertEqualDiff('<<<<<<< TREE\n'
'fie||||||| BASE-REVISION\n'
'foo=======\n'
'fee>>>>>>> MERGE-SOURCE\n',
open(pathjoin('b', 'hello')).read())
def test_pull_show_base_working_tree_only(self):
"""--show-base only allowed if there's a working tree
see https://bugs.launchpad.net/bzr/+bug/202374"""
# create a branch, see that --show-base fails
self.make_branch('from')
self.make_branch('to')
out=self.run_bzr(['pull','-d','to','from','--show-base'],retcode=3)
self.assertEqual(out,
('','bzr: ERROR: Need working tree for --show-base.\n'))
def test_pull_tag_conflicts(self):
"""pulling tags with conflicts will change the exit code"""
# create a branch, see that --show-base fails
from_tree = self.make_branch_and_tree('from')
from_tree.branch.tags.set_tag("mytag", "somerevid")
to_tree = self.make_branch_and_tree('to')
to_tree.branch.tags.set_tag("mytag", "anotherrevid")
out = self.run_bzr(['pull','-d','to','from'],retcode=1)
self.assertEqual(out,
('No revisions to pull.\nConflicting tags:\n mytag\n', ''))
def test_pull_tag_notification(self):
"""pulling tags with conflicts will change the exit code"""
# create a branch, see that --show-base fails
from_tree = self.make_branch_and_tree('from')
from_tree.branch.tags.set_tag("mytag", "somerevid")
to_tree = self.make_branch_and_tree('to')
out = self.run_bzr(['pull', '-d', 'to', 'from'])
self.assertEqual(out,
('1 tag(s) updated.\n', ''))
def test_pull_tag_overwrite(self):
"""pulling tags with --overwrite only reports changed tags."""
# create a branch, see that --show-base fails
from_tree = self.make_branch_and_tree('from')
from_tree.branch.tags.set_tag("mytag", "somerevid")
to_tree = self.make_branch_and_tree('to')
to_tree.branch.tags.set_tag("mytag", "somerevid")
out = self.run_bzr(['pull', '--overwrite', '-d', 'to', 'from'])
self.assertEqual(out,
('No revisions or tags to pull.\n', ''))
class TestPullOutput(script.TestCaseWithTransportAndScript):
def test_pull_log_format(self):
self.run_script("""
$ bzr init trunk
Created a standalone tree (format: 2a)
$ cd trunk
$ echo foo > file
$ bzr add
adding file
$ bzr commit -m 'we need some foo'
2>Committing to:...trunk/
2>added file
2>Committed revision 1.
$ cd ..
$ bzr init feature
Created a standalone tree (format: 2a)
$ cd feature
$ bzr pull -v ../trunk -Olog_format=line
Now on revision 1.
Added Revisions:
1: jrandom@example.com ...we need some foo
2>+N file
2>All changes applied successfully.
""")
|