1
# Copyright (C) 2005, 2006 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
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
6
from bzrlib.add import smart_add_tree
7
from bzrlib.builtins import merge
8
27
from bzrlib.conflicts import ContentsConflict, TextConflict, PathConflict
28
from bzrlib import errors
9
29
from bzrlib.errors import (NotBranchError, NotVersionedError,
10
30
WorkingTreeNotRevision, BzrCommandError, NoDiff3)
11
import bzrlib.inventory as inventory
12
from bzrlib.merge import Merge3Merger, Diff3Merger, WeaveMerger
13
from bzrlib.osutils import (file_kind, getcwd, mkdtemp, pathjoin, rename, rmtree,
31
from bzrlib import inventory
32
from bzrlib.merge import (
38
from bzrlib.osutils import (file_kind, getcwd, pathjoin, rename,
41
from bzrlib import progress
16
42
from bzrlib.transform import TreeTransform
17
43
from bzrlib.tests import TestCaseWithTransport, TestCase, TestSkipped
18
44
from bzrlib.workingtree import WorkingTree
176
214
def test_change_name(self):
177
215
"""Test renames"""
178
216
builder = MergeBuilder(getcwd())
179
builder.add_file("1", "TREE_ROOT", "name1", "hello1", True)
217
builder.add_file("1", builder.tree_root, "name1", "hello1", True)
180
218
builder.change_name("1", other="name2")
181
builder.add_file("2", "TREE_ROOT", "name3", "hello2", True)
219
builder.add_file("2", builder.tree_root, "name3", "hello2", True)
182
220
builder.change_name("2", base="name4")
183
builder.add_file("3", "TREE_ROOT", "name5", "hello3", True)
221
builder.add_file("3", builder.tree_root, "name5", "hello3", True)
184
222
builder.change_name("3", this="name6")
186
224
builder.cleanup()
187
225
builder = MergeBuilder(getcwd())
188
builder.add_file("1", "TREE_ROOT", "name1", "hello1", False)
226
builder.add_file("1", builder.tree_root, "name1", "hello1", False)
189
227
builder.change_name("1", other="name2", this="name3")
190
228
conflicts = builder.merge()
191
229
self.assertEqual(conflicts, [PathConflict('name3', 'name2', '1')])
194
232
def test_merge_one(self):
195
233
builder = MergeBuilder(getcwd())
196
builder.add_file("1", "TREE_ROOT", "name1", "hello1", True)
234
builder.add_file("1", builder.tree_root, "name1", "hello1", True)
197
235
builder.change_contents("1", other="text4")
198
builder.add_file("2", "TREE_ROOT", "name2", "hello1", True)
236
builder.add_file("2", builder.tree_root, "name2", "hello1", True)
199
237
builder.change_contents("2", other="text4")
200
238
builder.merge(interesting_ids=["1"])
201
239
self.assertEqual(builder.this.get_file("1").read(), "text4" )
205
243
def test_file_moves(self):
207
245
builder = MergeBuilder(getcwd())
208
builder.add_dir("1", "TREE_ROOT", "dir1")
209
builder.add_dir("2", "TREE_ROOT", "dir2")
246
builder.add_dir("1", builder.tree_root, "dir1")
247
builder.add_dir("2", builder.tree_root, "dir2")
210
248
builder.add_file("3", "1", "file1", "hello1", True)
211
249
builder.add_file("4", "1", "file2", "hello2", True)
212
250
builder.add_file("5", "1", "file3", "hello3", True)
217
255
builder.cleanup()
219
257
builder = MergeBuilder(getcwd())
220
builder.add_dir("1", "TREE_ROOT", "dir1")
221
builder.add_dir("2", "TREE_ROOT", "dir2")
222
builder.add_dir("3", "TREE_ROOT", "dir3")
258
builder.add_dir("1", builder.tree_root, "dir1")
259
builder.add_dir("2", builder.tree_root, "dir2")
260
builder.add_dir("3", builder.tree_root, "dir3")
223
261
builder.add_file("4", "1", "file1", "hello1", False)
224
262
builder.change_parent("4", other="2", this="3")
225
263
conflicts = builder.merge()
274
315
def contents_test_success(self, merge_factory):
275
316
builder = MergeBuilder(getcwd())
276
builder.add_file("1", "TREE_ROOT", "name1", "text1", True)
317
builder.add_file("1", builder.tree_root, "name1", "text1", True)
277
318
builder.change_contents("1", other="text4")
278
builder.add_file("2", "TREE_ROOT", "name3", "text2", False)
319
builder.add_file("2", builder.tree_root, "name3", "text2", False)
279
320
builder.change_contents("2", base="text5")
280
builder.add_file("3", "TREE_ROOT", "name5", "text3", True)
281
builder.add_file("4", "TREE_ROOT", "name6", "text4", True)
321
builder.add_file("3", builder.tree_root, "name5", "text3", True)
322
builder.add_file("4", builder.tree_root, "name6", "text4", True)
282
323
builder.remove_file("4", base=True)
283
builder.add_file("5", "TREE_ROOT", "name7", "a\nb\nc\nd\ne\nf\n", True)
324
builder.add_file("5", builder.tree_root, "name7", "a\nb\nc\nd\ne\nf\n",
284
326
builder.change_contents("5", other="a\nz\nc\nd\ne\nf\n",
285
327
this="a\nb\nc\nd\ne\nz\n")
286
builder.merge(merge_factory)
287
self.assertEqual(builder.this.get_file("1").read(), "text4" )
288
self.assertEqual(builder.this.get_file("2").read(), "text2" )
289
self.assertEqual(builder.this.get_file("5").read(),
290
"a\nz\nc\nd\ne\nz\n")
291
self.assertIs(builder.this.is_executable("1"), True)
292
self.assertIs(builder.this.is_executable("2"), False)
293
self.assertIs(builder.this.is_executable("3"), True)
328
conflicts = builder.merge(merge_factory)
330
self.assertEqual([], conflicts)
331
self.assertEqual("text4", builder.this.get_file("1").read())
332
self.assertEqual("text2", builder.this.get_file("2").read())
333
self.assertEqual("a\nz\nc\nd\ne\nz\n",
334
builder.this.get_file("5").read())
335
self.assertTrue(builder.this.is_executable("1"))
336
self.assertFalse(builder.this.is_executable("2"))
337
self.assertTrue(builder.this.is_executable("3"))
296
343
def contents_test_conflicts(self, merge_factory):
297
344
builder = MergeBuilder(getcwd())
298
builder.add_file("1", "TREE_ROOT", "name1", "text1", True)
345
builder.add_file("1", builder.tree_root, "name1", "text1", True)
299
346
builder.change_contents("1", other="text4", this="text3")
300
builder.add_file("2", "TREE_ROOT", "name2", "text1", True)
347
builder.add_file("2", builder.tree_root, "name2", "text1", True)
301
348
builder.change_contents("2", other="\x00", this="text3")
302
builder.add_file("3", "TREE_ROOT", "name3", "text5", False)
349
builder.add_file("3", builder.tree_root, "name3", "text5", False)
303
350
builder.change_perms("3", this=True)
304
351
builder.change_contents('3', this='moretext')
305
352
builder.remove_file('3', other=True)
323
370
def test_symlink_merge(self):
324
371
if sys.platform != "win32":
325
372
builder = MergeBuilder(getcwd())
326
builder.add_symlink("1", "TREE_ROOT", "name1", "target1")
327
builder.add_symlink("2", "TREE_ROOT", "name2", "target1")
328
builder.add_symlink("3", "TREE_ROOT", "name3", "target1")
373
builder.add_symlink("1", builder.tree_root, "name1", "target1")
374
builder.add_symlink("2", builder.tree_root, "name2", "target1")
375
builder.add_symlink("3", builder.tree_root, "name3", "target1")
329
376
builder.change_target("1", this="target2")
330
377
builder.change_target("2", base="target2")
331
378
builder.change_target("3", other="target2")
338
385
def test_no_passive_add(self):
339
386
builder = MergeBuilder(getcwd())
340
builder.add_file("1", "TREE_ROOT", "name1", "text1", True)
387
builder.add_file("1", builder.tree_root, "name1", "text1", True)
341
388
builder.remove_file("1", this=True)
343
390
builder.cleanup()
345
392
def test_perms_merge(self):
346
393
builder = MergeBuilder(getcwd())
347
builder.add_file("1", "TREE_ROOT", "name1", "text1", True)
394
builder.add_file("1", builder.tree_root, "name1", "text1", True)
348
395
builder.change_perms("1", other=False)
349
builder.add_file("2", "TREE_ROOT", "name2", "text2", True)
396
builder.add_file("2", builder.tree_root, "name2", "text2", True)
350
397
builder.change_perms("2", base=False)
351
builder.add_file("3", "TREE_ROOT", "name3", "text3", True)
398
builder.add_file("3", builder.tree_root, "name3", "text3", True)
352
399
builder.change_perms("3", this=False)
353
builder.add_file('4', 'TREE_ROOT', 'name4', 'text4', False)
400
builder.add_file('4', builder.tree_root, 'name4', 'text4', False)
354
401
builder.change_perms('4', this=True)
355
402
builder.remove_file('4', base=True)
362
409
def test_new_suffix(self):
363
410
builder = MergeBuilder(getcwd())
364
builder.add_file("1", "TREE_ROOT", "name1", "text1", True)
411
builder.add_file("1", builder.tree_root, "name1", "text1", True)
365
412
builder.change_contents("1", other="text3")
366
builder.add_file("2", "TREE_ROOT", "name1.new", "text2", True)
413
builder.add_file("2", builder.tree_root, "name1.new", "text2", True)
368
415
os.lstat(builder.this.id2abspath("2"))
369
416
builder.cleanup()
371
418
def test_spurious_conflict(self):
372
419
builder = MergeBuilder(getcwd())
373
builder.add_file("1", "TREE_ROOT", "name1", "text1", False)
420
builder.add_file("1", builder.tree_root, "name1", "text1", False)
374
421
builder.remove_file("1", other=True)
375
builder.add_file("2", "TREE_ROOT", "name1", "text1", False, this=False,
422
builder.add_file("2", builder.tree_root, "name1", "text1", False,
423
this=False, base=False)
377
424
conflicts = builder.merge()
378
425
self.assertEqual(conflicts, [])
379
426
builder.cleanup()
428
def test_merge_one_renamed(self):
429
builder = MergeBuilder(getcwd())
430
builder.add_file('1', builder.tree_root, 'name1', 'text1a', False)
431
builder.change_name('1', this='name2')
432
builder.change_contents('1', other='text2')
433
builder.merge(interesting_files=['name2'])
434
self.assertEqual('text2', builder.this.get_file('1').read())
382
437
class FunctionalMergeTest(TestCaseWithTransport):
406
461
mary_tree.commit("change file2")
407
462
# john should be able to merge with no conflicts.
408
merge_type = Merge3Merger
409
463
base = [None, None]
410
464
other = ("mary", -1)
411
self.assertRaises(BzrCommandError, merge, other, base, check_clean=True,
412
merge_type=WeaveMerger, this_dir="original",
414
merge(other, base, check_clean=True, merge_type=merge_type,
465
tree.merge_from_branch(mary_tree.branch)
416
466
self.assertEqual("John\n", open("original/file1", "rt").read())
417
467
self.assertEqual("Mary\n", open("original/file2", "rt").read())
430
480
file('b/file', 'wb').write('this contents contents\n')
431
481
wtb = d_b.open_workingtree()
432
482
wtb.commit('this revision', allow_pointless=False)
433
self.assertEqual(merge(['a', -1], [None, None], this_dir='b'), 1)
483
self.assertEqual(1, wtb.merge_from_branch(wta.branch))
434
484
self.assert_(os.path.lexists('b/file.THIS'))
435
485
self.assert_(os.path.lexists('b/file.BASE'))
436
486
self.assert_(os.path.lexists('b/file.OTHER'))
437
self.assertRaises(WorkingTreeNotRevision, merge, ['a', -1],
438
[None, None], this_dir='b', check_clean=False,
439
merge_type=WeaveMerger)
441
self.assertEqual(merge(['a', -1], [None, None], this_dir='b',
442
check_clean=False, merge_type=WeaveMerger), 1)
488
self.assertEqual(1, wtb.merge_from_branch(wta.branch,
489
merge_type=WeaveMerger))
443
490
self.assert_(os.path.lexists('b/file'))
444
491
self.assert_(os.path.lexists('b/file.THIS'))
445
492
self.assert_(not os.path.lexists('b/file.BASE'))
483
530
file('a/file', 'wb').write('contents\n')
485
532
wta.commit('a_revision', allow_pointless=False)
486
self.run_bzr('branch', 'a', 'b')
533
self.run_bzr('branch a b')
487
534
os.remove('a/file')
488
535
wta.commit('removed file', allow_pointless=False)
489
536
file('b/file', 'wb').write('changed contents\n')
490
537
wtb = WorkingTree.open('b')
491
538
wtb.commit('changed file', allow_pointless=False)
492
merge(['a', -1], ['a', 1], this_dir='b')
539
wtb.merge_from_branch(wta.branch, wta.branch.last_revision(),
540
wta.branch.get_rev_id(1))
493
541
self.failIf(os.path.lexists('b/file'))
495
543
def test_merge_metadata_vs_deletion(self):
506
554
self.assertEqual(a_wt.branch.revno(), 2)
507
555
self.assertFalse(os.path.exists('a/file'))
508
556
b_wt.commit('exec a')
509
merge(['b', -1], ['b', 0], this_dir='a')
557
a_wt.merge_from_branch(b_wt.branch, b_wt.last_revision(), 'null:')
510
558
self.assert_(os.path.exists('a/file'))
512
560
def test_merge_swapping_renames(self):
513
561
a_wt = self.make_branch_and_tree('a')
514
562
file('a/un','wb').write('UN')
515
563
file('a/deux','wb').write('DEUX')
517
a_wt.add('deux', 'deux')
564
a_wt.add('un', 'un-id')
565
a_wt.add('deux', 'deux-id')
518
566
a_wt.commit('r0', rev_id='r0')
519
self.run_bzr('branch', 'a', 'b')
567
self.run_bzr('branch a b')
520
568
b_wt = WorkingTree.open('b')
521
569
b_wt.rename_one('un','tmp')
522
570
b_wt.rename_one('deux','un')
523
571
b_wt.rename_one('tmp','deux')
524
572
b_wt.commit('r1', rev_id='r1')
525
self.assertEqual(0, merge(['b', -1], ['b', 1], this_dir='a'))
573
self.assertEqual(0, a_wt.merge_from_branch(b_wt.branch,
574
b_wt.branch.last_revision(), b_wt.branch.get_rev_id(1)))
526
575
self.failUnlessExists('a/un')
527
576
self.failUnless('a/deux')
528
577
self.assertFalse(os.path.exists('a/tmp'))
534
583
file('a/file', 'wb').write('THIS')
536
585
a_wt.commit('r0')
537
self.run_bzr('branch', 'a', 'b')
586
self.run_bzr('branch a b')
538
587
b_wt = WorkingTree.open('b')
539
588
os.remove('b/file')
540
589
b_wt.commit('r1')
541
590
file('b/file', 'wb').write('THAT')
543
592
b_wt.commit('r2')
544
merge(['b', -1],['b', 1],this_dir='a')
593
a_wt.merge_from_branch(b_wt.branch, b_wt.branch.last_revision(),
594
b_wt.branch.get_rev_id(1))
545
595
self.assert_(os.path.exists('a/file'))
546
596
self.assertEqual(file('a/file').read(),'THAT')
563
613
file('a/foo', 'wb').write('A/FOO')
565
615
a_wt.commit('added foo')
566
self.run_bzr('branch', 'a', 'b')
616
self.run_bzr('branch a b')
567
617
b_wt = WorkingTree.open('b')
568
618
b_wt.rename_one('foo', 'bar')
569
619
file('b/foo', 'wb').write('B/FOO')
571
621
b_wt.commit('moved foo to bar, added new foo')
572
merge(['b', -1],['b', 1],this_dir='a')
622
a_wt.merge_from_branch(b_wt.branch, b_wt.branch.last_revision(),
623
b_wt.branch.get_rev_id(1))
574
625
def test_merge_create_before_rename(self):
575
626
"""create before rename, target parents before children
591
642
file('a/foo', 'wb').write('A/FOO')
593
644
a_wt.commit('added foo')
594
self.run_bzr('branch', 'a', 'b')
645
self.run_bzr('branch a b')
595
646
b_wt = WorkingTree.open('b')
596
647
os.mkdir('b/bar')
598
649
b_wt.rename_one('foo', 'bar/foo')
599
650
b_wt.commit('created bar dir, moved foo into bar')
600
merge(['b', -1],['b', 1],this_dir='a')
651
a_wt.merge_from_branch(b_wt.branch, b_wt.branch.last_revision(),
652
b_wt.branch.get_rev_id(1))
602
654
def test_merge_rename_to_temp_before_delete(self):
603
655
"""rename to temp before delete, source children before parents
621
673
a_wt.add('foo/bar')
622
674
a_wt.commit('added foo/bar')
623
self.run_bzr('branch', 'a', 'b')
675
self.run_bzr('branch a b')
624
676
b_wt = WorkingTree.open('b')
625
677
b_wt.rename_one('foo/bar', 'bar')
626
678
os.rmdir('b/foo')
627
679
b_wt.remove('foo')
628
680
b_wt.commit('moved foo/bar to bar, deleted foo')
629
merge(['b', -1],['b', 1],this_dir='a')
681
a_wt.merge_from_branch(b_wt.branch, b_wt.branch.last_revision(),
682
b_wt.branch.get_rev_id(1))
631
684
def test_merge_delete_before_rename_to_temp(self):
632
685
"""delete before rename to temp
651
704
a_wt.commit('added foo and bar')
652
self.run_bzr('branch', 'a', 'b')
705
self.run_bzr('branch a b')
653
706
b_wt = WorkingTree.open('b')
654
707
os.unlink('b/foo')
655
708
b_wt.remove('foo')
656
709
b_wt.rename_one('bar', 'foo')
657
710
b_wt.commit('deleted foo, renamed bar to foo')
658
merge(['b', -1],['b', 1],this_dir='a')
711
a_wt.merge_from_branch(b_wt.branch, b_wt.branch.last_revision(),
712
b_wt.branch.get_rev_id(1))
715
class TestMerger(TestCaseWithTransport):
717
def set_up_trees(self):
718
this = self.make_branch_and_tree('this')
719
this.commit('rev1', rev_id='rev1')
720
other = this.bzrdir.sprout('other').open_workingtree()
721
this.commit('rev2a', rev_id='rev2a')
722
other.commit('rev2b', rev_id='rev2b')
725
def test_from_revision_ids(self):
726
this, other = self.set_up_trees()
727
self.assertRaises(errors.NoSuchRevision, Merger.from_revision_ids,
728
progress.DummyProgress(), this, 'rev2b')
730
self.addCleanup(this.unlock)
731
merger = Merger.from_revision_ids(progress.DummyProgress(), this,
732
'rev2b', other_branch=other.branch)
733
self.assertEqual('rev2b', merger.other_rev_id)
734
self.assertEqual('rev1', merger.base_rev_id)
735
merger = Merger.from_revision_ids(progress.DummyProgress(), this,
736
'rev2b', 'rev2a', other_branch=other.branch)
737
self.assertEqual('rev2a', merger.base_rev_id)
739
def test_from_uncommitted(self):
740
this, other = self.set_up_trees()
741
merger = Merger.from_uncommitted(this, other, progress.DummyProgress())
742
self.assertIs(other, merger.other_tree)
743
self.assertIs(None, merger.other_rev_id)
744
self.assertEqual('rev2b', merger.base_rev_id)
746
def prepare_for_merging(self):
747
this, other = self.set_up_trees()
748
other.commit('rev3', rev_id='rev3')
750
self.addCleanup(this.unlock)
753
def test_from_mergeable(self):
754
this, other = self.prepare_for_merging()
755
md = merge_directive.MergeDirective2.from_objects(
756
other.branch.repository, 'rev3', 0, 0, 'this')
758
self.addCleanup(other.unlock)
759
merger, verified = Merger.from_mergeable(this, md,
760
progress.DummyProgress())
762
merger, verified = Merger.from_mergeable(this, md,
763
progress.DummyProgress())
764
self.assertEqual('inapplicable', verified)
765
self.assertEqual('rev3', merger.other_rev_id)
766
self.assertEqual('rev1', merger.base_rev_id)
767
md.base_revision_id = 'rev2b'
768
merger, verified = Merger.from_mergeable(this, md,
769
progress.DummyProgress())
770
self.assertEqual('rev2b', merger.base_rev_id)
772
def test_from_mergeable_old_merge_directive(self):
773
this, other = self.prepare_for_merging()
775
self.addCleanup(other.unlock)
776
md = merge_directive.MergeDirective.from_objects(
777
other.branch.repository, 'rev3', 0, 0, 'this')
778
merger, verified = Merger.from_mergeable(this, md,
779
progress.DummyProgress())
780
self.assertEqual('rev3', merger.other_rev_id)
781
self.assertEqual('rev1', merger.base_rev_id)