1
# Copyright (C) 2005 by Canonical Ltd
2
# -*- coding: utf-8 -*-
1
# Copyright (C) 2005-2010 Canonical Ltd
4
3
# This program is free software; you can redistribute it and/or modify
5
4
# it under the terms of the GNU General Public License as published by
6
5
# the Free Software Foundation; either version 2 of the License, or
7
6
# (at your option) any later version.
9
8
# This program is distributed in the hope that it will be useful,
10
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
11
# GNU General Public License for more details.
14
13
# You should have received a copy of the GNU General Public License
15
14
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
"""Black-box tests for bzr pull.
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
"""Black-box tests for bzr pull."""
25
29
from bzrlib.branch import Branch
26
from bzrlib.osutils import abspath
27
from bzrlib.tests.blackbox import ExternalBase
30
from bzrlib.directory_service import directories
31
from bzrlib.osutils import pathjoin
32
from bzrlib.tests import TestCaseWithTransport
28
33
from bzrlib.uncommit import uncommit
31
class TestPull(ExternalBase):
33
def example_branch(test):
35
file('hello', 'wt').write('foo')
36
test.runbzr('add hello')
37
test.runbzr('commit -m setup hello')
38
file('goodbye', 'wt').write('baz')
39
test.runbzr('add goodbye')
40
test.runbzr('commit -m setup goodbye')
34
from bzrlib.workingtree import WorkingTree
37
class TestPull(TestCaseWithTransport):
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')
42
50
def test_pull(self):
43
51
"""Pull changes from one branch to another."""
52
a_tree = self.example_branch('a')
48
self.runbzr('pull', retcode=3)
49
self.runbzr('missing', retcode=3)
50
self.runbzr('missing .')
51
self.runbzr('missing')
52
if sys.platform not in ('win32', 'cygwin'):
53
# This is equivalent to doing "bzr pull ."
54
# Which means that bzr creates 2 branches grabbing
55
# the same location, and tries to pull.
56
# However, 2 branches mean 2 locks on the same file
57
# which ultimately implies a deadlock.
58
# (non windows platforms allow multiple locks on the
59
# same file by the same calling process)
61
self.runbzr('pull /', retcode=3)
62
if sys.platform not in ('win32', 'cygwin'):
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'):
66
self.runbzr('branch a b')
66
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
70
self.runbzr('add subdir')
71
self.runbzr('commit -m blah --unchanged')
74
b = Branch.open('../b')
75
self.assertEquals(a.revision_history(), b.revision_history()[:-1])
76
self.runbzr('pull ../b')
77
self.assertEquals(a.revision_history(), b.revision_history())
78
self.runbzr('commit -m blah2 --unchanged')
80
self.runbzr('commit -m blah3 --unchanged')
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)
82
self.runbzr('pull ../a', retcode=3)
85
self.run_bzr('pull ../a', retcode=3)
84
self.runbzr('branch b overwriteme')
87
b_tree.bzrdir.sprout('overwriteme')
85
88
os.chdir('overwriteme')
86
self.runbzr('pull --overwrite ../a')
89
self.run_bzr('pull --overwrite ../a')
87
90
overwritten = Branch.open('.')
88
91
self.assertEqual(overwritten.revision_history(),
89
92
a.revision_history())
91
self.runbzr('merge ../b')
92
self.runbzr('commit -m blah4 --unchanged')
93
a_tree.merge_from_branch(b_tree.branch)
94
a_tree.commit(message="blah4", allow_pointless=True)
93
95
os.chdir('../b/subdir')
94
self.runbzr('pull ../../a')
95
self.assertEquals(a.revision_history()[-1], b.revision_history()[-1])
96
self.runbzr('commit -m blah5 --unchanged')
97
self.runbzr('commit -m blah6 --unchanged')
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)
99
self.runbzr('pull ../a')
102
self.run_bzr('pull ../a')
101
self.runbzr('commit -m blah7 --unchanged')
102
self.runbzr('merge ../b')
103
self.runbzr('commit -m blah8 --unchanged')
104
self.runbzr('pull ../b')
105
self.runbzr('pull ../b')
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'])
107
121
def test_pull_revision(self):
108
122
"""Pull some changes from one branch to another."""
112
self.example_branch()
113
file('hello2', 'wt').write('foo')
114
self.runbzr('add hello2')
115
self.runbzr('commit -m setup hello2')
116
file('goodbye2', 'wt').write('baz')
117
self.runbzr('add goodbye2')
118
self.runbzr('commit -m setup goodbye2')
121
self.runbzr('branch -r 1 a b')
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()
123
self.runbzr('pull -r 2')
135
self.run_bzr('pull -r 2')
124
136
a = Branch.open('../a')
125
137
b = Branch.open('.')
126
self.assertEquals(a.revno(),4)
127
self.assertEquals(b.revno(),2)
128
self.runbzr('pull -r 3')
129
self.assertEquals(b.revno(),3)
130
self.runbzr('pull -r 4')
131
self.assertEquals(a.revision_history(), b.revision_history())
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())
145
def test_pull_tags(self):
146
"""Tags are updated by pull, and revisions named in those tags are
149
# Make a source, sprout a target off it
150
builder = self.make_branch_builder('source')
151
builder.build_commit(message="Rev 1", rev_id='rev-1')
152
source = builder.get_branch()
153
target_bzrdir = source.bzrdir.sprout('target')
154
# Add a non-ancestry tag to source
155
builder.build_commit(message="Rev 2", rev_id='rev-2')
156
source.tags.set_tag('tag-a', 'rev-2')
157
source.set_last_revision_info(1, 'rev-1')
159
self.run_bzr('pull -d target source')
160
target = target_bzrdir.open_branch()
161
# The tag is present, and so is its revision.
162
self.assertEqual('rev-2', target.tags.lookup_tag('tag-a'))
163
target.repository.get_revision('rev-2')
134
165
def test_overwrite_uptodate(self):
135
166
# Make sure pull --overwrite overwrites
136
167
# even if the target branch has merged
137
168
# everything already.
140
def get_rh(expected_len):
141
rh = self.capture('revision-history')
142
# Make sure we don't have trailing empty revisions
143
rh = rh.strip().split('\n')
144
self.assertEqual(len(rh), expected_len)
150
open('foo', 'wb').write('original\n')
152
bzr('commit', '-m', 'initial commit')
155
bzr('branch', 'a', 'b')
158
open('foo', 'wb').write('changed\n')
159
bzr('commit', '-m', 'later change')
161
open('foo', 'wb').write('another\n')
162
bzr('commit', '-m', 'a third change')
164
rev_history_a = get_rh(3)
168
bzr('commit', '-m', 'merge')
170
rev_history_b = get_rh(2)
172
bzr('pull', '--overwrite', '../a')
173
rev_history_b = get_rh(3)
169
a_tree = self.make_branch_and_tree('a')
170
self.build_tree_contents([('a/foo', 'original\n')])
172
a_tree.commit(message='initial commit')
174
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
176
self.build_tree_contents([('a/foo', 'changed\n')])
177
a_tree.commit(message='later change')
179
self.build_tree_contents([('a/foo', 'a third change')])
180
a_tree.commit(message='a third change')
182
rev_history_a = a_tree.branch.revision_history()
183
self.assertEqual(len(rev_history_a), 3)
185
b_tree.merge_from_branch(a_tree.branch)
186
b_tree.commit(message='merge')
188
self.assertEqual(len(b_tree.branch.revision_history()), 2)
191
self.run_bzr('pull --overwrite ../a')
192
rev_history_b = b_tree.branch.revision_history()
193
self.assertEqual(len(rev_history_b), 3)
175
195
self.assertEqual(rev_history_b, rev_history_a)
177
197
def test_overwrite_children(self):
178
198
# Make sure pull --overwrite sets the revision-history
179
199
# to be identical to the pull source, even if we have convergence
182
def get_rh(expected_len):
183
rh = self.capture('revision-history')
184
# Make sure we don't have trailing empty revisions
185
rh = rh.strip().split('\n')
186
self.assertEqual(len(rh), expected_len)
192
open('foo', 'wb').write('original\n')
194
bzr('commit', '-m', 'initial commit')
197
bzr('branch', 'a', 'b')
200
open('foo', 'wb').write('changed\n')
201
bzr('commit', '-m', 'later change')
203
open('foo', 'wb').write('another\n')
204
bzr('commit', '-m', 'a third change')
206
rev_history_a = get_rh(3)
210
bzr('commit', '-m', 'merge')
212
rev_history_b = get_rh(2)
215
open('foo', 'wb').write('a fourth change\n')
216
bzr('commit', '-m', 'a fourth change')
218
rev_history_a = get_rh(4)
200
a_tree = self.make_branch_and_tree('a')
201
self.build_tree_contents([('a/foo', 'original\n')])
203
a_tree.commit(message='initial commit')
205
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
207
self.build_tree_contents([('a/foo', 'changed\n')])
208
a_tree.commit(message='later change')
210
self.build_tree_contents([('a/foo', 'a third change')])
211
a_tree.commit(message='a third change')
213
self.assertEqual(len(a_tree.branch.revision_history()), 3)
215
b_tree.merge_from_branch(a_tree.branch)
216
b_tree.commit(message='merge')
218
self.assertEqual(len(b_tree.branch.revision_history()), 2)
220
self.build_tree_contents([('a/foo', 'a fourth change\n')])
221
a_tree.commit(message='a fourth change')
223
rev_history_a = a_tree.branch.revision_history()
224
self.assertEqual(len(rev_history_a), 4)
220
226
# With convergence, we could just pull over the
221
227
# new change, but with --overwrite, we want to switch our history
223
bzr('pull', '--overwrite', '../a')
224
rev_history_b = get_rh(4)
229
self.run_bzr('pull --overwrite ../a')
230
rev_history_b = b_tree.branch.revision_history()
231
self.assertEqual(len(rev_history_b), 4)
226
233
self.assertEqual(rev_history_b, rev_history_a)
246
253
self.assertEqual(None, branch_b.get_parent())
247
254
# test pull for failure without parent set
248
255
os.chdir('branch_b')
249
out = self.runbzr('pull', retcode=3)
250
self.assertEquals(out,
256
out = self.run_bzr('pull', retcode=3)
257
self.assertEqual(out,
251
258
('','bzr: ERROR: No pull location known or specified.\n'))
252
259
# test implicit --remember when no parent set, this pull conflicts
253
260
self.build_tree(['d'])
255
262
tree_b.commit('commit d')
256
out = self.runbzr('pull ../branch_a', retcode=3)
257
self.assertEquals(out,
258
('','bzr: ERROR: These branches have diverged. Try merge.\n'))
259
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
263
out = self.run_bzr('pull ../branch_a', retcode=3)
264
self.assertEqual(out,
265
('','bzr: ERROR: These branches have diverged.'
266
' Use the missing command to see how.\n'
267
'Use the merge command to reconcile them.\n'))
268
self.assertEqual(branch_b.get_parent(), parent)
260
269
# test implicit --remember after resolving previous failure
261
270
uncommit(branch=branch_b, tree=tree_b)
262
271
transport.delete('branch_b/d')
264
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
273
self.assertEqual(branch_b.get_parent(), parent)
265
274
# test explicit --remember
266
self.runbzr('pull ../branch_c --remember')
267
self.assertEquals(abspath(branch_b.get_parent()),
268
abspath(branch_c.bzrdir.root_transport.base))
275
self.run_bzr('pull ../branch_c --remember')
276
self.assertEqual(branch_b.get_parent(),
277
branch_c.bzrdir.root_transport.base)
279
def test_pull_bundle(self):
280
from bzrlib.testament import Testament
281
# Build up 2 trees and prepare for a pull
282
tree_a = self.make_branch_and_tree('branch_a')
283
f = open('branch_a/a', 'wb')
287
tree_a.commit('message')
289
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
291
# Make a change to 'a' that 'b' can pull
292
f = open('branch_a/a', 'wb')
295
tree_a.commit('message')
297
# Create the bundle for 'b' to pull
299
self.run_bzr('bundle ../branch_b -o ../bundle')
301
os.chdir('../branch_b')
302
out, err = self.run_bzr('pull ../bundle')
303
self.assertEqual(out,
304
'Now on revision 2.\n')
305
self.assertEqual(err,
306
' M a\nAll changes applied successfully.\n')
308
self.assertEqualDiff(tree_a.branch.revision_history(),
309
tree_b.branch.revision_history())
311
testament_a = Testament.from_revision(tree_a.branch.repository,
312
tree_a.get_parent_ids()[0])
313
testament_b = Testament.from_revision(tree_b.branch.repository,
314
tree_b.get_parent_ids()[0])
315
self.assertEqualDiff(testament_a.as_text(),
316
testament_b.as_text())
318
# it is legal to attempt to pull an already-merged bundle
319
out, err = self.run_bzr('pull ../bundle')
320
self.assertEqual(err, '')
321
self.assertEqual(out, 'No revisions to pull.\n')
323
def test_pull_verbose_no_files(self):
324
"""Pull --verbose should not list modified files"""
325
tree_a = self.make_branch_and_tree('tree_a')
326
self.build_tree(['tree_a/foo'])
329
tree_b = self.make_branch_and_tree('tree_b')
330
out = self.run_bzr('pull --verbose -d tree_b tree_a')[0]
331
self.assertContainsRe(out, 'bar')
332
self.assertNotContainsRe(out, 'added:')
333
self.assertNotContainsRe(out, 'foo')
335
def test_pull_quiet(self):
336
"""Check that bzr pull --quiet does not print anything"""
337
tree_a = self.make_branch_and_tree('tree_a')
338
self.build_tree(['tree_a/foo'])
340
revision_id = tree_a.commit('bar')
341
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
342
out, err = self.run_bzr('pull --quiet -d tree_b')
343
self.assertEqual(out, '')
344
self.assertEqual(err, '')
345
self.assertEqual(tree_b.last_revision(), revision_id)
346
self.build_tree(['tree_a/moo'])
348
revision_id = tree_a.commit('quack')
349
out, err = self.run_bzr('pull --quiet -d tree_b')
350
self.assertEqual(out, '')
351
self.assertEqual(err, '')
352
self.assertEqual(tree_b.last_revision(), revision_id)
354
def test_pull_from_directory_service(self):
355
source = self.make_branch_and_tree('source')
356
source.commit('commit 1')
357
target = source.bzrdir.sprout('target').open_workingtree()
358
source_last = source.commit('commit 2')
359
class FooService(object):
360
"""A directory service that always returns source"""
362
def look_up(self, name, url):
364
directories.register('foo:', FooService, 'Testing directory service')
365
self.addCleanup(directories.remove, 'foo:')
366
self.run_bzr('pull foo:bar -d target')
367
self.assertEqual(source_last, target.last_revision())
369
def test_pull_verbose_defaults_to_long(self):
370
tree = self.example_branch('source')
371
target = self.make_branch_and_tree('target')
372
out = self.run_bzr('pull -v source -d target')[0]
373
self.assertContainsRe(out,
374
r'revno: 1\ncommitter: .*\nbranch nick: source')
375
self.assertNotContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
377
def test_pull_verbose_uses_default_log(self):
378
tree = self.example_branch('source')
379
target = self.make_branch_and_tree('target')
380
target_config = target.branch.get_config()
381
target_config.set_user_option('log_format', 'short')
382
out = self.run_bzr('pull -v source -d target')[0]
383
self.assertContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
384
self.assertNotContainsRe(
385
out, r'revno: 1\ncommitter: .*\nbranch nick: source')
387
def test_pull_smart_stacked_streaming_acceptance(self):
388
"""'bzr pull -r 123' works on stacked, smart branches, even when the
389
revision specified by the revno is only present in the fallback
392
See <https://launchpad.net/bugs/380314>
394
self.setup_smart_server_with_call_log()
395
# Make a stacked-on branch with two commits so that the
396
# revision-history can't be determined just by looking at the parent
397
# field in the revision in the stacked repo.
398
parent = self.make_branch_and_tree('parent', format='1.9')
399
parent.commit(message='first commit')
400
parent.commit(message='second commit')
401
local = parent.bzrdir.sprout('local').open_workingtree()
402
local.commit(message='local commit')
403
local.branch.create_clone_on_transport(
404
self.get_transport('stacked'), stacked_on=self.get_url('parent'))
405
empty = self.make_branch_and_tree('empty', format='1.9')
406
self.reset_smart_call_log()
407
self.run_bzr(['pull', '-r', '1', self.get_url('stacked')],
409
# This figure represent the amount of work to perform this use case. It
410
# is entirely ok to reduce this number if a test fails due to rpc_count
411
# being too low. If rpc_count increases, more network roundtrips have
412
# become necessary for this use case. Please do not adjust this number
413
# upwards without agreement from bzr's network support maintainers.
414
self.assertLength(18, self.hpss_calls)
415
remote = Branch.open('stacked')
416
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
418
def test_pull_cross_format_warning(self):
419
"""You get a warning for probably slow cross-format pulls.
421
# this is assumed to be going through InterDifferingSerializer
422
from_tree = self.make_branch_and_tree('from', format='2a')
423
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
424
from_tree.commit(message='first commit')
425
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
426
self.assertContainsRe(err,
427
"(?m)Doing on-the-fly conversion")
429
def test_pull_cross_format_warning_no_IDS(self):
430
"""You get a warning for probably slow cross-format pulls.
432
# this simulates what would happen across the network, where
433
# interdifferingserializer is not active
435
debug.debug_flags.add('IDS_never')
436
# TestCase take care of restoring them
438
from_tree = self.make_branch_and_tree('from', format='2a')
439
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
440
from_tree.commit(message='first commit')
441
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
442
self.assertContainsRe(err,
443
"(?m)Doing on-the-fly conversion")
445
def test_pull_cross_format_from_network(self):
446
self.setup_smart_server_with_call_log()
447
from_tree = self.make_branch_and_tree('from', format='2a')
448
to_tree = self.make_branch_and_tree('to', format='1.14-rich-root')
449
self.assertIsInstance(from_tree.branch, remote.RemoteBranch)
450
from_tree.commit(message='first commit')
451
out, err = self.run_bzr(['pull', '-d', 'to',
452
from_tree.branch.bzrdir.root_transport.base])
453
self.assertContainsRe(err,
454
"(?m)Doing on-the-fly conversion")
456
def test_pull_to_experimental_format_warning(self):
457
"""You get a warning for pulling into experimental formats.
459
from_tree = self.make_branch_and_tree('from', format='development-subtree')
460
to_tree = self.make_branch_and_tree('to', format='development-subtree')
461
from_tree.commit(message='first commit')
462
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
463
self.assertContainsRe(err,
464
"(?m)Fetching into experimental format")
466
def test_pull_cross_to_experimental_format_warning(self):
467
"""You get a warning for pulling into experimental formats.
469
from_tree = self.make_branch_and_tree('from', format='2a')
470
to_tree = self.make_branch_and_tree('to', format='development-subtree')
471
from_tree.commit(message='first commit')
472
out, err = self.run_bzr(['pull', '-d', 'to', 'from'])
473
self.assertContainsRe(err,
474
"(?m)Fetching into experimental format")
476
def test_pull_show_base(self):
477
"""bzr pull supports --show-base
479
see https://bugs.launchpad.net/bzr/+bug/202374"""
480
# create two trees with conflicts, setup conflict, check that
481
# conflicted file looks correct
482
a_tree = self.example_branch('a')
483
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
485
f = open(pathjoin('a', 'hello'),'wt')
490
f = open(pathjoin('b', 'hello'),'wt')
494
out,err=self.run_bzr(['pull','-d','b','a','--show-base'])
496
# check for message here
497
self.assertEqual(err,
498
' M hello\nText conflict in hello\n1 conflicts encountered.\n')
500
self.assertEqualDiff('<<<<<<< TREE\n'
501
'fie||||||| BASE-REVISION\n'
503
'fee>>>>>>> MERGE-SOURCE\n',
504
open(pathjoin('b', 'hello')).read())
506
def test_pull_show_base_working_tree_only(self):
507
"""--show-base only allowed if there's a working tree
509
see https://bugs.launchpad.net/bzr/+bug/202374"""
510
# create a branch, see that --show-base fails
511
self.make_branch('from')
512
self.make_branch('to')
513
out=self.run_bzr(['pull','-d','to','from','--show-base'],retcode=3)
514
self.assertEqual(out,
515
('','bzr: ERROR: Need working tree for --show-base.\n'))
517
def test_pull_tag_conflicts(self):
518
"""pulling tags with conflicts will change the exit code"""
519
# create a branch, see that --show-base fails
520
from_tree = self.make_branch_and_tree('from')
521
from_tree.branch.tags.set_tag("mytag", "somerevid")
522
to_tree = self.make_branch_and_tree('to')
523
to_tree.branch.tags.set_tag("mytag", "anotherrevid")
524
out = self.run_bzr(['pull','-d','to','from'],retcode=1)
525
self.assertEqual(out,
526
('No revisions to pull.\nConflicting tags:\n mytag\n', ''))