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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
# Author: Aaron Bentley <aaron.bentley@utoronto.ca>
19
19
"""Black-box tests for bzr merge.
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)
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')
77
44
def test_merge_reprocess(self):
78
d = bzrdir.BzrDir.create_standalone_workingtree('.')
45
d = BzrDir.create_standalone_workingtree('.')
80
self.run_bzr('merge . --reprocess --merge-type weave')
47
self.run_bzr('merge', '.', '--reprocess', '--merge-type', 'weave')
82
49
def test_merge(self):
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")
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"])
89
self.build_tree_contents([('a/hello', 'quuux')])
62
file('hello', 'wt').write('quuux')
90
63
# We can't merge when there are in-tree changes
92
self.run_bzr('merge ../b', retcode=3)
93
a = workingtree.WorkingTree.open('.')
94
a_tip = a.commit("Like an epidemic of u's")
95
self.run_bzr('merge ../b -r last:1..last:1 --merge-type blooof',
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',
97
self.run_bzr('merge ../b -r last:1..last:1 --merge-type merge3')
98
a_tree.revert(backups=False)
99
self.run_bzr('merge ../b -r last:1..last:1 --merge-type weave')
100
a_tree.revert(backups=False)
101
self.run_bzr('merge ../b -r last:1..last:1 --merge-type lca')
102
a_tree.revert(backups=False)
103
self.run_bzr_error(['Show-base is not supported for this merge type'],
104
'merge ../b -r last:1..last:1 --merge-type weave'
106
a_tree.revert(backups=False)
107
self.run_bzr('merge ../b -r last:1..last:1 --reprocess')
108
a_tree.revert(backups=False)
109
self.run_bzr('merge ../b -r last:1')
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')
110
75
self.check_file_contents('goodbye', 'quux')
111
76
# Merging a branch pulls its revision into the tree
112
b = branch.Branch.open('../b')
113
b_tip = b.last_revision()
114
self.assertTrue(a.branch.repository.has_revision(b_tip))
115
self.assertEqual([a_tip, b_tip], a.get_parent_ids())
116
a_tree.revert(backups=False)
117
out, err = self.run_bzr('merge -r revno:1:./hello', retcode=3)
118
self.assertTrue("Not a branch" in err)
119
self.run_bzr('merge -r revno:%d:./..revno:%d:../b'
120
%(ancestor,b.revno()))
121
self.assertEquals(a.get_parent_ids(),
122
[a.branch.last_revision(), b.last_revision()])
123
self.check_file_contents('goodbye', 'quux')
124
a_tree.revert(backups=False)
125
self.run_bzr('merge -r revno:%d:../b'%b.revno())
126
self.assertEquals(a.get_parent_ids(),
127
[a.branch.last_revision(), b.last_revision()])
128
a_tip = a.commit('merged')
129
self.run_bzr('merge ../b -r last:1')
130
self.assertEqual([a_tip], a.get_parent_ids())
132
def test_merge_defaults_to_reprocess(self):
133
tree, other = self.create_conflicting_branches()
134
# The default merge algorithm should enable 'reprocess' because
135
# 'show-base' is not set
136
self.run_bzr('merge ../other', working_dir='tree',
138
self.assertEqualDiff('a\n'
144
'>>>>>>> MERGE-SOURCE\n',
145
tree.get_file_text('f-id'))
147
def test_merge_explicit_reprocess_show_base(self):
148
tree, other = self.create_conflicting_branches()
149
# Explicitly setting --reprocess, and --show-base is an error
150
self.run_bzr_error(['Cannot do conflict reduction and show base'],
151
'merge ../other --reprocess --show-base',
154
def test_merge_override_reprocess(self):
155
tree, other = self.create_conflicting_branches()
156
# Explicitly disable reprocess
157
self.run_bzr('merge ../other --no-reprocess', working_dir='tree',
159
self.assertEqualDiff('a\n'
166
'>>>>>>> MERGE-SOURCE\n',
167
tree.get_file_text('f-id'))
169
def test_merge_override_show_base(self):
170
tree, other = self.create_conflicting_branches()
171
# Setting '--show-base' will auto-disable '--reprocess'
172
self.run_bzr('merge ../other --show-base', working_dir='tree',
174
self.assertEqualDiff('a\n'
178
'||||||| BASE-REVISION\n'
184
'>>>>>>> MERGE-SOURCE\n',
185
tree.get_file_text('f-id'))
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(), [])
187
87
def test_merge_with_missing_file(self):
188
88
"""Merge handles missing file conflicts"""
189
self.build_tree_contents([
192
('a/sub/a.txt', 'hello\n'),
193
('a/b.txt', 'hello\n'),
194
('a/sub/c.txt', 'hello\n')])
195
a_tree = self.make_branch_and_tree('a')
196
a_tree.add(['sub', 'b.txt', 'sub/c.txt', 'sub/a.txt'])
197
a_tree.commit(message='added a')
198
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
199
self.build_tree_contents([
200
('a/sub/a.txt', 'hello\nthere\n'),
201
('a/b.txt', 'hello\nthere\n'),
202
('a/sub/c.txt', 'hello\nthere\n')])
203
a_tree.commit(message='Added there')
204
os.remove('a/sub/a.txt')
205
os.remove('a/sub/c.txt')
208
a_tree.commit(message='Removed a.txt')
209
self.build_tree_contents([
210
('b/sub/a.txt', 'hello\nsomething\n'),
211
('b/b.txt', 'hello\nsomething\n'),
212
('b/sub/c.txt', 'hello\nsomething\n')])
213
b_tree.commit(message='Modified a.txt')
215
self.run_bzr('merge ../a/', retcode=1)
216
self.assertPathExists('sub/a.txt.THIS')
217
self.assertPathExists('sub/a.txt.BASE')
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'))
219
self.run_bzr('merge ../b/', retcode=1)
220
self.assertPathExists('sub/a.txt.OTHER')
221
self.assertPathExists('sub/a.txt.BASE')
223
def test_conflict_leaves_base_this_other_files(self):
224
tree, other = self.create_conflicting_branches()
225
self.run_bzr('merge ../other', working_dir='tree',
227
self.assertFileEqual('a\nb\nc\n', 'tree/fname.BASE')
228
self.assertFileEqual('a\nB\nD\n', 'tree/fname.OTHER')
229
self.assertFileEqual('a\nB\nC\n', 'tree/fname.THIS')
231
def test_weave_conflict_leaves_base_this_other_files(self):
232
tree, other = self.create_conflicting_branches()
233
self.run_bzr('merge ../other --weave', working_dir='tree',
235
self.assertFileEqual('a\nb\nc\n', 'tree/fname.BASE')
236
self.assertFileEqual('a\nB\nD\n', 'tree/fname.OTHER')
237
self.assertFileEqual('a\nB\nC\n', 'tree/fname.THIS')
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'))
239
121
def test_merge_remember(self):
240
"""Merge changes from one branch to another, test submit location."""
122
"""Merge changes from one branch to another and test parent location."""
241
123
tree_a = self.make_branch_and_tree('branch_a')
242
124
branch_a = tree_a.branch
243
125
self.build_tree(['branch_a/a'])
259
141
self.assertEqual(None, branch_b.get_parent())
260
142
# test merge for failure without parent set
261
143
os.chdir('branch_b')
262
out = self.run_bzr('merge', retcode=3)
144
out = self.runbzr('merge', retcode=3)
263
145
self.assertEquals(out,
264
146
('','bzr: ERROR: No location specified or remembered\n'))
266
# test uncommitted changes
147
# test implicit --remember when no parent set, this merge conflicts
267
148
self.build_tree(['d'])
269
self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
272
# merge should now pass and implicitly remember merge location
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
273
155
tree_b.commit('commit d')
274
out, err = self.run_bzr('merge ../branch_a')
156
out, err = self.runbzr('merge')
276
158
base = urlutils.local_path_from_url(branch_a.base)
277
self.assertEndsWith(err, '+N b\nAll changes applied successfully.\n')
278
self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
279
osutils.abspath(parent))
280
# test implicit --remember when committing new file
281
self.build_tree(['e'])
283
tree_b.commit('commit e')
284
out, err = self.run_bzr('merge')
285
self.assertStartsWith(err,
286
'Merging from remembered submit location %s\n' % (base,))
287
# re-open tree as external run_bzr modified it
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
288
163
tree_b = branch_b.bzrdir.open_workingtree()
289
164
tree_b.commit('merge branch_a')
290
165
# test explicit --remember
291
out, err = self.run_bzr('merge ../branch_c --remember')
166
out, err = self.runbzr('merge ../branch_c --remember')
292
167
self.assertEquals(out, '')
293
self.assertEquals(err, '+N c\nAll changes applied successfully.\n')
294
self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
295
osutils.abspath(branch_c.bzrdir.root_transport.base))
296
# re-open tree as external run_bzr modified it
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
297
172
tree_b = branch_b.bzrdir.open_workingtree()
298
173
tree_b.commit('merge branch_c')
300
175
def test_merge_bundle(self):
301
176
from bzrlib.testament import Testament
302
177
tree_a = self.make_branch_and_tree('branch_a')
303
self.build_tree_contents([('branch_a/a', 'hello')])
178
f = file('branch_a/a', 'wb')
305
182
tree_a.commit('message')
307
184
tree_b = tree_a.bzrdir.sprout('branch_b').open_workingtree()
308
self.build_tree_contents([('branch_a/a', 'hey there')])
185
f = file('branch_a/a', 'wb')
309
188
tree_a.commit('message')
311
self.build_tree_contents([('branch_b/a', 'goodbye')])
190
f = file('branch_b/a', 'wb')
312
193
tree_b.commit('message')
313
194
os.chdir('branch_b')
314
self.run_bzr('bundle ../branch_a -o ../bundle')
195
file('../bundle', 'wb').write(self.runbzr('bundle ../branch_a')[0])
315
196
os.chdir('../branch_a')
316
self.run_bzr('merge ../bundle', retcode=1)
317
testament_a = Testament.from_revision(tree_a.branch.repository,
318
tree_b.get_parent_ids()[0])
197
self.runbzr('merge ../bundle', retcode=1)
198
testament_a = Testament.from_revision(tree_a.branch.repository,
199
tree_b.last_revision())
319
200
testament_b = Testament.from_revision(tree_b.branch.repository,
320
tree_b.get_parent_ids()[0])
201
tree_b.last_revision())
321
202
self.assertEqualDiff(testament_a.as_text(),
322
203
testament_b.as_text())
323
tree_a.set_conflicts(conflicts.ConflictList())
204
tree_a.set_conflicts(ConflictList())
324
205
tree_a.commit('message')
325
206
# it is legal to attempt to merge an already-merged bundle
326
output = self.run_bzr('merge ../bundle')[1]
207
output = self.runbzr('merge ../bundle')[1]
327
208
# but it does nothing
328
209
self.assertFalse(tree_a.changes_from(tree_a.basis_tree()).has_changed())
329
210
self.assertEqual('Nothing to do.\n', output)
331
def test_merge_uncommitted(self):
332
"""Check that merge --uncommitted behaves properly"""
333
tree_a = self.make_branch_and_tree('a')
334
self.build_tree(['a/file_1', 'a/file_2'])
335
tree_a.add(['file_1', 'file_2'])
336
tree_a.commit('commit 1')
337
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
338
self.assertPathExists('b/file_1')
339
tree_a.rename_one('file_1', 'file_i')
340
tree_a.commit('commit 2')
341
tree_a.rename_one('file_2', 'file_ii')
343
self.run_bzr('merge a --uncommitted -d b')
344
self.assertPathExists('b/file_1')
345
self.assertPathExists('b/file_ii')
347
self.run_bzr_error(('Cannot use --uncommitted and --revision',),
348
'merge /a --uncommitted -r1 -d b')
350
def test_merge_uncommitted_file(self):
351
"""It should be possible to merge changes from a single file."""
352
tree_a = self.make_branch_and_tree('tree_a')
353
tree_a.commit('initial commit')
354
tree_a.bzrdir.sprout('tree_b')
355
self.build_tree(['tree_a/file1', 'tree_a/file2'])
356
tree_a.add(['file1', 'file2'])
358
self.run_bzr(['merge', '--uncommitted', '../tree_a/file1'])
359
self.assertPathExists('file1')
360
self.assertPathDoesNotExist('file2')
362
def test_merge_nonexistent_file(self):
363
"""It should not be possible to merge changes from a file which
365
tree_a = self.make_branch_and_tree('tree_a')
366
self.build_tree_contents([('tree_a/file', 'bar\n')])
368
tree_a.commit('commit 1')
370
self.run_bzr_error(('Path\(s\) do not exist: non/existing',),
371
['merge', 'non/existing'])
373
def pullable_branch(self):
374
tree_a = self.make_branch_and_tree('a')
375
self.build_tree_contents([('a/file', 'bar\n')])
377
self.id1 = tree_a.commit('commit 1')
379
tree_b = self.make_branch_and_tree('b')
380
tree_b.pull(tree_a.branch)
381
self.build_tree_contents([('b/file', 'foo\n')])
382
self.id2 = tree_b.commit('commit 2')
384
def test_merge_pull(self):
385
self.pullable_branch()
387
(out, err) = self.run_bzr('merge --pull ../b')
388
self.assertContainsRe(out, 'Now on revision 2\\.')
389
tree_a = workingtree.WorkingTree.open('.')
390
self.assertEqual([self.id2], tree_a.get_parent_ids())
392
def test_merge_pull_preview(self):
393
self.pullable_branch()
394
(out, err) = self.run_bzr('merge --pull --preview -d a b')
395
self.assertThat(out, matchers.DocTestMatches(
396
"""=== modified file 'file'
403
""", doctest.ELLIPSIS | doctest.REPORT_UDIFF))
404
tree_a = workingtree.WorkingTree.open('a')
405
self.assertEqual([self.id1], tree_a.get_parent_ids())
407
def test_merge_kind_change(self):
408
tree_a = self.make_branch_and_tree('tree_a')
409
self.build_tree_contents([('tree_a/file', 'content_1')])
410
tree_a.add('file', 'file-id')
411
tree_a.commit('added file')
412
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
413
os.unlink('tree_a/file')
414
self.build_tree(['tree_a/file/'])
415
tree_a.commit('changed file to directory')
417
self.run_bzr('merge ../tree_a')
418
self.assertEqual('directory', osutils.file_kind('file'))
420
self.assertEqual('file', osutils.file_kind('file'))
421
self.build_tree_contents([('file', 'content_2')])
422
tree_b.commit('content change')
423
self.run_bzr('merge ../tree_a', retcode=1)
424
self.assertEqual(tree_b.conflicts(),
425
[conflicts.ContentsConflict('file',
428
def test_directive_cherrypick(self):
429
source = self.make_branch_and_tree('source')
430
source.commit("nothing")
431
# see https://bugs.launchpad.net/bzr/+bug/409688 - trying to
432
# cherrypick from one branch into another unrelated branch with a
433
# different root id will give shape conflicts. as a workaround we
434
# make sure they share the same root id.
435
target = source.bzrdir.sprout('target').open_workingtree()
436
self.build_tree(['source/a'])
438
source.commit('Added a', rev_id='rev1')
439
self.build_tree(['source/b'])
441
source.commit('Added b', rev_id='rev2')
442
target.commit('empty commit')
443
self.write_directive('directive', source.branch, 'target', 'rev2',
445
out, err = self.run_bzr('merge -d target directive')
446
self.assertPathDoesNotExist('target/a')
447
self.assertPathExists('target/b')
448
self.assertContainsRe(err, 'Performing cherrypick')
450
def write_directive(self, filename, source, target, revision_id,
451
base_revision_id=None, mangle_patch=False):
452
md = merge_directive.MergeDirective2.from_objects(
453
source.repository, revision_id, 0, 0, target,
454
base_revision_id=base_revision_id)
457
self.build_tree_contents([(filename, ''.join(md.to_lines()))])
459
def test_directive_verify_warning(self):
460
source = self.make_branch_and_tree('source')
461
self.build_tree(['source/a'])
463
source.commit('Added a', rev_id='rev1')
464
target = self.make_branch_and_tree('target')
465
target.commit('empty commit')
466
self.write_directive('directive', source.branch, 'target', 'rev1')
467
err = self.run_bzr('merge -d target directive')[1]
468
self.assertNotContainsRe(err, 'Preview patch does not match changes')
470
self.write_directive('directive', source.branch, 'target', 'rev1',
472
err = self.run_bzr('merge -d target directive')[1]
473
self.assertContainsRe(err, 'Preview patch does not match changes')
475
def test_merge_arbitrary(self):
476
target = self.make_branch_and_tree('target')
477
target.commit('empty')
478
# We need a revision that has no integer revno
479
branch_a = target.bzrdir.sprout('branch_a').open_workingtree()
480
self.build_tree(['branch_a/file1'])
481
branch_a.add('file1')
482
branch_a.commit('added file1', rev_id='rev2a')
483
branch_b = target.bzrdir.sprout('branch_b').open_workingtree()
484
self.build_tree(['branch_b/file2'])
485
branch_b.add('file2')
486
branch_b.commit('added file2', rev_id='rev2b')
487
branch_b.merge_from_branch(branch_a.branch)
488
self.assertPathExists('branch_b/file1')
489
branch_b.commit('merged branch_a', rev_id='rev3b')
491
# It works if the revid has an interger revno
492
self.run_bzr('merge -d target -r revid:rev2a branch_a')
493
self.assertPathExists('target/file1')
494
self.assertPathDoesNotExist('target/file2')
497
# It should work if the revid has no integer revno
498
self.run_bzr('merge -d target -r revid:rev2a branch_b')
499
self.assertPathExists('target/file1')
500
self.assertPathDoesNotExist('target/file2')
502
def assertDirectoryContent(self, directory, entries, message=''):
503
"""Assert whether entries (file or directories) exist in a directory.
505
It also checks that there are no extra entries.
507
ondisk = os.listdir(directory)
508
if set(ondisk) == set(entries):
512
raise AssertionError(
513
'%s"%s" directory content is different:\na = %s\nb = %s\n'
514
% (message, directory, sorted(entries), sorted(ondisk)))
516
def test_cherrypicking_merge(self):
518
source = self.make_branch_and_tree('source')
519
for f in ('a', 'b', 'c', 'd'):
520
self.build_tree(['source/'+f])
522
source.commit('added '+f, rev_id='rev_'+f)
524
target = source.bzrdir.sprout('target', 'rev_a').open_workingtree()
525
self.assertDirectoryContent('target', ['.bzr', 'a'])
527
self.run_bzr('merge -d target -r revid:rev_b..revid:rev_c source')
528
self.assertDirectoryContent('target', ['.bzr', 'a', 'c'])
531
self.run_bzr('merge -d target -r revid:rev_b..revid:rev_d source')
532
self.assertDirectoryContent('target', ['.bzr', 'a', 'c', 'd'])
534
# pick 1 revision with option --changes
535
self.run_bzr('merge -d target -c revid:rev_d source')
536
self.assertDirectoryContent('target', ['.bzr', 'a', 'd'])
538
def test_merge_criss_cross(self):
539
tree_a = self.make_branch_and_tree('a')
540
tree_a.commit('', rev_id='rev1')
541
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
542
tree_a.commit('', rev_id='rev2a')
543
tree_b.commit('', rev_id='rev2b')
544
tree_a.merge_from_branch(tree_b.branch)
545
tree_b.merge_from_branch(tree_a.branch)
546
tree_a.commit('', rev_id='rev3a')
547
tree_b.commit('', rev_id='rev3b')
548
graph = tree_a.branch.repository.get_graph(tree_b.branch.repository)
549
out, err = self.run_bzr(['merge', '-d', 'a', 'b'])
550
self.assertContainsRe(err, 'Warning: criss-cross merge encountered.')
552
def test_merge_from_submit(self):
553
tree_a = self.make_branch_and_tree('a')
554
tree_a.commit('test')
555
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
556
tree_c = tree_a.bzrdir.sprout('c').open_workingtree()
557
out, err = self.run_bzr(['merge', '-d', 'c'])
558
self.assertContainsRe(err, 'Merging from remembered parent location .*a\/')
559
tree_c.branch.set_submit_branch(tree_b.bzrdir.root_transport.base)
560
out, err = self.run_bzr(['merge', '-d', 'c'])
561
self.assertContainsRe(err, 'Merging from remembered submit location .*b\/')
563
def test_remember_sets_submit(self):
564
tree_a = self.make_branch_and_tree('a')
565
tree_a.commit('rev1')
566
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
567
self.assertIs(tree_b.branch.get_submit_branch(), None)
569
# Remember should not happen if using default from parent
570
out, err = self.run_bzr(['merge', '-d', 'b'])
571
self.assertIs(tree_b.branch.get_submit_branch(), None)
573
# Remember should happen if user supplies location
574
out, err = self.run_bzr(['merge', '-d', 'b', 'a'])
575
self.assertEqual(tree_b.branch.get_submit_branch(),
576
tree_a.bzrdir.root_transport.base)
578
def test_no_remember_dont_set_submit(self):
579
tree_a = self.make_branch_and_tree('a')
580
self.build_tree_contents([('a/file', "a\n")])
582
tree_a.commit('rev1')
583
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
584
self.assertIs(tree_b.branch.get_submit_branch(), None)
586
# Remember should not happen if using default from parent
587
out, err = self.run_bzr(['merge', '-d', 'b', '--no-remember'])
588
self.assertEquals(None, tree_b.branch.get_submit_branch())
590
# Remember should not happen if user supplies location but ask for not
592
out, err = self.run_bzr(['merge', '-d', 'b', '--no-remember', 'a'])
593
self.assertEqual(None, tree_b.branch.get_submit_branch())
595
def test_weave_cherrypick(self):
596
this_tree = self.make_branch_and_tree('this')
597
self.build_tree_contents([('this/file', "a\n")])
598
this_tree.add('file')
599
this_tree.commit('rev1')
600
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
601
self.build_tree_contents([('other/file', "a\nb\n")])
602
other_tree.commit('rev2b')
603
self.build_tree_contents([('other/file', "c\na\nb\n")])
604
other_tree.commit('rev3b')
605
self.run_bzr('merge --weave -d this other -r -2..-1')
606
self.assertFileEqual('c\na\n', 'this/file')
608
def test_lca_merge_criss_cross(self):
609
tree_a = self.make_branch_and_tree('a')
610
self.build_tree_contents([('a/file', 'base-contents\n')])
612
tree_a.commit('', rev_id='rev1')
613
tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
614
self.build_tree_contents([('a/file',
615
'base-contents\nthis-contents\n')])
616
tree_a.commit('', rev_id='rev2a')
617
self.build_tree_contents([('b/file',
618
'base-contents\nother-contents\n')])
619
tree_b.commit('', rev_id='rev2b')
620
tree_a.merge_from_branch(tree_b.branch)
621
self.build_tree_contents([('a/file',
622
'base-contents\nthis-contents\n')])
623
tree_a.set_conflicts(conflicts.ConflictList())
624
tree_b.merge_from_branch(tree_a.branch)
625
self.build_tree_contents([('b/file',
626
'base-contents\nother-contents\n')])
627
tree_b.set_conflicts(conflicts.ConflictList())
628
tree_a.commit('', rev_id='rev3a')
629
tree_b.commit('', rev_id='rev3b')
630
out, err = self.run_bzr(['merge', '-d', 'a', 'b', '--lca'], retcode=1)
631
self.assertFileEqual('base-contents\n<<<<<<< TREE\nthis-contents\n'
632
'=======\nother-contents\n>>>>>>> MERGE-SOURCE\n',
635
def test_merge_preview(self):
636
this_tree = self.make_branch_and_tree('this')
637
this_tree.commit('rev1')
638
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
639
self.build_tree_contents([('other/file', 'new line')])
640
other_tree.add('file')
641
other_tree.commit('rev2a')
642
this_tree.commit('rev2b')
643
out, err = self.run_bzr(['merge', '-d', 'this', 'other', '--preview'])
644
self.assertContainsRe(out, '\+new line')
645
self.assertNotContainsRe(err, '\+N file\n')
646
this_tree.lock_read()
647
self.addCleanup(this_tree.unlock)
649
list(this_tree.iter_changes(this_tree.basis_tree())))
651
def test_merge_missing_second_revision_spec(self):
652
"""Merge uses branch basis when the second revision is unspecified."""
653
this = self.make_branch_and_tree('this')
655
other = self.make_branch_and_tree('other')
656
self.build_tree(['other/other_file'])
657
other.add('other_file')
658
other.commit('rev1b')
659
self.run_bzr('merge -d this other -r0..')
660
self.assertPathExists('this/other_file')
662
def test_merge_interactive_unlocks_branch(self):
663
this = self.make_branch_and_tree('this')
664
this.commit('empty commit')
665
other = this.bzrdir.sprout('other').open_workingtree()
666
other.commit('empty commit 2')
667
self.run_bzr('merge -i -d this other')
671
def test_merge_fetches_tags(self):
672
"""Tags are updated by merge, and revisions named in those tags are
675
# Make a source, sprout a target off it
676
builder = self.make_branch_builder('source')
677
builder.build_commit(message="Rev 1", rev_id='rev-1')
678
source = builder.get_branch()
679
target_bzrdir = source.bzrdir.sprout('target')
680
# Add a non-ancestry tag to source
681
builder.build_commit(message="Rev 2a", rev_id='rev-2a')
682
source.tags.set_tag('tag-a', 'rev-2a')
683
source.set_last_revision_info(1, 'rev-1')
684
builder.build_commit(message="Rev 2b", rev_id='rev-2b')
686
self.run_bzr('merge -d target source')
687
target = target_bzrdir.open_branch()
688
# The tag is present, and so is its revision.
689
self.assertEqual('rev-2a', target.tags.lookup_tag('tag-a'))
690
target.repository.get_revision('rev-2a')
693
class TestMergeRevisionRange(tests.TestCaseWithTransport):
695
scenarios = (('whole-tree', dict(context='.')),
696
('file-only', dict(context='a')))
699
super(TestMergeRevisionRange, self).setUp()
700
self.tree = self.make_branch_and_tree(".")
701
self.tree.commit('initial commit')
705
self.tree.commit("added " + f)
707
def test_merge_reversed_revision_range(self):
708
self.run_bzr("merge -r 2..1 " + self.context)
709
self.assertPathDoesNotExist("a")
710
self.assertPathExists("b")
713
class TestMergeScript(script.TestCaseWithTransportAndScript):
714
def test_merge_empty_branch(self):
715
source = self.make_branch_and_tree('source')
716
self.build_tree(['source/a'])
718
source.commit('Added a', rev_id='rev1')
719
target = self.make_branch_and_tree('target')
721
$ bzr merge -d target source
722
2>bzr: ERROR: Merging into empty branches not currently supported, https://bugs.launchpad.net/bzr/+bug/308562
725
class TestMergeForce(tests.TestCaseWithTransport):
728
super(TestMergeForce, self).setUp()
729
self.tree_a = self.make_branch_and_tree('a')
730
self.build_tree(['a/foo'])
731
self.tree_a.add(['foo'])
732
self.tree_a.commit('add file')
733
self.tree_b = self.tree_a.bzrdir.sprout('b').open_workingtree()
734
self.build_tree_contents([('a/foo', 'change 1')])
735
self.tree_a.commit('change file')
736
self.tree_b.merge_from_branch(self.tree_a.branch)
738
def test_merge_force(self):
739
self.tree_a.commit('empty change to allow merge to run')
740
# Second merge on top of the uncommitted one
741
self.run_bzr(['merge', '../a', '--force'], working_dir='b')
744
def test_merge_with_uncommitted_changes(self):
745
self.run_bzr_error(['Working tree .* has uncommitted changes'],
746
['merge', '../a'], working_dir='b')
748
def test_merge_with_pending_merges(self):
749
# Revert the changes keeping the pending merge
750
self.run_bzr(['revert', 'b'])
751
self.run_bzr_error(['Working tree .* has uncommitted changes'],
752
['merge', '../a'], working_dir='b')