13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
# Author: Aaron Bentley <aaron.bentley@utoronto.ca>
19
19
"""Black-box tests for bzr merge.
24
from bzrlib.branch import Branch
25
from bzrlib.bzrdir import BzrDir
26
from bzrlib.osutils import abspath
27
from bzrlib.tests.blackbox import ExternalBase
28
from bzrlib.workingtree import WorkingTree
31
class TestMerge(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')
25
from testtools import matchers
37
from bzrlib.tests import (
43
load_tests = scenarios.load_tests_apply_scenarios
46
class TestMerge(tests.TestCaseWithTransport):
48
def example_branch(self, path='.'):
49
tree = self.make_branch_and_tree(path)
50
self.build_tree_contents([
51
(osutils.pathjoin(path, 'hello'), 'foo'),
52
(osutils.pathjoin(path, 'goodbye'), 'baz')])
54
tree.commit(message='setup')
56
tree.commit(message='setup')
59
def create_conflicting_branches(self):
60
"""Create two branches which have overlapping modifications.
62
:return: (tree, other_branch) Where merging other_branch causes a file
65
builder = self.make_branch_builder('branch')
66
builder.build_snapshot('rev1', None,
67
[('add', ('', 'root-id', 'directory', None)),
68
('add', ('fname', 'f-id', 'file', 'a\nb\nc\n'))])
69
builder.build_snapshot('rev2other', ['rev1'],
70
[('modify', ('f-id', 'a\nB\nD\n'))])
71
other = builder.get_branch().bzrdir.sprout('other').open_branch()
72
builder.build_snapshot('rev2this', ['rev1'],
73
[('modify', ('f-id', 'a\nB\nC\n'))])
74
tree = builder.get_branch().create_checkout('tree', lightweight=True)
42
77
def test_merge_reprocess(self):
43
d = BzrDir.create_standalone_workingtree('.')
78
d = controldir.ControlDir.create_standalone_workingtree('.')
45
self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
80
self.run_bzr('merge . --reprocess --merge-type weave')
47
82
def test_merge(self):
48
from bzrlib.branch import Branch
54
self.runbzr('branch a b')
56
file('goodbye', 'wt').write('quux')
57
self.runbzr(['commit', '-m', "more u's are always good"])
83
a_tree = self.example_branch('a')
84
ancestor = a_tree.branch.revno()
85
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
86
self.build_tree_contents([('b/goodbye', 'quux')])
87
b_tree.commit(message="more u's are always good")
60
file('hello', 'wt').write('quuux')
89
self.build_tree_contents([('a/hello', 'quuux')])
61
90
# We can't merge when there are in-tree changes
62
self.runbzr('merge ../b', retcode=3)
63
self.runbzr(['commit', '-m', "Like an epidemic of u's"])
64
self.runbzr('merge ../b -r last:1..last:1 --merge-type blooof',
66
self.runbzr('merge ../b -r last:1..last:1 --merge-type merge3')
67
self.runbzr('revert --no-backup')
68
self.runbzr('merge ../b -r last:1..last:1 --merge-type weave')
69
self.runbzr('revert --no-backup')
70
self.runbzr('merge ../b -r last:1..last:1 --reprocess')
71
self.runbzr('revert --no-backup')
72
self.runbzr('merge ../b -r last:1')
73
self.check_file_contents('goodbye', 'quux')
91
self.run_bzr('merge ../b', retcode=3, working_dir='a')
92
a = workingtree.WorkingTree.open('a')
93
a_tip = a.commit("Like an epidemic of u's")
95
def run_merge_then_revert(args, retcode=None, working_dir='a'):
96
self.run_bzr(['merge', '../b', '-r', 'last:1..last:1'] + args,
97
retcode=retcode, working_dir=working_dir)
99
a_tree.revert(backups=False)
101
run_merge_then_revert(['--merge-type', 'bloof'], retcode=3)
102
run_merge_then_revert(['--merge-type', 'merge3'])
103
run_merge_then_revert(['--merge-type', 'weave'])
104
run_merge_then_revert(['--merge-type', 'lca'])
105
self.run_bzr_error(['Show-base is not supported for this merge type'],
106
'merge ../b -r last:1..last:1 --merge-type weave'
107
' --show-base', working_dir='a')
108
a_tree.revert(backups=False)
109
self.run_bzr('merge ../b -r last:1..last:1 --reprocess',
111
a_tree.revert(backups=False)
112
self.run_bzr('merge ../b -r last:1', working_dir='a')
113
self.check_file_contents('a/goodbye', 'quux')
74
114
# Merging a branch pulls its revision into the tree
75
a = WorkingTree.open('.')
76
b = Branch.open('../b')
77
a.branch.repository.get_revision_xml(b.last_revision())
78
self.log('pending merges: %s', a.pending_merges())
79
self.assertEquals(a.pending_merges(),
81
self.runbzr('commit -m merged')
82
self.runbzr('merge ../b -r last:1')
83
self.assertEqual(a.pending_merges(), [])
115
b = branch.Branch.open('b')
116
b_tip = b.last_revision()
117
self.assertTrue(a.branch.repository.has_revision(b_tip))
118
self.assertEqual([a_tip, b_tip], a.get_parent_ids())
119
a_tree.revert(backups=False)
120
out, err = self.run_bzr('merge -r revno:1:./hello', retcode=3,
122
self.assertTrue("Not a branch" in err)
123
self.run_bzr('merge -r revno:%d:./..revno:%d:../b'
124
%(ancestor,b.revno()), working_dir='a')
125
self.assertEquals(a.get_parent_ids(),
126
[a.branch.last_revision(), b.last_revision()])
127
self.check_file_contents('a/goodbye', 'quux')
128
a_tree.revert(backups=False)
129
self.run_bzr('merge -r revno:%d:../b'%b.revno(), working_dir='a')
130
self.assertEquals(a.get_parent_ids(),
131
[a.branch.last_revision(), b.last_revision()])
132
a_tip = a.commit('merged')
133
self.run_bzr('merge ../b -r last:1', working_dir='a')
134
self.assertEqual([a_tip], a.get_parent_ids())
136
def test_merge_defaults_to_reprocess(self):
137
tree, other = self.create_conflicting_branches()
138
# The default merge algorithm should enable 'reprocess' because
139
# 'show-base' is not set
140
self.run_bzr('merge ../other', working_dir='tree',
142
self.assertEqualDiff('a\n'
148
'>>>>>>> MERGE-SOURCE\n',
149
tree.get_file_text('f-id'))
151
def test_merge_explicit_reprocess_show_base(self):
152
tree, other = self.create_conflicting_branches()
153
# Explicitly setting --reprocess, and --show-base is an error
154
self.run_bzr_error(['Cannot do conflict reduction and show base'],
155
'merge ../other --reprocess --show-base',
158
def test_merge_override_reprocess(self):
159
tree, other = self.create_conflicting_branches()
160
# Explicitly disable reprocess
161
self.run_bzr('merge ../other --no-reprocess', working_dir='tree',
163
self.assertEqualDiff('a\n'
170
'>>>>>>> MERGE-SOURCE\n',
171
tree.get_file_text('f-id'))
173
def test_merge_override_show_base(self):
174
tree, other = self.create_conflicting_branches()
175
# Setting '--show-base' will auto-disable '--reprocess'
176
self.run_bzr('merge ../other --show-base', working_dir='tree',
178
self.assertEqualDiff('a\n'
182
'||||||| BASE-REVISION\n'
188
'>>>>>>> MERGE-SOURCE\n',
189
tree.get_file_text('f-id'))
85
191
def test_merge_with_missing_file(self):
86
192
"""Merge handles missing file conflicts"""
90
print >> file('sub/a.txt', 'wb'), "hello"
91
print >> file('b.txt', 'wb'), "hello"
92
print >> file('sub/c.txt', 'wb'), "hello"
95
self.runbzr(('commit', '-m', 'added a'))
96
self.runbzr('branch . ../b')
97
print >> file('sub/a.txt', 'ab'), "there"
98
print >> file('b.txt', 'ab'), "there"
99
print >> file('sub/c.txt', 'ab'), "there"
100
self.runbzr(('commit', '-m', 'Added there'))
101
os.unlink('sub/a.txt')
102
os.unlink('sub/c.txt')
105
self.runbzr(('commit', '-m', 'Removed a.txt'))
107
print >> file('sub/a.txt', 'ab'), "something"
108
print >> file('b.txt', 'ab'), "something"
109
print >> file('sub/c.txt', 'ab'), "something"
110
self.runbzr(('commit', '-m', 'Modified a.txt'))
111
self.runbzr('merge ../a/', retcode=1)
112
self.assert_(os.path.exists('sub/a.txt.THIS'))
113
self.assert_(os.path.exists('sub/a.txt.BASE'))
115
self.runbzr('merge ../b/', retcode=1)
116
self.assert_(os.path.exists('sub/a.txt.OTHER'))
117
self.assert_(os.path.exists('sub/a.txt.BASE'))
193
self.build_tree_contents([
196
('a/sub/a.txt', 'hello\n'),
197
('a/b.txt', 'hello\n'),
198
('a/sub/c.txt', 'hello\n')])
199
a_tree = self.make_branch_and_tree('a')
200
a_tree.add(['sub', 'b.txt', 'sub/c.txt', 'sub/a.txt'])
201
a_tree.commit(message='added a')
202
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
203
self.build_tree_contents([
204
('a/sub/a.txt', 'hello\nthere\n'),
205
('a/b.txt', 'hello\nthere\n'),
206
('a/sub/c.txt', 'hello\nthere\n')])
207
a_tree.commit(message='Added there')
208
os.remove('a/sub/a.txt')
209
os.remove('a/sub/c.txt')
212
a_tree.commit(message='Removed a.txt')
213
self.build_tree_contents([
214
('b/sub/a.txt', 'hello\nsomething\n'),
215
('b/b.txt', 'hello\nsomething\n'),
216
('b/sub/c.txt', 'hello\nsomething\n')])
217
b_tree.commit(message='Modified a.txt')
219
self.run_bzr('merge ../a/', retcode=1, working_dir='b')
220
self.assertPathExists('b/sub/a.txt.THIS')
221
self.assertPathExists('b/sub/a.txt.BASE')
223
self.run_bzr('merge ../b/', retcode=1, working_dir='a')
224
self.assertPathExists('a/sub/a.txt.OTHER')
225
self.assertPathExists('a/sub/a.txt.BASE')
227
def test_conflict_leaves_base_this_other_files(self):
228
tree, other = self.create_conflicting_branches()
229
self.run_bzr('merge ../other', working_dir='tree',
231
self.assertFileEqual('a\nb\nc\n', 'tree/fname.BASE')
232
self.assertFileEqual('a\nB\nD\n', 'tree/fname.OTHER')
233
self.assertFileEqual('a\nB\nC\n', 'tree/fname.THIS')
235
def test_weave_conflict_leaves_base_this_other_files(self):
236
tree, other = self.create_conflicting_branches()
237
self.run_bzr('merge ../other --weave', working_dir='tree',
239
self.assertFileEqual('a\nb\nc\n', 'tree/fname.BASE')
240
self.assertFileEqual('a\nB\nD\n', 'tree/fname.OTHER')
241
self.assertFileEqual('a\nB\nC\n', 'tree/fname.THIS')
119
243
def test_merge_remember(self):
120
"""Merge changes from one branch to another and test parent location."""
244
"""Merge changes from one branch to another, test submit location."""
121
245
tree_a = self.make_branch_and_tree('branch_a')
122
246
branch_a = tree_a.branch
123
247
self.build_tree(['branch_a/a'])
138
262
branch_b.set_parent(None)
139
263
self.assertEqual(None, branch_b.get_parent())
140
264
# test merge for failure without parent set
142
out = self.runbzr('merge', retcode=3)
265
out = self.run_bzr('merge', retcode=3, working_dir='branch_b')
143
266
self.assertEquals(out,
144
('','bzr: ERROR: No merge branch known or specified.\n'))
145
# test implicit --remember when no parent set, this merge conflicts
146
self.build_tree(['d'])
267
('','bzr: ERROR: No location specified or remembered\n'))
269
# test uncommitted changes
270
self.build_tree(['branch_b/d'])
148
out = self.runbzr('merge ../branch_a', retcode=3)
149
self.assertEquals(out,
150
('','bzr: ERROR: Working tree has uncommitted changes.\n'))
151
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
152
# test implicit --remember after resolving conflict
272
self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
273
'merge', working_dir='branch_b')
275
# merge should now pass and implicitly remember merge location
153
276
tree_b.commit('commit d')
154
out, err = self.runbzr('merge')
155
self.assertEquals(out, 'Using saved branch: ../branch_a\n')
156
self.assertEquals(err, 'All changes applied successfully.\n')
157
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
158
# re-open tree as external runbzr modified it
277
out, err = self.run_bzr('merge ../branch_a', working_dir='branch_b')
279
base = urlutils.local_path_from_url(branch_a.base)
280
self.assertEndsWith(err, '+N b\nAll changes applied successfully.\n')
281
# re-open branch as external run_bzr modified it
282
branch_b = branch_b.bzrdir.open_branch()
283
self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
284
osutils.abspath(parent))
285
# test implicit --remember when committing new file
286
self.build_tree(['branch_b/e'])
288
tree_b.commit('commit e')
289
out, err = self.run_bzr('merge', working_dir='branch_b')
290
self.assertStartsWith(
291
err, 'Merging from remembered submit location %s\n' % (base,))
292
# re-open tree as external run_bzr modified it
159
293
tree_b = branch_b.bzrdir.open_workingtree()
160
294
tree_b.commit('merge branch_a')
161
295
# test explicit --remember
162
out, err = self.runbzr('merge ../branch_c --remember')
296
out, err = self.run_bzr('merge ../branch_c --remember',
297
working_dir='branch_b')
163
298
self.assertEquals(out, '')
164
self.assertEquals(err, 'All changes applied successfully.\n')
165
self.assertEquals(abspath(branch_b.get_parent()),
166
abspath(branch_c.bzrdir.root_transport.base))
167
# re-open tree as external runbzr modified it
299
self.assertEquals(err, '+N c\nAll changes applied successfully.\n')
300
# re-open branch as external run_bzr modified it
301
branch_b = branch_b.bzrdir.open_branch()
302
self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
303
osutils.abspath(branch_c.bzrdir.root_transport.base))
304
# re-open tree as external run_bzr modified it
168
305
tree_b = branch_b.bzrdir.open_workingtree()
169
306
tree_b.commit('merge branch_c')
308
def test_merge_bundle(self):
309
from bzrlib.testament import Testament
310
tree_a = self.make_branch_and_tree('branch_a')
311
self.build_tree_contents([('branch_a/a', 'hello')])
313
tree_a.commit('message')
315
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
316
self.build_tree_contents([('branch_a/a', 'hey there')])
317
tree_a.commit('message')
319
self.build_tree_contents([('branch_b/a', 'goodbye')])
320
tree_b.commit('message')
321
self.run_bzr('bundle ../branch_a -o ../bundle', working_dir='branch_b')
322
self.run_bzr('merge ../bundle', retcode=1, working_dir='branch_a')
323
testament_a = Testament.from_revision(tree_a.branch.repository,
324
tree_b.get_parent_ids()[0])
325
testament_b = Testament.from_revision(tree_b.branch.repository,
326
tree_b.get_parent_ids()[0])
327
self.assertEqualDiff(testament_a.as_text(),
328
testament_b.as_text())
329
tree_a.set_conflicts(conflicts.ConflictList())
330
tree_a.commit('message')
331
# it is legal to attempt to merge an already-merged bundle
332
err = self.run_bzr('merge ../bundle', working_dir='branch_a')[1]
333
# but it does nothing
334
self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
335
self.assertEqual('Nothing to do.\n', err)
337
def test_merge_uncommitted(self):
338
"""Check that merge --uncommitted behaves properly"""
339
tree_a = self.make_branch_and_tree('a')
340
self.build_tree(['a/file_1', 'a/file_2'])
341
tree_a.add(['file_1', 'file_2'])
342
tree_a.commit('commit 1')
343
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
344
self.assertPathExists('b/file_1')
345
tree_a.rename_one('file_1', 'file_i')
346
tree_a.commit('commit 2')
347
tree_a.rename_one('file_2', 'file_ii')
348
self.run_bzr('merge a --uncommitted -d b')
349
self.assertPathExists('b/file_1')
350
self.assertPathExists('b/file_ii')
352
self.run_bzr_error(('Cannot use --uncommitted and --revision',),
353
'merge /a --uncommitted -r1 -d b')
355
def test_merge_uncommitted_file(self):
356
"""It should be possible to merge changes from a single file."""
357
tree_a = self.make_branch_and_tree('tree_a')
358
tree_a.commit('initial commit')
359
tree_a.bzrdir.sprout('tree_b')
360
self.build_tree(['tree_a/file1', 'tree_a/file2'])
361
tree_a.add(['file1', 'file2'])
362
self.run_bzr(['merge', '--uncommitted', '../tree_a/file1'],
363
working_dir='tree_b')
364
self.assertPathExists('tree_b/file1')
365
self.assertPathDoesNotExist('tree_b/file2')
367
def test_merge_nonexistent_file(self):
368
"""It should not be possible to merge changes from a file which
370
tree_a = self.make_branch_and_tree('tree_a')
371
self.build_tree_contents([('tree_a/file', 'bar\n')])
373
tree_a.commit('commit 1')
374
self.run_bzr_error(('Path\(s\) do not exist: non/existing',),
375
['merge', 'non/existing'], working_dir='tree_a')
377
def pullable_branch(self):
378
tree_a = self.make_branch_and_tree('a')
379
self.build_tree_contents([('a/file', 'bar\n')])
381
self.id1 = tree_a.commit('commit 1')
383
tree_b = self.make_branch_and_tree('b')
384
tree_b.pull(tree_a.branch)
385
self.build_tree_contents([('b/file', 'foo\n')])
386
self.id2 = tree_b.commit('commit 2')
388
def test_merge_pull(self):
389
self.pullable_branch()
390
(out, err) = self.run_bzr('merge --pull ../b', working_dir='a')
391
self.assertContainsRe(out, 'Now on revision 2\\.')
392
tree_a = workingtree.WorkingTree.open('a')
393
self.assertEqual([self.id2], tree_a.get_parent_ids())
395
def test_merge_pull_preview(self):
396
self.pullable_branch()
397
(out, err) = self.run_bzr('merge --pull --preview -d a b')
398
self.assertThat(out, matchers.DocTestMatches(
399
"""=== modified file 'file'
406
""", doctest.ELLIPSIS | doctest.REPORT_UDIFF))
407
tree_a = workingtree.WorkingTree.open('a')
408
self.assertEqual([self.id1], tree_a.get_parent_ids())
410
def test_merge_kind_change(self):
411
tree_a = self.make_branch_and_tree('tree_a')
412
self.build_tree_contents([('tree_a/file', 'content_1')])
413
tree_a.add('file', 'file-id')
414
tree_a.commit('added file')
415
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
416
os.unlink('tree_a/file')
417
self.build_tree(['tree_a/file/'])
418
tree_a.commit('changed file to directory')
419
self.run_bzr('merge ../tree_a', working_dir='tree_b')
420
self.assertEqual('directory', osutils.file_kind('tree_b/file'))
422
self.assertEqual('file', osutils.file_kind('tree_b/file'))
423
self.build_tree_contents([('tree_b/file', 'content_2')])
424
tree_b.commit('content change')
425
self.run_bzr('merge ../tree_a', retcode=1, working_dir='tree_b')
426
self.assertEqual(tree_b.conflicts(),
427
[conflicts.ContentsConflict('file',
430
def test_directive_cherrypick(self):
431
source = self.make_branch_and_tree('source')
432
source.commit("nothing")
433
# see https://bugs.launchpad.net/bzr/+bug/409688 - trying to
434
# cherrypick from one branch into another unrelated branch with a
435
# different root id will give shape conflicts. as a workaround we
436
# make sure they share the same root id.
437
target = source.bzrdir.sprout('target').open_workingtree()
438
self.build_tree(['source/a'])
440
source.commit('Added a', rev_id='rev1')
441
self.build_tree(['source/b'])
443
source.commit('Added b', rev_id='rev2')
444
target.commit('empty commit')
445
self.write_directive('directive', source.branch, 'target', 'rev2',
447
out, err = self.run_bzr('merge -d target directive')
448
self.assertPathDoesNotExist('target/a')
449
self.assertPathExists('target/b')
450
self.assertContainsRe(err, 'Performing cherrypick')
452
def write_directive(self, filename, source, target, revision_id,
453
base_revision_id=None, mangle_patch=False):
454
md = merge_directive.MergeDirective2.from_objects(
455
source.repository, revision_id, 0, 0, target,
456
base_revision_id=base_revision_id)
459
self.build_tree_contents([(filename, ''.join(md.to_lines()))])
461
def test_directive_verify_warning(self):
462
source = self.make_branch_and_tree('source')
463
self.build_tree(['source/a'])
465
source.commit('Added a', rev_id='rev1')
466
target = self.make_branch_and_tree('target')
467
target.commit('empty commit')
468
self.write_directive('directive', source.branch, 'target', 'rev1')
469
err = self.run_bzr('merge -d target directive')[1]
470
self.assertNotContainsRe(err, 'Preview patch does not match changes')
472
self.write_directive('directive', source.branch, 'target', 'rev1',
474
err = self.run_bzr('merge -d target directive')[1]
475
self.assertContainsRe(err, 'Preview patch does not match changes')
477
def test_merge_arbitrary(self):
478
target = self.make_branch_and_tree('target')
479
target.commit('empty')
480
# We need a revision that has no integer revno
481
branch_a = target.bzrdir.sprout('branch_a').open_workingtree()
482
self.build_tree(['branch_a/file1'])
483
branch_a.add('file1')
484
branch_a.commit('added file1', rev_id='rev2a')
485
branch_b = target.bzrdir.sprout('branch_b').open_workingtree()
486
self.build_tree(['branch_b/file2'])
487
branch_b.add('file2')
488
branch_b.commit('added file2', rev_id='rev2b')
489
branch_b.merge_from_branch(branch_a.branch)
490
self.assertPathExists('branch_b/file1')
491
branch_b.commit('merged branch_a', rev_id='rev3b')
493
# It works if the revid has an interger revno
494
self.run_bzr('merge -d target -r revid:rev2a branch_a')
495
self.assertPathExists('target/file1')
496
self.assertPathDoesNotExist('target/file2')
499
# It should work if the revid has no integer revno
500
self.run_bzr('merge -d target -r revid:rev2a branch_b')
501
self.assertPathExists('target/file1')
502
self.assertPathDoesNotExist('target/file2')
504
def assertDirectoryContent(self, directory, entries, message=''):
505
"""Assert whether entries (file or directories) exist in a directory.
507
It also checks that there are no extra entries.
509
ondisk = os.listdir(directory)
510
if set(ondisk) == set(entries):
514
raise AssertionError(
515
'%s"%s" directory content is different:\na = %s\nb = %s\n'
516
% (message, directory, sorted(entries), sorted(ondisk)))
518
def test_cherrypicking_merge(self):
520
source = self.make_branch_and_tree('source')
521
for f in ('a', 'b', 'c', 'd'):
522
self.build_tree(['source/'+f])
524
source.commit('added '+f, rev_id='rev_'+f)
526
target = source.bzrdir.sprout('target', 'rev_a').open_workingtree()
527
self.assertDirectoryContent('target', ['.bzr', 'a'])
529
self.run_bzr('merge -d target -r revid:rev_b..revid:rev_c source')
530
self.assertDirectoryContent('target', ['.bzr', 'a', 'c'])
533
self.run_bzr('merge -d target -r revid:rev_b..revid:rev_d source')
534
self.assertDirectoryContent('target', ['.bzr', 'a', 'c', 'd'])
536
# pick 1 revision with option --changes
537
self.run_bzr('merge -d target -c revid:rev_d source')
538
self.assertDirectoryContent('target', ['.bzr', 'a', 'd'])
540
def test_merge_criss_cross(self):
541
tree_a = self.make_branch_and_tree('a')
542
tree_a.commit('', rev_id='rev1')
543
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
544
tree_a.commit('', rev_id='rev2a')
545
tree_b.commit('', rev_id='rev2b')
546
tree_a.merge_from_branch(tree_b.branch)
547
tree_b.merge_from_branch(tree_a.branch)
548
tree_a.commit('', rev_id='rev3a')
549
tree_b.commit('', rev_id='rev3b')
550
graph = tree_a.branch.repository.get_graph(tree_b.branch.repository)
551
out, err = self.run_bzr(['merge', '-d', 'a', 'b'])
552
self.assertContainsRe(err, 'Warning: criss-cross merge encountered.')
554
def test_merge_from_submit(self):
555
tree_a = self.make_branch_and_tree('a')
556
tree_a.commit('test')
557
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
558
tree_c = tree_a.bzrdir.sprout('c').open_workingtree()
559
out, err = self.run_bzr(['merge', '-d', 'c'])
560
self.assertContainsRe(err,
561
'Merging from remembered parent location .*a\/')
562
tree_c.branch.lock_write()
564
tree_c.branch.set_submit_branch(tree_b.bzrdir.root_transport.base)
566
tree_c.branch.unlock()
567
out, err = self.run_bzr(['merge', '-d', 'c'])
568
self.assertContainsRe(err,
569
'Merging from remembered submit location .*b\/')
571
def test_remember_sets_submit(self):
572
tree_a = self.make_branch_and_tree('a')
573
tree_a.commit('rev1')
574
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
575
self.assertIs(tree_b.branch.get_submit_branch(), None)
577
# Remember should not happen if using default from parent
578
out, err = self.run_bzr(['merge', '-d', 'b'])
579
refreshed = workingtree.WorkingTree.open('b')
580
self.assertIs(refreshed.branch.get_submit_branch(), None)
582
# Remember should happen if user supplies location
583
out, err = self.run_bzr(['merge', '-d', 'b', 'a'])
584
refreshed = workingtree.WorkingTree.open('b')
585
self.assertEqual(refreshed.branch.get_submit_branch(),
586
tree_a.bzrdir.root_transport.base)
588
def test_no_remember_dont_set_submit(self):
589
tree_a = self.make_branch_and_tree('a')
590
self.build_tree_contents([('a/file', "a\n")])
592
tree_a.commit('rev1')
593
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
594
self.assertIs(tree_b.branch.get_submit_branch(), None)
596
# Remember should not happen if using default from parent
597
out, err = self.run_bzr(['merge', '-d', 'b', '--no-remember'])
598
self.assertEquals(None, tree_b.branch.get_submit_branch())
600
# Remember should not happen if user supplies location but ask for not
602
out, err = self.run_bzr(['merge', '-d', 'b', '--no-remember', 'a'])
603
self.assertEqual(None, tree_b.branch.get_submit_branch())
605
def test_weave_cherrypick(self):
606
this_tree = self.make_branch_and_tree('this')
607
self.build_tree_contents([('this/file', "a\n")])
608
this_tree.add('file')
609
this_tree.commit('rev1')
610
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
611
self.build_tree_contents([('other/file', "a\nb\n")])
612
other_tree.commit('rev2b')
613
self.build_tree_contents([('other/file', "c\na\nb\n")])
614
other_tree.commit('rev3b')
615
self.run_bzr('merge --weave -d this other -r -2..-1')
616
self.assertFileEqual('c\na\n', 'this/file')
618
def test_lca_merge_criss_cross(self):
619
tree_a = self.make_branch_and_tree('a')
620
self.build_tree_contents([('a/file', 'base-contents\n')])
622
tree_a.commit('', rev_id='rev1')
623
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
624
self.build_tree_contents([('a/file',
625
'base-contents\nthis-contents\n')])
626
tree_a.commit('', rev_id='rev2a')
627
self.build_tree_contents([('b/file',
628
'base-contents\nother-contents\n')])
629
tree_b.commit('', rev_id='rev2b')
630
tree_a.merge_from_branch(tree_b.branch)
631
self.build_tree_contents([('a/file',
632
'base-contents\nthis-contents\n')])
633
tree_a.set_conflicts(conflicts.ConflictList())
634
tree_b.merge_from_branch(tree_a.branch)
635
self.build_tree_contents([('b/file',
636
'base-contents\nother-contents\n')])
637
tree_b.set_conflicts(conflicts.ConflictList())
638
tree_a.commit('', rev_id='rev3a')
639
tree_b.commit('', rev_id='rev3b')
640
out, err = self.run_bzr(['merge', '-d', 'a', 'b', '--lca'], retcode=1)
641
self.assertFileEqual('base-contents\n<<<<<<< TREE\nthis-contents\n'
642
'=======\nother-contents\n>>>>>>> MERGE-SOURCE\n',
645
def test_merge_preview(self):
646
this_tree = self.make_branch_and_tree('this')
647
this_tree.commit('rev1')
648
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
649
self.build_tree_contents([('other/file', 'new line')])
650
other_tree.add('file')
651
other_tree.commit('rev2a')
652
this_tree.commit('rev2b')
653
out, err = self.run_bzr(['merge', '-d', 'this', 'other', '--preview'])
654
self.assertContainsRe(out, '\+new line')
655
self.assertNotContainsRe(err, '\+N file\n')
656
this_tree.lock_read()
657
self.addCleanup(this_tree.unlock)
659
list(this_tree.iter_changes(this_tree.basis_tree())))
661
def test_merge_missing_second_revision_spec(self):
662
"""Merge uses branch basis when the second revision is unspecified."""
663
this = self.make_branch_and_tree('this')
665
other = self.make_branch_and_tree('other')
666
self.build_tree(['other/other_file'])
667
other.add('other_file')
668
other.commit('rev1b')
669
self.run_bzr('merge -d this other -r0..')
670
self.assertPathExists('this/other_file')
672
def test_merge_interactive_unlocks_branch(self):
673
this = self.make_branch_and_tree('this')
674
this.commit('empty commit')
675
other = this.bzrdir.sprout('other').open_workingtree()
676
other.commit('empty commit 2')
677
self.run_bzr('merge -i -d this other')
681
def test_merge_fetches_tags(self):
682
"""Tags are updated by merge, and revisions named in those tags are
685
# Make a source, sprout a target off it
686
builder = self.make_branch_builder('source')
687
builder.build_commit(message="Rev 1", rev_id='rev-1')
688
source = builder.get_branch()
689
target_bzrdir = source.bzrdir.sprout('target')
690
# Add a non-ancestry tag to source
691
builder.build_commit(message="Rev 2a", rev_id='rev-2a')
692
source.tags.set_tag('tag-a', 'rev-2a')
693
source.set_last_revision_info(1, 'rev-1')
694
source.get_config_stack().set('branch.fetch_tags', True)
695
builder.build_commit(message="Rev 2b", rev_id='rev-2b')
697
self.run_bzr('merge -d target source')
698
target = target_bzrdir.open_branch()
699
# The tag is present, and so is its revision.
700
self.assertEqual('rev-2a', target.tags.lookup_tag('tag-a'))
701
target.repository.get_revision('rev-2a')
704
class TestMergeRevisionRange(tests.TestCaseWithTransport):
706
scenarios = (('whole-tree', dict(context='.')),
707
('file-only', dict(context='a')))
710
super(TestMergeRevisionRange, self).setUp()
711
self.tree = self.make_branch_and_tree(".")
712
self.tree.commit('initial commit')
716
self.tree.commit("added " + f)
718
def test_merge_reversed_revision_range(self):
719
self.run_bzr("merge -r 2..1 " + self.context)
720
self.assertPathDoesNotExist("a")
721
self.assertPathExists("b")
724
class TestMergeScript(script.TestCaseWithTransportAndScript):
725
def test_merge_empty_branch(self):
726
source = self.make_branch_and_tree('source')
727
self.build_tree(['source/a'])
729
source.commit('Added a', rev_id='rev1')
730
target = self.make_branch_and_tree('target')
732
$ bzr merge -d target source
733
2>bzr: ERROR: Merging into empty branches not currently supported, https://bugs.launchpad.net/bzr/+bug/308562
736
class TestMergeForce(tests.TestCaseWithTransport):
739
super(TestMergeForce, self).setUp()
740
self.tree_a = self.make_branch_and_tree('a')
741
self.build_tree(['a/foo'])
742
self.tree_a.add(['foo'])
743
self.tree_a.commit('add file')
744
self.tree_b = self.tree_a.bzrdir.sprout('b').open_workingtree()
745
self.build_tree_contents([('a/foo', 'change 1')])
746
self.tree_a.commit('change file')
747
self.tree_b.merge_from_branch(self.tree_a.branch)
749
def test_merge_force(self):
750
self.tree_a.commit('empty change to allow merge to run')
751
# Second merge on top of the uncommitted one
752
self.run_bzr(['merge', '../a', '--force'], working_dir='b')
755
def test_merge_with_uncommitted_changes(self):
756
self.run_bzr_error(['Working tree .* has uncommitted changes'],
757
['merge', '../a'], working_dir='b')
759
def test_merge_with_pending_merges(self):
760
# Revert the changes keeping the pending merge
761
self.run_bzr(['revert', 'b'])
762
self.run_bzr_error(['Working tree .* has uncommitted changes'],
763
['merge', '../a'], working_dir='b')