24
from bzrlib.branch import Branch
25
from bzrlib.bzrdir import BzrDir
26
from bzrlib.conflicts import ConflictList
27
from bzrlib.osutils import abspath
28
from bzrlib.tests.blackbox import ExternalBase
29
import bzrlib.urlutils as urlutils
30
from bzrlib.workingtree import WorkingTree
33
class TestMerge(ExternalBase):
35
def example_branch(test):
37
file('hello', 'wt').write('foo')
38
test.runbzr('add hello')
39
test.runbzr('commit -m setup hello')
40
file('goodbye', 'wt').write('baz')
41
test.runbzr('add goodbye')
42
test.runbzr('commit -m setup goodbye')
36
class TestMerge(tests.TestCaseWithTransport):
38
def example_branch(self, path='.'):
39
tree = self.make_branch_and_tree(path)
40
self.build_tree_contents([
41
(osutils.pathjoin(path, 'hello'), 'foo'),
42
(osutils.pathjoin(path, 'goodbye'), 'baz')])
44
tree.commit(message='setup')
46
tree.commit(message='setup')
49
def create_conflicting_branches(self):
50
"""Create two branches which have overlapping modifications.
52
:return: (tree, other_branch) Where merging other_branch causes a file
55
builder = self.make_branch_builder('branch')
56
builder.build_snapshot('rev1', None,
57
[('add', ('', 'root-id', 'directory', None)),
58
('add', ('fname', 'f-id', 'file', 'a\nb\nc\n'))])
59
builder.build_snapshot('rev2other', ['rev1'],
60
[('modify', ('f-id', 'a\nB\nD\n'))])
61
other = builder.get_branch().bzrdir.sprout('other').open_branch()
62
builder.build_snapshot('rev2this', ['rev1'],
63
[('modify', ('f-id', 'a\nB\nC\n'))])
64
tree = builder.get_branch().create_checkout('tree', lightweight=True)
44
67
def test_merge_reprocess(self):
45
d = BzrDir.create_standalone_workingtree('.')
68
d = bzrdir.BzrDir.create_standalone_workingtree('.')
47
self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
70
self.run_bzr('merge . --reprocess --merge-type weave')
49
72
def test_merge(self):
50
from bzrlib.branch import Branch
56
self.runbzr('branch a b')
58
file('goodbye', 'wt').write('quux')
59
self.runbzr(['commit', '-m', "more u's are always good"])
73
a_tree = self.example_branch('a')
74
ancestor = a_tree.branch.revno()
75
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
76
self.build_tree_contents([('b/goodbye', 'quux')])
77
b_tree.commit(message="more u's are always good")
62
file('hello', 'wt').write('quuux')
79
self.build_tree_contents([('a/hello', 'quuux')])
63
80
# We can't merge when there are in-tree changes
64
self.runbzr('merge ../b', retcode=3)
65
a = WorkingTree.open('.')
82
self.run_bzr('merge ../b', retcode=3)
83
a = workingtree.WorkingTree.open('.')
66
84
a_tip = a.commit("Like an epidemic of u's")
67
self.runbzr('merge ../b -r last:1..last:1 --merge-type blooof',
85
self.run_bzr('merge ../b -r last:1..last:1 --merge-type blooof',
69
self.runbzr('merge ../b -r last:1..last:1 --merge-type merge3')
70
self.runbzr('revert --no-backup')
71
self.runbzr('merge ../b -r last:1..last:1 --merge-type weave')
72
self.runbzr('revert --no-backup')
73
self.runbzr('merge ../b -r last:1..last:1 --reprocess')
74
self.runbzr('revert --no-backup')
75
self.runbzr('merge ../b -r last:1')
87
self.run_bzr('merge ../b -r last:1..last:1 --merge-type merge3')
88
a_tree.revert(backups=False)
89
self.run_bzr('merge ../b -r last:1..last:1 --merge-type weave')
90
a_tree.revert(backups=False)
91
self.run_bzr('merge ../b -r last:1..last:1 --merge-type lca')
92
a_tree.revert(backups=False)
93
self.run_bzr_error(['Show-base is not supported for this merge type'],
94
'merge ../b -r last:1..last:1 --merge-type weave'
96
a_tree.revert(backups=False)
97
self.run_bzr('merge ../b -r last:1..last:1 --reprocess')
98
a_tree.revert(backups=False)
99
self.run_bzr('merge ../b -r last:1')
76
100
self.check_file_contents('goodbye', 'quux')
77
101
# Merging a branch pulls its revision into the tree
78
b = Branch.open('../b')
102
b = branch.Branch.open('../b')
79
103
b_tip = b.last_revision()
80
104
self.failUnless(a.branch.repository.has_revision(b_tip))
81
105
self.assertEqual([a_tip, b_tip], a.get_parent_ids())
106
a_tree.revert(backups=False)
107
out, err = self.run_bzr('merge -r revno:1:./hello', retcode=3)
108
self.assertTrue("Not a branch" in err)
109
self.run_bzr('merge -r revno:%d:./..revno:%d:../b'
110
%(ancestor,b.revno()))
111
self.assertEquals(a.get_parent_ids(),
112
[a.branch.last_revision(), b.last_revision()])
113
self.check_file_contents('goodbye', 'quux')
114
a_tree.revert(backups=False)
115
self.run_bzr('merge -r revno:%d:../b'%b.revno())
116
self.assertEquals(a.get_parent_ids(),
117
[a.branch.last_revision(), b.last_revision()])
82
118
a_tip = a.commit('merged')
83
self.runbzr('merge ../b -r last:1')
119
self.run_bzr('merge ../b -r last:1')
84
120
self.assertEqual([a_tip], a.get_parent_ids())
122
def test_merge_defaults_to_reprocess(self):
123
tree, other = self.create_conflicting_branches()
124
# The default merge algorithm should enable 'reprocess' because
125
# 'show-base' is not set
126
self.run_bzr('merge ../other', working_dir='tree',
128
self.assertEqualDiff('a\n'
134
'>>>>>>> MERGE-SOURCE\n',
135
tree.get_file_text('f-id'))
137
def test_merge_explicit_reprocess_show_base(self):
138
tree, other = self.create_conflicting_branches()
139
# Explicitly setting --reprocess, and --show-base is an error
140
self.run_bzr_error(['Cannot do conflict reduction and show base'],
141
'merge ../other --reprocess --show-base',
144
def test_merge_override_reprocess(self):
145
tree, other = self.create_conflicting_branches()
146
# Explicitly disable reprocess
147
self.run_bzr('merge ../other --no-reprocess', working_dir='tree',
149
self.assertEqualDiff('a\n'
156
'>>>>>>> MERGE-SOURCE\n',
157
tree.get_file_text('f-id'))
159
def test_merge_override_show_base(self):
160
tree, other = self.create_conflicting_branches()
161
# Setting '--show-base' will auto-disable '--reprocess'
162
self.run_bzr('merge ../other --show-base', working_dir='tree',
164
self.assertEqualDiff('a\n'
168
'||||||| BASE-REVISION\n'
174
'>>>>>>> MERGE-SOURCE\n',
175
tree.get_file_text('f-id'))
86
177
def test_merge_with_missing_file(self):
87
178
"""Merge handles missing file conflicts"""
91
print >> file('sub/a.txt', 'wb'), "hello"
92
print >> file('b.txt', 'wb'), "hello"
93
print >> file('sub/c.txt', 'wb'), "hello"
96
self.runbzr(('commit', '-m', 'added a'))
97
self.runbzr('branch . ../b')
98
print >> file('sub/a.txt', 'ab'), "there"
99
print >> file('b.txt', 'ab'), "there"
100
print >> file('sub/c.txt', 'ab'), "there"
101
self.runbzr(('commit', '-m', 'Added there'))
102
os.unlink('sub/a.txt')
103
os.unlink('sub/c.txt')
106
self.runbzr(('commit', '-m', 'Removed a.txt'))
108
print >> file('sub/a.txt', 'ab'), "something"
109
print >> file('b.txt', 'ab'), "something"
110
print >> file('sub/c.txt', 'ab'), "something"
111
self.runbzr(('commit', '-m', 'Modified a.txt'))
112
self.runbzr('merge ../a/', retcode=1)
113
self.assert_(os.path.exists('sub/a.txt.THIS'))
114
self.assert_(os.path.exists('sub/a.txt.BASE'))
179
self.build_tree_contents([
182
('a/sub/a.txt', 'hello\n'),
183
('a/b.txt', 'hello\n'),
184
('a/sub/c.txt', 'hello\n')])
185
a_tree = self.make_branch_and_tree('a')
186
a_tree.add(['sub', 'b.txt', 'sub/c.txt', 'sub/a.txt'])
187
a_tree.commit(message='added a')
188
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
189
self.build_tree_contents([
190
('a/sub/a.txt', 'hello\nthere\n'),
191
('a/b.txt', 'hello\nthere\n'),
192
('a/sub/c.txt', 'hello\nthere\n')])
193
a_tree.commit(message='Added there')
194
os.remove('a/sub/a.txt')
195
os.remove('a/sub/c.txt')
198
a_tree.commit(message='Removed a.txt')
199
self.build_tree_contents([
200
('b/sub/a.txt', 'hello\nsomething\n'),
201
('b/b.txt', 'hello\nsomething\n'),
202
('b/sub/c.txt', 'hello\nsomething\n')])
203
b_tree.commit(message='Modified a.txt')
205
self.run_bzr('merge ../a/', retcode=1)
206
self.failUnlessExists('sub/a.txt.THIS')
207
self.failUnlessExists('sub/a.txt.BASE')
116
self.runbzr('merge ../b/', retcode=1)
117
self.assert_(os.path.exists('sub/a.txt.OTHER'))
118
self.assert_(os.path.exists('sub/a.txt.BASE'))
209
self.run_bzr('merge ../b/', retcode=1)
210
self.failUnlessExists('sub/a.txt.OTHER')
211
self.failUnlessExists('sub/a.txt.BASE')
213
def test_conflict_leaves_base_this_other_files(self):
214
tree, other = self.create_conflicting_branches()
215
self.run_bzr('merge ../other', working_dir='tree',
217
self.assertFileEqual('a\nb\nc\n', 'tree/fname.BASE')
218
self.assertFileEqual('a\nB\nD\n', 'tree/fname.OTHER')
219
self.assertFileEqual('a\nB\nC\n', 'tree/fname.THIS')
221
def test_weave_conflict_leaves_base_this_other_files(self):
222
tree, other = self.create_conflicting_branches()
223
self.run_bzr('merge ../other --weave', working_dir='tree',
225
self.assertFileEqual('a\nb\nc\n', 'tree/fname.BASE')
226
self.assertFileEqual('a\nB\nD\n', 'tree/fname.OTHER')
227
self.assertFileEqual('a\nB\nC\n', 'tree/fname.THIS')
120
229
def test_merge_remember(self):
121
"""Merge changes from one branch to another and test parent location."""
230
"""Merge changes from one branch to another, test submit location."""
122
231
tree_a = self.make_branch_and_tree('branch_a')
123
232
branch_a = tree_a.branch
124
233
self.build_tree(['branch_a/a'])
140
249
self.assertEqual(None, branch_b.get_parent())
141
250
# test merge for failure without parent set
142
251
os.chdir('branch_b')
143
out = self.runbzr('merge', retcode=3)
252
out = self.run_bzr('merge', retcode=3)
144
253
self.assertEquals(out,
145
254
('','bzr: ERROR: No location specified or remembered\n'))
146
# test implicit --remember when no parent set, this merge conflicts
256
# test uncommitted changes
147
257
self.build_tree(['d'])
149
out = self.runbzr('merge ../branch_a', retcode=3)
150
self.assertEquals(out,
151
('','bzr: ERROR: Working tree has uncommitted changes.\n'))
152
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
153
# test implicit --remember after resolving conflict
259
self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
262
# merge should now pass and implicitly remember merge location
154
263
tree_b.commit('commit d')
155
out, err = self.runbzr('merge')
264
out, err = self.run_bzr('merge ../branch_a')
157
266
base = urlutils.local_path_from_url(branch_a.base)
158
self.assertEquals(out, 'Merging from remembered location %s\n' % (base,))
159
self.assertEquals(err, 'All changes applied successfully.\n')
160
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
161
# re-open tree as external runbzr modified it
267
self.assertEndsWith(err, '+N b\nAll changes applied successfully.\n')
268
self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
269
osutils.abspath(parent))
270
# test implicit --remember when committing new file
271
self.build_tree(['e'])
273
tree_b.commit('commit e')
274
out, err = self.run_bzr('merge')
275
self.assertStartsWith(err,
276
'Merging from remembered submit location %s\n' % (base,))
277
# re-open tree as external run_bzr modified it
162
278
tree_b = branch_b.bzrdir.open_workingtree()
163
279
tree_b.commit('merge branch_a')
164
280
# test explicit --remember
165
out, err = self.runbzr('merge ../branch_c --remember')
281
out, err = self.run_bzr('merge ../branch_c --remember')
166
282
self.assertEquals(out, '')
167
self.assertEquals(err, 'All changes applied successfully.\n')
168
self.assertEquals(abspath(branch_b.get_parent()),
169
abspath(branch_c.bzrdir.root_transport.base))
170
# re-open tree as external runbzr modified it
283
self.assertEquals(err, '+N c\nAll changes applied successfully.\n')
284
self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
285
osutils.abspath(branch_c.bzrdir.root_transport.base))
286
# re-open tree as external run_bzr modified it
171
287
tree_b = branch_b.bzrdir.open_workingtree()
172
288
tree_b.commit('merge branch_c')
174
290
def test_merge_bundle(self):
175
291
from bzrlib.testament import Testament
176
292
tree_a = self.make_branch_and_tree('branch_a')
177
f = file('branch_a/a', 'wb')
293
self.build_tree_contents([('branch_a/a', 'hello')])
181
295
tree_a.commit('message')
183
297
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
184
f = file('branch_a/a', 'wb')
298
self.build_tree_contents([('branch_a/a', 'hey there')])
187
299
tree_a.commit('message')
189
f = file('branch_b/a', 'wb')
301
self.build_tree_contents([('branch_b/a', 'goodbye')])
192
302
tree_b.commit('message')
193
303
os.chdir('branch_b')
194
file('../bundle', 'wb').write(self.runbzr('bundle ../branch_a')[0])
304
self.run_bzr('bundle ../branch_a -o ../bundle')
195
305
os.chdir('../branch_a')
196
self.runbzr('merge ../bundle', retcode=1)
197
testament_a = Testament.from_revision(tree_a.branch.repository,
198
tree_b.last_revision())
306
self.run_bzr('merge ../bundle', retcode=1)
307
testament_a = Testament.from_revision(tree_a.branch.repository,
308
tree_b.get_parent_ids()[0])
199
309
testament_b = Testament.from_revision(tree_b.branch.repository,
200
tree_b.last_revision())
310
tree_b.get_parent_ids()[0])
201
311
self.assertEqualDiff(testament_a.as_text(),
202
312
testament_b.as_text())
203
tree_a.set_conflicts(ConflictList())
313
tree_a.set_conflicts(conflicts.ConflictList())
204
314
tree_a.commit('message')
205
315
# it is legal to attempt to merge an already-merged bundle
206
output = self.runbzr('merge ../bundle')[1]
316
output = self.run_bzr('merge ../bundle')[1]
207
317
# but it does nothing
208
318
self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
209
319
self.assertEqual('Nothing to do.\n', output)
219
329
tree_a.rename_one('file_1', 'file_i')
220
330
tree_a.commit('commit 2')
221
331
tree_a.rename_one('file_2', 'file_ii')
223
self.run_bzr('merge', '../a', '--uncommitted')
224
self.failUnlessExists('file_1')
225
self.failUnlessExists('file_ii')
227
self.run_bzr_error(('Cannot use --uncommitted and --revision',),
228
'merge', '../a', '--uncommitted', '-r1')
333
self.run_bzr('merge a --uncommitted -d b')
334
self.failUnlessExists('b/file_1')
335
self.failUnlessExists('b/file_ii')
337
self.run_bzr_error(('Cannot use --uncommitted and --revision',),
338
'merge /a --uncommitted -r1 -d b')
340
def test_merge_uncommitted_file(self):
341
"""It should be possible to merge changes from a single file."""
342
tree_a = self.make_branch_and_tree('tree_a')
343
tree_a.commit('initial commit')
344
tree_a.bzrdir.sprout('tree_b')
345
self.build_tree(['tree_a/file1', 'tree_a/file2'])
346
tree_a.add(['file1', 'file2'])
348
self.run_bzr(['merge', '--uncommitted', '../tree_a/file1'])
349
self.failUnlessExists('file1')
350
self.failIfExists('file2')
352
def pullable_branch(self):
353
tree_a = self.make_branch_and_tree('a')
354
self.build_tree(['a/file'])
356
self.id1 = tree_a.commit('commit 1')
358
tree_b = self.make_branch_and_tree('b')
359
tree_b.pull(tree_a.branch)
360
file('b/file', 'wb').write('foo')
361
self.id2 = tree_b.commit('commit 2')
363
def test_merge_pull(self):
364
self.pullable_branch()
366
(out, err) = self.run_bzr('merge --pull ../b')
367
self.assertContainsRe(out, 'Now on revision 2\\.')
368
tree_a = workingtree.WorkingTree.open('.')
369
self.assertEqual([self.id2], tree_a.get_parent_ids())
371
def test_merge_kind_change(self):
372
tree_a = self.make_branch_and_tree('tree_a')
373
self.build_tree_contents([('tree_a/file', 'content_1')])
374
tree_a.add('file', 'file-id')
375
tree_a.commit('added file')
376
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
377
os.unlink('tree_a/file')
378
self.build_tree(['tree_a/file/'])
379
tree_a.commit('changed file to directory')
381
self.run_bzr('merge ../tree_a')
382
self.assertEqual('directory', osutils.file_kind('file'))
384
self.assertEqual('file', osutils.file_kind('file'))
385
self.build_tree_contents([('file', 'content_2')])
386
tree_b.commit('content change')
387
self.run_bzr('merge ../tree_a', retcode=1)
388
self.assertEqual(tree_b.conflicts(),
389
[conflicts.ContentsConflict('file',
392
def test_directive_cherrypick(self):
393
source = self.make_branch_and_tree('source')
394
source.commit("nothing")
395
# see https://bugs.launchpad.net/bzr/+bug/409688 - trying to
396
# cherrypick from one branch into another unrelated branch with a
397
# different root id will give shape conflicts. as a workaround we
398
# make sure they share the same root id.
399
target = source.bzrdir.sprout('target').open_workingtree()
400
self.build_tree(['source/a'])
402
source.commit('Added a', rev_id='rev1')
403
self.build_tree(['source/b'])
405
source.commit('Added b', rev_id='rev2')
406
target.commit('empty commit')
407
self.write_directive('directive', source.branch, 'target', 'rev2',
409
out, err = self.run_bzr('merge -d target directive')
410
self.failIfExists('target/a')
411
self.failUnlessExists('target/b')
412
self.assertContainsRe(err, 'Performing cherrypick')
414
def write_directive(self, filename, source, target, revision_id,
415
base_revision_id=None, mangle_patch=False):
416
md = merge_directive.MergeDirective2.from_objects(
417
source.repository, revision_id, 0, 0, target,
418
base_revision_id=base_revision_id)
421
self.build_tree_contents([(filename, ''.join(md.to_lines()))])
423
def test_directive_verify_warning(self):
424
source = self.make_branch_and_tree('source')
425
self.build_tree(['source/a'])
427
source.commit('Added a', rev_id='rev1')
428
target = self.make_branch_and_tree('target')
429
target.commit('empty commit')
430
self.write_directive('directive', source.branch, 'target', 'rev1')
431
err = self.run_bzr('merge -d target directive')[1]
432
self.assertNotContainsRe(err, 'Preview patch does not match changes')
434
self.write_directive('directive', source.branch, 'target', 'rev1',
436
err = self.run_bzr('merge -d target directive')[1]
437
self.assertContainsRe(err, 'Preview patch does not match changes')
439
def test_merge_arbitrary(self):
440
target = self.make_branch_and_tree('target')
441
target.commit('empty')
442
# We need a revision that has no integer revno
443
branch_a = target.bzrdir.sprout('branch_a').open_workingtree()
444
self.build_tree(['branch_a/file1'])
445
branch_a.add('file1')
446
branch_a.commit('added file1', rev_id='rev2a')
447
branch_b = target.bzrdir.sprout('branch_b').open_workingtree()
448
self.build_tree(['branch_b/file2'])
449
branch_b.add('file2')
450
branch_b.commit('added file2', rev_id='rev2b')
451
branch_b.merge_from_branch(branch_a.branch)
452
self.failUnlessExists('branch_b/file1')
453
branch_b.commit('merged branch_a', rev_id='rev3b')
455
# It works if the revid has an interger revno
456
self.run_bzr('merge -d target -r revid:rev2a branch_a')
457
self.failUnlessExists('target/file1')
458
self.failIfExists('target/file2')
461
# It should work if the revid has no integer revno
462
self.run_bzr('merge -d target -r revid:rev2a branch_b')
463
self.failUnlessExists('target/file1')
464
self.failIfExists('target/file2')
466
def assertDirectoryContent(self, directory, entries, message=''):
467
"""Assert whether entries (file or directories) exist in a directory.
469
It also checks that there are no extra entries.
471
ondisk = os.listdir(directory)
472
if set(ondisk) == set(entries):
476
raise AssertionError(
477
'%s"%s" directory content is different:\na = %s\nb = %s\n'
478
% (message, directory, sorted(entries), sorted(ondisk)))
480
def test_cherrypicking_merge(self):
482
source = self.make_branch_and_tree('source')
483
for f in ('a', 'b', 'c', 'd'):
484
self.build_tree(['source/'+f])
486
source.commit('added '+f, rev_id='rev_'+f)
488
target = source.bzrdir.sprout('target', 'rev_a').open_workingtree()
489
self.assertDirectoryContent('target', ['.bzr', 'a'])
491
self.run_bzr('merge -d target -r revid:rev_b..revid:rev_c source')
492
self.assertDirectoryContent('target', ['.bzr', 'a', 'c'])
495
self.run_bzr('merge -d target -r revid:rev_b..revid:rev_d source')
496
self.assertDirectoryContent('target', ['.bzr', 'a', 'c', 'd'])
498
# pick 1 revision with option --changes
499
self.run_bzr('merge -d target -c revid:rev_d source')
500
self.assertDirectoryContent('target', ['.bzr', 'a', 'd'])
502
def test_merge_criss_cross(self):
503
tree_a = self.make_branch_and_tree('a')
504
tree_a.commit('', rev_id='rev1')
505
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
506
tree_a.commit('', rev_id='rev2a')
507
tree_b.commit('', rev_id='rev2b')
508
tree_a.merge_from_branch(tree_b.branch)
509
tree_b.merge_from_branch(tree_a.branch)
510
tree_a.commit('', rev_id='rev3a')
511
tree_b.commit('', rev_id='rev3b')
512
graph = tree_a.branch.repository.get_graph(tree_b.branch.repository)
513
out, err = self.run_bzr(['merge', '-d', 'a', 'b'])
514
self.assertContainsRe(err, 'Warning: criss-cross merge encountered.')
516
def test_merge_from_submit(self):
517
tree_a = self.make_branch_and_tree('a')
518
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
519
tree_c = tree_a.bzrdir.sprout('c').open_workingtree()
520
out, err = self.run_bzr(['merge', '-d', 'c'])
521
self.assertContainsRe(err, 'Merging from remembered parent location .*a\/')
522
tree_c.branch.set_submit_branch(tree_b.bzrdir.root_transport.base)
523
out, err = self.run_bzr(['merge', '-d', 'c'])
524
self.assertContainsRe(err, 'Merging from remembered submit location .*b\/')
526
def test_remember_sets_submit(self):
527
tree_a = self.make_branch_and_tree('a')
528
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
529
self.assertIs(tree_b.branch.get_submit_branch(), None)
531
# Remember should not happen if using default from parent
532
out, err = self.run_bzr(['merge', '-d', 'b'])
533
self.assertIs(tree_b.branch.get_submit_branch(), None)
535
# Remember should happen if user supplies location
536
out, err = self.run_bzr(['merge', '-d', 'b', 'a'])
537
self.assertEqual(tree_b.branch.get_submit_branch(),
538
tree_a.bzrdir.root_transport.base)
540
def test_weave_cherrypick(self):
541
this_tree = self.make_branch_and_tree('this')
542
self.build_tree_contents([('this/file', "a\n")])
543
this_tree.add('file')
544
this_tree.commit('rev1')
545
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
546
self.build_tree_contents([('other/file', "a\nb\n")])
547
other_tree.commit('rev2b')
548
self.build_tree_contents([('other/file', "c\na\nb\n")])
549
other_tree.commit('rev3b')
550
self.run_bzr('merge --weave -d this other -r -2..-1')
551
self.assertFileEqual('c\na\n', 'this/file')
553
def test_lca_merge_criss_cross(self):
554
tree_a = self.make_branch_and_tree('a')
555
self.build_tree_contents([('a/file', 'base-contents\n')])
557
tree_a.commit('', rev_id='rev1')
558
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
559
self.build_tree_contents([('a/file',
560
'base-contents\nthis-contents\n')])
561
tree_a.commit('', rev_id='rev2a')
562
self.build_tree_contents([('b/file',
563
'base-contents\nother-contents\n')])
564
tree_b.commit('', rev_id='rev2b')
565
tree_a.merge_from_branch(tree_b.branch)
566
self.build_tree_contents([('a/file',
567
'base-contents\nthis-contents\n')])
568
tree_a.set_conflicts(conflicts.ConflictList())
569
tree_b.merge_from_branch(tree_a.branch)
570
self.build_tree_contents([('b/file',
571
'base-contents\nother-contents\n')])
572
tree_b.set_conflicts(conflicts.ConflictList())
573
tree_a.commit('', rev_id='rev3a')
574
tree_b.commit('', rev_id='rev3b')
575
out, err = self.run_bzr(['merge', '-d', 'a', 'b', '--lca'], retcode=1)
576
self.assertFileEqual('base-contents\n<<<<<<< TREE\nthis-contents\n'
577
'=======\nother-contents\n>>>>>>> MERGE-SOURCE\n',
580
def test_merge_preview(self):
581
this_tree = self.make_branch_and_tree('this')
582
this_tree.commit('rev1')
583
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
584
self.build_tree_contents([('other/file', 'new line')])
585
other_tree.add('file')
586
other_tree.commit('rev2a')
587
this_tree.commit('rev2b')
588
out, err = self.run_bzr(['merge', '-d', 'this', 'other', '--preview'])
589
self.assertContainsRe(out, '\+new line')
590
self.assertNotContainsRe(err, '\+N file\n')
591
this_tree.lock_read()
592
self.addCleanup(this_tree.unlock)
594
list(this_tree.iter_changes(this_tree.basis_tree())))
596
def test_merge_missing_second_revision_spec(self):
597
"""Merge uses branch basis when the second revision is unspecified."""
598
this = self.make_branch_and_tree('this')
600
other = self.make_branch_and_tree('other')
601
self.build_tree(['other/other_file'])
602
other.add('other_file')
603
other.commit('rev1b')
604
self.run_bzr('merge -d this other -r0..')
605
self.failUnlessExists('this/other_file')
607
def test_merge_interactive_unlocks_branch(self):
608
this = self.make_branch_and_tree('this')
609
other = self.make_branch_and_tree('other')
610
other.commit('empty commit')
611
self.run_bzr('merge -i -d this other')
615
def test_merge_reversed_revision_range(self):
616
tree = self.make_branch_and_tree(".")
620
tree.commit("added "+f)
621
for context in (".", "", "a"):
622
self.run_bzr("merge -r 1..0 " + context)
623
self.failIfExists("a")
625
self.failUnlessExists("a")
627
def test_merge_fetches_tags(self):
628
"""Tags are updated by merge, and revisions named in those tags are
631
# Make a source, sprout a target off it
632
builder = self.make_branch_builder('source')
633
builder.build_commit(message="Rev 1", rev_id='rev-1')
634
source = builder.get_branch()
635
target_bzrdir = source.bzrdir.sprout('target')
636
# Add a non-ancestry tag to source
637
builder.build_commit(message="Rev 2a", rev_id='rev-2a')
638
source.tags.set_tag('tag-a', 'rev-2a')
639
source.set_last_revision_info(1, 'rev-1')
640
builder.build_commit(message="Rev 2b", rev_id='rev-2b')
642
self.run_bzr('merge -d target source')
643
target = target_bzrdir.open_branch()
644
# The tag is present, and so is its revision.
645
self.assertEqual('rev-2a', target.tags.lookup_tag('tag-a'))
646
target.repository.get_revision('rev-2a')
649
class TestMergeForce(tests.TestCaseWithTransport):
652
super(TestMergeForce, self).setUp()
653
self.tree_a = self.make_branch_and_tree('a')
654
self.build_tree(['a/foo'])
655
self.tree_a.add(['foo'])
656
self.tree_a.commit('add file')
657
self.tree_b = self.tree_a.bzrdir.sprout('b').open_workingtree()
658
self.build_tree_contents([('a/foo', 'change 1')])
659
self.tree_a.commit('change file')
660
self.tree_b.merge_from_branch(self.tree_a.branch)
662
def test_merge_force(self):
663
self.tree_a.commit('empty change to allow merge to run')
664
# Second merge on top of the uncommitted one
665
self.run_bzr(['merge', '../a', '--force'], working_dir='b')
668
def test_merge_with_uncommitted_changes(self):
669
self.run_bzr_error(['Working tree .* has uncommitted changes'],
670
['merge', '../a'], working_dir='b')
672
def test_merge_with_pending_merges(self):
673
# Revert the changes keeping the pending merge
674
self.run_bzr(['revert', 'b'])
675
self.run_bzr_error(['Working tree .* has uncommitted changes'],
676
['merge', '../a'], working_dir='b')