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')
37
class TestMerge(tests.TestCaseWithTransport):
39
def example_branch(self, path='.'):
40
tree = self.make_branch_and_tree(path)
41
self.build_tree_contents([
42
(osutils.pathjoin(path, 'hello'), 'foo'),
43
(osutils.pathjoin(path, 'goodbye'), 'baz')])
45
tree.commit(message='setup')
47
tree.commit(message='setup')
50
def create_conflicting_branches(self):
51
"""Create two branches which have overlapping modifications.
53
:return: (tree, other_branch) Where merging other_branch causes a file
56
builder = self.make_branch_builder('branch')
57
builder.build_snapshot('rev1', None,
58
[('add', ('', 'root-id', 'directory', None)),
59
('add', ('fname', 'f-id', 'file', 'a\nb\nc\n'))])
60
builder.build_snapshot('rev2other', ['rev1'],
61
[('modify', ('f-id', 'a\nB\nD\n'))])
62
other = builder.get_branch().bzrdir.sprout('other').open_branch()
63
builder.build_snapshot('rev2this', ['rev1'],
64
[('modify', ('f-id', 'a\nB\nC\n'))])
65
tree = builder.get_branch().create_checkout('tree', lightweight=True)
44
68
def test_merge_reprocess(self):
45
d = BzrDir.create_standalone_workingtree('.')
69
d = bzrdir.BzrDir.create_standalone_workingtree('.')
47
self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
71
self.run_bzr('merge . --reprocess --merge-type weave')
49
73
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"])
74
a_tree = self.example_branch('a')
75
ancestor = a_tree.branch.revno()
76
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
77
self.build_tree_contents([('b/goodbye', 'quux')])
78
b_tree.commit(message="more u's are always good")
62
file('hello', 'wt').write('quuux')
80
self.build_tree_contents([('a/hello', 'quuux')])
63
81
# We can't merge when there are in-tree changes
64
self.runbzr('merge ../b', retcode=3)
65
self.runbzr(['commit', '-m', "Like an epidemic of u's"])
66
self.runbzr('merge ../b -r last:1..last:1 --merge-type blooof',
83
self.run_bzr('merge ../b', retcode=3)
84
a = workingtree.WorkingTree.open('.')
85
a_tip = a.commit("Like an epidemic of u's")
86
self.run_bzr('merge ../b -r last:1..last:1 --merge-type blooof',
68
self.runbzr('merge ../b -r last:1..last:1 --merge-type merge3')
69
self.runbzr('revert --no-backup')
70
self.runbzr('merge ../b -r last:1..last:1 --merge-type weave')
71
self.runbzr('revert --no-backup')
72
self.runbzr('merge ../b -r last:1..last:1 --reprocess')
73
self.runbzr('revert --no-backup')
74
self.runbzr('merge ../b -r last:1')
88
self.run_bzr('merge ../b -r last:1..last:1 --merge-type merge3')
89
a_tree.revert(backups=False)
90
self.run_bzr('merge ../b -r last:1..last:1 --merge-type weave')
91
a_tree.revert(backups=False)
92
self.run_bzr('merge ../b -r last:1..last:1 --merge-type lca')
93
a_tree.revert(backups=False)
94
self.run_bzr_error(['Show-base is not supported for this merge type'],
95
'merge ../b -r last:1..last:1 --merge-type weave'
97
a_tree.revert(backups=False)
98
self.run_bzr('merge ../b -r last:1..last:1 --reprocess')
99
a_tree.revert(backups=False)
100
self.run_bzr('merge ../b -r last:1')
75
101
self.check_file_contents('goodbye', 'quux')
76
102
# Merging a branch pulls its revision into the tree
77
a = WorkingTree.open('.')
78
b = Branch.open('../b')
79
a.branch.repository.get_revision_xml(b.last_revision())
80
self.log('pending merges: %s', a.pending_merges())
81
self.assertEquals(a.pending_merges(),
83
self.runbzr('commit -m merged')
84
self.runbzr('merge ../b -r last:1')
85
self.assertEqual(a.pending_merges(), [])
103
b = branch.Branch.open('../b')
104
b_tip = b.last_revision()
105
self.failUnless(a.branch.repository.has_revision(b_tip))
106
self.assertEqual([a_tip, b_tip], a.get_parent_ids())
107
a_tree.revert(backups=False)
108
out, err = self.run_bzr('merge -r revno:1:./hello', retcode=3)
109
self.assertTrue("Not a branch" in err)
110
self.run_bzr('merge -r revno:%d:./..revno:%d:../b'
111
%(ancestor,b.revno()))
112
self.assertEquals(a.get_parent_ids(),
113
[a.branch.last_revision(), b.last_revision()])
114
self.check_file_contents('goodbye', 'quux')
115
a_tree.revert(backups=False)
116
self.run_bzr('merge -r revno:%d:../b'%b.revno())
117
self.assertEquals(a.get_parent_ids(),
118
[a.branch.last_revision(), b.last_revision()])
119
a_tip = a.commit('merged')
120
self.run_bzr('merge ../b -r last:1')
121
self.assertEqual([a_tip], a.get_parent_ids())
123
def test_merge_defaults_to_reprocess(self):
124
tree, other = self.create_conflicting_branches()
125
# The default merge algorithm should enable 'reprocess' because
126
# 'show-base' is not set
127
self.run_bzr('merge ../other', working_dir='tree',
129
self.assertEqualDiff('a\n'
135
'>>>>>>> MERGE-SOURCE\n',
136
tree.get_file_text('f-id'))
138
def test_merge_explicit_reprocess_show_base(self):
139
tree, other = self.create_conflicting_branches()
140
# Explicitly setting --reprocess, and --show-base is an error
141
self.run_bzr_error(['Cannot do conflict reduction and show base'],
142
'merge ../other --reprocess --show-base',
145
def test_merge_override_reprocess(self):
146
tree, other = self.create_conflicting_branches()
147
# Explicitly disable reprocess
148
self.run_bzr('merge ../other --no-reprocess', working_dir='tree',
150
self.assertEqualDiff('a\n'
157
'>>>>>>> MERGE-SOURCE\n',
158
tree.get_file_text('f-id'))
160
def test_merge_override_show_base(self):
161
tree, other = self.create_conflicting_branches()
162
# Setting '--show-base' will auto-disable '--reprocess'
163
self.run_bzr('merge ../other --show-base', working_dir='tree',
165
self.assertEqualDiff('a\n'
169
'||||||| BASE-REVISION\n'
175
'>>>>>>> MERGE-SOURCE\n',
176
tree.get_file_text('f-id'))
87
178
def test_merge_with_missing_file(self):
88
179
"""Merge handles missing file conflicts"""
92
print >> file('sub/a.txt', 'wb'), "hello"
93
print >> file('b.txt', 'wb'), "hello"
94
print >> file('sub/c.txt', 'wb'), "hello"
97
self.runbzr(('commit', '-m', 'added a'))
98
self.runbzr('branch . ../b')
99
print >> file('sub/a.txt', 'ab'), "there"
100
print >> file('b.txt', 'ab'), "there"
101
print >> file('sub/c.txt', 'ab'), "there"
102
self.runbzr(('commit', '-m', 'Added there'))
103
os.unlink('sub/a.txt')
104
os.unlink('sub/c.txt')
107
self.runbzr(('commit', '-m', 'Removed a.txt'))
109
print >> file('sub/a.txt', 'ab'), "something"
110
print >> file('b.txt', 'ab'), "something"
111
print >> file('sub/c.txt', 'ab'), "something"
112
self.runbzr(('commit', '-m', 'Modified a.txt'))
113
self.runbzr('merge ../a/', retcode=1)
114
self.assert_(os.path.exists('sub/a.txt.THIS'))
115
self.assert_(os.path.exists('sub/a.txt.BASE'))
180
self.build_tree_contents([
183
('a/sub/a.txt', 'hello\n'),
184
('a/b.txt', 'hello\n'),
185
('a/sub/c.txt', 'hello\n')])
186
a_tree = self.make_branch_and_tree('a')
187
a_tree.add(['sub', 'b.txt', 'sub/c.txt', 'sub/a.txt'])
188
a_tree.commit(message='added a')
189
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
190
self.build_tree_contents([
191
('a/sub/a.txt', 'hello\nthere\n'),
192
('a/b.txt', 'hello\nthere\n'),
193
('a/sub/c.txt', 'hello\nthere\n')])
194
a_tree.commit(message='Added there')
195
os.remove('a/sub/a.txt')
196
os.remove('a/sub/c.txt')
199
a_tree.commit(message='Removed a.txt')
200
self.build_tree_contents([
201
('b/sub/a.txt', 'hello\nsomething\n'),
202
('b/b.txt', 'hello\nsomething\n'),
203
('b/sub/c.txt', 'hello\nsomething\n')])
204
b_tree.commit(message='Modified a.txt')
206
self.run_bzr('merge ../a/', retcode=1)
207
self.failUnlessExists('sub/a.txt.THIS')
208
self.failUnlessExists('sub/a.txt.BASE')
117
self.runbzr('merge ../b/', retcode=1)
118
self.assert_(os.path.exists('sub/a.txt.OTHER'))
119
self.assert_(os.path.exists('sub/a.txt.BASE'))
210
self.run_bzr('merge ../b/', retcode=1)
211
self.failUnlessExists('sub/a.txt.OTHER')
212
self.failUnlessExists('sub/a.txt.BASE')
214
def test_conflict_leaves_base_this_other_files(self):
215
tree, other = self.create_conflicting_branches()
216
self.run_bzr('merge ../other', working_dir='tree',
218
self.assertFileEqual('a\nb\nc\n', 'tree/fname.BASE')
219
self.assertFileEqual('a\nB\nD\n', 'tree/fname.OTHER')
220
self.assertFileEqual('a\nB\nC\n', 'tree/fname.THIS')
222
def test_weave_conflict_leaves_base_this_other_files(self):
223
tree, other = self.create_conflicting_branches()
224
self.run_bzr('merge ../other --weave', working_dir='tree',
226
self.assertFileEqual('a\nb\nc\n', 'tree/fname.BASE')
227
self.assertFileEqual('a\nB\nD\n', 'tree/fname.OTHER')
228
self.assertFileEqual('a\nB\nC\n', 'tree/fname.THIS')
121
230
def test_merge_remember(self):
122
"""Merge changes from one branch to another and test parent location."""
231
"""Merge changes from one branch to another, test submit location."""
123
232
tree_a = self.make_branch_and_tree('branch_a')
124
233
branch_a = tree_a.branch
125
234
self.build_tree(['branch_a/a'])
141
250
self.assertEqual(None, branch_b.get_parent())
142
251
# test merge for failure without parent set
143
252
os.chdir('branch_b')
144
out = self.runbzr('merge', retcode=3)
253
out = self.run_bzr('merge', retcode=3)
145
254
self.assertEquals(out,
146
255
('','bzr: ERROR: No location specified or remembered\n'))
147
# test implicit --remember when no parent set, this merge conflicts
257
# test uncommitted changes
148
258
self.build_tree(['d'])
150
out = self.runbzr('merge ../branch_a', retcode=3)
151
self.assertEquals(out,
152
('','bzr: ERROR: Working tree has uncommitted changes.\n'))
153
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
154
# test implicit --remember after resolving conflict
260
self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
263
# merge should now pass and implicitly remember merge location
155
264
tree_b.commit('commit d')
156
out, err = self.runbzr('merge')
265
out, err = self.run_bzr('merge ../branch_a')
158
267
base = urlutils.local_path_from_url(branch_a.base)
159
self.assertEquals(out, 'Merging from remembered location %s\n' % (base,))
160
self.assertEquals(err, 'All changes applied successfully.\n')
161
self.assertEquals(abspath(branch_b.get_parent()), abspath(parent))
162
# re-open tree as external runbzr modified it
268
self.assertEndsWith(err, '+N b\nAll changes applied successfully.\n')
269
self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
270
osutils.abspath(parent))
271
# test implicit --remember when committing new file
272
self.build_tree(['e'])
274
tree_b.commit('commit e')
275
out, err = self.run_bzr('merge')
276
self.assertStartsWith(err,
277
'Merging from remembered submit location %s\n' % (base,))
278
# re-open tree as external run_bzr modified it
163
279
tree_b = branch_b.bzrdir.open_workingtree()
164
280
tree_b.commit('merge branch_a')
165
281
# test explicit --remember
166
out, err = self.runbzr('merge ../branch_c --remember')
282
out, err = self.run_bzr('merge ../branch_c --remember')
167
283
self.assertEquals(out, '')
168
self.assertEquals(err, 'All changes applied successfully.\n')
169
self.assertEquals(abspath(branch_b.get_parent()),
170
abspath(branch_c.bzrdir.root_transport.base))
171
# re-open tree as external runbzr modified it
284
self.assertEquals(err, '+N c\nAll changes applied successfully.\n')
285
self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
286
osutils.abspath(branch_c.bzrdir.root_transport.base))
287
# re-open tree as external run_bzr modified it
172
288
tree_b = branch_b.bzrdir.open_workingtree()
173
289
tree_b.commit('merge branch_c')
175
291
def test_merge_bundle(self):
176
292
from bzrlib.testament import Testament
177
293
tree_a = self.make_branch_and_tree('branch_a')
178
f = file('branch_a/a', 'wb')
294
self.build_tree_contents([('branch_a/a', 'hello')])
182
296
tree_a.commit('message')
184
298
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
185
f = file('branch_a/a', 'wb')
299
self.build_tree_contents([('branch_a/a', 'hey there')])
188
300
tree_a.commit('message')
190
f = file('branch_b/a', 'wb')
302
self.build_tree_contents([('branch_b/a', 'goodbye')])
193
303
tree_b.commit('message')
194
304
os.chdir('branch_b')
195
file('../bundle', 'wb').write(self.runbzr('bundle ../branch_a')[0])
305
self.run_bzr('bundle ../branch_a -o ../bundle')
196
306
os.chdir('../branch_a')
197
self.runbzr('merge ../bundle', retcode=1)
198
testament_a = Testament.from_revision(tree_a.branch.repository,
199
tree_b.last_revision())
307
self.run_bzr('merge ../bundle', retcode=1)
308
testament_a = Testament.from_revision(tree_a.branch.repository,
309
tree_b.get_parent_ids()[0])
200
310
testament_b = Testament.from_revision(tree_b.branch.repository,
201
tree_b.last_revision())
311
tree_b.get_parent_ids()[0])
202
312
self.assertEqualDiff(testament_a.as_text(),
203
313
testament_b.as_text())
204
tree_a.set_conflicts(ConflictList())
314
tree_a.set_conflicts(conflicts.ConflictList())
205
315
tree_a.commit('message')
206
316
# it is legal to attempt to merge an already-merged bundle
207
output = self.runbzr('merge ../bundle')[1]
317
output = self.run_bzr('merge ../bundle')[1]
208
318
# but it does nothing
209
319
self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
210
320
self.assertEqual('Nothing to do.\n', output)
322
def test_merge_uncommitted(self):
323
"""Check that merge --uncommitted behaves properly"""
324
tree_a = self.make_branch_and_tree('a')
325
self.build_tree(['a/file_1', 'a/file_2'])
326
tree_a.add(['file_1', 'file_2'])
327
tree_a.commit('commit 1')
328
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
329
self.failUnlessExists('b/file_1')
330
tree_a.rename_one('file_1', 'file_i')
331
tree_a.commit('commit 2')
332
tree_a.rename_one('file_2', 'file_ii')
334
self.run_bzr('merge a --uncommitted -d b')
335
self.failUnlessExists('b/file_1')
336
self.failUnlessExists('b/file_ii')
338
self.run_bzr_error(('Cannot use --uncommitted and --revision',),
339
'merge /a --uncommitted -r1 -d b')
341
def test_merge_uncommitted_file(self):
342
"""It should be possible to merge changes from a single file."""
343
tree_a = self.make_branch_and_tree('tree_a')
344
tree_a.commit('initial commit')
345
tree_a.bzrdir.sprout('tree_b')
346
self.build_tree(['tree_a/file1', 'tree_a/file2'])
347
tree_a.add(['file1', 'file2'])
349
self.run_bzr(['merge', '--uncommitted', '../tree_a/file1'])
350
self.failUnlessExists('file1')
351
self.failIfExists('file2')
353
def pullable_branch(self):
354
tree_a = self.make_branch_and_tree('a')
355
self.build_tree(['a/file'])
357
self.id1 = tree_a.commit('commit 1')
359
tree_b = self.make_branch_and_tree('b')
360
tree_b.pull(tree_a.branch)
361
file('b/file', 'wb').write('foo')
362
self.id2 = tree_b.commit('commit 2')
364
def test_merge_pull(self):
365
self.pullable_branch()
367
(out, err) = self.run_bzr('merge --pull ../b')
368
self.assertContainsRe(out, 'Now on revision 2\\.')
369
tree_a = workingtree.WorkingTree.open('.')
370
self.assertEqual([self.id2], tree_a.get_parent_ids())
372
def test_merge_kind_change(self):
373
tree_a = self.make_branch_and_tree('tree_a')
374
self.build_tree_contents([('tree_a/file', 'content_1')])
375
tree_a.add('file', 'file-id')
376
tree_a.commit('added file')
377
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
378
os.unlink('tree_a/file')
379
self.build_tree(['tree_a/file/'])
380
tree_a.commit('changed file to directory')
382
self.run_bzr('merge ../tree_a')
383
self.assertEqual('directory', osutils.file_kind('file'))
385
self.assertEqual('file', osutils.file_kind('file'))
386
self.build_tree_contents([('file', 'content_2')])
387
tree_b.commit('content change')
388
self.run_bzr('merge ../tree_a', retcode=1)
389
self.assertEqual(tree_b.conflicts(),
390
[conflicts.ContentsConflict('file',
393
def test_directive_cherrypick(self):
394
source = self.make_branch_and_tree('source')
395
source.commit("nothing")
396
# see https://bugs.edge.launchpad.net/bzr/+bug/409688 - trying to
397
# cherrypick from one branch into another unrelated branch with a
398
# different root id will give shape conflicts. as a workaround we
399
# make sure they share the same root id.
400
target = source.bzrdir.sprout('target').open_workingtree()
401
self.build_tree(['source/a'])
403
source.commit('Added a', rev_id='rev1')
404
self.build_tree(['source/b'])
406
source.commit('Added b', rev_id='rev2')
407
target.commit('empty commit')
408
self.write_directive('directive', source.branch, 'target', 'rev2',
410
out, err = self.run_bzr('merge -d target directive')
411
self.failIfExists('target/a')
412
self.failUnlessExists('target/b')
413
self.assertContainsRe(err, 'Performing cherrypick')
415
def write_directive(self, filename, source, target, revision_id,
416
base_revision_id=None, mangle_patch=False):
417
md = merge_directive.MergeDirective2.from_objects(
418
source.repository, revision_id, 0, 0, target,
419
base_revision_id=base_revision_id)
422
self.build_tree_contents([(filename, ''.join(md.to_lines()))])
424
def test_directive_verify_warning(self):
425
source = self.make_branch_and_tree('source')
426
self.build_tree(['source/a'])
428
source.commit('Added a', rev_id='rev1')
429
target = self.make_branch_and_tree('target')
430
target.commit('empty commit')
431
self.write_directive('directive', source.branch, 'target', 'rev1')
432
err = self.run_bzr('merge -d target directive')[1]
433
self.assertNotContainsRe(err, 'Preview patch does not match changes')
435
self.write_directive('directive', source.branch, 'target', 'rev1',
437
err = self.run_bzr('merge -d target directive')[1]
438
self.assertContainsRe(err, 'Preview patch does not match changes')
440
def test_merge_arbitrary(self):
441
target = self.make_branch_and_tree('target')
442
target.commit('empty')
443
# We need a revision that has no integer revno
444
branch_a = target.bzrdir.sprout('branch_a').open_workingtree()
445
self.build_tree(['branch_a/file1'])
446
branch_a.add('file1')
447
branch_a.commit('added file1', rev_id='rev2a')
448
branch_b = target.bzrdir.sprout('branch_b').open_workingtree()
449
self.build_tree(['branch_b/file2'])
450
branch_b.add('file2')
451
branch_b.commit('added file2', rev_id='rev2b')
452
branch_b.merge_from_branch(branch_a.branch)
453
self.failUnlessExists('branch_b/file1')
454
branch_b.commit('merged branch_a', rev_id='rev3b')
456
# It works if the revid has an interger revno
457
self.run_bzr('merge -d target -r revid:rev2a branch_a')
458
self.failUnlessExists('target/file1')
459
self.failIfExists('target/file2')
462
# It should work if the revid has no integer revno
463
self.run_bzr('merge -d target -r revid:rev2a branch_b')
464
self.failUnlessExists('target/file1')
465
self.failIfExists('target/file2')
467
def assertDirectoryContent(self, directory, entries, message=''):
468
"""Assert whether entries (file or directories) exist in a directory.
470
It also checks that there are no extra entries.
472
ondisk = os.listdir(directory)
473
if set(ondisk) == set(entries):
477
raise AssertionError(
478
'%s"%s" directory content is different:\na = %s\nb = %s\n'
479
% (message, directory, sorted(entries), sorted(ondisk)))
481
def test_cherrypicking_merge(self):
483
source = self.make_branch_and_tree('source')
484
for f in ('a', 'b', 'c', 'd'):
485
self.build_tree(['source/'+f])
487
source.commit('added '+f, rev_id='rev_'+f)
489
target = source.bzrdir.sprout('target', 'rev_a').open_workingtree()
490
self.assertDirectoryContent('target', ['.bzr', 'a'])
492
self.run_bzr('merge -d target -r revid:rev_b..revid:rev_c source')
493
self.assertDirectoryContent('target', ['.bzr', 'a', 'c'])
496
self.run_bzr('merge -d target -r revid:rev_b..revid:rev_d source')
497
self.assertDirectoryContent('target', ['.bzr', 'a', 'c', 'd'])
499
# pick 1 revision with option --changes
500
self.run_bzr('merge -d target -c revid:rev_d source')
501
self.assertDirectoryContent('target', ['.bzr', 'a', 'd'])
503
def test_merge_criss_cross(self):
504
tree_a = self.make_branch_and_tree('a')
505
tree_a.commit('', rev_id='rev1')
506
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
507
tree_a.commit('', rev_id='rev2a')
508
tree_b.commit('', rev_id='rev2b')
509
tree_a.merge_from_branch(tree_b.branch)
510
tree_b.merge_from_branch(tree_a.branch)
511
tree_a.commit('', rev_id='rev3a')
512
tree_b.commit('', rev_id='rev3b')
513
graph = tree_a.branch.repository.get_graph(tree_b.branch.repository)
514
out, err = self.run_bzr(['merge', '-d', 'a', 'b'])
515
self.assertContainsRe(err, 'Warning: criss-cross merge encountered.')
517
def test_merge_from_submit(self):
518
tree_a = self.make_branch_and_tree('a')
519
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
520
tree_c = tree_a.bzrdir.sprout('c').open_workingtree()
521
out, err = self.run_bzr(['merge', '-d', 'c'])
522
self.assertContainsRe(err, 'Merging from remembered parent location .*a\/')
523
tree_c.branch.set_submit_branch(tree_b.bzrdir.root_transport.base)
524
out, err = self.run_bzr(['merge', '-d', 'c'])
525
self.assertContainsRe(err, 'Merging from remembered submit location .*b\/')
527
def test_remember_sets_submit(self):
528
tree_a = self.make_branch_and_tree('a')
529
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
530
self.assertIs(tree_b.branch.get_submit_branch(), None)
532
# Remember should not happen if using default from parent
533
out, err = self.run_bzr(['merge', '-d', 'b'])
534
self.assertIs(tree_b.branch.get_submit_branch(), None)
536
# Remember should happen if user supplies location
537
out, err = self.run_bzr(['merge', '-d', 'b', 'a'])
538
self.assertEqual(tree_b.branch.get_submit_branch(),
539
tree_a.bzrdir.root_transport.base)
541
def test_weave_cherrypick(self):
542
this_tree = self.make_branch_and_tree('this')
543
self.build_tree_contents([('this/file', "a\n")])
544
this_tree.add('file')
545
this_tree.commit('rev1')
546
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
547
self.build_tree_contents([('other/file', "a\nb\n")])
548
other_tree.commit('rev2b')
549
self.build_tree_contents([('other/file', "c\na\nb\n")])
550
other_tree.commit('rev3b')
551
self.run_bzr('merge --weave -d this other -r -2..-1')
552
self.assertFileEqual('c\na\n', 'this/file')
554
def test_lca_merge_criss_cross(self):
555
tree_a = self.make_branch_and_tree('a')
556
self.build_tree_contents([('a/file', 'base-contents\n')])
558
tree_a.commit('', rev_id='rev1')
559
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
560
self.build_tree_contents([('a/file',
561
'base-contents\nthis-contents\n')])
562
tree_a.commit('', rev_id='rev2a')
563
self.build_tree_contents([('b/file',
564
'base-contents\nother-contents\n')])
565
tree_b.commit('', rev_id='rev2b')
566
tree_a.merge_from_branch(tree_b.branch)
567
self.build_tree_contents([('a/file',
568
'base-contents\nthis-contents\n')])
569
tree_a.set_conflicts(conflicts.ConflictList())
570
tree_b.merge_from_branch(tree_a.branch)
571
self.build_tree_contents([('b/file',
572
'base-contents\nother-contents\n')])
573
tree_b.set_conflicts(conflicts.ConflictList())
574
tree_a.commit('', rev_id='rev3a')
575
tree_b.commit('', rev_id='rev3b')
576
out, err = self.run_bzr(['merge', '-d', 'a', 'b', '--lca'], retcode=1)
577
self.assertFileEqual('base-contents\n<<<<<<< TREE\nthis-contents\n'
578
'=======\nother-contents\n>>>>>>> MERGE-SOURCE\n',
581
def test_merge_preview(self):
582
this_tree = self.make_branch_and_tree('this')
583
this_tree.commit('rev1')
584
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
585
self.build_tree_contents([('other/file', 'new line')])
586
other_tree.add('file')
587
other_tree.commit('rev2a')
588
this_tree.commit('rev2b')
589
out, err = self.run_bzr(['merge', '-d', 'this', 'other', '--preview'])
590
self.assertContainsRe(out, '\+new line')
591
self.assertNotContainsRe(err, '\+N file\n')
592
this_tree.lock_read()
593
self.addCleanup(this_tree.unlock)
595
list(this_tree.iter_changes(this_tree.basis_tree())))
597
def test_merge_missing_second_revision_spec(self):
598
"""Merge uses branch basis when the second revision is unspecified."""
599
this = self.make_branch_and_tree('this')
601
other = self.make_branch_and_tree('other')
602
self.build_tree(['other/other_file'])
603
other.add('other_file')
604
other.commit('rev1b')
605
self.run_bzr('merge -d this other -r0..')
606
self.failUnlessExists('this/other_file')
608
def test_merge_interactive_unlocks_branch(self):
609
this = self.make_branch_and_tree('this')
610
other = self.make_branch_and_tree('other')
611
other.commit('empty commit')
612
self.run_bzr('merge -i -d this other')
616
def test_merge_reversed_revision_range(self):
617
tree = self.make_branch_and_tree(".")
621
tree.commit("added "+f)
622
for context in (".", "", "a"):
623
self.run_bzr("merge -r 1..0 " + context)
624
self.failIfExists("a")
626
self.failUnlessExists("a")
629
class TestMergeForce(tests.TestCaseWithTransport):
632
super(TestMergeForce, self).setUp()
633
self.tree_a = self.make_branch_and_tree('a')
634
self.build_tree(['a/foo'])
635
self.tree_a.add(['foo'])
636
self.tree_a.commit('add file')
637
self.tree_b = self.tree_a.bzrdir.sprout('b').open_workingtree()
638
self.build_tree_contents([('a/foo', 'change 1')])
639
self.tree_a.commit('change file')
640
self.tree_b.merge_from_branch(self.tree_a.branch)
642
def test_merge_force(self):
643
self.tree_a.commit('empty change to allow merge to run')
644
# Second merge on top of the uncommitted one
645
self.run_bzr(['merge', '../a', '--force'], working_dir='b')
648
def test_merge_with_uncommitted_changes(self):
649
self.run_bzr_error(['Working tree .* has uncommitted changes'],
650
['merge', '../a'], working_dir='b')
652
def test_merge_with_pending_merges(self):
653
# Revert the changes keeping the pending merge
654
self.run_bzr(['revert', 'b'])
655
self.run_bzr_error(['Working tree .* has uncommitted changes'],
656
['merge', '../a'], working_dir='b')