384
417
'>>>>>>> MERGE-SOURCE\n',
420
def test_merge_reverse_revision_range(self):
421
tree = self.make_branch_and_tree(".")
423
self.addCleanup(tree.unlock)
424
self.build_tree(['a'])
426
first_rev = tree.commit("added a")
427
merger = _mod_merge.Merger.from_revision_ids(tree,
428
_mod_revision.NULL_REVISION,
430
merger.merge_type = _mod_merge.Merge3Merger
431
merger.interesting_files = 'a'
432
conflict_count = merger.do_merge()
433
self.assertEqual(0, conflict_count)
435
self.assertPathDoesNotExist("a")
437
self.assertPathExists("a")
387
439
def test_make_merger(self):
388
440
this_tree = self.make_branch_and_tree('this')
389
this_tree.commit('rev1', rev_id='rev1')
390
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
391
this_tree.commit('rev2', rev_id='rev2a')
392
other_tree.commit('rev2', rev_id='rev2b')
441
this_tree.commit('rev1', rev_id=b'rev1')
442
other_tree = this_tree.controldir.sprout('other').open_workingtree()
443
this_tree.commit('rev2', rev_id=b'rev2a')
444
other_tree.commit('rev2', rev_id=b'rev2b')
393
445
this_tree.lock_write()
394
446
self.addCleanup(this_tree.unlock)
395
merger = _mod_merge.Merger.from_revision_ids(None,
447
merger = _mod_merge.Merger.from_revision_ids(
396
448
this_tree, 'rev2b', other_branch=other_tree.branch)
397
449
merger.merge_type = _mod_merge.Merge3Merger
398
450
tree_merger = merger.make_merger()
399
451
self.assertIs(_mod_merge.Merge3Merger, tree_merger.__class__)
400
self.assertEqual('rev2b', tree_merger.other_tree.get_revision_id())
401
self.assertEqual('rev1', tree_merger.base_tree.get_revision_id())
452
self.assertEqual('rev2b',
453
tree_merger.other_tree.get_revision_id())
454
self.assertEqual('rev1',
455
tree_merger.base_tree.get_revision_id())
456
self.assertEqual(other_tree.branch, tree_merger.other_branch)
403
458
def test_make_preview_transform(self):
404
459
this_tree = self.make_branch_and_tree('this')
405
self.build_tree_contents([('this/file', '1\n')])
406
this_tree.add('file', 'file-id')
407
this_tree.commit('rev1', rev_id='rev1')
408
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
409
self.build_tree_contents([('this/file', '1\n2a\n')])
410
this_tree.commit('rev2', rev_id='rev2a')
411
self.build_tree_contents([('other/file', '2b\n1\n')])
412
other_tree.commit('rev2', rev_id='rev2b')
460
self.build_tree_contents([('this/file', b'1\n')])
461
this_tree.add('file', b'file-id')
462
this_tree.commit('rev1', rev_id=b'rev1')
463
other_tree = this_tree.controldir.sprout('other').open_workingtree()
464
self.build_tree_contents([('this/file', b'1\n2a\n')])
465
this_tree.commit('rev2', rev_id=b'rev2a')
466
self.build_tree_contents([('other/file', b'2b\n1\n')])
467
other_tree.commit('rev2', rev_id=b'rev2b')
413
468
this_tree.lock_write()
414
469
self.addCleanup(this_tree.unlock)
415
merger = _mod_merge.Merger.from_revision_ids(None,
416
this_tree, 'rev2b', other_branch=other_tree.branch)
470
merger = _mod_merge.Merger.from_revision_ids(
471
this_tree, b'rev2b', other_branch=other_tree.branch)
417
472
merger.merge_type = _mod_merge.Merge3Merger
418
473
tree_merger = merger.make_merger()
419
474
tt = tree_merger.make_preview_transform()
420
475
self.addCleanup(tt.finalize)
421
476
preview_tree = tt.get_preview_tree()
422
tree_file = this_tree.get_file('file-id')
477
tree_file = this_tree.get_file('file')
424
479
self.assertEqual('1\n2a\n', tree_file.read())
426
481
tree_file.close()
427
preview_file = preview_tree.get_file('file-id')
482
preview_file = preview_tree.get_file('file')
429
484
self.assertEqual('2b\n1\n2a\n', preview_file.read())
433
488
def test_do_merge(self):
434
489
this_tree = self.make_branch_and_tree('this')
435
self.build_tree_contents([('this/file', '1\n')])
436
this_tree.add('file', 'file-id')
437
this_tree.commit('rev1', rev_id='rev1')
438
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
439
self.build_tree_contents([('this/file', '1\n2a\n')])
440
this_tree.commit('rev2', rev_id='rev2a')
441
self.build_tree_contents([('other/file', '2b\n1\n')])
442
other_tree.commit('rev2', rev_id='rev2b')
490
self.build_tree_contents([('this/file', b'1\n')])
491
this_tree.add('file', b'file-id')
492
this_tree.commit('rev1', rev_id=b'rev1')
493
other_tree = this_tree.controldir.sprout('other').open_workingtree()
494
self.build_tree_contents([('this/file', b'1\n2a\n')])
495
this_tree.commit('rev2', rev_id=b'rev2a')
496
self.build_tree_contents([('other/file', b'2b\n1\n')])
497
other_tree.commit('rev2', rev_id=b'rev2b')
443
498
this_tree.lock_write()
444
499
self.addCleanup(this_tree.unlock)
445
merger = _mod_merge.Merger.from_revision_ids(None,
446
this_tree, 'rev2b', other_branch=other_tree.branch)
500
merger = _mod_merge.Merger.from_revision_ids(
501
this_tree, b'rev2b', other_branch=other_tree.branch)
447
502
merger.merge_type = _mod_merge.Merge3Merger
448
503
tree_merger = merger.make_merger()
449
504
tt = tree_merger.do_merge()
450
tree_file = this_tree.get_file('file-id')
505
tree_file = this_tree.get_file('file')
452
507
self.assertEqual('2b\n1\n2a\n', tree_file.read())
454
509
tree_file.close()
511
def test_merge_require_tree_root(self):
512
tree = self.make_branch_and_tree(".")
514
self.addCleanup(tree.unlock)
515
self.build_tree(['a'])
517
first_rev = tree.commit("added a")
518
old_root_id = tree.get_root_id()
519
merger = _mod_merge.Merger.from_revision_ids(tree,
520
_mod_revision.NULL_REVISION,
522
merger.merge_type = _mod_merge.Merge3Merger
523
conflict_count = merger.do_merge()
524
self.assertEqual(0, conflict_count)
525
self.assertEqual({''}, set(tree.all_versioned_paths()))
526
tree.set_parent_ids([])
456
528
def test_merge_add_into_deleted_root(self):
457
529
# Yes, people actually do this. And report bugs if it breaks.
458
530
source = self.make_branch_and_tree('source', format='rich-root-pack')
459
531
self.build_tree(['source/foo/'])
460
source.add('foo', 'foo-id')
532
source.add('foo', b'foo-id')
461
533
source.commit('Add foo')
462
target = source.bzrdir.sprout('target').open_workingtree()
463
subtree = target.extract('foo-id')
534
target = source.controldir.sprout('target').open_workingtree()
535
subtree = target.extract('foo', b'foo-id')
464
536
subtree.commit('Delete root')
465
537
self.build_tree(['source/bar'])
466
source.add('bar', 'bar-id')
538
source.add('bar', b'bar-id')
467
539
source.commit('Add bar')
468
540
subtree.merge_from_branch(source.branch)
1306
1397
class TestMergerEntriesLCA(TestMergerBase):
1308
1399
def make_merge_obj(self, builder, other_revision_id,
1309
interesting_files=None, interesting_ids=None):
1400
interesting_files=None):
1310
1401
merger = self.make_Merger(builder, other_revision_id,
1311
interesting_files=interesting_files,
1312
interesting_ids=interesting_ids)
1402
interesting_files=interesting_files)
1313
1403
return merger.make_merger()
1315
1405
def test_simple(self):
1316
1406
builder = self.get_builder()
1317
builder.build_snapshot('A-id', None,
1407
builder.build_snapshot(None,
1318
1408
[('add', (u'', 'a-root-id', 'directory', None)),
1319
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1320
builder.build_snapshot('C-id', ['A-id'],
1321
[('modify', ('a-id', 'a\nb\nC\nc\n'))])
1322
builder.build_snapshot('B-id', ['A-id'],
1323
[('modify', ('a-id', 'a\nB\nb\nc\n'))])
1324
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1325
[('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1326
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1327
[('modify', ('a-id', 'a\nB\nb\nC\nc\n'))])
1409
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))],
1411
builder.build_snapshot(['A-id'],
1412
[('modify', ('a', 'a\nb\nC\nc\n'))],
1414
builder.build_snapshot(['A-id'],
1415
[('modify', ('a', 'a\nB\nb\nc\n'))],
1417
builder.build_snapshot(['C-id', 'B-id'],
1418
[('modify', ('a', 'a\nB\nb\nC\nc\nE\n'))],
1420
builder.build_snapshot(['B-id', 'C-id'],
1421
[('modify', ('a', 'a\nB\nb\nC\nc\n'))],
1422
revision_id='D-id', )
1328
1423
merge_obj = self.make_merge_obj(builder, 'E-id')
1330
1425
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1354
1450
# G modifies 'bar'
1356
1452
builder = self.get_builder()
1357
builder.build_snapshot('A-id', None,
1358
[('add', (u'', 'a-root-id', 'directory', None))])
1359
builder.build_snapshot('B-id', ['A-id'],
1360
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1361
builder.build_snapshot('C-id', ['A-id'],
1362
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1363
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1364
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1365
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1366
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1367
builder.build_snapshot('G-id', ['E-id', 'D-id'],
1368
[('modify', (u'bar-id', 'd\ne\nf\nG\n'))])
1369
builder.build_snapshot('F-id', ['D-id', 'E-id'], [])
1453
builder.build_snapshot(None,
1454
[('add', (u'', 'a-root-id', 'directory', None))],
1456
builder.build_snapshot(['A-id'],
1457
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))],
1459
builder.build_snapshot(['A-id'],
1460
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))],
1462
builder.build_snapshot(['B-id', 'C-id'],
1463
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))],
1465
builder.build_snapshot(['C-id', 'B-id'],
1466
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))],
1468
builder.build_snapshot(['E-id', 'D-id'],
1469
[('modify', (u'bar', 'd\ne\nf\nG\n'))],
1471
builder.build_snapshot(['D-id', 'E-id'], [], revision_id='F-id')
1370
1472
merge_obj = self.make_merge_obj(builder, 'G-id')
1372
1474
self.assertEqual(['D-id', 'E-id'], [t.get_revision_id()
1493
1610
# In this case, we have a conflict of how the changes were resolved. E
1494
1611
# picked C and D picked B, so we should issue a conflict
1495
1612
builder = self.get_builder()
1496
builder.build_snapshot('A-id', None,
1613
builder.build_snapshot(None,
1497
1614
[('add', (u'', 'a-root-id', 'directory', None)),
1498
('add', (u'foo', 'foo-id', 'file', 'content\n'))])
1499
builder.build_snapshot('B-id', ['A-id'], [
1500
('modify', ('foo-id', 'new-content\n'))])
1501
builder.build_snapshot('C-id', ['A-id'],
1502
[('unversion', 'foo-id')])
1503
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1504
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1615
('add', (u'foo', 'foo-id', 'file', 'content\n'))],
1617
builder.build_snapshot(['A-id'], [
1618
('modify', ('foo', 'new-content\n'))],
1620
builder.build_snapshot(['A-id'],
1621
[('unversion', 'foo')],
1623
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
1624
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1505
1625
merge_obj = self.make_merge_obj(builder, 'E-id')
1507
1627
entries = list(merge_obj._entries_lca())
1508
1628
root_id = 'a-root-id'
1509
1629
self.assertEqual([('foo-id', True,
1630
((u'foo', [u'foo', None]), None, u'foo'),
1510
1631
((root_id, [root_id, None]), None, root_id),
1511
1632
((u'foo', [u'foo', None]), None, 'foo'),
1512
1633
((False, [False, None]), None, False)),
1576
1703
# though its LCAs disagree. This is because the modification in E
1577
1704
# completely supersedes the value in D.
1578
1705
builder = self.get_builder()
1579
builder.build_snapshot('A-id', None,
1706
builder.build_snapshot(None,
1580
1707
[('add', (u'', 'a-root-id', 'directory', None)),
1581
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1582
builder.build_snapshot('C-id', ['A-id'], [])
1583
builder.build_snapshot('B-id', ['A-id'],
1584
[('modify', ('foo-id', 'B content\n'))])
1585
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1586
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1587
[('modify', ('foo-id', 'E content\n'))])
1588
builder.build_snapshot('G-id', ['E-id', 'D-id'], [])
1589
builder.build_snapshot('F-id', ['D-id', 'E-id'],
1590
[('modify', ('foo-id', 'F content\n'))])
1708
('add', (u'foo', 'foo-id', 'file', 'A content\n'))],
1710
builder.build_snapshot(['A-id'], [], revision_id='C-id')
1711
builder.build_snapshot(['A-id'],
1712
[('modify', ('foo', 'B content\n'))],
1714
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1715
builder.build_snapshot(['C-id', 'B-id'],
1716
[('modify', ('foo', 'E content\n'))],
1718
builder.build_snapshot(['E-id', 'D-id'], [], revision_id='G-id')
1719
builder.build_snapshot(['D-id', 'E-id'],
1720
[('modify', ('foo', 'F content\n'))],
1591
1722
merge_obj = self.make_merge_obj(builder, 'G-id')
1593
1724
self.assertEqual([], list(merge_obj._entries_lca()))
1622
1753
# aren't supporting it yet.
1624
1755
builder = self.get_builder()
1625
builder.build_snapshot('A-id', None,
1756
builder.build_snapshot(None,
1626
1757
[('add', (u'', 'a-root-id', 'directory', None)),
1627
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1628
builder.build_snapshot('C-id', ['A-id'], [])
1629
builder.build_snapshot('B-id', ['A-id'],
1630
[('rename', ('foo', 'bar'))])
1631
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1632
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1633
[('rename', ('foo', 'bing'))]) # override to bing
1634
builder.build_snapshot('G-id', ['E-id', 'D-id'],
1635
[('rename', ('bing', 'barry'))]) # override to barry
1636
builder.build_snapshot('F-id', ['D-id', 'E-id'],
1637
[('rename', ('bar', 'bing'))]) # Merge in E's change
1758
('add', (u'foo', 'foo-id', 'file', 'A content\n'))],
1760
builder.build_snapshot(['A-id'], [], revision_id='C-id')
1761
builder.build_snapshot(['A-id'],
1762
[('rename', ('foo', 'bar'))],
1764
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1765
builder.build_snapshot(['C-id', 'B-id'],
1766
[('rename', ('foo', 'bing'))],
1767
revision_id='E-id') # override to bing
1768
builder.build_snapshot(['E-id', 'D-id'],
1769
[('rename', ('bing', 'barry'))],
1770
revision_id='G-id') # override to barry
1771
builder.build_snapshot(['D-id', 'E-id'],
1772
[('rename', ('bar', 'bing'))],
1773
revision_id='F-id') # Merge in E's change
1638
1774
merge_obj = self.make_merge_obj(builder, 'G-id')
1640
1776
self.expectFailure("We don't do an actual heads() check on lca values,"
1655
1791
# be pruned from the LCAs, even though it was newly introduced by E
1656
1792
# (superseding B).
1657
1793
builder = self.get_builder()
1658
builder.build_snapshot('A-id', None,
1794
builder.build_snapshot(None,
1659
1795
[('add', (u'', 'a-root-id', 'directory', None)),
1660
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1661
builder.build_snapshot('C-id', ['A-id'], [])
1662
builder.build_snapshot('B-id', ['A-id'],
1663
[('rename', ('foo', 'bar'))])
1664
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1665
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1666
builder.build_snapshot('G-id', ['E-id', 'D-id'],
1667
[('rename', ('foo', 'bar'))])
1668
builder.build_snapshot('F-id', ['D-id', 'E-id'],
1669
[('rename', ('bar', 'bing'))]) # should end up conflicting
1796
('add', (u'foo', 'foo-id', 'file', 'A content\n'))],
1798
builder.build_snapshot(['A-id'], [], revision_id='C-id')
1799
builder.build_snapshot(['A-id'],
1800
[('rename', ('foo', 'bar'))],
1802
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1803
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
1804
builder.build_snapshot(['E-id', 'D-id'],
1805
[('rename', ('foo', 'bar'))],
1807
builder.build_snapshot(['D-id', 'E-id'],
1808
[('rename', ('bar', 'bing'))],
1809
revision_id='F-id') # should end up conflicting
1670
1810
merge_obj = self.make_merge_obj(builder, 'G-id')
1672
1812
entries = list(merge_obj._entries_lca())
1688
1828
# D E D reverts to B, E reverts to C
1689
1829
# This should conflict
1690
1830
builder = self.get_builder()
1691
builder.build_snapshot('A-id', None,
1831
builder.build_snapshot(None,
1692
1832
[('add', (u'', 'a-root-id', 'directory', None)),
1693
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1694
builder.build_snapshot('B-id', ['A-id'],
1695
[('modify', ('foo-id', 'B content\n'))])
1696
builder.build_snapshot('C-id', ['A-id'],
1697
[('modify', ('foo-id', 'C content\n'))])
1698
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1699
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1833
('add', (u'foo', 'foo-id', 'file', 'A content\n'))],
1835
builder.build_snapshot(['A-id'],
1836
[('modify', ('foo', 'B content\n'))],
1838
builder.build_snapshot(['A-id'],
1839
[('modify', ('foo', 'C content\n'))],
1841
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
1842
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1700
1843
merge_obj = self.make_merge_obj(builder, 'E-id')
1702
1845
entries = list(merge_obj._entries_lca())
1703
1846
root_id = 'a-root-id'
1704
1847
self.assertEqual([('foo-id', True,
1848
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1705
1849
((root_id, [root_id, root_id]), root_id, root_id),
1706
1850
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1707
1851
((False, [False, False]), False, False)),
1719
1863
# We need to emit an entry for 'foo', because D & E differed on the
1720
1864
# merge resolution
1721
1865
builder = self.get_builder()
1722
builder.build_snapshot('A-id', None,
1866
builder.build_snapshot(None,
1723
1867
[('add', (u'', 'a-root-id', 'directory', None)),
1724
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1725
builder.build_snapshot('B-id', ['A-id'],
1726
[('modify', ('foo-id', 'B content\n'))])
1727
builder.build_snapshot('C-id', ['A-id'],
1728
[('modify', ('foo-id', 'C content\n'))])
1729
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1730
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1731
builder.build_snapshot('F-id', ['D-id'],
1732
[('modify', ('foo-id', 'F content\n'))])
1868
('add', (u'foo', 'foo-id', 'file', 'A content\n'))],
1870
builder.build_snapshot(['A-id'],
1871
[('modify', ('foo', 'B content\n'))],
1873
builder.build_snapshot(['A-id'],
1874
[('modify', ('foo', 'C content\n'))],
1875
revision_id='C-id', )
1876
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
1877
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1878
builder.build_snapshot(['D-id'],
1879
[('modify', ('foo', 'F content\n'))],
1733
1881
merge_obj = self.make_merge_obj(builder, 'E-id')
1735
1883
entries = list(merge_obj._entries_lca())
1736
1884
root_id = 'a-root-id'
1737
1885
self.assertEqual([('foo-id', True,
1886
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1738
1887
((root_id, [root_id, root_id]), root_id, root_id),
1739
1888
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1740
1889
((False, [False, False]), False, False)),
1755
1904
# We need to conflict.
1757
1906
builder = self.get_builder()
1758
builder.build_snapshot('A-id', None,
1907
builder.build_snapshot(None,
1759
1908
[('add', (u'', 'a-root-id', 'directory', None)),
1760
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1761
builder.build_snapshot('B-id', ['A-id'],
1762
[('modify', ('foo-id', 'B content\n'))])
1763
builder.build_snapshot('C-id', ['A-id'],
1764
[('modify', ('foo-id', 'C content\n'))])
1765
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1766
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1767
[('modify', ('foo-id', 'C content\n'))]) # Same as E
1768
builder.build_snapshot('F-id', ['D-id'],
1769
[('modify', ('foo-id', 'F content\n'))])
1909
('add', (u'foo', 'foo-id', 'file', 'A content\n'))],
1911
builder.build_snapshot(['A-id'],
1912
[('modify', ('foo', 'B content\n'))],
1914
builder.build_snapshot(['A-id'],
1915
[('modify', ('foo', 'C content\n'))],
1917
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
1918
builder.build_snapshot(['B-id', 'C-id'],
1919
[('modify', ('foo', 'C content\n'))],
1920
revision_id='D-id') # Same as E
1921
builder.build_snapshot(['D-id'],
1922
[('modify', ('foo', 'F content\n'))],
1770
1924
merge_obj = self.make_merge_obj(builder, 'E-id')
1772
1926
entries = list(merge_obj._entries_lca())
1777
1931
def test_only_path_changed(self):
1778
1932
builder = self.get_builder()
1779
builder.build_snapshot('A-id', None,
1933
builder.build_snapshot(None,
1780
1934
[('add', (u'', 'a-root-id', 'directory', None)),
1781
('add', (u'a', 'a-id', 'file', 'content\n'))])
1782
builder.build_snapshot('B-id', ['A-id'], [])
1783
builder.build_snapshot('C-id', ['A-id'], [])
1784
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1785
[('rename', (u'a', u'b'))])
1786
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1935
('add', (u'a', 'a-id', 'file', 'content\n'))],
1937
builder.build_snapshot(['A-id'], [], revision_id='B-id')
1938
builder.build_snapshot(['A-id'], [], revision_id='C-id')
1939
builder.build_snapshot(['C-id', 'B-id'],
1940
[('rename', (u'a', u'b'))],
1942
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1787
1943
merge_obj = self.make_merge_obj(builder, 'E-id')
1788
1944
entries = list(merge_obj._entries_lca())
1789
1945
root_id = 'a-root-id'
1790
1946
# The content was not changed, only the path
1791
1947
self.assertEqual([('a-id', False,
1948
((u'a', [u'a', u'a']), u'b', u'a'),
1792
1949
((root_id, [root_id, root_id]), root_id, root_id),
1793
1950
((u'a', [u'a', u'a']), u'b', u'a'),
1794
1951
((False, [False, False]), False, False)),
1797
1954
def test_kind_changed(self):
1798
1955
# Identical content, except 'D' changes a-id into a directory
1799
1956
builder = self.get_builder()
1800
builder.build_snapshot('A-id', None,
1957
builder.build_snapshot(None,
1801
1958
[('add', (u'', 'a-root-id', 'directory', None)),
1802
('add', (u'a', 'a-id', 'file', 'content\n'))])
1803
builder.build_snapshot('B-id', ['A-id'], [])
1804
builder.build_snapshot('C-id', ['A-id'], [])
1805
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1806
[('unversion', 'a-id'),
1807
('add', (u'a', 'a-id', 'directory', None))])
1808
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1959
('add', (u'a', 'a-id', 'file', 'content\n'))],
1961
builder.build_snapshot(['A-id'], [], revision_id='B-id')
1962
builder.build_snapshot(['A-id'], [], revision_id='C-id')
1963
builder.build_snapshot(['C-id', 'B-id'],
1964
[('unversion', 'a'),
1966
('add', (u'a', 'a-id', 'directory', None))],
1968
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1809
1969
merge_obj = self.make_merge_obj(builder, 'E-id')
1810
1970
entries = list(merge_obj._entries_lca())
1811
1971
root_id = 'a-root-id'
1812
1972
# Only the kind was changed (content)
1813
1973
self.assertEqual([('a-id', True,
1974
((u'a', [u'a', u'a']), u'a', u'a'),
1814
1975
((root_id, [root_id, root_id]), root_id, root_id),
1815
1976
((u'a', [u'a', u'a']), u'a', u'a'),
1816
1977
((False, [False, False]), False, False)),
1836
2000
def test_interesting_files(self):
1837
2001
# Two files modified, but we should filter one of them
1838
2002
builder = self.get_builder()
1839
builder.build_snapshot('A-id', None,
2003
builder.build_snapshot(None,
1840
2004
[('add', (u'', 'a-root-id', 'directory', None)),
1841
2005
('add', (u'a', 'a-id', 'file', 'content\n')),
1842
('add', (u'b', 'b-id', 'file', 'content\n'))])
1843
builder.build_snapshot('B-id', ['A-id'], [])
1844
builder.build_snapshot('C-id', ['A-id'], [])
1845
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1846
[('modify', ('a-id', 'new-content\n')),
1847
('modify', ('b-id', 'new-content\n'))])
1848
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2006
('add', (u'b', 'b-id', 'file', 'content\n'))],
2008
builder.build_snapshot(['A-id'], [], revision_id='B-id')
2009
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2010
builder.build_snapshot(['C-id', 'B-id'],
2011
[('modify', ('a', 'new-content\n')),
2012
('modify', ('b', 'new-content\n'))],
2014
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1849
2015
merge_obj = self.make_merge_obj(builder, 'E-id',
1850
2016
interesting_files=['b'])
1851
2017
entries = list(merge_obj._entries_lca())
1852
2018
root_id = 'a-root-id'
1853
2019
self.assertEqual([('b-id', True,
2020
((u'b', [u'b', u'b']), u'b', u'b'),
1854
2021
((root_id, [root_id, root_id]), root_id, root_id),
1855
2022
((u'b', [u'b', u'b']), u'b', u'b'),
1856
2023
((False, [False, False]), False, False)),
1859
2026
def test_interesting_file_in_this(self):
1860
2027
# This renamed the file, but it should still match the entry in other
1861
2028
builder = self.get_builder()
1862
builder.build_snapshot('A-id', None,
2029
builder.build_snapshot(None,
1863
2030
[('add', (u'', 'a-root-id', 'directory', None)),
1864
2031
('add', (u'a', 'a-id', 'file', 'content\n')),
1865
('add', (u'b', 'b-id', 'file', 'content\n'))])
1866
builder.build_snapshot('B-id', ['A-id'], [])
1867
builder.build_snapshot('C-id', ['A-id'], [])
1868
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1869
[('modify', ('a-id', 'new-content\n')),
1870
('modify', ('b-id', 'new-content\n'))])
1871
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1872
[('rename', ('b', 'c'))])
2032
('add', (u'b', 'b-id', 'file', 'content\n'))],
2034
builder.build_snapshot(['A-id'], [], revision_id='B-id')
2035
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2036
builder.build_snapshot(['C-id', 'B-id'],
2037
[('modify', ('a', 'new-content\n')),
2038
('modify', ('b', 'new-content\n'))],
2040
builder.build_snapshot(['B-id', 'C-id'],
2041
[('rename', ('b', 'c'))],
1873
2043
merge_obj = self.make_merge_obj(builder, 'E-id',
1874
2044
interesting_files=['c'])
1875
2045
entries = list(merge_obj._entries_lca())
1876
2046
root_id = 'a-root-id'
1877
2047
self.assertEqual([('b-id', True,
2048
((u'b', [u'b', u'b']), u'b', u'c'),
1878
2049
((root_id, [root_id, root_id]), root_id, root_id),
1879
2050
((u'b', [u'b', u'b']), u'b', u'c'),
1880
2051
((False, [False, False]), False, False)),
1883
2054
def test_interesting_file_in_base(self):
1884
2055
# This renamed the file, but it should still match the entry in BASE
1885
2056
builder = self.get_builder()
1886
builder.build_snapshot('A-id', None,
2057
builder.build_snapshot(None,
1887
2058
[('add', (u'', 'a-root-id', 'directory', None)),
1888
2059
('add', (u'a', 'a-id', 'file', 'content\n')),
1889
('add', (u'c', 'c-id', 'file', 'content\n'))])
1890
builder.build_snapshot('B-id', ['A-id'],
1891
[('rename', ('c', 'b'))])
1892
builder.build_snapshot('C-id', ['A-id'],
1893
[('rename', ('c', 'b'))])
1894
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1895
[('modify', ('a-id', 'new-content\n')),
1896
('modify', ('c-id', 'new-content\n'))])
1897
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2060
('add', (u'c', 'c-id', 'file', 'content\n'))],
2062
builder.build_snapshot(['A-id'],
2063
[('rename', ('c', 'b'))],
2065
builder.build_snapshot(['A-id'],
2066
[('rename', ('c', 'b'))],
2068
builder.build_snapshot(['C-id', 'B-id'],
2069
[('modify', ('a', 'new-content\n')),
2070
('modify', ('b', 'new-content\n'))],
2072
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1898
2073
merge_obj = self.make_merge_obj(builder, 'E-id',
1899
2074
interesting_files=['c'])
1900
2075
entries = list(merge_obj._entries_lca())
1901
2076
root_id = 'a-root-id'
1902
2077
self.assertEqual([('c-id', True,
2078
((u'c', [u'b', u'b']), u'b', u'b'),
1903
2079
((root_id, [root_id, root_id]), root_id, root_id),
1904
2080
((u'c', [u'b', u'b']), u'b', u'b'),
1905
2081
((False, [False, False]), False, False)),
1908
2084
def test_interesting_file_in_lca(self):
1909
2085
# This renamed the file, but it should still match the entry in LCA
1910
2086
builder = self.get_builder()
1911
builder.build_snapshot('A-id', None,
2087
builder.build_snapshot(None,
1912
2088
[('add', (u'', 'a-root-id', 'directory', None)),
1913
2089
('add', (u'a', 'a-id', 'file', 'content\n')),
1914
('add', (u'b', 'b-id', 'file', 'content\n'))])
1915
builder.build_snapshot('B-id', ['A-id'],
1916
[('rename', ('b', 'c'))])
1917
builder.build_snapshot('C-id', ['A-id'], [])
1918
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1919
[('modify', ('a-id', 'new-content\n')),
1920
('modify', ('b-id', 'new-content\n'))])
1921
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1922
[('rename', ('c', 'b'))])
2090
('add', (u'b', 'b-id', 'file', 'content\n'))],
2092
builder.build_snapshot(['A-id'],
2093
[('rename', ('b', 'c'))], revision_id='B-id')
2094
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2095
builder.build_snapshot(['C-id', 'B-id'],
2096
[('modify', ('a', 'new-content\n')),
2097
('modify', ('b', 'new-content\n'))],
2099
builder.build_snapshot(['B-id', 'C-id'],
2100
[('rename', ('c', 'b'))], revision_id='D-id')
1923
2101
merge_obj = self.make_merge_obj(builder, 'E-id',
1924
2102
interesting_files=['c'])
1925
2103
entries = list(merge_obj._entries_lca())
1926
2104
root_id = 'a-root-id'
1927
2105
self.assertEqual([('b-id', True,
2106
((u'b', [u'c', u'b']), u'b', u'b'),
1928
2107
((root_id, [root_id, root_id]), root_id, root_id),
1929
2108
((u'b', [u'c', u'b']), u'b', u'b'),
1930
2109
((False, [False, False]), False, False)),
1933
def test_interesting_ids(self):
2112
def test_interesting_files(self):
1934
2113
# Two files modified, but we should filter one of them
1935
2114
builder = self.get_builder()
1936
builder.build_snapshot('A-id', None,
2115
builder.build_snapshot(None,
1937
2116
[('add', (u'', 'a-root-id', 'directory', None)),
1938
2117
('add', (u'a', 'a-id', 'file', 'content\n')),
1939
('add', (u'b', 'b-id', 'file', 'content\n'))])
1940
builder.build_snapshot('B-id', ['A-id'], [])
1941
builder.build_snapshot('C-id', ['A-id'], [])
1942
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1943
[('modify', ('a-id', 'new-content\n')),
1944
('modify', ('b-id', 'new-content\n'))])
1945
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2118
('add', (u'b', 'b-id', 'file', 'content\n'))],
2120
builder.build_snapshot(['A-id'], [], revision_id='B-id')
2121
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2122
builder.build_snapshot(['C-id', 'B-id'],
2123
[('modify', ('a', 'new-content\n')),
2124
('modify', ('b', 'new-content\n'))], revision_id='E-id')
2125
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1946
2126
merge_obj = self.make_merge_obj(builder, 'E-id',
1947
interesting_ids=['b-id'])
2127
interesting_files=['b'])
1948
2128
entries = list(merge_obj._entries_lca())
1949
2129
root_id = 'a-root-id'
1950
2130
self.assertEqual([('b-id', True,
2131
((u'b', [u'b', u'b']), u'b', u'b'),
1951
2132
((root_id, [root_id, root_id]), root_id, root_id),
1952
2133
((u'b', [u'b', u'b']), u'b', u'b'),
1953
2134
((False, [False, False]), False, False)),
1979
2160
def do_merge(self, builder, other_revision_id):
1980
2161
wt = self.get_wt_from_builder(builder)
1981
merger = _mod_merge.Merger.from_revision_ids(None,
2162
merger = _mod_merge.Merger.from_revision_ids(
1982
2163
wt, other_revision_id)
1983
2164
merger.merge_type = _mod_merge.Merge3Merger
1984
2165
return wt, merger.do_merge()
1986
2167
def test_simple_lca(self):
1987
2168
builder = self.get_builder()
1988
builder.build_snapshot('A-id', None,
2169
builder.build_snapshot(None,
1989
2170
[('add', (u'', 'a-root-id', 'directory', None)),
1990
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1991
builder.build_snapshot('C-id', ['A-id'], [])
1992
builder.build_snapshot('B-id', ['A-id'], [])
1993
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1994
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1995
[('modify', ('a-id', 'a\nb\nc\nd\ne\nf\n'))])
2171
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))],
2173
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2174
builder.build_snapshot(['A-id'], [], revision_id='B-id')
2175
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
2176
builder.build_snapshot(['B-id', 'C-id'],
2177
[('modify', ('a', 'a\nb\nc\nd\ne\nf\n'))],
1996
2179
wt, conflicts = self.do_merge(builder, 'E-id')
1997
2180
self.assertEqual(0, conflicts)
1998
2181
# The merge should have simply update the contents of 'a'
1999
self.assertEqual('a\nb\nc\nd\ne\nf\n', wt.get_file_text('a-id'))
2182
self.assertEqual('a\nb\nc\nd\ne\nf\n', wt.get_file_text('a'))
2001
2184
def test_conflict_without_lca(self):
2002
2185
# This test would cause a merge conflict, unless we use the lca trees
2012
2195
# F Path at 'baz' in F, which supersedes 'bar' and 'foo'
2013
2196
builder = self.get_builder()
2014
builder.build_snapshot('A-id', None,
2197
builder.build_snapshot(None,
2015
2198
[('add', (u'', 'a-root-id', 'directory', None)),
2016
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2017
builder.build_snapshot('C-id', ['A-id'], [])
2018
builder.build_snapshot('B-id', ['A-id'],
2019
[('rename', ('foo', 'bar'))])
2020
builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
2021
[('rename', ('foo', 'bar'))])
2022
builder.build_snapshot('F-id', ['E-id'],
2023
[('rename', ('bar', 'baz'))])
2024
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2199
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))],
2201
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2202
builder.build_snapshot(['A-id'],
2203
[('rename', ('foo', 'bar'))], revision_id='B-id', )
2204
builder.build_snapshot(['C-id', 'B-id'], # merge the rename
2205
[('rename', ('foo', 'bar'))], revision_id='E-id')
2206
builder.build_snapshot(['E-id'],
2207
[('rename', ('bar', 'baz'))], revision_id='F-id')
2208
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
2025
2209
wt, conflicts = self.do_merge(builder, 'F-id')
2026
2210
self.assertEqual(0, conflicts)
2027
2211
# The merge should simply recognize that the final rename takes
2029
self.assertEqual('baz', wt.id2path('foo-id'))
2213
self.assertEqual('baz', wt.id2path(b'foo-id'))
2031
2215
def test_other_deletes_lca_renames(self):
2032
2216
# This test would cause a merge conflict, unless we use the lca trees
2068
2253
# F Executable bit changed
2069
2254
builder = self.get_builder()
2070
builder.build_snapshot('A-id', None,
2255
builder.build_snapshot(None,
2071
2256
[('add', (u'', 'a-root-id', 'directory', None)),
2072
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2073
builder.build_snapshot('C-id', ['A-id'], [])
2074
builder.build_snapshot('B-id', ['A-id'], [])
2075
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2076
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2257
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))],
2259
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2260
builder.build_snapshot(['A-id'], [], revision_id='B-id')
2261
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
2262
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
2077
2263
# Have to use a real WT, because BranchBuilder doesn't support exec bit
2078
2264
wt = self.get_wt_from_builder(builder)
2079
2265
tt = transform.TreeTransform(wt)
2081
tt.set_executability(True, tt.trans_id_tree_file_id('foo-id'))
2267
tt.set_executability(True, tt.trans_id_tree_path('foo'))
2086
self.assertTrue(wt.is_executable('foo-id'))
2087
wt.commit('F-id', rev_id='F-id')
2272
self.assertTrue(wt.is_executable('foo'))
2273
wt.commit('F-id', rev_id=b'F-id')
2088
2274
# Reset to D, so that we can merge F
2089
2275
wt.set_parent_ids(['D-id'])
2090
2276
wt.branch.set_last_revision_info(3, 'D-id')
2092
self.assertFalse(wt.is_executable('foo-id'))
2278
self.assertFalse(wt.is_executable('foo'))
2093
2279
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2094
2280
self.assertEqual(0, conflicts)
2095
self.assertTrue(wt.is_executable('foo-id'))
2281
self.assertTrue(wt.is_executable('foo'))
2097
2283
def test_create_symlink(self):
2098
self.requireFeature(tests.SymlinkFeature)
2284
self.requireFeature(features.SymlinkFeature)
2108
2294
# Have to use a real WT, because BranchBuilder and MemoryTree don't
2109
2295
# have symlink support
2110
2296
builder = self.get_builder()
2111
builder.build_snapshot('A-id', None,
2112
[('add', (u'', 'a-root-id', 'directory', None))])
2113
builder.build_snapshot('C-id', ['A-id'], [])
2114
builder.build_snapshot('B-id', ['A-id'], [])
2115
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2116
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2297
builder.build_snapshot(None,
2298
[('add', (u'', 'a-root-id', 'directory', None))],
2300
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2301
builder.build_snapshot(['A-id'], [], revision_id='B-id')
2302
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
2303
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
2117
2304
# Have to use a real WT, because BranchBuilder doesn't support exec bit
2118
2305
wt = self.get_wt_from_builder(builder)
2119
2306
os.symlink('bar', 'path/foo')
2120
wt.add(['foo'], ['foo-id'])
2121
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2122
wt.commit('add symlink', rev_id='F-id')
2307
wt.add(['foo'], [b'foo-id'])
2308
self.assertEqual('bar', wt.get_symlink_target('foo'))
2309
wt.commit('add symlink', rev_id=b'F-id')
2123
2310
# Reset to D, so that we can merge F
2124
wt.set_parent_ids(['D-id'])
2125
wt.branch.set_last_revision_info(3, 'D-id')
2311
wt.set_parent_ids([b'D-id'])
2312
wt.branch.set_last_revision_info(3, b'D-id')
2127
self.assertIs(None, wt.path2id('foo'))
2128
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2314
self.assertFalse(wt.is_versioned('foo'))
2315
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2129
2316
self.assertEqual(0, conflicts)
2130
self.assertEqual('foo-id', wt.path2id('foo'))
2131
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2317
self.assertEqual(b'foo-id', wt.path2id('foo'))
2318
self.assertEqual('bar', wt.get_symlink_target('foo'))
2133
2320
def test_both_sides_revert(self):
2134
2321
# Both sides of a criss-cross revert the text to the lca
2180
2372
wt.lock_write()
2181
2373
self.addCleanup(wt.unlock)
2182
2374
os.symlink('bar', 'path/foo')
2183
wt.add(['foo'], ['foo-id'])
2184
wt.commit('add symlink', rev_id='A-id')
2375
wt.add(['foo'], [b'foo-id'])
2376
wt.commit('add symlink', rev_id=b'A-id')
2185
2377
os.remove('path/foo')
2186
2378
os.symlink('baz', 'path/foo')
2187
wt.commit('foo => baz', rev_id='B-id')
2188
wt.set_last_revision('A-id')
2379
wt.commit('foo => baz', rev_id=b'B-id')
2380
wt.set_last_revision(b'A-id')
2189
2381
wt.branch.set_last_revision_info(1, 'A-id')
2191
wt.commit('C', rev_id='C-id')
2192
wt.merge_from_branch(wt.branch, 'B-id')
2193
self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2194
wt.commit('E merges C & B', rev_id='E-id')
2383
wt.commit('C', rev_id=b'C-id')
2384
wt.merge_from_branch(wt.branch, b'B-id')
2385
self.assertEqual('baz', wt.get_symlink_target('foo'))
2386
wt.commit('E merges C & B', rev_id=b'E-id')
2195
2387
os.remove('path/foo')
2196
2388
os.symlink('bing', 'path/foo')
2197
wt.commit('F foo => bing', rev_id='F-id')
2389
wt.commit('F foo => bing', rev_id=b'F-id')
2198
2390
wt.set_last_revision('B-id')
2199
wt.branch.set_last_revision_info(2, 'B-id')
2391
wt.branch.set_last_revision_info(2, b'B-id')
2201
wt.merge_from_branch(wt.branch, 'C-id')
2202
wt.commit('D merges B & C', rev_id='D-id')
2203
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2393
wt.merge_from_branch(wt.branch, b'C-id')
2394
wt.commit('D merges B & C', rev_id=b'D-id')
2395
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2204
2396
self.assertEqual(0, conflicts)
2205
self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2397
self.assertEqual('bing', wt.get_symlink_target('foo'))
2207
2399
def test_renamed_symlink(self):
2208
self.requireFeature(tests.SymlinkFeature)
2400
self.requireFeature(features.SymlinkFeature)
2209
2401
# A Create symlink foo => bar
2211
2403
# B C B renames foo => barry
2223
2415
wt.lock_write()
2224
2416
self.addCleanup(wt.unlock)
2225
2417
os.symlink('bar', 'path/foo')
2226
wt.add(['foo'], ['foo-id'])
2227
wt.commit('A add symlink', rev_id='A-id')
2418
wt.add(['foo'], [b'foo-id'])
2419
wt.commit('A add symlink', rev_id=b'A-id')
2228
2420
wt.rename_one('foo', 'barry')
2229
wt.commit('B foo => barry', rev_id='B-id')
2421
wt.commit('B foo => barry', rev_id=b'B-id')
2230
2422
wt.set_last_revision('A-id')
2231
wt.branch.set_last_revision_info(1, 'A-id')
2423
wt.branch.set_last_revision_info(1, b'A-id')
2233
wt.commit('C', rev_id='C-id')
2234
wt.merge_from_branch(wt.branch, 'B-id')
2235
self.assertEqual('barry', wt.id2path('foo-id'))
2236
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2237
wt.commit('E merges C & B', rev_id='E-id')
2425
wt.commit('C', rev_id=b'C-id')
2426
wt.merge_from_branch(wt.branch, b'B-id')
2427
self.assertEqual('barry', wt.id2path(b'foo-id'))
2428
self.assertEqual('bar', wt.get_symlink_target('barry'))
2429
wt.commit('E merges C & B', rev_id=b'E-id')
2238
2430
wt.rename_one('barry', 'blah')
2239
wt.commit('F barry => blah', rev_id='F-id')
2240
wt.set_last_revision('B-id')
2241
wt.branch.set_last_revision_info(2, 'B-id')
2431
wt.commit('F barry => blah', rev_id=b'F-id')
2432
wt.set_last_revision(b'B-id')
2433
wt.branch.set_last_revision_info(2, b'B-id')
2243
wt.merge_from_branch(wt.branch, 'C-id')
2244
wt.commit('D merges B & C', rev_id='D-id')
2245
self.assertEqual('barry', wt.id2path('foo-id'))
2435
wt.merge_from_branch(wt.branch, b'C-id')
2436
wt.commit('D merges B & C', rev_id=b'D-id')
2437
self.assertEqual('barry', wt.id2path(b'foo-id'))
2246
2438
# Check the output of the Merger object directly
2247
merger = _mod_merge.Merger.from_revision_ids(None,
2439
merger = _mod_merge.Merger.from_revision_ids(wt, b'F-id')
2249
2440
merger.merge_type = _mod_merge.Merge3Merger
2250
2441
merge_obj = merger.make_merger()
2251
2442
root_id = wt.path2id('')
2252
2443
entries = list(merge_obj._entries_lca())
2253
2444
# No content change, just a path change
2254
self.assertEqual([('foo-id', False,
2445
self.assertEqual([(b'foo-id', False,
2446
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2255
2447
((root_id, [root_id, root_id]), root_id, root_id),
2256
2448
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2257
2449
((False, [False, False]), False, False)),
2259
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2451
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2260
2452
self.assertEqual(0, conflicts)
2261
self.assertEqual('blah', wt.id2path('foo-id'))
2453
self.assertEqual('blah', wt.id2path(b'foo-id'))
2263
2455
def test_symlink_no_content_change(self):
2264
self.requireFeature(tests.SymlinkFeature)
2456
self.requireFeature(features.SymlinkFeature)
2265
2457
# A Create symlink foo => bar
2267
2459
# B C B relinks foo => baz
2278
2470
wt.lock_write()
2279
2471
self.addCleanup(wt.unlock)
2280
2472
os.symlink('bar', 'path/foo')
2281
wt.add(['foo'], ['foo-id'])
2282
wt.commit('add symlink', rev_id='A-id')
2473
wt.add(['foo'], [b'foo-id'])
2474
wt.commit('add symlink', rev_id=b'A-id')
2283
2475
os.remove('path/foo')
2284
2476
os.symlink('baz', 'path/foo')
2285
wt.commit('foo => baz', rev_id='B-id')
2286
wt.set_last_revision('A-id')
2287
wt.branch.set_last_revision_info(1, 'A-id')
2289
wt.commit('C', rev_id='C-id')
2290
wt.merge_from_branch(wt.branch, 'B-id')
2291
self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2292
wt.commit('E merges C & B', rev_id='E-id')
2293
wt.set_last_revision('B-id')
2294
wt.branch.set_last_revision_info(2, 'B-id')
2296
wt.merge_from_branch(wt.branch, 'C-id')
2297
wt.commit('D merges B & C', rev_id='D-id')
2477
wt.commit('foo => baz', rev_id=b'B-id')
2478
wt.set_last_revision(b'A-id')
2479
wt.branch.set_last_revision_info(1, b'A-id')
2481
wt.commit('C', rev_id=b'C-id')
2482
wt.merge_from_branch(wt.branch, b'B-id')
2483
self.assertEqual('baz', wt.get_symlink_target('foo'))
2484
wt.commit('E merges C & B', rev_id=b'E-id')
2485
wt.set_last_revision(b'B-id')
2486
wt.branch.set_last_revision_info(2, b'B-id')
2488
wt.merge_from_branch(wt.branch, b'C-id')
2489
wt.commit('D merges B & C', rev_id=b'D-id')
2298
2490
os.remove('path/foo')
2299
2491
os.symlink('bing', 'path/foo')
2300
wt.commit('F foo => bing', rev_id='F-id')
2492
wt.commit('F foo => bing', rev_id=b'F-id')
2302
2494
# Check the output of the Merger object directly
2303
merger = _mod_merge.Merger.from_revision_ids(None,
2495
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2305
2496
merger.merge_type = _mod_merge.Merge3Merger
2306
2497
merge_obj = merger.make_merger()
2307
2498
# Nothing interesting happened in OTHER relative to BASE
2308
2499
self.assertEqual([], list(merge_obj._entries_lca()))
2309
2500
# Now do a real merge, just to test the rest of the stack
2310
conflicts = wt.merge_from_branch(wt.branch, to_revision='E-id')
2501
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'E-id')
2311
2502
self.assertEqual(0, conflicts)
2312
self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2503
self.assertEqual('bing', wt.get_symlink_target('foo'))
2314
2505
def test_symlink_this_changed_kind(self):
2315
self.requireFeature(tests.SymlinkFeature)
2506
self.requireFeature(features.SymlinkFeature)
2318
2509
# B C B creates symlink foo => bar
2326
2517
wt = self.make_branch_and_tree('path')
2327
2518
wt.lock_write()
2328
2519
self.addCleanup(wt.unlock)
2329
wt.commit('base', rev_id='A-id')
2520
wt.commit('base', rev_id=b'A-id')
2330
2521
os.symlink('bar', 'path/foo')
2331
wt.add(['foo'], ['foo-id'])
2332
wt.commit('add symlink foo => bar', rev_id='B-id')
2333
wt.set_last_revision('A-id')
2334
wt.branch.set_last_revision_info(1, 'A-id')
2522
wt.add(['foo'], [b'foo-id'])
2523
wt.commit('add symlink foo => bar', rev_id=b'B-id')
2524
wt.set_last_revision(b'A-id')
2525
wt.branch.set_last_revision_info(1, b'A-id')
2336
wt.commit('C', rev_id='C-id')
2337
wt.merge_from_branch(wt.branch, 'B-id')
2338
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2527
wt.commit('C', rev_id=b'C-id')
2528
wt.merge_from_branch(wt.branch, b'B-id')
2529
self.assertEqual('bar', wt.get_symlink_target('foo'))
2339
2530
os.remove('path/foo')
2340
2531
# We have to change the link in E, or it won't try to do a comparison
2341
2532
os.symlink('bing', 'path/foo')
2342
wt.commit('E merges C & B, overrides to bing', rev_id='E-id')
2533
wt.commit('E merges C & B, overrides to bing', rev_id=b'E-id')
2343
2534
wt.set_last_revision('B-id')
2344
wt.branch.set_last_revision_info(2, 'B-id')
2535
wt.branch.set_last_revision_info(2, b'B-id')
2346
wt.merge_from_branch(wt.branch, 'C-id')
2537
wt.merge_from_branch(wt.branch, b'C-id')
2347
2538
os.remove('path/foo')
2348
self.build_tree_contents([('path/foo', 'file content\n')])
2539
self.build_tree_contents([('path/foo', b'file content\n')])
2349
2540
# XXX: workaround, WT doesn't detect kind changes unless you do
2350
2541
# iter_changes()
2351
2542
list(wt.iter_changes(wt.basis_tree()))
2352
wt.commit('D merges B & C, makes it a file', rev_id='D-id')
2543
wt.commit('D merges B & C, makes it a file', rev_id=b'D-id')
2354
merger = _mod_merge.Merger.from_revision_ids(None,
2545
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2356
2546
merger.merge_type = _mod_merge.Merge3Merger
2357
2547
merge_obj = merger.make_merger()
2358
2548
entries = list(merge_obj._entries_lca())
2359
2549
root_id = wt.path2id('')
2360
self.assertEqual([('foo-id', True,
2550
self.assertEqual([(b'foo-id', True,
2551
((None, [u'foo', None]), u'foo', u'foo'),
2361
2552
((None, [root_id, None]), root_id, root_id),
2362
2553
((None, [u'foo', None]), u'foo', u'foo'),
2363
2554
((None, [False, None]), False, False)),
2382
2573
wt.lock_write()
2383
2574
self.addCleanup(wt.unlock)
2384
2575
os.symlink('bar', 'path/foo')
2385
wt.add(['foo'], ['foo-id'])
2386
wt.commit('add symlink', rev_id='A-id')
2576
wt.add(['foo'], [b'foo-id'])
2577
wt.commit('add symlink', rev_id=b'A-id')
2387
2578
os.remove('path/foo')
2388
2579
os.symlink('baz', 'path/foo')
2389
wt.commit('foo => baz', rev_id='B-id')
2390
wt.set_last_revision('A-id')
2391
wt.branch.set_last_revision_info(1, 'A-id')
2580
wt.commit('foo => baz', rev_id=b'B-id')
2581
wt.set_last_revision(b'A-id')
2582
wt.branch.set_last_revision_info(1, b'A-id')
2393
wt.commit('C', rev_id='C-id')
2394
wt.merge_from_branch(wt.branch, 'B-id')
2395
self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2396
wt.commit('E merges C & B', rev_id='E-id')
2584
wt.commit('C', rev_id=b'C-id')
2585
wt.merge_from_branch(wt.branch, b'B-id')
2586
self.assertEqual('baz', wt.get_symlink_target('foo'))
2587
wt.commit('E merges C & B', rev_id=b'E-id')
2397
2588
os.remove('path/foo')
2398
2589
os.symlink('bing', 'path/foo')
2399
wt.commit('F foo => bing', rev_id='F-id')
2590
wt.commit('F foo => bing', rev_id=b'F-id')
2400
2591
wt.set_last_revision('B-id')
2401
wt.branch.set_last_revision_info(2, 'B-id')
2592
wt.branch.set_last_revision_info(2, b'B-id')
2403
wt.merge_from_branch(wt.branch, 'C-id')
2404
wt.commit('D merges B & C', rev_id='D-id')
2405
wt_base = wt.bzrdir.sprout('base', 'A-id').open_workingtree()
2594
wt.merge_from_branch(wt.branch, b'C-id')
2595
wt.commit('D merges B & C', rev_id=b'D-id')
2596
wt_base = wt.controldir.sprout('base', b'A-id').open_workingtree()
2406
2597
wt_base.lock_read()
2407
2598
self.addCleanup(wt_base.unlock)
2408
wt_lca1 = wt.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2599
wt_lca1 = wt.controldir.sprout('b-tree', b'B-id').open_workingtree()
2409
2600
wt_lca1.lock_read()
2410
2601
self.addCleanup(wt_lca1.unlock)
2411
wt_lca2 = wt.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2602
wt_lca2 = wt.controldir.sprout('c-tree', b'C-id').open_workingtree()
2412
2603
wt_lca2.lock_read()
2413
2604
self.addCleanup(wt_lca2.unlock)
2414
wt_other = wt.bzrdir.sprout('other', 'F-id').open_workingtree()
2605
wt_other = wt.controldir.sprout('other', b'F-id').open_workingtree()
2415
2606
wt_other.lock_read()
2416
2607
self.addCleanup(wt_other.unlock)
2417
2608
merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2418
2609
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2419
2610
entries = list(merge_obj._entries_lca())
2420
2611
root_id = wt.path2id('')
2421
self.assertEqual([('foo-id', True,
2612
self.assertEqual([(b'foo-id', True,
2613
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2422
2614
((root_id, [root_id, root_id]), root_id, root_id),
2423
2615
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2424
2616
((False, [False, False]), False, False)),
2436
2628
# F Path at 'foo'
2437
2629
builder = self.get_builder()
2438
builder.build_snapshot('A-id', None,
2439
[('add', (u'', 'a-root-id', 'directory', None)),
2440
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2441
builder.build_snapshot('C-id', ['A-id'], [])
2442
builder.build_snapshot('B-id', ['A-id'],
2443
[('rename', ('foo', 'bar'))])
2444
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2445
[('rename', ('foo', 'bar'))]) # merge the rename
2446
builder.build_snapshot('F-id', ['E-id'],
2447
[('rename', ('bar', 'foo'))]) # Rename back to BASE
2448
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2449
wt, conflicts = self.do_merge(builder, 'F-id')
2630
builder.build_snapshot(None,
2631
[('add', (u'', b'a-root-id', 'directory', None)),
2632
('add', (u'foo', b'foo-id', 'file', 'a\nb\nc\n'))],
2633
revision_id=b'A-id')
2634
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2635
builder.build_snapshot([b'A-id'],
2636
[('rename', ('foo', 'bar'))], revision_id=b'B-id')
2637
builder.build_snapshot([b'C-id', b'B-id'],
2638
[('rename', ('foo', 'bar'))], revision_id=b'E-id') # merge the rename
2639
builder.build_snapshot([b'E-id'],
2640
[('rename', ('bar', 'foo'))], revision_id=b'F-id') # Rename back to BASE
2641
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2642
wt, conflicts = self.do_merge(builder, b'F-id')
2450
2643
self.assertEqual(0, conflicts)
2451
self.assertEqual('foo', wt.id2path('foo-id'))
2644
self.assertEqual('foo', wt.id2path(b'foo-id'))
2453
2646
def test_other_reverted_content_to_base(self):
2454
2647
builder = self.get_builder()
2455
builder.build_snapshot('A-id', None,
2456
[('add', (u'', 'a-root-id', 'directory', None)),
2457
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2458
builder.build_snapshot('C-id', ['A-id'], [])
2459
builder.build_snapshot('B-id', ['A-id'],
2460
[('modify', ('foo-id', 'B content\n'))])
2461
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2462
[('modify', ('foo-id', 'B content\n'))]) # merge the content
2463
builder.build_snapshot('F-id', ['E-id'],
2464
[('modify', ('foo-id', 'base content\n'))]) # Revert back to BASE
2465
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2466
wt, conflicts = self.do_merge(builder, 'F-id')
2648
builder.build_snapshot(None,
2649
[('add', (u'', b'a-root-id', 'directory', None)),
2650
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2651
revision_id=b'A-id')
2652
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2653
builder.build_snapshot([b'A-id'],
2654
[('modify', ('foo', b'B content\n'))],
2655
revision_id=b'B-id')
2656
builder.build_snapshot([b'C-id', b'B-id'],
2657
[('modify', ('foo', b'B content\n'))],
2658
revision_id=b'E-id') # merge the content
2659
builder.build_snapshot([b'E-id'],
2660
[('modify', ('foo', b'base content\n'))],
2661
revision_id=b'F-id') # Revert back to BASE
2662
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2663
wt, conflicts = self.do_merge(builder, b'F-id')
2467
2664
self.assertEqual(0, conflicts)
2468
2665
# TODO: We need to use the per-file graph to properly select a BASE
2469
2666
# before this will work. Or at least use the LCA trees to find
2470
2667
# the appropriate content base. (which is B, not A).
2471
self.assertEqual('base content\n', wt.get_file_text('foo-id'))
2668
self.assertEqual('base content\n', wt.get_file_text('foo'))
2473
2670
def test_other_modified_content(self):
2474
2671
builder = self.get_builder()
2475
builder.build_snapshot('A-id', None,
2476
[('add', (u'', 'a-root-id', 'directory', None)),
2477
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2478
builder.build_snapshot('C-id', ['A-id'], [])
2479
builder.build_snapshot('B-id', ['A-id'],
2480
[('modify', ('foo-id', 'B content\n'))])
2481
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2482
[('modify', ('foo-id', 'B content\n'))]) # merge the content
2483
builder.build_snapshot('F-id', ['E-id'],
2484
[('modify', ('foo-id', 'F content\n'))]) # Override B content
2485
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2486
wt, conflicts = self.do_merge(builder, 'F-id')
2672
builder.build_snapshot(None,
2673
[('add', (u'', b'a-root-id', 'directory', None)),
2674
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2675
revision_id=b'A-id')
2676
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2677
builder.build_snapshot([b'A-id'],
2678
[('modify', ('foo', b'B content\n'))],
2679
revision_id=b'B-id')
2680
builder.build_snapshot([b'C-id', b'B-id'],
2681
[('modify', ('foo', b'B content\n'))],
2682
revision_id=b'E-id') # merge the content
2683
builder.build_snapshot([b'E-id'],
2684
[('modify', ('foo', b'F content\n'))],
2685
revision_id=b'F-id') # Override B content
2686
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2687
wt, conflicts = self.do_merge(builder, b'F-id')
2487
2688
self.assertEqual(0, conflicts)
2488
self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2689
self.assertEqual(b'F content\n', wt.get_file_text('foo'))
2490
2691
def test_all_wt(self):
2491
2692
"""Check behavior if all trees are Working Trees."""
2499
2700
# D E E updates content, renames 'b' => 'c'
2500
2701
builder = self.get_builder()
2501
builder.build_snapshot('A-id', None,
2702
builder.build_snapshot(None,
2502
2703
[('add', (u'', 'a-root-id', 'directory', None)),
2503
2704
('add', (u'a', 'a-id', 'file', 'base content\n')),
2504
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2505
builder.build_snapshot('B-id', ['A-id'],
2506
[('modify', ('foo-id', 'B content\n'))])
2507
builder.build_snapshot('C-id', ['A-id'],
2508
[('rename', ('a', 'b'))])
2509
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2705
('add', (u'foo', 'foo-id', 'file', 'base content\n'))],
2707
builder.build_snapshot(['A-id'],
2708
[('modify', ('foo', 'B content\n'))],
2710
builder.build_snapshot(['A-id'],
2711
[('rename', ('a', 'b'))],
2713
builder.build_snapshot(['C-id', 'B-id'],
2510
2714
[('rename', ('b', 'c')),
2511
('modify', ('foo-id', 'E content\n'))])
2512
builder.build_snapshot('D-id', ['B-id', 'C-id'],
2513
[('rename', ('a', 'b'))]) # merged change
2715
('modify', ('foo', 'E content\n'))],
2717
builder.build_snapshot(['B-id', 'C-id'],
2718
[('rename', ('a', 'b'))], revision_id='D-id') # merged change
2514
2719
wt_this = self.get_wt_from_builder(builder)
2515
wt_base = wt_this.bzrdir.sprout('base', 'A-id').open_workingtree()
2720
wt_base = wt_this.controldir.sprout('base', 'A-id').open_workingtree()
2516
2721
wt_base.lock_read()
2517
2722
self.addCleanup(wt_base.unlock)
2518
wt_lca1 = wt_this.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2723
wt_lca1 = wt_this.controldir.sprout('b-tree', 'B-id').open_workingtree()
2519
2724
wt_lca1.lock_read()
2520
2725
self.addCleanup(wt_lca1.unlock)
2521
wt_lca2 = wt_this.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2726
wt_lca2 = wt_this.controldir.sprout('c-tree', 'C-id').open_workingtree()
2522
2727
wt_lca2.lock_read()
2523
2728
self.addCleanup(wt_lca2.unlock)
2524
wt_other = wt_this.bzrdir.sprout('other', 'E-id').open_workingtree()
2729
wt_other = wt_this.controldir.sprout('other', 'E-id').open_workingtree()
2525
2730
wt_other.lock_read()
2526
2731
self.addCleanup(wt_other.unlock)
2527
2732
merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2542
2749
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2543
2750
# 'tree-reference'
2544
2751
wt = self.make_branch_and_tree('tree',
2545
format='dirstate-with-subtree')
2752
format='development-subtree')
2546
2753
wt.lock_write()
2547
2754
self.addCleanup(wt.unlock)
2548
2755
sub_tree = self.make_branch_and_tree('tree/sub-tree',
2549
format='dirstate-with-subtree')
2550
wt.set_root_id('a-root-id')
2551
sub_tree.set_root_id('sub-tree-root')
2552
self.build_tree_contents([('tree/sub-tree/file', 'text1')])
2756
format='development-subtree')
2757
wt.set_root_id(b'a-root-id')
2758
sub_tree.set_root_id(b'sub-tree-root')
2759
self.build_tree_contents([('tree/sub-tree/file', b'text1')])
2553
2760
sub_tree.add('file')
2554
sub_tree.commit('foo', rev_id='sub-A-id')
2761
sub_tree.commit('foo', rev_id=b'sub-A-id')
2555
2762
wt.add_reference(sub_tree)
2556
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2763
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2557
2764
# Now create a criss-cross merge in the parent, without modifying the
2559
wt.commit('B', rev_id='B-id', recursive=None)
2766
wt.commit('B', rev_id=b'B-id', recursive=None)
2560
2767
wt.set_last_revision('A-id')
2561
2768
wt.branch.set_last_revision_info(1, 'A-id')
2562
wt.commit('C', rev_id='C-id', recursive=None)
2769
wt.commit('C', rev_id=b'C-id', recursive=None)
2563
2770
wt.merge_from_branch(wt.branch, to_revision='B-id')
2564
wt.commit('E', rev_id='E-id', recursive=None)
2771
wt.commit('E', rev_id=b'E-id', recursive=None)
2565
2772
wt.set_parent_ids(['B-id', 'C-id'])
2566
2773
wt.branch.set_last_revision_info(2, 'B-id')
2567
wt.commit('D', rev_id='D-id', recursive=None)
2774
wt.commit('D', rev_id=b'D-id', recursive=None)
2569
merger = _mod_merge.Merger.from_revision_ids(None,
2776
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2571
2777
merger.merge_type = _mod_merge.Merge3Merger
2572
2778
merge_obj = merger.make_merger()
2573
2779
entries = list(merge_obj._entries_lca())
2577
2783
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2578
2784
# 'tree-reference'
2579
2785
wt = self.make_branch_and_tree('tree',
2580
format='dirstate-with-subtree')
2786
format='development-subtree')
2581
2787
wt.lock_write()
2582
2788
self.addCleanup(wt.unlock)
2583
2789
sub_tree = self.make_branch_and_tree('tree/sub',
2584
format='dirstate-with-subtree')
2585
wt.set_root_id('a-root-id')
2586
sub_tree.set_root_id('sub-tree-root')
2587
self.build_tree_contents([('tree/sub/file', 'text1')])
2790
format='development-subtree')
2791
wt.set_root_id(b'a-root-id')
2792
sub_tree.set_root_id(b'sub-tree-root')
2793
self.build_tree_contents([('tree/sub/file', b'text1')])
2588
2794
sub_tree.add('file')
2589
sub_tree.commit('foo', rev_id='sub-A-id')
2795
sub_tree.commit('foo', rev_id=b'sub-A-id')
2590
2796
wt.add_reference(sub_tree)
2591
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2797
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2592
2798
# Now create a criss-cross merge in the parent, without modifying the
2594
wt.commit('B', rev_id='B-id', recursive=None)
2800
wt.commit('B', rev_id=b'B-id', recursive=None)
2595
2801
wt.set_last_revision('A-id')
2596
2802
wt.branch.set_last_revision_info(1, 'A-id')
2597
wt.commit('C', rev_id='C-id', recursive=None)
2803
wt.commit('C', rev_id=b'C-id', recursive=None)
2598
2804
wt.merge_from_branch(wt.branch, to_revision='B-id')
2599
self.build_tree_contents([('tree/sub/file', 'text2')])
2600
sub_tree.commit('modify contents', rev_id='sub-B-id')
2601
wt.commit('E', rev_id='E-id', recursive=None)
2805
self.build_tree_contents([('tree/sub/file', b'text2')])
2806
sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2807
wt.commit('E', rev_id=b'E-id', recursive=None)
2602
2808
wt.set_parent_ids(['B-id', 'C-id'])
2603
2809
wt.branch.set_last_revision_info(2, 'B-id')
2604
wt.commit('D', rev_id='D-id', recursive=None)
2810
wt.commit('D', rev_id=b'D-id', recursive=None)
2606
merger = _mod_merge.Merger.from_revision_ids(None,
2812
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2608
2813
merger.merge_type = _mod_merge.Merge3Merger
2609
2814
merge_obj = merger.make_merger()
2610
2815
entries = list(merge_obj._entries_lca())
2616
2821
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2617
2822
# 'tree-reference'
2618
2823
wt = self.make_branch_and_tree('tree',
2619
format='dirstate-with-subtree')
2824
format='development-subtree')
2620
2825
wt.lock_write()
2621
2826
self.addCleanup(wt.unlock)
2622
2827
sub_tree = self.make_branch_and_tree('tree/sub',
2623
format='dirstate-with-subtree')
2624
wt.set_root_id('a-root-id')
2625
sub_tree.set_root_id('sub-tree-root')
2626
self.build_tree_contents([('tree/sub/file', 'text1')])
2828
format='development-subtree')
2829
wt.set_root_id(b'a-root-id')
2830
sub_tree.set_root_id(b'sub-tree-root')
2831
self.build_tree_contents([('tree/sub/file', b'text1')])
2627
2832
sub_tree.add('file')
2628
sub_tree.commit('foo', rev_id='sub-A-id')
2833
sub_tree.commit('foo', rev_id=b'sub-A-id')
2629
2834
wt.add_reference(sub_tree)
2630
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2835
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2631
2836
# Now create a criss-cross merge in the parent, without modifying the
2633
wt.commit('B', rev_id='B-id', recursive=None)
2838
wt.commit('B', rev_id=b'B-id', recursive=None)
2634
2839
wt.set_last_revision('A-id')
2635
2840
wt.branch.set_last_revision_info(1, 'A-id')
2636
wt.commit('C', rev_id='C-id', recursive=None)
2841
wt.commit('C', rev_id=b'C-id', recursive=None)
2637
2842
wt.merge_from_branch(wt.branch, to_revision='B-id')
2638
2843
wt.rename_one('sub', 'alt_sub')
2639
wt.commit('E', rev_id='E-id', recursive=None)
2844
wt.commit('E', rev_id=b'E-id', recursive=None)
2640
2845
wt.set_last_revision('B-id')
2642
2847
wt.set_parent_ids(['B-id', 'C-id'])
2643
2848
wt.branch.set_last_revision_info(2, 'B-id')
2644
wt.commit('D', rev_id='D-id', recursive=None)
2849
wt.commit('D', rev_id=b'D-id', recursive=None)
2646
merger = _mod_merge.Merger.from_revision_ids(None,
2851
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2648
2852
merger.merge_type = _mod_merge.Merge3Merger
2649
2853
merge_obj = merger.make_merger()
2650
2854
entries = list(merge_obj._entries_lca())
2651
2855
root_id = 'a-root-id'
2652
2856
self.assertEqual([('sub-tree-root', False,
2857
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2653
2858
((root_id, [root_id, root_id]), root_id, root_id),
2654
2859
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2655
2860
((False, [False, False]), False, False)),
2659
2864
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2660
2865
# 'tree-reference'
2661
2866
wt = self.make_branch_and_tree('tree',
2662
format='dirstate-with-subtree')
2867
format='development-subtree')
2663
2868
wt.lock_write()
2664
2869
self.addCleanup(wt.unlock)
2665
2870
sub_tree = self.make_branch_and_tree('tree/sub',
2666
format='dirstate-with-subtree')
2667
wt.set_root_id('a-root-id')
2668
sub_tree.set_root_id('sub-tree-root')
2669
self.build_tree_contents([('tree/sub/file', 'text1')])
2871
format='development-subtree')
2872
wt.set_root_id(b'a-root-id')
2873
sub_tree.set_root_id(b'sub-tree-root')
2874
self.build_tree_contents([('tree/sub/file', b'text1')])
2670
2875
sub_tree.add('file')
2671
sub_tree.commit('foo', rev_id='sub-A-id')
2876
sub_tree.commit('foo', rev_id=b'sub-A-id')
2672
2877
wt.add_reference(sub_tree)
2673
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2878
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2674
2879
# Now create a criss-cross merge in the parent, without modifying the
2676
wt.commit('B', rev_id='B-id', recursive=None)
2881
wt.commit('B', rev_id=b'B-id', recursive=None)
2677
2882
wt.set_last_revision('A-id')
2678
2883
wt.branch.set_last_revision_info(1, 'A-id')
2679
wt.commit('C', rev_id='C-id', recursive=None)
2884
wt.commit('C', rev_id=b'C-id', recursive=None)
2680
2885
wt.merge_from_branch(wt.branch, to_revision='B-id')
2681
self.build_tree_contents([('tree/sub/file', 'text2')])
2682
sub_tree.commit('modify contents', rev_id='sub-B-id')
2886
self.build_tree_contents([('tree/sub/file', b'text2')])
2887
sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2683
2888
wt.rename_one('sub', 'alt_sub')
2684
wt.commit('E', rev_id='E-id', recursive=None)
2889
wt.commit('E', rev_id=b'E-id', recursive=None)
2685
2890
wt.set_last_revision('B-id')
2687
2892
wt.set_parent_ids(['B-id', 'C-id'])
2688
2893
wt.branch.set_last_revision_info(2, 'B-id')
2689
wt.commit('D', rev_id='D-id', recursive=None)
2894
wt.commit('D', rev_id=b'D-id', recursive=None)
2691
merger = _mod_merge.Merger.from_revision_ids(None,
2896
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2693
2897
merger.merge_type = _mod_merge.Merge3Merger
2694
2898
merge_obj = merger.make_merger()
2695
2899
entries = list(merge_obj._entries_lca())
2696
2900
root_id = 'a-root-id'
2697
2901
self.assertEqual([('sub-tree-root', False,
2902
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2698
2903
((root_id, [root_id, root_id]), root_id, root_id),
2699
2904
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2700
2905
((False, [False, False]), False, False)),
2917
3122
conflicts = builder.merge()
2918
3123
# The hook should not call the merge_text() method
2919
3124
self.assertEqual([], self.calls)
3127
class TestMergeIntoBase(tests.TestCaseWithTransport):
3129
def setup_simple_branch(self, relpath, shape=None, root_id=None):
3130
"""One commit, containing tree specified by optional shape.
3132
Default is empty tree (just root entry).
3135
root_id = '%s-root-id' % (relpath,)
3136
wt = self.make_branch_and_tree(relpath)
3137
wt.set_root_id(root_id)
3138
if shape is not None:
3139
adjusted_shape = [relpath + '/' + elem for elem in shape]
3140
self.build_tree(adjusted_shape)
3141
ids = ['%s-%s-id' % (relpath, basename(elem.rstrip('/')))
3143
wt.add(shape, ids=ids)
3144
rev_id = 'r1-%s' % (relpath,)
3145
wt.commit("Initial commit of %s" % (relpath,), rev_id=rev_id)
3146
self.assertEqual(root_id, wt.path2id(''))
3149
def setup_two_branches(self, custom_root_ids=True):
3150
"""Setup 2 branches, one will be a library, the other a project."""
3154
root_id = inventory.ROOT_ID
3155
project_wt = self.setup_simple_branch(
3156
'project', ['README', 'dir/', 'dir/file.c'],
3158
lib_wt = self.setup_simple_branch(
3159
'lib1', ['README', 'Makefile', 'foo.c'], root_id)
3161
return project_wt, lib_wt
3163
def do_merge_into(self, location, merge_as):
3164
"""Helper for using MergeIntoMerger.
3166
:param location: location of directory to merge from, either the
3167
location of a branch or of a path inside a branch.
3168
:param merge_as: the path in a tree to add the new directory as.
3169
:returns: the conflicts from 'do_merge'.
3171
operation = cleanup.OperationWithCleanups(self._merge_into)
3172
return operation.run(location, merge_as)
3174
def _merge_into(self, op, location, merge_as):
3175
# Open and lock the various tree and branch objects
3176
wt, subdir_relpath = WorkingTree.open_containing(merge_as)
3177
op.add_cleanup(wt.lock_write().unlock)
3178
branch_to_merge, subdir_to_merge = _mod_branch.Branch.open_containing(
3180
op.add_cleanup(branch_to_merge.lock_read().unlock)
3181
other_tree = branch_to_merge.basis_tree()
3182
op.add_cleanup(other_tree.lock_read().unlock)
3184
merger = _mod_merge.MergeIntoMerger(this_tree=wt, other_tree=other_tree,
3185
other_branch=branch_to_merge, target_subdir=subdir_relpath,
3186
source_subpath=subdir_to_merge)
3187
merger.set_base_revision(_mod_revision.NULL_REVISION, branch_to_merge)
3188
conflicts = merger.do_merge()
3189
merger.set_pending()
3192
def assertTreeEntriesEqual(self, expected_entries, tree):
3193
"""Assert that 'tree' contains the expected inventory entries.
3195
:param expected_entries: sequence of (path, file-id) pairs.
3197
files = [(path, ie.file_id) for path, ie in tree.iter_entries_by_dir()]
3198
self.assertEqual(expected_entries, files)
3201
class TestMergeInto(TestMergeIntoBase):
3203
def test_newdir_with_unique_roots(self):
3204
"""Merge a branch with a unique root into a new directory."""
3205
project_wt, lib_wt = self.setup_two_branches()
3206
self.do_merge_into('lib1', 'project/lib1')
3207
project_wt.lock_read()
3208
self.addCleanup(project_wt.unlock)
3209
# The r1-lib1 revision should be merged into this one
3210
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3211
self.assertTreeEntriesEqual(
3212
[('', 'project-root-id'),
3213
('README', 'project-README-id'),
3214
('dir', 'project-dir-id'),
3215
('lib1', 'lib1-root-id'),
3216
('dir/file.c', 'project-file.c-id'),
3217
('lib1/Makefile', 'lib1-Makefile-id'),
3218
('lib1/README', 'lib1-README-id'),
3219
('lib1/foo.c', 'lib1-foo.c-id'),
3222
def test_subdir(self):
3223
"""Merge a branch into a subdirectory of an existing directory."""
3224
project_wt, lib_wt = self.setup_two_branches()
3225
self.do_merge_into('lib1', 'project/dir/lib1')
3226
project_wt.lock_read()
3227
self.addCleanup(project_wt.unlock)
3228
# The r1-lib1 revision should be merged into this one
3229
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3230
self.assertTreeEntriesEqual(
3231
[('', 'project-root-id'),
3232
('README', 'project-README-id'),
3233
('dir', 'project-dir-id'),
3234
('dir/file.c', 'project-file.c-id'),
3235
('dir/lib1', 'lib1-root-id'),
3236
('dir/lib1/Makefile', 'lib1-Makefile-id'),
3237
('dir/lib1/README', 'lib1-README-id'),
3238
('dir/lib1/foo.c', 'lib1-foo.c-id'),
3241
def test_newdir_with_repeat_roots(self):
3242
"""If the file-id of the dir to be merged already exists a new ID will
3243
be allocated to let the merge happen.
3245
project_wt, lib_wt = self.setup_two_branches(custom_root_ids=False)
3246
root_id = project_wt.path2id('')
3247
self.do_merge_into('lib1', 'project/lib1')
3248
project_wt.lock_read()
3249
self.addCleanup(project_wt.unlock)
3250
# The r1-lib1 revision should be merged into this one
3251
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3252
new_lib1_id = project_wt.path2id('lib1')
3253
self.assertNotEqual(None, new_lib1_id)
3254
self.assertTreeEntriesEqual(
3256
('README', 'project-README-id'),
3257
('dir', 'project-dir-id'),
3258
('lib1', new_lib1_id),
3259
('dir/file.c', 'project-file.c-id'),
3260
('lib1/Makefile', 'lib1-Makefile-id'),
3261
('lib1/README', 'lib1-README-id'),
3262
('lib1/foo.c', 'lib1-foo.c-id'),
3265
def test_name_conflict(self):
3266
"""When the target directory name already exists a conflict is
3267
generated and the original directory is renamed to foo.moved.
3269
dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt'])
3270
src_wt = self.setup_simple_branch('src', ['README'])
3271
conflicts = self.do_merge_into('src', 'dest/dir')
3272
self.assertEqual(1, conflicts)
3274
self.addCleanup(dest_wt.unlock)
3275
# The r1-lib1 revision should be merged into this one
3276
self.assertEqual(['r1-dest', 'r1-src'], dest_wt.get_parent_ids())
3277
self.assertTreeEntriesEqual(
3278
[('', 'dest-root-id'),
3279
('dir', 'src-root-id'),
3280
('dir.moved', 'dest-dir-id'),
3281
('dir/README', 'src-README-id'),
3282
('dir.moved/file.txt', 'dest-file.txt-id'),
3285
def test_file_id_conflict(self):
3286
"""A conflict is generated if the merge-into adds a file (or other
3287
inventory entry) with a file-id that already exists in the target tree.
3289
dest_wt = self.setup_simple_branch('dest', ['file.txt'])
3290
# Make a second tree with a file-id that will clash with file.txt in
3292
src_wt = self.make_branch_and_tree('src')
3293
self.build_tree(['src/README'])
3294
src_wt.add(['README'], ids=[b'dest-file.txt-id'])
3295
src_wt.commit("Rev 1 of src.", rev_id=b'r1-src')
3296
conflicts = self.do_merge_into('src', 'dest/dir')
3297
# This is an edge case that shouldn't happen to users very often. So
3298
# we don't care really about the exact presentation of the conflict,
3299
# just that there is one.
3300
self.assertEqual(1, conflicts)
3302
def test_only_subdir(self):
3303
"""When the location points to just part of a tree, merge just that
3306
dest_wt = self.setup_simple_branch('dest')
3307
src_wt = self.setup_simple_branch(
3308
'src', ['hello.txt', 'dir/', 'dir/foo.c'])
3309
conflicts = self.do_merge_into('src/dir', 'dest/dir')
3311
self.addCleanup(dest_wt.unlock)
3312
# The r1-lib1 revision should NOT be merged into this one (this is a
3314
self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
3315
self.assertTreeEntriesEqual(
3316
[('', 'dest-root-id'),
3317
('dir', 'src-dir-id'),
3318
('dir/foo.c', 'src-foo.c-id'),
3321
def test_only_file(self):
3322
"""An edge case: merge just one file, not a whole dir."""
3323
dest_wt = self.setup_simple_branch('dest')
3324
two_file_wt = self.setup_simple_branch(
3325
'two-file', ['file1.txt', 'file2.txt'])
3326
conflicts = self.do_merge_into('two-file/file1.txt', 'dest/file1.txt')
3328
self.addCleanup(dest_wt.unlock)
3329
# The r1-lib1 revision should NOT be merged into this one
3330
self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
3331
self.assertTreeEntriesEqual(
3332
[('', 'dest-root-id'), ('file1.txt', 'two-file-file1.txt-id')],
3335
def test_no_such_source_path(self):
3336
"""PathNotInTree is raised if the specified path in the source tree
3339
dest_wt = self.setup_simple_branch('dest')
3340
two_file_wt = self.setup_simple_branch('src', ['dir/'])
3341
self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3342
'src/no-such-dir', 'dest/foo')
3344
self.addCleanup(dest_wt.unlock)
3345
# The dest tree is unmodified.
3346
self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
3347
self.assertTreeEntriesEqual([('', 'dest-root-id')], dest_wt)
3349
def test_no_such_target_path(self):
3350
"""PathNotInTree is also raised if the specified path in the target
3351
tree does not exist.
3353
dest_wt = self.setup_simple_branch('dest')
3354
two_file_wt = self.setup_simple_branch('src', ['file.txt'])
3355
self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3356
'src', 'dest/no-such-dir/foo')
3358
self.addCleanup(dest_wt.unlock)
3359
# The dest tree is unmodified.
3360
self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
3361
self.assertTreeEntriesEqual([('', 'dest-root-id')], dest_wt)
3364
class TestMergeHooks(TestCaseWithTransport):
3367
super(TestMergeHooks, self).setUp()
3368
self.tree_a = self.make_branch_and_tree('tree_a')
3369
self.build_tree_contents([('tree_a/file', b'content_1')])
3370
self.tree_a.add('file', b'file-id')
3371
self.tree_a.commit('added file')
3373
self.tree_b = self.tree_a.controldir.sprout('tree_b').open_workingtree()
3374
self.build_tree_contents([('tree_b/file', b'content_2')])
3375
self.tree_b.commit('modify file')
3377
def test_pre_merge_hook_inject_different_tree(self):
3378
tree_c = self.tree_b.controldir.sprout('tree_c').open_workingtree()
3379
self.build_tree_contents([('tree_c/file', b'content_3')])
3380
tree_c.commit("more content")
3382
def factory(merger):
3383
self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3384
merger.other_tree = tree_c
3385
calls.append(merger)
3386
_mod_merge.Merger.hooks.install_named_hook('pre_merge',
3387
factory, 'test factory')
3388
self.tree_a.merge_from_branch(self.tree_b.branch)
3390
self.assertFileEqual("content_3", 'tree_a/file')
3391
self.assertLength(1, calls)
3393
def test_post_merge_hook_called(self):
3395
def factory(merger):
3396
self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3397
calls.append(merger)
3398
_mod_merge.Merger.hooks.install_named_hook('post_merge',
3399
factory, 'test factory')
3401
self.tree_a.merge_from_branch(self.tree_b.branch)
3403
self.assertFileEqual("content_2", 'tree_a/file')
3404
self.assertLength(1, calls)