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')
452
self.assertEqual('2b\n1\n2a\n', tree_file.read())
505
with this_tree.get_file('file') as tree_file:
506
self.assertEqual(b'2b\n1\n2a\n', tree_file.read())
508
def test_merge_require_tree_root(self):
509
tree = self.make_branch_and_tree(".")
511
self.addCleanup(tree.unlock)
512
self.build_tree(['a'])
514
first_rev = tree.commit("added a")
515
old_root_id = tree.get_root_id()
516
merger = _mod_merge.Merger.from_revision_ids(tree,
517
_mod_revision.NULL_REVISION,
519
merger.merge_type = _mod_merge.Merge3Merger
520
conflict_count = merger.do_merge()
521
self.assertEqual(0, conflict_count)
522
self.assertEqual({''}, set(tree.all_versioned_paths()))
523
tree.set_parent_ids([])
456
525
def test_merge_add_into_deleted_root(self):
457
526
# Yes, people actually do this. And report bugs if it breaks.
458
527
source = self.make_branch_and_tree('source', format='rich-root-pack')
459
528
self.build_tree(['source/foo/'])
460
source.add('foo', 'foo-id')
529
source.add('foo', b'foo-id')
461
530
source.commit('Add foo')
462
target = source.bzrdir.sprout('target').open_workingtree()
463
subtree = target.extract('foo-id')
531
target = source.controldir.sprout('target').open_workingtree()
532
subtree = target.extract('foo', b'foo-id')
464
533
subtree.commit('Delete root')
465
534
self.build_tree(['source/bar'])
466
source.add('bar', 'bar-id')
535
source.add('bar', b'bar-id')
467
536
source.commit('Add bar')
468
537
subtree.merge_from_branch(source.branch)
1210
1277
class TestMergerInMemory(TestMergerBase):
1212
1279
def test_cache_trees_with_revision_ids_None(self):
1213
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1280
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1214
1281
original_cache = dict(merger._cached_trees)
1215
1282
merger.cache_trees_with_revision_ids([None])
1216
1283
self.assertEqual(original_cache, merger._cached_trees)
1218
1285
def test_cache_trees_with_revision_ids_no_revision_id(self):
1219
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1286
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1220
1287
original_cache = dict(merger._cached_trees)
1221
1288
tree = self.make_branch_and_memory_tree('tree')
1222
1289
merger.cache_trees_with_revision_ids([tree])
1223
1290
self.assertEqual(original_cache, merger._cached_trees)
1225
1292
def test_cache_trees_with_revision_ids_having_revision_id(self):
1226
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1293
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1227
1294
original_cache = dict(merger._cached_trees)
1228
tree = merger.this_branch.repository.revision_tree('B-id')
1295
tree = merger.this_branch.repository.revision_tree(b'B-id')
1229
1296
original_cache['B-id'] = tree
1230
1297
merger.cache_trees_with_revision_ids([tree])
1231
1298
self.assertEqual(original_cache, merger._cached_trees)
1233
1300
def test_find_base(self):
1234
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1235
self.assertEqual('A-id', merger.base_rev_id)
1301
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1302
self.assertEqual(b'A-id', merger.base_rev_id)
1236
1303
self.assertFalse(merger._is_criss_cross)
1237
1304
self.assertIs(None, merger._lca_trees)
1239
1306
def test_find_base_criss_cross(self):
1240
1307
builder = self.setup_criss_cross_graph()
1241
merger = self.make_Merger(builder, 'E-id')
1242
self.assertEqual('A-id', merger.base_rev_id)
1308
merger = self.make_Merger(builder, b'E-id')
1309
self.assertEqual(b'A-id', merger.base_rev_id)
1243
1310
self.assertTrue(merger._is_criss_cross)
1244
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1311
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1245
1312
for t in merger._lca_trees])
1246
1313
# If we swap the order, we should get a different lca order
1247
builder.build_snapshot('F-id', ['E-id'], [])
1248
merger = self.make_Merger(builder, 'D-id')
1249
self.assertEqual(['C-id', 'B-id'], [t.get_revision_id()
1314
builder.build_snapshot([b'E-id'], [], revision_id=b'F-id')
1315
merger = self.make_Merger(builder, b'D-id')
1316
self.assertEqual([b'C-id', b'B-id'], [t.get_revision_id()
1250
1317
for t in merger._lca_trees])
1252
1319
def test_find_base_triple_criss_cross(self):
1262
1329
builder = self.setup_criss_cross_graph()
1263
builder.build_snapshot('F-id', ['A-id'], [])
1264
builder.build_snapshot('H-id', ['E-id', 'F-id'], [])
1265
builder.build_snapshot('G-id', ['D-id', 'F-id'], [])
1266
merger = self.make_Merger(builder, 'H-id')
1267
self.assertEqual(['B-id', 'C-id', 'F-id'],
1330
builder.build_snapshot([b'A-id'], [], revision_id=b'F-id')
1331
builder.build_snapshot([b'E-id', b'F-id'], [], revision_id=b'H-id')
1332
builder.build_snapshot([b'D-id', b'F-id'], [], revision_id=b'G-id')
1333
merger = self.make_Merger(builder, b'H-id')
1334
self.assertEqual([b'B-id', b'C-id', b'F-id'],
1268
1335
[t.get_revision_id() for t in merger._lca_trees])
1337
def test_find_base_new_root_criss_cross(self):
1343
builder = self.get_builder()
1344
builder.build_snapshot(None,
1345
[('add', ('', None, 'directory', None))],
1346
revision_id=b'A-id')
1347
builder.build_snapshot([],
1348
[('add', ('', None, 'directory', None))],
1349
revision_id=b'B-id')
1350
builder.build_snapshot([b'A-id', b'B-id'], [], revision_id=b'D-id')
1351
builder.build_snapshot([b'A-id', b'B-id'], [], revision_id=b'C-id')
1352
merger = self.make_Merger(builder, b'D-id')
1353
self.assertEqual(b'A-id', merger.base_rev_id)
1354
self.assertTrue(merger._is_criss_cross)
1355
self.assertEqual([b'A-id', b'B-id'], [t.get_revision_id()
1356
for t in merger._lca_trees])
1270
1358
def test_no_criss_cross_passed_to_merge_type(self):
1271
1359
class LCATreesMerger(LoggingMerger):
1272
1360
supports_lca_trees = True
1274
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1362
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1275
1363
merger.merge_type = LCATreesMerger
1276
1364
merge_obj = merger.make_merger()
1277
1365
self.assertIsInstance(merge_obj, LCATreesMerger)
1278
1366
self.assertFalse('lca_trees' in merge_obj.kwargs)
1280
1368
def test_criss_cross_passed_to_merge_type(self):
1281
merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
1369
merger = self.make_Merger(self.setup_criss_cross_graph(), b'E-id')
1282
1370
merger.merge_type = _mod_merge.Merge3Merger
1283
1371
merge_obj = merger.make_merger()
1284
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1372
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1285
1373
for t in merger._lca_trees])
1287
1375
def test_criss_cross_not_supported_merge_type(self):
1288
merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
1376
merger = self.make_Merger(self.setup_criss_cross_graph(), b'E-id')
1289
1377
# We explicitly do not define supports_lca_trees
1290
1378
merger.merge_type = LoggingMerger
1291
1379
merge_obj = merger.make_merger()
1306
1394
class TestMergerEntriesLCA(TestMergerBase):
1308
1396
def make_merge_obj(self, builder, other_revision_id,
1309
interesting_files=None, interesting_ids=None):
1397
interesting_files=None):
1310
1398
merger = self.make_Merger(builder, other_revision_id,
1311
interesting_files=interesting_files,
1312
interesting_ids=interesting_ids)
1399
interesting_files=interesting_files)
1313
1400
return merger.make_merger()
1315
1402
def test_simple(self):
1316
1403
builder = self.get_builder()
1317
builder.build_snapshot('A-id', None,
1318
[('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'))])
1328
merge_obj = self.make_merge_obj(builder, 'E-id')
1404
builder.build_snapshot(None,
1405
[('add', (u'', b'a-root-id', 'directory', None)),
1406
('add', (u'a', b'a-id', 'file', 'a\nb\nc\n'))],
1407
revision_id=b'A-id')
1408
builder.build_snapshot([b'A-id'],
1409
[('modify', ('a', b'a\nb\nC\nc\n'))],
1410
revision_id=b'C-id')
1411
builder.build_snapshot([b'A-id'],
1412
[('modify', ('a', b'a\nB\nb\nc\n'))],
1413
revision_id=b'B-id')
1414
builder.build_snapshot([b'C-id', b'B-id'],
1415
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1416
revision_id=b'E-id')
1417
builder.build_snapshot([b'B-id', b'C-id'],
1418
[('modify', ('a', b'a\nB\nb\nC\nc\n'))],
1419
revision_id=b'D-id', )
1420
merge_obj = self.make_merge_obj(builder, b'E-id')
1330
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1422
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1331
1423
for t in merge_obj._lca_trees])
1332
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1424
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1333
1425
entries = list(merge_obj._entries_lca())
1335
1427
# (file_id, changed, parents, names, executable)
1336
1428
# BASE, lca1, lca2, OTHER, THIS
1337
root_id = 'a-root-id'
1338
self.assertEqual([('a-id', True,
1429
root_id = b'a-root-id'
1430
self.assertEqual([(b'a-id', True,
1431
((u'a', [u'a', u'a']), u'a', u'a'),
1339
1432
((root_id, [root_id, root_id]), root_id, root_id),
1340
1433
((u'a', [u'a', u'a']), u'a', u'a'),
1341
1434
((False, [False, False]), False, False)),
1354
1447
# G modifies 'bar'
1356
1449
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'], [])
1370
merge_obj = self.make_merge_obj(builder, 'G-id')
1450
builder.build_snapshot(None,
1451
[('add', (u'', b'a-root-id', 'directory', None))],
1452
revision_id=b'A-id')
1453
builder.build_snapshot([b'A-id'],
1454
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1455
revision_id=b'B-id')
1456
builder.build_snapshot([b'A-id'],
1457
[('add', (u'bar', 'bar-id', 'file', b'd\ne\nf\n'))],
1458
revision_id=b'C-id')
1459
builder.build_snapshot([b'B-id', b'C-id'],
1460
[('add', (u'bar', 'bar-id', 'file', b'd\ne\nf\n'))],
1461
revision_id=b'D-id')
1462
builder.build_snapshot([b'C-id', b'B-id'],
1463
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1464
revision_id=b'E-id')
1465
builder.build_snapshot([b'E-id', b'D-id'],
1466
[('modify', (u'bar', b'd\ne\nf\nG\n'))],
1467
revision_id=b'G-id')
1468
builder.build_snapshot(['D-id', b'E-id'], [], revision_id=b'F-id')
1469
merge_obj = self.make_merge_obj(builder, b'G-id')
1372
self.assertEqual(['D-id', 'E-id'], [t.get_revision_id()
1471
self.assertEqual([b'D-id', b'E-id'], [t.get_revision_id()
1373
1472
for t in merge_obj._lca_trees])
1374
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1473
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1375
1474
entries = list(merge_obj._entries_lca())
1376
root_id = 'a-root-id'
1377
self.assertEqual([('bar-id', True,
1475
root_id = b'a-root-id'
1476
self.assertEqual([(b'bar-id', True,
1477
((None, [u'bar', u'bar']), u'bar', u'bar'),
1378
1478
((None, [root_id, root_id]), root_id, root_id),
1379
1479
((None, [u'bar', u'bar']), u'bar', u'bar'),
1380
1480
((None, [False, False]), False, False)),
1383
1483
def test_not_in_this(self):
1384
1484
builder = self.get_builder()
1385
builder.build_snapshot('A-id', None,
1386
[('add', (u'', 'a-root-id', 'directory', None)),
1387
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1388
builder.build_snapshot('B-id', ['A-id'],
1389
[('modify', ('a-id', 'a\nB\nb\nc\n'))])
1390
builder.build_snapshot('C-id', ['A-id'],
1391
[('modify', ('a-id', 'a\nb\nC\nc\n'))])
1392
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1393
[('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1394
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1395
[('unversion', 'a-id')])
1396
merge_obj = self.make_merge_obj(builder, 'E-id')
1485
builder.build_snapshot(None,
1486
[('add', (u'', b'a-root-id', 'directory', None)),
1487
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1488
revision_id=b'A-id')
1489
builder.build_snapshot([b'A-id'],
1490
[('modify', ('a', b'a\nB\nb\nc\n'))],
1491
revision_id=b'B-id')
1492
builder.build_snapshot([b'A-id'],
1493
[('modify', ('a', b'a\nb\nC\nc\n'))],
1494
revision_id=b'C-id')
1495
builder.build_snapshot([b'C-id', b'B-id'],
1496
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1497
revision_id=b'E-id')
1498
builder.build_snapshot([b'B-id', b'C-id'],
1499
[('unversion', 'a')],
1500
revision_id=b'D-id')
1501
merge_obj = self.make_merge_obj(builder, b'E-id')
1398
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1503
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1399
1504
for t in merge_obj._lca_trees])
1400
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1505
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1402
1507
entries = list(merge_obj._entries_lca())
1403
root_id = 'a-root-id'
1404
self.assertEqual([('a-id', True,
1508
root_id = b'a-root-id'
1509
self.assertEqual([(b'a-id', True,
1510
((u'a', [u'a', u'a']), u'a', None),
1405
1511
((root_id, [root_id, root_id]), root_id, None),
1406
1512
((u'a', [u'a', u'a']), u'a', None),
1407
1513
((False, [False, False]), False, None)),
1415
1521
# D E # D and E both have the file, unchanged from C
1416
1522
builder = self.get_builder()
1417
builder.build_snapshot('A-id', None,
1418
[('add', (u'', 'a-root-id', 'directory', None))])
1419
builder.build_snapshot('B-id', ['A-id'], [])
1420
builder.build_snapshot('C-id', ['A-id'],
1421
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1422
builder.build_snapshot('E-id', ['C-id', 'B-id'], []) # Inherited from C
1423
builder.build_snapshot('D-id', ['B-id', 'C-id'], # Merged from C
1424
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1425
merge_obj = self.make_merge_obj(builder, 'E-id')
1523
builder.build_snapshot(None,
1524
[('add', (u'', b'a-root-id', 'directory', None))],
1525
revision_id=b'A-id')
1526
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1527
builder.build_snapshot([b'A-id'],
1528
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1529
revision_id=b'C-id')
1530
builder.build_snapshot([b'C-id', b'B-id'],
1531
[], revision_id=b'E-id') # Inherited from C
1532
builder.build_snapshot([b'B-id', b'C-id'], # Merged from C
1533
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1534
revision_id=b'D-id')
1535
merge_obj = self.make_merge_obj(builder, b'E-id')
1427
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1537
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1428
1538
for t in merge_obj._lca_trees])
1429
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1539
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1431
1541
entries = list(merge_obj._entries_lca())
1432
1542
self.assertEqual([], entries)
1434
1544
def test_not_in_other(self):
1435
1545
builder = self.get_builder()
1436
builder.build_snapshot('A-id', None,
1437
[('add', (u'', 'a-root-id', 'directory', None)),
1438
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1439
builder.build_snapshot('B-id', ['A-id'], [])
1440
builder.build_snapshot('C-id', ['A-id'], [])
1441
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1442
[('unversion', 'a-id')])
1443
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1444
merge_obj = self.make_merge_obj(builder, 'E-id')
1546
builder.build_snapshot(None,
1547
[('add', (u'', b'a-root-id', 'directory', None)),
1548
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1549
revision_id=b'A-id')
1550
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1551
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1552
builder.build_snapshot(
1554
[('unversion', 'a')], revision_id=b'E-id')
1555
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1556
merge_obj = self.make_merge_obj(builder, b'E-id')
1446
1558
entries = list(merge_obj._entries_lca())
1447
root_id = 'a-root-id'
1448
self.assertEqual([('a-id', True,
1559
root_id = b'a-root-id'
1560
self.assertEqual([(b'a-id', True,
1561
((u'a', [u'a', u'a']), None, u'a'),
1449
1562
((root_id, [root_id, root_id]), None, root_id),
1450
1563
((u'a', [u'a', u'a']), None, u'a'),
1451
1564
((False, [False, False]), None, False)),
1493
1607
# In this case, we have a conflict of how the changes were resolved. E
1494
1608
# picked C and D picked B, so we should issue a conflict
1495
1609
builder = self.get_builder()
1496
builder.build_snapshot('A-id', None,
1497
[('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'], [])
1505
merge_obj = self.make_merge_obj(builder, 'E-id')
1610
builder.build_snapshot(None,
1611
[('add', (u'', b'a-root-id', 'directory', None)),
1612
('add', (u'foo', b'foo-id', 'file', b'content\n'))],
1613
revision_id=b'A-id')
1614
builder.build_snapshot([b'A-id'], [
1615
('modify', ('foo', b'new-content\n'))],
1616
revision_id=b'B-id')
1617
builder.build_snapshot([b'A-id'],
1618
[('unversion', 'foo')],
1619
revision_id=b'C-id')
1620
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1621
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1622
merge_obj = self.make_merge_obj(builder, b'E-id')
1507
1624
entries = list(merge_obj._entries_lca())
1508
root_id = 'a-root-id'
1509
self.assertEqual([('foo-id', True,
1625
root_id = b'a-root-id'
1626
self.assertEqual([(b'foo-id', True,
1627
((u'foo', [u'foo', None]), None, u'foo'),
1510
1628
((root_id, [root_id, None]), None, root_id),
1511
1629
((u'foo', [u'foo', None]), None, 'foo'),
1512
1630
((False, [False, None]), None, False)),
1529
1647
# A => C, add file, thus C supersedes B
1530
1648
# w/ C=BASE, D=THIS, E=OTHER we have 'happy convergence'
1531
1649
builder = self.get_builder()
1532
builder.build_snapshot('A-id', None,
1533
[('add', (u'', 'a-root-id', 'directory', None))])
1534
builder.build_snapshot('B-id', ['A-id'], [])
1535
builder.build_snapshot('C-id', ['A-id'],
1536
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1537
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1538
[('unversion', 'a-id')])
1539
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1540
merge_obj = self.make_merge_obj(builder, 'E-id')
1650
builder.build_snapshot(None,
1651
[('add', (u'', b'a-root-id', 'directory', None))],
1652
revision_id=b'A-id')
1653
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1654
builder.build_snapshot([b'A-id'],
1655
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1656
revision_id=b'C-id')
1657
builder.build_snapshot([b'C-id', b'B-id'],
1658
[('unversion', 'a')],
1659
revision_id=b'E-id')
1660
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1661
merge_obj = self.make_merge_obj(builder, b'E-id')
1542
1663
entries = list(merge_obj._entries_lca())
1543
1664
self.assertEqual([], entries)
1545
1666
def test_only_in_other(self):
1546
1667
builder = self.get_builder()
1547
builder.build_snapshot('A-id', None,
1548
[('add', (u'', 'a-root-id', 'directory', None))])
1549
builder.build_snapshot('B-id', ['A-id'], [])
1550
builder.build_snapshot('C-id', ['A-id'], [])
1551
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1552
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1553
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1554
merge_obj = self.make_merge_obj(builder, 'E-id')
1668
builder.build_snapshot(None,
1669
[('add', (u'', b'a-root-id', 'directory', None))],
1670
revision_id=b'A-id')
1671
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1672
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1673
builder.build_snapshot([b'C-id', b'B-id'],
1674
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1675
revision_id=b'E-id')
1676
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1677
merge_obj = self.make_merge_obj(builder, b'E-id')
1556
1679
entries = list(merge_obj._entries_lca())
1557
root_id = 'a-root-id'
1558
self.assertEqual([('a-id', True,
1680
root_id = b'a-root-id'
1681
self.assertEqual([(b'a-id', True,
1682
((None, [None, None]), u'a', None),
1559
1683
((None, [None, None]), root_id, None),
1560
1684
((None, [None, None]), u'a', None),
1561
1685
((None, [None, None]), False, None)),
1576
1700
# though its LCAs disagree. This is because the modification in E
1577
1701
# completely supersedes the value in D.
1578
1702
builder = self.get_builder()
1579
builder.build_snapshot('A-id', None,
1580
[('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'))])
1591
merge_obj = self.make_merge_obj(builder, 'G-id')
1703
builder.build_snapshot(None,
1704
[('add', (u'', b'a-root-id', 'directory', None)),
1705
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1706
revision_id=b'A-id')
1707
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1708
builder.build_snapshot([b'A-id'],
1709
[('modify', ('foo', b'B content\n'))],
1710
revision_id=b'B-id')
1711
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1712
builder.build_snapshot([b'C-id', b'B-id'],
1713
[('modify', ('foo', b'E content\n'))],
1714
revision_id=b'E-id')
1715
builder.build_snapshot([b'E-id', b'D-id'], [], revision_id=b'G-id')
1716
builder.build_snapshot([b'D-id', b'E-id'],
1717
[('modify', ('foo', b'F content\n'))],
1718
revision_id=b'F-id')
1719
merge_obj = self.make_merge_obj(builder, b'G-id')
1593
1721
self.assertEqual([], list(merge_obj._entries_lca()))
1622
1750
# aren't supporting it yet.
1624
1752
builder = self.get_builder()
1625
builder.build_snapshot('A-id', None,
1626
[('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
1638
merge_obj = self.make_merge_obj(builder, 'G-id')
1753
builder.build_snapshot(None,
1754
[('add', (u'', b'a-root-id', 'directory', None)),
1755
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1756
revision_id=b'A-id')
1757
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1758
builder.build_snapshot([b'A-id'],
1759
[('rename', ('foo', 'bar'))],
1760
revision_id=b'B-id')
1761
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1762
builder.build_snapshot([b'C-id', b'B-id'],
1763
[('rename', ('foo', 'bing'))],
1764
revision_id=b'E-id') # override to bing
1765
builder.build_snapshot([b'E-id', b'D-id'],
1766
[('rename', ('bing', 'barry'))],
1767
revision_id=b'G-id') # override to barry
1768
builder.build_snapshot([b'D-id', b'E-id'],
1769
[('rename', ('bar', 'bing'))],
1770
revision_id=b'F-id') # Merge in E's change
1771
merge_obj = self.make_merge_obj(builder, b'G-id')
1640
1773
self.expectFailure("We don't do an actual heads() check on lca values,"
1641
1774
" or use the per-attribute graph",
1655
1788
# be pruned from the LCAs, even though it was newly introduced by E
1656
1789
# (superseding B).
1657
1790
builder = self.get_builder()
1658
builder.build_snapshot('A-id', None,
1659
[('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
1670
merge_obj = self.make_merge_obj(builder, 'G-id')
1791
builder.build_snapshot(None,
1792
[('add', (u'', b'a-root-id', 'directory', None)),
1793
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1794
revision_id=b'A-id')
1795
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1796
builder.build_snapshot([b'A-id'],
1797
[('rename', ('foo', 'bar'))],
1798
revision_id=b'B-id')
1799
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1800
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1801
builder.build_snapshot([b'E-id', b'D-id'],
1802
[('rename', ('foo', 'bar'))],
1803
revision_id=b'G-id')
1804
builder.build_snapshot([b'D-id', b'E-id'],
1805
[('rename', ('bar', 'bing'))],
1806
revision_id=b'F-id') # should end up conflicting
1807
merge_obj = self.make_merge_obj(builder, b'G-id')
1672
1809
entries = list(merge_obj._entries_lca())
1673
root_id = 'a-root-id'
1810
root_id = b'a-root-id'
1674
1811
self.expectFailure("We prune values from BASE even when relevant.",
1675
1812
self.assertEqual,
1677
1814
((root_id, [root_id, root_id]), root_id, root_id),
1678
1815
((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1679
1816
((False, [False, False]), False, False)),
1688
1825
# D E D reverts to B, E reverts to C
1689
1826
# This should conflict
1690
1827
builder = self.get_builder()
1691
builder.build_snapshot('A-id', None,
1692
[('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'], [])
1700
merge_obj = self.make_merge_obj(builder, 'E-id')
1828
builder.build_snapshot(None,
1829
[('add', (u'', b'a-root-id', 'directory', None)),
1830
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1831
revision_id=b'A-id')
1832
builder.build_snapshot([b'A-id'],
1833
[('modify', ('foo', b'B content\n'))],
1834
revision_id=b'B-id')
1835
builder.build_snapshot([b'A-id'],
1836
[('modify', ('foo', 'C content\n'))],
1837
revision_id=b'C-id')
1838
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1839
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1840
merge_obj = self.make_merge_obj(builder, b'E-id')
1702
1842
entries = list(merge_obj._entries_lca())
1703
root_id = 'a-root-id'
1704
self.assertEqual([('foo-id', True,
1843
root_id = b'a-root-id'
1844
self.assertEqual([(b'foo-id', True,
1845
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1705
1846
((root_id, [root_id, root_id]), root_id, root_id),
1706
1847
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1707
1848
((False, [False, False]), False, False)),
1719
1860
# We need to emit an entry for 'foo', because D & E differed on the
1720
1861
# merge resolution
1721
1862
builder = self.get_builder()
1722
builder.build_snapshot('A-id', None,
1723
[('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'))])
1733
merge_obj = self.make_merge_obj(builder, 'E-id')
1863
builder.build_snapshot(None,
1864
[('add', (u'', b'a-root-id', 'directory', None)),
1865
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1866
revision_id=b'A-id')
1867
builder.build_snapshot([b'A-id'],
1868
[('modify', ('foo', 'B content\n'))],
1869
revision_id=b'B-id')
1870
builder.build_snapshot([b'A-id'],
1871
[('modify', ('foo', b'C content\n'))],
1872
revision_id=b'C-id', )
1873
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1874
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1875
builder.build_snapshot([b'D-id'],
1876
[('modify', ('foo', b'F content\n'))],
1877
revision_id=b'F-id')
1878
merge_obj = self.make_merge_obj(builder, b'E-id')
1735
1880
entries = list(merge_obj._entries_lca())
1736
root_id = 'a-root-id'
1737
self.assertEqual([('foo-id', True,
1881
root_id = b'a-root-id'
1882
self.assertEqual([(b'foo-id', True,
1883
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1738
1884
((root_id, [root_id, root_id]), root_id, root_id),
1739
1885
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1740
1886
((False, [False, False]), False, False)),
1755
1901
# We need to conflict.
1757
1903
builder = self.get_builder()
1758
builder.build_snapshot('A-id', None,
1759
[('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'))])
1770
merge_obj = self.make_merge_obj(builder, 'E-id')
1904
builder.build_snapshot(None,
1905
[('add', (u'', b'a-root-id', 'directory', None)),
1906
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1907
revision_id=b'A-id')
1908
builder.build_snapshot([b'A-id'],
1909
[('modify', ('foo', b'B content\n'))],
1910
revision_id=b'B-id')
1911
builder.build_snapshot([b'A-id'],
1912
[('modify', ('foo', b'C content\n'))],
1913
revision_id=b'C-id')
1914
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1915
builder.build_snapshot([b'B-id', b'C-id'],
1916
[('modify', ('foo', b'C content\n'))],
1917
revision_id=b'D-id') # Same as E
1918
builder.build_snapshot([b'D-id'],
1919
[('modify', ('foo', b'F content\n'))],
1920
revision_id=b'F-id')
1921
merge_obj = self.make_merge_obj(builder, b'E-id')
1772
1923
entries = list(merge_obj._entries_lca())
1773
1924
self.expectFailure("We don't detect that LCA resolution was the"
1777
1928
def test_only_path_changed(self):
1778
1929
builder = self.get_builder()
1779
builder.build_snapshot('A-id', None,
1780
[('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'], [])
1787
merge_obj = self.make_merge_obj(builder, 'E-id')
1930
builder.build_snapshot(None,
1931
[('add', (u'', b'a-root-id', 'directory', None)),
1932
('add', (u'a', b'a-id', 'file', b'content\n'))],
1933
revision_id=b'A-id')
1934
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1935
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1936
builder.build_snapshot([b'C-id', b'B-id'],
1937
[('rename', (u'a', u'b'))],
1938
revision_id=b'E-id')
1939
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1940
merge_obj = self.make_merge_obj(builder, b'E-id')
1788
1941
entries = list(merge_obj._entries_lca())
1789
root_id = 'a-root-id'
1942
root_id = b'a-root-id'
1790
1943
# The content was not changed, only the path
1791
self.assertEqual([('a-id', False,
1944
self.assertEqual([(b'a-id', False,
1945
((u'a', [u'a', u'a']), u'b', u'a'),
1792
1946
((root_id, [root_id, root_id]), root_id, root_id),
1793
1947
((u'a', [u'a', u'a']), u'b', u'a'),
1794
1948
((False, [False, False]), False, False)),
1797
1951
def test_kind_changed(self):
1798
1952
# Identical content, except 'D' changes a-id into a directory
1799
1953
builder = self.get_builder()
1800
builder.build_snapshot('A-id', None,
1801
[('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'], [])
1809
merge_obj = self.make_merge_obj(builder, 'E-id')
1954
builder.build_snapshot(None,
1955
[('add', (u'', b'a-root-id', 'directory', None)),
1956
('add', (u'a', b'a-id', 'file', b'content\n'))],
1957
revision_id=b'A-id')
1958
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1959
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1960
builder.build_snapshot([b'C-id', b'B-id'],
1961
[('unversion', 'a'),
1963
('add', (u'a', b'a-id', 'directory', None))],
1964
revision_id=b'E-id')
1965
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1966
merge_obj = self.make_merge_obj(builder, b'E-id')
1810
1967
entries = list(merge_obj._entries_lca())
1811
root_id = 'a-root-id'
1968
root_id = b'a-root-id'
1812
1969
# Only the kind was changed (content)
1813
self.assertEqual([('a-id', True,
1970
self.assertEqual([(b'a-id', True,
1971
((u'a', [u'a', u'a']), u'a', u'a'),
1814
1972
((root_id, [root_id, root_id]), root_id, root_id),
1815
1973
((u'a', [u'a', u'a']), u'a', u'a'),
1816
1974
((False, [False, False]), False, False)),
1836
1997
def test_interesting_files(self):
1837
1998
# Two files modified, but we should filter one of them
1838
1999
builder = self.get_builder()
1839
builder.build_snapshot('A-id', None,
1840
[('add', (u'', 'a-root-id', 'directory', None)),
1841
('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'], [])
1849
merge_obj = self.make_merge_obj(builder, 'E-id',
2000
builder.build_snapshot(None,
2001
[('add', (u'', b'a-root-id', 'directory', None)),
2002
('add', (u'a', b'a-id', 'file', b'content\n')),
2003
('add', (u'b', b'b-id', 'file', b'content\n'))],
2004
revision_id=b'A-id')
2005
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2006
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2007
builder.build_snapshot([b'C-id', b'B-id'],
2008
[('modify', ('a', b'new-content\n')),
2009
('modify', ('b', b'new-content\n'))],
2010
revision_id=b'E-id')
2011
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2012
merge_obj = self.make_merge_obj(builder, b'E-id',
1850
2013
interesting_files=['b'])
1851
2014
entries = list(merge_obj._entries_lca())
1852
root_id = 'a-root-id'
1853
self.assertEqual([('b-id', True,
2015
root_id = b'a-root-id'
2016
self.assertEqual([(b'b-id', True,
2017
((u'b', [u'b', u'b']), u'b', u'b'),
1854
2018
((root_id, [root_id, root_id]), root_id, root_id),
1855
2019
((u'b', [u'b', u'b']), u'b', u'b'),
1856
2020
((False, [False, False]), False, False)),
1859
2023
def test_interesting_file_in_this(self):
1860
2024
# This renamed the file, but it should still match the entry in other
1861
2025
builder = self.get_builder()
1862
builder.build_snapshot('A-id', None,
1863
[('add', (u'', 'a-root-id', 'directory', None)),
1864
('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'))])
1873
merge_obj = self.make_merge_obj(builder, 'E-id',
2026
builder.build_snapshot(None,
2027
[('add', (u'', b'a-root-id', 'directory', None)),
2028
('add', (u'a', b'a-id', 'file', b'content\n')),
2029
('add', (u'b', b'b-id', 'file', b'content\n'))],
2030
revision_id=b'A-id')
2031
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2032
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2033
builder.build_snapshot([b'C-id', b'B-id'],
2034
[('modify', ('a', 'new-content\n')),
2035
('modify', ('b', 'new-content\n'))],
2036
revision_id=b'E-id')
2037
builder.build_snapshot([b'B-id', b'C-id'],
2038
[('rename', ('b', 'c'))],
2039
revision_id=b'D-id')
2040
merge_obj = self.make_merge_obj(builder, b'E-id',
1874
2041
interesting_files=['c'])
1875
2042
entries = list(merge_obj._entries_lca())
1876
root_id = 'a-root-id'
1877
self.assertEqual([('b-id', True,
2043
root_id = b'a-root-id'
2044
self.assertEqual([(b'b-id', True,
2045
((u'b', [u'b', u'b']), u'b', u'c'),
1878
2046
((root_id, [root_id, root_id]), root_id, root_id),
1879
2047
((u'b', [u'b', u'b']), u'b', u'c'),
1880
2048
((False, [False, False]), False, False)),
1883
2051
def test_interesting_file_in_base(self):
1884
2052
# This renamed the file, but it should still match the entry in BASE
1885
2053
builder = self.get_builder()
1886
builder.build_snapshot('A-id', None,
1887
[('add', (u'', 'a-root-id', 'directory', None)),
1888
('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'], [])
1898
merge_obj = self.make_merge_obj(builder, 'E-id',
2054
builder.build_snapshot(None,
2055
[('add', (u'', b'a-root-id', 'directory', None)),
2056
('add', (u'a', b'a-id', 'file', b'content\n')),
2057
('add', (u'c', b'c-id', 'file', b'content\n'))],
2058
revision_id=b'A-id')
2059
builder.build_snapshot([b'A-id'],
2060
[('rename', ('c', 'b'))],
2061
revision_id=b'B-id')
2062
builder.build_snapshot([b'A-id'],
2063
[('rename', ('c', 'b'))],
2064
revision_id=b'C-id')
2065
builder.build_snapshot([b'C-id', b'B-id'],
2066
[('modify', ('a', b'new-content\n')),
2067
('modify', ('b', b'new-content\n'))],
2068
revision_id=b'E-id')
2069
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2070
merge_obj = self.make_merge_obj(builder, b'E-id',
1899
2071
interesting_files=['c'])
1900
2072
entries = list(merge_obj._entries_lca())
1901
root_id = 'a-root-id'
1902
self.assertEqual([('c-id', True,
2073
root_id = b'a-root-id'
2074
self.assertEqual([(b'c-id', True,
2075
((u'c', [u'b', u'b']), u'b', u'b'),
1903
2076
((root_id, [root_id, root_id]), root_id, root_id),
1904
2077
((u'c', [u'b', u'b']), u'b', u'b'),
1905
2078
((False, [False, False]), False, False)),
1908
2081
def test_interesting_file_in_lca(self):
1909
2082
# This renamed the file, but it should still match the entry in LCA
1910
2083
builder = self.get_builder()
1911
builder.build_snapshot('A-id', None,
1912
[('add', (u'', 'a-root-id', 'directory', None)),
1913
('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'))])
1923
merge_obj = self.make_merge_obj(builder, 'E-id',
2084
builder.build_snapshot(None,
2085
[('add', (u'', b'a-root-id', 'directory', None)),
2086
('add', (u'a', b'a-id', 'file', b'content\n')),
2087
('add', (u'b', 'b-id', 'file', b'content\n'))],
2088
revision_id=b'A-id')
2089
builder.build_snapshot([b'A-id'],
2090
[('rename', ('b', 'c'))], revision_id=b'B-id')
2091
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2092
builder.build_snapshot([b'C-id', b'B-id'],
2093
[('modify', ('a', b'new-content\n')),
2094
('modify', ('b', b'new-content\n'))],
2095
revision_id=b'E-id')
2096
builder.build_snapshot(['B-id', b'C-id'],
2097
[('rename', ('c', 'b'))], revision_id=b'D-id')
2098
merge_obj = self.make_merge_obj(builder, b'E-id',
1924
2099
interesting_files=['c'])
1925
2100
entries = list(merge_obj._entries_lca())
1926
root_id = 'a-root-id'
1927
self.assertEqual([('b-id', True,
2101
root_id = b'a-root-id'
2102
self.assertEqual([(b'b-id', True,
2103
((u'b', [u'c', u'b']), u'b', u'b'),
1928
2104
((root_id, [root_id, root_id]), root_id, root_id),
1929
2105
((u'b', [u'c', u'b']), u'b', u'b'),
1930
2106
((False, [False, False]), False, False)),
1933
def test_interesting_ids(self):
2109
def test_interesting_files(self):
1934
2110
# Two files modified, but we should filter one of them
1935
2111
builder = self.get_builder()
1936
builder.build_snapshot('A-id', None,
1937
[('add', (u'', 'a-root-id', 'directory', None)),
1938
('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'], [])
1946
merge_obj = self.make_merge_obj(builder, 'E-id',
1947
interesting_ids=['b-id'])
2112
builder.build_snapshot(None,
2113
[('add', (u'', b'a-root-id', 'directory', None)),
2114
('add', (u'a', b'a-id', 'file', b'content\n')),
2115
('add', (u'b', b'b-id', 'file', b'content\n'))],
2116
revision_id=b'A-id')
2117
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2118
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2119
builder.build_snapshot([b'C-id', b'B-id'],
2120
[('modify', ('a', b'new-content\n')),
2121
('modify', ('b', b'new-content\n'))], revision_id=b'E-id')
2122
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2123
merge_obj = self.make_merge_obj(builder, b'E-id',
2124
interesting_files=['b'])
1948
2125
entries = list(merge_obj._entries_lca())
1949
root_id = 'a-root-id'
1950
self.assertEqual([('b-id', True,
2126
root_id = b'a-root-id'
2127
self.assertEqual([(b'b-id', True,
2128
((u'b', [u'b', u'b']), u'b', u'b'),
1951
2129
((root_id, [root_id, root_id]), root_id, root_id),
1952
2130
((u'b', [u'b', u'b']), u'b', u'b'),
1953
2131
((False, [False, False]), False, False)),
1979
2157
def do_merge(self, builder, other_revision_id):
1980
2158
wt = self.get_wt_from_builder(builder)
1981
merger = _mod_merge.Merger.from_revision_ids(None,
2159
merger = _mod_merge.Merger.from_revision_ids(
1982
2160
wt, other_revision_id)
1983
2161
merger.merge_type = _mod_merge.Merge3Merger
1984
2162
return wt, merger.do_merge()
1986
2164
def test_simple_lca(self):
1987
2165
builder = self.get_builder()
1988
builder.build_snapshot('A-id', None,
1989
[('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'))])
1996
wt, conflicts = self.do_merge(builder, 'E-id')
2166
builder.build_snapshot(None,
2167
[('add', (u'', b'a-root-id', 'directory', None)),
2168
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
2169
revision_id=b'A-id')
2170
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2171
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2172
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
2173
builder.build_snapshot([b'B-id', b'C-id'],
2174
[('modify', ('a', b'a\nb\nc\nd\ne\nf\n'))],
2175
revision_id=b'D-id')
2176
wt, conflicts = self.do_merge(builder, b'E-id')
1997
2177
self.assertEqual(0, conflicts)
1998
2178
# 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'))
2179
self.assertEqual(b'a\nb\nc\nd\ne\nf\n', wt.get_file_text('a'))
2001
2181
def test_conflict_without_lca(self):
2002
2182
# This test would cause a merge conflict, unless we use the lca trees
2012
2192
# F Path at 'baz' in F, which supersedes 'bar' and 'foo'
2013
2193
builder = self.get_builder()
2014
builder.build_snapshot('A-id', None,
2015
[('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'], [])
2025
wt, conflicts = self.do_merge(builder, 'F-id')
2194
builder.build_snapshot(None,
2195
[('add', (u'', b'a-root-id', 'directory', None)),
2196
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2197
revision_id=b'A-id')
2198
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2199
builder.build_snapshot([b'A-id'],
2200
[('rename', ('foo', 'bar'))], revision_id=b'B-id', )
2201
builder.build_snapshot([b'C-id', b'B-id'], # merge the rename
2202
[('rename', ('foo', 'bar'))], revision_id=b'E-id')
2203
builder.build_snapshot([b'E-id'],
2204
[('rename', ('bar', 'baz'))], revision_id=b'F-id')
2205
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2206
wt, conflicts = self.do_merge(builder, b'F-id')
2026
2207
self.assertEqual(0, conflicts)
2027
2208
# The merge should simply recognize that the final rename takes
2029
self.assertEqual('baz', wt.id2path('foo-id'))
2210
self.assertEqual('baz', wt.id2path(b'foo-id'))
2031
2212
def test_other_deletes_lca_renames(self):
2032
2213
# This test would cause a merge conflict, unless we use the lca trees
2042
2223
# F F deletes 'bar'
2043
2224
builder = self.get_builder()
2044
builder.build_snapshot('A-id', None,
2045
[('add', (u'', 'a-root-id', 'directory', None)),
2046
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2047
builder.build_snapshot('C-id', ['A-id'], [])
2048
builder.build_snapshot('B-id', ['A-id'],
2049
[('rename', ('foo', 'bar'))])
2050
builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
2051
[('rename', ('foo', 'bar'))])
2052
builder.build_snapshot('F-id', ['E-id'],
2053
[('unversion', 'foo-id')])
2054
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2055
wt, conflicts = self.do_merge(builder, 'F-id')
2225
builder.build_snapshot(None,
2226
[('add', (u'', b'a-root-id', 'directory', None)),
2227
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2228
revision_id=b'A-id')
2229
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2230
builder.build_snapshot([b'A-id'],
2231
[('rename', ('foo', 'bar'))], revision_id=b'B-id')
2232
builder.build_snapshot([b'C-id', b'B-id'], # merge the rename
2233
[('rename', ('foo', 'bar'))], revision_id=b'E-id')
2234
builder.build_snapshot([b'E-id'],
2235
[('unversion', 'bar')], revision_id=b'F-id')
2236
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2237
wt, conflicts = self.do_merge(builder, b'F-id')
2056
2238
self.assertEqual(0, conflicts)
2057
self.assertRaises(errors.NoSuchId, wt.id2path, 'foo-id')
2239
self.assertRaises(errors.NoSuchId, wt.id2path, b'foo-id')
2059
2241
def test_executable_changes(self):
2060
2242
# A Path at 'foo'
2068
2250
# F Executable bit changed
2069
2251
builder = self.get_builder()
2070
builder.build_snapshot('A-id', None,
2071
[('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'], [])
2252
builder.build_snapshot(None,
2253
[('add', (u'', b'a-root-id', 'directory', None)),
2254
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2255
revision_id=b'A-id')
2256
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2257
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2258
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2259
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
2077
2260
# Have to use a real WT, because BranchBuilder doesn't support exec bit
2078
2261
wt = self.get_wt_from_builder(builder)
2079
2262
tt = transform.TreeTransform(wt)
2081
tt.set_executability(True, tt.trans_id_tree_file_id('foo-id'))
2264
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')
2269
self.assertTrue(wt.is_executable('foo'))
2270
wt.commit('F-id', rev_id=b'F-id')
2088
2271
# Reset to D, so that we can merge F
2089
wt.set_parent_ids(['D-id'])
2090
wt.branch.set_last_revision_info(3, 'D-id')
2272
wt.set_parent_ids([b'D-id'])
2273
wt.branch.set_last_revision_info(3, b'D-id')
2092
self.assertFalse(wt.is_executable('foo-id'))
2093
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2275
self.assertFalse(wt.is_executable('foo'))
2276
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2094
2277
self.assertEqual(0, conflicts)
2095
self.assertTrue(wt.is_executable('foo-id'))
2278
self.assertTrue(wt.is_executable('foo'))
2097
2280
def test_create_symlink(self):
2098
self.requireFeature(tests.SymlinkFeature)
2281
self.requireFeature(features.SymlinkFeature)
2108
2291
# Have to use a real WT, because BranchBuilder and MemoryTree don't
2109
2292
# have symlink support
2110
2293
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'], [])
2294
builder.build_snapshot(None,
2295
[('add', (u'', b'a-root-id', 'directory', None))],
2296
revision_id=b'A-id')
2297
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2298
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2299
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2300
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
2117
2301
# Have to use a real WT, because BranchBuilder doesn't support exec bit
2118
2302
wt = self.get_wt_from_builder(builder)
2119
2303
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')
2304
wt.add(['foo'], [b'foo-id'])
2305
self.assertEqual('bar', wt.get_symlink_target('foo'))
2306
wt.commit('add symlink', rev_id=b'F-id')
2123
2307
# 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')
2308
wt.set_parent_ids([b'D-id'])
2309
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')
2311
self.assertFalse(wt.is_versioned('foo'))
2312
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2129
2313
self.assertEqual(0, conflicts)
2130
self.assertEqual('foo-id', wt.path2id('foo'))
2131
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2314
self.assertEqual(b'foo-id', wt.path2id('foo'))
2315
self.assertEqual('bar', wt.get_symlink_target('foo'))
2133
2317
def test_both_sides_revert(self):
2134
2318
# Both sides of a criss-cross revert the text to the lca
2141
2325
# This must be done with a real WorkingTree, because normally their
2142
2326
# inventory contains "None" rather than a real sha1
2143
2327
builder = self.get_builder()
2144
builder.build_snapshot('A-id', None,
2145
[('add', (u'', 'a-root-id', 'directory', None)),
2146
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
2147
builder.build_snapshot('B-id', ['A-id'],
2148
[('modify', ('foo-id', 'B content\n'))])
2149
builder.build_snapshot('C-id', ['A-id'],
2150
[('modify', ('foo-id', 'C content\n'))])
2151
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2152
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2153
wt, conflicts = self.do_merge(builder, 'E-id')
2328
builder.build_snapshot(None,
2329
[('add', (u'', b'a-root-id', 'directory', None)),
2330
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
2331
revision_id=b'A-id')
2332
builder.build_snapshot([b'A-id'],
2333
[('modify', ('foo', b'B content\n'))],
2334
revision_id=b'B-id')
2335
builder.build_snapshot([b'A-id'],
2336
[('modify', ('foo', b'C content\n'))],
2337
revision_id=b'C-id')
2338
builder.build_snapshot([b'C-id', b'B-id'], [],
2339
revision_id=b'E-id')
2340
builder.build_snapshot([b'B-id', b'C-id'], [],
2341
revision_id=b'D-id')
2342
wt, conflicts = self.do_merge(builder, b'E-id')
2154
2343
self.assertEqual(1, conflicts)
2155
self.assertEqualDiff('<<<<<<< TREE\n'
2159
'>>>>>>> MERGE-SOURCE\n',
2160
wt.get_file_text('foo-id'))
2344
self.assertEqualDiff(b'<<<<<<< TREE\n'
2348
b'>>>>>>> MERGE-SOURCE\n',
2349
wt.get_file_text('foo'))
2162
2351
def test_modified_symlink(self):
2163
self.requireFeature(tests.SymlinkFeature)
2352
self.requireFeature(features.SymlinkFeature)
2164
2353
# A Create symlink foo => bar
2166
2355
# B C B relinks foo => baz
2180
2369
wt.lock_write()
2181
2370
self.addCleanup(wt.unlock)
2182
2371
os.symlink('bar', 'path/foo')
2183
wt.add(['foo'], ['foo-id'])
2184
wt.commit('add symlink', rev_id='A-id')
2372
wt.add(['foo'], [b'foo-id'])
2373
wt.commit('add symlink', rev_id=b'A-id')
2185
2374
os.remove('path/foo')
2186
2375
os.symlink('baz', 'path/foo')
2187
wt.commit('foo => baz', rev_id='B-id')
2188
wt.set_last_revision('A-id')
2189
wt.branch.set_last_revision_info(1, 'A-id')
2376
wt.commit('foo => baz', rev_id=b'B-id')
2377
wt.set_last_revision(b'A-id')
2378
wt.branch.set_last_revision_info(1, b'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')
2380
wt.commit('C', rev_id=b'C-id')
2381
wt.merge_from_branch(wt.branch, b'B-id')
2382
self.assertEqual('baz', wt.get_symlink_target('foo'))
2383
wt.commit('E merges C & B', rev_id=b'E-id')
2195
2384
os.remove('path/foo')
2196
2385
os.symlink('bing', 'path/foo')
2197
wt.commit('F foo => bing', rev_id='F-id')
2198
wt.set_last_revision('B-id')
2199
wt.branch.set_last_revision_info(2, 'B-id')
2386
wt.commit('F foo => bing', rev_id=b'F-id')
2387
wt.set_last_revision(b'B-id')
2388
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')
2390
wt.merge_from_branch(wt.branch, b'C-id')
2391
wt.commit('D merges B & C', rev_id=b'D-id')
2392
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2204
2393
self.assertEqual(0, conflicts)
2205
self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2394
self.assertEqual('bing', wt.get_symlink_target('foo'))
2207
2396
def test_renamed_symlink(self):
2208
self.requireFeature(tests.SymlinkFeature)
2397
self.requireFeature(features.SymlinkFeature)
2209
2398
# A Create symlink foo => bar
2211
2400
# B C B renames foo => barry
2223
2412
wt.lock_write()
2224
2413
self.addCleanup(wt.unlock)
2225
2414
os.symlink('bar', 'path/foo')
2226
wt.add(['foo'], ['foo-id'])
2227
wt.commit('A add symlink', rev_id='A-id')
2415
wt.add(['foo'], [b'foo-id'])
2416
wt.commit('A add symlink', rev_id=b'A-id')
2228
2417
wt.rename_one('foo', 'barry')
2229
wt.commit('B foo => barry', rev_id='B-id')
2230
wt.set_last_revision('A-id')
2231
wt.branch.set_last_revision_info(1, 'A-id')
2418
wt.commit('B foo => barry', rev_id=b'B-id')
2419
wt.set_last_revision(b'A-id')
2420
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')
2422
wt.commit('C', rev_id=b'C-id')
2423
wt.merge_from_branch(wt.branch, b'B-id')
2424
self.assertEqual('barry', wt.id2path(b'foo-id'))
2425
self.assertEqual('bar', wt.get_symlink_target('barry'))
2426
wt.commit('E merges C & B', rev_id=b'E-id')
2238
2427
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')
2428
wt.commit('F barry => blah', rev_id=b'F-id')
2429
wt.set_last_revision(b'B-id')
2430
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'))
2432
wt.merge_from_branch(wt.branch, b'C-id')
2433
wt.commit('D merges B & C', rev_id=b'D-id')
2434
self.assertEqual('barry', wt.id2path(b'foo-id'))
2246
2435
# Check the output of the Merger object directly
2247
merger = _mod_merge.Merger.from_revision_ids(None,
2436
merger = _mod_merge.Merger.from_revision_ids(wt, b'F-id')
2249
2437
merger.merge_type = _mod_merge.Merge3Merger
2250
2438
merge_obj = merger.make_merger()
2251
2439
root_id = wt.path2id('')
2252
2440
entries = list(merge_obj._entries_lca())
2253
2441
# No content change, just a path change
2254
self.assertEqual([('foo-id', False,
2442
self.assertEqual([(b'foo-id', False,
2443
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2255
2444
((root_id, [root_id, root_id]), root_id, root_id),
2256
2445
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2257
2446
((False, [False, False]), False, False)),
2259
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2448
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2260
2449
self.assertEqual(0, conflicts)
2261
self.assertEqual('blah', wt.id2path('foo-id'))
2450
self.assertEqual('blah', wt.id2path(b'foo-id'))
2263
2452
def test_symlink_no_content_change(self):
2264
self.requireFeature(tests.SymlinkFeature)
2453
self.requireFeature(features.SymlinkFeature)
2265
2454
# A Create symlink foo => bar
2267
2456
# B C B relinks foo => baz
2278
2467
wt.lock_write()
2279
2468
self.addCleanup(wt.unlock)
2280
2469
os.symlink('bar', 'path/foo')
2281
wt.add(['foo'], ['foo-id'])
2282
wt.commit('add symlink', rev_id='A-id')
2470
wt.add(['foo'], [b'foo-id'])
2471
wt.commit('add symlink', rev_id=b'A-id')
2283
2472
os.remove('path/foo')
2284
2473
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')
2474
wt.commit('foo => baz', rev_id=b'B-id')
2475
wt.set_last_revision(b'A-id')
2476
wt.branch.set_last_revision_info(1, b'A-id')
2478
wt.commit('C', rev_id=b'C-id')
2479
wt.merge_from_branch(wt.branch, b'B-id')
2480
self.assertEqual('baz', wt.get_symlink_target('foo'))
2481
wt.commit('E merges C & B', rev_id=b'E-id')
2482
wt.set_last_revision(b'B-id')
2483
wt.branch.set_last_revision_info(2, b'B-id')
2485
wt.merge_from_branch(wt.branch, b'C-id')
2486
wt.commit('D merges B & C', rev_id=b'D-id')
2298
2487
os.remove('path/foo')
2299
2488
os.symlink('bing', 'path/foo')
2300
wt.commit('F foo => bing', rev_id='F-id')
2489
wt.commit('F foo => bing', rev_id=b'F-id')
2302
2491
# Check the output of the Merger object directly
2303
merger = _mod_merge.Merger.from_revision_ids(None,
2492
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2305
2493
merger.merge_type = _mod_merge.Merge3Merger
2306
2494
merge_obj = merger.make_merger()
2307
2495
# Nothing interesting happened in OTHER relative to BASE
2308
2496
self.assertEqual([], list(merge_obj._entries_lca()))
2309
2497
# 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')
2498
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'E-id')
2311
2499
self.assertEqual(0, conflicts)
2312
self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2500
self.assertEqual('bing', wt.get_symlink_target('foo'))
2314
2502
def test_symlink_this_changed_kind(self):
2315
self.requireFeature(tests.SymlinkFeature)
2503
self.requireFeature(features.SymlinkFeature)
2318
2506
# B C B creates symlink foo => bar
2326
2514
wt = self.make_branch_and_tree('path')
2327
2515
wt.lock_write()
2328
2516
self.addCleanup(wt.unlock)
2329
wt.commit('base', rev_id='A-id')
2517
wt.commit('base', rev_id=b'A-id')
2330
2518
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')
2519
wt.add(['foo'], [b'foo-id'])
2520
wt.commit('add symlink foo => bar', rev_id=b'B-id')
2521
wt.set_last_revision(b'A-id')
2522
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'))
2524
wt.commit('C', rev_id=b'C-id')
2525
wt.merge_from_branch(wt.branch, b'B-id')
2526
self.assertEqual('bar', wt.get_symlink_target('foo'))
2339
2527
os.remove('path/foo')
2340
2528
# We have to change the link in E, or it won't try to do a comparison
2341
2529
os.symlink('bing', 'path/foo')
2342
wt.commit('E merges C & B, overrides to bing', rev_id='E-id')
2343
wt.set_last_revision('B-id')
2344
wt.branch.set_last_revision_info(2, 'B-id')
2530
wt.commit('E merges C & B, overrides to bing', rev_id=b'E-id')
2531
wt.set_last_revision(b'B-id')
2532
wt.branch.set_last_revision_info(2, b'B-id')
2346
wt.merge_from_branch(wt.branch, 'C-id')
2534
wt.merge_from_branch(wt.branch, b'C-id')
2347
2535
os.remove('path/foo')
2348
self.build_tree_contents([('path/foo', 'file content\n')])
2536
self.build_tree_contents([('path/foo', b'file content\n')])
2349
2537
# XXX: workaround, WT doesn't detect kind changes unless you do
2350
2538
# iter_changes()
2351
2539
list(wt.iter_changes(wt.basis_tree()))
2352
wt.commit('D merges B & C, makes it a file', rev_id='D-id')
2540
wt.commit('D merges B & C, makes it a file', rev_id=b'D-id')
2354
merger = _mod_merge.Merger.from_revision_ids(None,
2542
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2356
2543
merger.merge_type = _mod_merge.Merge3Merger
2357
2544
merge_obj = merger.make_merger()
2358
2545
entries = list(merge_obj._entries_lca())
2359
2546
root_id = wt.path2id('')
2360
self.assertEqual([('foo-id', True,
2547
self.assertEqual([(b'foo-id', True,
2548
((None, [u'foo', None]), u'foo', u'foo'),
2361
2549
((None, [root_id, None]), root_id, root_id),
2362
2550
((None, [u'foo', None]), u'foo', u'foo'),
2363
2551
((None, [False, None]), False, False)),
2382
2570
wt.lock_write()
2383
2571
self.addCleanup(wt.unlock)
2384
2572
os.symlink('bar', 'path/foo')
2385
wt.add(['foo'], ['foo-id'])
2386
wt.commit('add symlink', rev_id='A-id')
2573
wt.add(['foo'], [b'foo-id'])
2574
wt.commit('add symlink', rev_id=b'A-id')
2387
2575
os.remove('path/foo')
2388
2576
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')
2577
wt.commit('foo => baz', rev_id=b'B-id')
2578
wt.set_last_revision(b'A-id')
2579
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')
2581
wt.commit('C', rev_id=b'C-id')
2582
wt.merge_from_branch(wt.branch, b'B-id')
2583
self.assertEqual('baz', wt.get_symlink_target('foo'))
2584
wt.commit('E merges C & B', rev_id=b'E-id')
2397
2585
os.remove('path/foo')
2398
2586
os.symlink('bing', 'path/foo')
2399
wt.commit('F foo => bing', rev_id='F-id')
2400
wt.set_last_revision('B-id')
2401
wt.branch.set_last_revision_info(2, 'B-id')
2587
wt.commit('F foo => bing', rev_id=b'F-id')
2588
wt.set_last_revision(b'B-id')
2589
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()
2591
wt.merge_from_branch(wt.branch, b'C-id')
2592
wt.commit('D merges B & C', rev_id=b'D-id')
2593
wt_base = wt.controldir.sprout('base', b'A-id').open_workingtree()
2406
2594
wt_base.lock_read()
2407
2595
self.addCleanup(wt_base.unlock)
2408
wt_lca1 = wt.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2596
wt_lca1 = wt.controldir.sprout('b-tree', b'B-id').open_workingtree()
2409
2597
wt_lca1.lock_read()
2410
2598
self.addCleanup(wt_lca1.unlock)
2411
wt_lca2 = wt.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2599
wt_lca2 = wt.controldir.sprout('c-tree', b'C-id').open_workingtree()
2412
2600
wt_lca2.lock_read()
2413
2601
self.addCleanup(wt_lca2.unlock)
2414
wt_other = wt.bzrdir.sprout('other', 'F-id').open_workingtree()
2602
wt_other = wt.controldir.sprout('other', b'F-id').open_workingtree()
2415
2603
wt_other.lock_read()
2416
2604
self.addCleanup(wt_other.unlock)
2417
2605
merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2418
2606
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2419
2607
entries = list(merge_obj._entries_lca())
2420
2608
root_id = wt.path2id('')
2421
self.assertEqual([('foo-id', True,
2609
self.assertEqual([(b'foo-id', True,
2610
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2422
2611
((root_id, [root_id, root_id]), root_id, root_id),
2423
2612
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2424
2613
((False, [False, False]), False, False)),
2436
2625
# F Path at 'foo'
2437
2626
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')
2627
builder.build_snapshot(None,
2628
[('add', (u'', b'a-root-id', 'directory', None)),
2629
('add', (u'foo', b'foo-id', 'file', 'a\nb\nc\n'))],
2630
revision_id=b'A-id')
2631
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2632
builder.build_snapshot([b'A-id'],
2633
[('rename', ('foo', 'bar'))], revision_id=b'B-id')
2634
builder.build_snapshot([b'C-id', b'B-id'],
2635
[('rename', ('foo', 'bar'))], revision_id=b'E-id') # merge the rename
2636
builder.build_snapshot([b'E-id'],
2637
[('rename', ('bar', 'foo'))], revision_id=b'F-id') # Rename back to BASE
2638
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2639
wt, conflicts = self.do_merge(builder, b'F-id')
2450
2640
self.assertEqual(0, conflicts)
2451
self.assertEqual('foo', wt.id2path('foo-id'))
2641
self.assertEqual('foo', wt.id2path(b'foo-id'))
2453
2643
def test_other_reverted_content_to_base(self):
2454
2644
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')
2645
builder.build_snapshot(None,
2646
[('add', (u'', b'a-root-id', 'directory', None)),
2647
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2648
revision_id=b'A-id')
2649
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2650
builder.build_snapshot([b'A-id'],
2651
[('modify', ('foo', b'B content\n'))],
2652
revision_id=b'B-id')
2653
builder.build_snapshot([b'C-id', b'B-id'],
2654
[('modify', ('foo', b'B content\n'))],
2655
revision_id=b'E-id') # merge the content
2656
builder.build_snapshot([b'E-id'],
2657
[('modify', ('foo', b'base content\n'))],
2658
revision_id=b'F-id') # Revert back to BASE
2659
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2660
wt, conflicts = self.do_merge(builder, b'F-id')
2467
2661
self.assertEqual(0, conflicts)
2468
2662
# TODO: We need to use the per-file graph to properly select a BASE
2469
2663
# before this will work. Or at least use the LCA trees to find
2470
2664
# the appropriate content base. (which is B, not A).
2471
self.assertEqual('base content\n', wt.get_file_text('foo-id'))
2665
self.assertEqual(b'base content\n', wt.get_file_text('foo'))
2473
2667
def test_other_modified_content(self):
2474
2668
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')
2669
builder.build_snapshot(None,
2670
[('add', (u'', b'a-root-id', 'directory', None)),
2671
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2672
revision_id=b'A-id')
2673
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2674
builder.build_snapshot([b'A-id'],
2675
[('modify', ('foo', b'B content\n'))],
2676
revision_id=b'B-id')
2677
builder.build_snapshot([b'C-id', b'B-id'],
2678
[('modify', ('foo', b'B content\n'))],
2679
revision_id=b'E-id') # merge the content
2680
builder.build_snapshot([b'E-id'],
2681
[('modify', ('foo', b'F content\n'))],
2682
revision_id=b'F-id') # Override B content
2683
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2684
wt, conflicts = self.do_merge(builder, b'F-id')
2487
2685
self.assertEqual(0, conflicts)
2488
self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2686
self.assertEqual(b'F content\n', wt.get_file_text('foo'))
2490
2688
def test_all_wt(self):
2491
2689
"""Check behavior if all trees are Working Trees."""
2499
2697
# D E E updates content, renames 'b' => 'c'
2500
2698
builder = self.get_builder()
2501
builder.build_snapshot('A-id', None,
2502
[('add', (u'', 'a-root-id', 'directory', None)),
2503
('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'],
2699
builder.build_snapshot(None,
2700
[('add', (u'', b'a-root-id', 'directory', None)),
2701
('add', (u'a', b'a-id', 'file', b'base content\n')),
2702
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2703
revision_id=b'A-id')
2704
builder.build_snapshot([b'A-id'],
2705
[('modify', ('foo', b'B content\n'))],
2706
revision_id=b'B-id')
2707
builder.build_snapshot([b'A-id'],
2708
[('rename', ('a', 'b'))],
2709
revision_id=b'C-id')
2710
builder.build_snapshot([b'C-id', b'B-id'],
2510
2711
[('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
2712
('modify', ('foo', b'E content\n'))],
2713
revision_id=b'E-id')
2714
builder.build_snapshot([b'B-id', b'C-id'],
2715
[('rename', ('a', 'b'))], revision_id=b'D-id') # merged change
2514
2716
wt_this = self.get_wt_from_builder(builder)
2515
wt_base = wt_this.bzrdir.sprout('base', 'A-id').open_workingtree()
2717
wt_base = wt_this.controldir.sprout('base', b'A-id').open_workingtree()
2516
2718
wt_base.lock_read()
2517
2719
self.addCleanup(wt_base.unlock)
2518
wt_lca1 = wt_this.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2720
wt_lca1 = wt_this.controldir.sprout('b-tree', b'B-id').open_workingtree()
2519
2721
wt_lca1.lock_read()
2520
2722
self.addCleanup(wt_lca1.unlock)
2521
wt_lca2 = wt_this.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2723
wt_lca2 = wt_this.controldir.sprout('c-tree', b'C-id').open_workingtree()
2522
2724
wt_lca2.lock_read()
2523
2725
self.addCleanup(wt_lca2.unlock)
2524
wt_other = wt_this.bzrdir.sprout('other', 'E-id').open_workingtree()
2726
wt_other = wt_this.controldir.sprout('other', b'E-id').open_workingtree()
2525
2727
wt_other.lock_read()
2526
2728
self.addCleanup(wt_other.unlock)
2527
2729
merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2528
2730
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2529
2731
entries = list(merge_obj._entries_lca())
2530
root_id = 'a-root-id'
2531
self.assertEqual([('a-id', False,
2732
root_id = b'a-root-id'
2733
self.assertEqual([(b'a-id', False,
2734
((u'a', [u'a', u'b']), u'c', u'b'),
2532
2735
((root_id, [root_id, root_id]), root_id, root_id),
2533
2736
((u'a', [u'a', u'b']), u'c', u'b'),
2534
2737
((False, [False, False]), False, False)),
2739
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2536
2740
((root_id, [root_id, root_id]), root_id, root_id),
2537
2741
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2538
2742
((False, [False, False]), False, False)),
2542
2746
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2543
2747
# 'tree-reference'
2544
2748
wt = self.make_branch_and_tree('tree',
2545
format='dirstate-with-subtree')
2749
format='development-subtree')
2546
2750
wt.lock_write()
2547
2751
self.addCleanup(wt.unlock)
2548
2752
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')])
2753
format='development-subtree')
2754
wt.set_root_id(b'a-root-id')
2755
sub_tree.set_root_id(b'sub-tree-root')
2756
self.build_tree_contents([('tree/sub-tree/file', b'text1')])
2553
2757
sub_tree.add('file')
2554
sub_tree.commit('foo', rev_id='sub-A-id')
2758
sub_tree.commit('foo', rev_id=b'sub-A-id')
2555
2759
wt.add_reference(sub_tree)
2556
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2760
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2557
2761
# Now create a criss-cross merge in the parent, without modifying the
2559
wt.commit('B', rev_id='B-id', recursive=None)
2560
wt.set_last_revision('A-id')
2561
wt.branch.set_last_revision_info(1, 'A-id')
2562
wt.commit('C', rev_id='C-id', recursive=None)
2763
wt.commit('B', rev_id=b'B-id', recursive=None)
2764
wt.set_last_revision(b'A-id')
2765
wt.branch.set_last_revision_info(1, b'A-id')
2766
wt.commit('C', rev_id=b'C-id', recursive=None)
2563
2767
wt.merge_from_branch(wt.branch, to_revision='B-id')
2564
wt.commit('E', rev_id='E-id', recursive=None)
2565
wt.set_parent_ids(['B-id', 'C-id'])
2566
wt.branch.set_last_revision_info(2, 'B-id')
2567
wt.commit('D', rev_id='D-id', recursive=None)
2768
wt.commit('E', rev_id=b'E-id', recursive=None)
2769
wt.set_parent_ids([b'B-id', b'C-id'])
2770
wt.branch.set_last_revision_info(2, b'B-id')
2771
wt.commit('D', rev_id=b'D-id', recursive=None)
2569
merger = _mod_merge.Merger.from_revision_ids(None,
2773
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2571
2774
merger.merge_type = _mod_merge.Merge3Merger
2572
2775
merge_obj = merger.make_merger()
2573
2776
entries = list(merge_obj._entries_lca())
2577
2780
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2578
2781
# 'tree-reference'
2579
2782
wt = self.make_branch_and_tree('tree',
2580
format='dirstate-with-subtree')
2783
format='development-subtree')
2581
2784
wt.lock_write()
2582
2785
self.addCleanup(wt.unlock)
2583
2786
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')])
2787
format='development-subtree')
2788
wt.set_root_id(b'a-root-id')
2789
sub_tree.set_root_id(b'sub-tree-root')
2790
self.build_tree_contents([('tree/sub/file', b'text1')])
2588
2791
sub_tree.add('file')
2589
sub_tree.commit('foo', rev_id='sub-A-id')
2792
sub_tree.commit('foo', rev_id=b'sub-A-id')
2590
2793
wt.add_reference(sub_tree)
2591
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2794
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2592
2795
# Now create a criss-cross merge in the parent, without modifying the
2594
wt.commit('B', rev_id='B-id', recursive=None)
2595
wt.set_last_revision('A-id')
2596
wt.branch.set_last_revision_info(1, 'A-id')
2597
wt.commit('C', rev_id='C-id', recursive=None)
2797
wt.commit('B', rev_id=b'B-id', recursive=None)
2798
wt.set_last_revision(b'A-id')
2799
wt.branch.set_last_revision_info(1, b'A-id')
2800
wt.commit('C', rev_id=b'C-id', recursive=None)
2598
2801
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)
2602
wt.set_parent_ids(['B-id', 'C-id'])
2603
wt.branch.set_last_revision_info(2, 'B-id')
2604
wt.commit('D', rev_id='D-id', recursive=None)
2802
self.build_tree_contents([('tree/sub/file', b'text2')])
2803
sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2804
wt.commit('E', rev_id=b'E-id', recursive=None)
2805
wt.set_parent_ids(['B-id', b'C-id'])
2806
wt.branch.set_last_revision_info(2, b'B-id')
2807
wt.commit('D', rev_id=b'D-id', recursive=None)
2606
merger = _mod_merge.Merger.from_revision_ids(None,
2809
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2608
2810
merger.merge_type = _mod_merge.Merge3Merger
2609
2811
merge_obj = merger.make_merger()
2610
2812
entries = list(merge_obj._entries_lca())
2616
2818
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2617
2819
# 'tree-reference'
2618
2820
wt = self.make_branch_and_tree('tree',
2619
format='dirstate-with-subtree')
2821
format='development-subtree')
2620
2822
wt.lock_write()
2621
2823
self.addCleanup(wt.unlock)
2622
2824
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')])
2825
format='development-subtree')
2826
wt.set_root_id(b'a-root-id')
2827
sub_tree.set_root_id(b'sub-tree-root')
2828
self.build_tree_contents([('tree/sub/file', b'text1')])
2627
2829
sub_tree.add('file')
2628
sub_tree.commit('foo', rev_id='sub-A-id')
2830
sub_tree.commit('foo', rev_id=b'sub-A-id')
2629
2831
wt.add_reference(sub_tree)
2630
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2832
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2631
2833
# Now create a criss-cross merge in the parent, without modifying the
2633
wt.commit('B', rev_id='B-id', recursive=None)
2634
wt.set_last_revision('A-id')
2635
wt.branch.set_last_revision_info(1, 'A-id')
2636
wt.commit('C', rev_id='C-id', recursive=None)
2637
wt.merge_from_branch(wt.branch, to_revision='B-id')
2835
wt.commit('B', rev_id=b'B-id', recursive=None)
2836
wt.set_last_revision(b'A-id')
2837
wt.branch.set_last_revision_info(1, b'A-id')
2838
wt.commit('C', rev_id=b'C-id', recursive=None)
2839
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2638
2840
wt.rename_one('sub', 'alt_sub')
2639
wt.commit('E', rev_id='E-id', recursive=None)
2640
wt.set_last_revision('B-id')
2841
wt.commit('E', rev_id=b'E-id', recursive=None)
2842
wt.set_last_revision(b'B-id')
2642
wt.set_parent_ids(['B-id', 'C-id'])
2643
wt.branch.set_last_revision_info(2, 'B-id')
2644
wt.commit('D', rev_id='D-id', recursive=None)
2844
wt.set_parent_ids([b'B-id', b'C-id'])
2845
wt.branch.set_last_revision_info(2, b'B-id')
2846
wt.commit('D', rev_id=b'D-id', recursive=None)
2646
merger = _mod_merge.Merger.from_revision_ids(None,
2848
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2648
2849
merger.merge_type = _mod_merge.Merge3Merger
2649
2850
merge_obj = merger.make_merger()
2650
2851
entries = list(merge_obj._entries_lca())
2651
root_id = 'a-root-id'
2852
root_id = b'a-root-id'
2652
2853
self.assertEqual([('sub-tree-root', False,
2854
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2653
2855
((root_id, [root_id, root_id]), root_id, root_id),
2654
2856
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2655
2857
((False, [False, False]), False, False)),
2659
2861
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2660
2862
# 'tree-reference'
2661
2863
wt = self.make_branch_and_tree('tree',
2662
format='dirstate-with-subtree')
2864
format='development-subtree')
2663
2865
wt.lock_write()
2664
2866
self.addCleanup(wt.unlock)
2665
2867
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')])
2868
format='development-subtree')
2869
wt.set_root_id(b'a-root-id')
2870
sub_tree.set_root_id(b'sub-tree-root')
2871
self.build_tree_contents([('tree/sub/file', b'text1')])
2670
2872
sub_tree.add('file')
2671
sub_tree.commit('foo', rev_id='sub-A-id')
2873
sub_tree.commit('foo', rev_id=b'sub-A-id')
2672
2874
wt.add_reference(sub_tree)
2673
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2875
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2674
2876
# Now create a criss-cross merge in the parent, without modifying the
2676
wt.commit('B', rev_id='B-id', recursive=None)
2677
wt.set_last_revision('A-id')
2678
wt.branch.set_last_revision_info(1, 'A-id')
2679
wt.commit('C', rev_id='C-id', recursive=None)
2878
wt.commit('B', rev_id=b'B-id', recursive=None)
2879
wt.set_last_revision(b'A-id')
2880
wt.branch.set_last_revision_info(1, b'A-id')
2881
wt.commit('C', rev_id=b'C-id', recursive=None)
2680
2882
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')
2883
self.build_tree_contents([('tree/sub/file', b'text2')])
2884
sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2683
2885
wt.rename_one('sub', 'alt_sub')
2684
wt.commit('E', rev_id='E-id', recursive=None)
2685
wt.set_last_revision('B-id')
2886
wt.commit('E', rev_id=b'E-id', recursive=None)
2887
wt.set_last_revision(b'B-id')
2687
wt.set_parent_ids(['B-id', 'C-id'])
2688
wt.branch.set_last_revision_info(2, 'B-id')
2689
wt.commit('D', rev_id='D-id', recursive=None)
2889
wt.set_parent_ids([b'B-id', b'C-id'])
2890
wt.branch.set_last_revision_info(2, b'B-id')
2891
wt.commit('D', rev_id=b'D-id', recursive=None)
2691
merger = _mod_merge.Merger.from_revision_ids(None,
2893
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2693
2894
merger.merge_type = _mod_merge.Merge3Merger
2694
2895
merge_obj = merger.make_merger()
2695
2896
entries = list(merge_obj._entries_lca())
2696
root_id = 'a-root-id'
2897
root_id = b'a-root-id'
2697
2898
self.assertEqual([('sub-tree-root', False,
2899
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2698
2900
((root_id, [root_id, root_id]), root_id, root_id),
2699
2901
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2700
2902
((False, [False, False]), False, False)),
2917
3119
conflicts = builder.merge()
2918
3120
# The hook should not call the merge_text() method
2919
3121
self.assertEqual([], self.calls)
3124
class TestMergeIntoBase(tests.TestCaseWithTransport):
3126
def setup_simple_branch(self, relpath, shape=None, root_id=None):
3127
"""One commit, containing tree specified by optional shape.
3129
Default is empty tree (just root entry).
3132
root_id = ('%s-root-id' % (relpath,)).encode('ascii')
3133
wt = self.make_branch_and_tree(relpath)
3134
wt.set_root_id(root_id)
3135
if shape is not None:
3136
adjusted_shape = [relpath + '/' + elem for elem in shape]
3137
self.build_tree(adjusted_shape)
3139
('%s-%s-id' % (relpath, basename(elem.rstrip('/')))).encode('ascii')
3141
wt.add(shape, ids=ids)
3142
rev_id = 'r1-%s' % (relpath,)
3143
wt.commit("Initial commit of %s" % (relpath,), rev_id=rev_id)
3144
self.assertEqual(root_id, wt.path2id(''))
3147
def setup_two_branches(self, custom_root_ids=True):
3148
"""Setup 2 branches, one will be a library, the other a project."""
3152
root_id = inventory.ROOT_ID
3153
project_wt = self.setup_simple_branch(
3154
'project', ['README', 'dir/', 'dir/file.c'],
3156
lib_wt = self.setup_simple_branch(
3157
'lib1', ['README', 'Makefile', 'foo.c'], root_id)
3159
return project_wt, lib_wt
3161
def do_merge_into(self, location, merge_as):
3162
"""Helper for using MergeIntoMerger.
3164
:param location: location of directory to merge from, either the
3165
location of a branch or of a path inside a branch.
3166
:param merge_as: the path in a tree to add the new directory as.
3167
:returns: the conflicts from 'do_merge'.
3169
operation = cleanup.OperationWithCleanups(self._merge_into)
3170
return operation.run(location, merge_as)
3172
def _merge_into(self, op, location, merge_as):
3173
# Open and lock the various tree and branch objects
3174
wt, subdir_relpath = WorkingTree.open_containing(merge_as)
3175
op.add_cleanup(wt.lock_write().unlock)
3176
branch_to_merge, subdir_to_merge = _mod_branch.Branch.open_containing(
3178
op.add_cleanup(branch_to_merge.lock_read().unlock)
3179
other_tree = branch_to_merge.basis_tree()
3180
op.add_cleanup(other_tree.lock_read().unlock)
3182
merger = _mod_merge.MergeIntoMerger(this_tree=wt, other_tree=other_tree,
3183
other_branch=branch_to_merge, target_subdir=subdir_relpath,
3184
source_subpath=subdir_to_merge)
3185
merger.set_base_revision(_mod_revision.NULL_REVISION, branch_to_merge)
3186
conflicts = merger.do_merge()
3187
merger.set_pending()
3190
def assertTreeEntriesEqual(self, expected_entries, tree):
3191
"""Assert that 'tree' contains the expected inventory entries.
3193
:param expected_entries: sequence of (path, file-id) pairs.
3195
files = [(path, ie.file_id) for path, ie in tree.iter_entries_by_dir()]
3196
self.assertEqual(expected_entries, files)
3199
class TestMergeInto(TestMergeIntoBase):
3201
def test_newdir_with_unique_roots(self):
3202
"""Merge a branch with a unique root into a new directory."""
3203
project_wt, lib_wt = self.setup_two_branches()
3204
self.do_merge_into('lib1', 'project/lib1')
3205
project_wt.lock_read()
3206
self.addCleanup(project_wt.unlock)
3207
# The r1-lib1 revision should be merged into this one
3208
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3209
self.assertTreeEntriesEqual(
3210
[('', 'project-root-id'),
3211
('README', 'project-README-id'),
3212
('dir', 'project-dir-id'),
3213
('lib1', 'lib1-root-id'),
3214
('dir/file.c', 'project-file.c-id'),
3215
('lib1/Makefile', 'lib1-Makefile-id'),
3216
('lib1/README', 'lib1-README-id'),
3217
('lib1/foo.c', 'lib1-foo.c-id'),
3220
def test_subdir(self):
3221
"""Merge a branch into a subdirectory of an existing directory."""
3222
project_wt, lib_wt = self.setup_two_branches()
3223
self.do_merge_into('lib1', 'project/dir/lib1')
3224
project_wt.lock_read()
3225
self.addCleanup(project_wt.unlock)
3226
# The r1-lib1 revision should be merged into this one
3227
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3228
self.assertTreeEntriesEqual(
3229
[('', 'project-root-id'),
3230
('README', 'project-README-id'),
3231
('dir', 'project-dir-id'),
3232
('dir/file.c', 'project-file.c-id'),
3233
('dir/lib1', 'lib1-root-id'),
3234
('dir/lib1/Makefile', 'lib1-Makefile-id'),
3235
('dir/lib1/README', 'lib1-README-id'),
3236
('dir/lib1/foo.c', 'lib1-foo.c-id'),
3239
def test_newdir_with_repeat_roots(self):
3240
"""If the file-id of the dir to be merged already exists a new ID will
3241
be allocated to let the merge happen.
3243
project_wt, lib_wt = self.setup_two_branches(custom_root_ids=False)
3244
root_id = project_wt.path2id('')
3245
self.do_merge_into('lib1', 'project/lib1')
3246
project_wt.lock_read()
3247
self.addCleanup(project_wt.unlock)
3248
# The r1-lib1 revision should be merged into this one
3249
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3250
new_lib1_id = project_wt.path2id('lib1')
3251
self.assertNotEqual(None, new_lib1_id)
3252
self.assertTreeEntriesEqual(
3254
('README', 'project-README-id'),
3255
('dir', 'project-dir-id'),
3256
('lib1', new_lib1_id),
3257
('dir/file.c', 'project-file.c-id'),
3258
('lib1/Makefile', 'lib1-Makefile-id'),
3259
('lib1/README', 'lib1-README-id'),
3260
('lib1/foo.c', 'lib1-foo.c-id'),
3263
def test_name_conflict(self):
3264
"""When the target directory name already exists a conflict is
3265
generated and the original directory is renamed to foo.moved.
3267
dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt'])
3268
src_wt = self.setup_simple_branch('src', ['README'])
3269
conflicts = self.do_merge_into('src', 'dest/dir')
3270
self.assertEqual(1, conflicts)
3272
self.addCleanup(dest_wt.unlock)
3273
# The r1-lib1 revision should be merged into this one
3274
self.assertEqual(['r1-dest', 'r1-src'], dest_wt.get_parent_ids())
3275
self.assertTreeEntriesEqual(
3276
[('', 'dest-root-id'),
3277
('dir', 'src-root-id'),
3278
('dir.moved', 'dest-dir-id'),
3279
('dir/README', 'src-README-id'),
3280
('dir.moved/file.txt', 'dest-file.txt-id'),
3283
def test_file_id_conflict(self):
3284
"""A conflict is generated if the merge-into adds a file (or other
3285
inventory entry) with a file-id that already exists in the target tree.
3287
dest_wt = self.setup_simple_branch('dest', ['file.txt'])
3288
# Make a second tree with a file-id that will clash with file.txt in
3290
src_wt = self.make_branch_and_tree('src')
3291
self.build_tree(['src/README'])
3292
src_wt.add(['README'], ids=[b'dest-file.txt-id'])
3293
src_wt.commit("Rev 1 of src.", rev_id=b'r1-src')
3294
conflicts = self.do_merge_into('src', 'dest/dir')
3295
# This is an edge case that shouldn't happen to users very often. So
3296
# we don't care really about the exact presentation of the conflict,
3297
# just that there is one.
3298
self.assertEqual(1, conflicts)
3300
def test_only_subdir(self):
3301
"""When the location points to just part of a tree, merge just that
3304
dest_wt = self.setup_simple_branch('dest')
3305
src_wt = self.setup_simple_branch(
3306
'src', ['hello.txt', 'dir/', 'dir/foo.c'])
3307
conflicts = self.do_merge_into('src/dir', 'dest/dir')
3309
self.addCleanup(dest_wt.unlock)
3310
# The r1-lib1 revision should NOT be merged into this one (this is a
3312
self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
3313
self.assertTreeEntriesEqual(
3314
[('', 'dest-root-id'),
3315
('dir', 'src-dir-id'),
3316
('dir/foo.c', 'src-foo.c-id'),
3319
def test_only_file(self):
3320
"""An edge case: merge just one file, not a whole dir."""
3321
dest_wt = self.setup_simple_branch('dest')
3322
two_file_wt = self.setup_simple_branch(
3323
'two-file', ['file1.txt', 'file2.txt'])
3324
conflicts = self.do_merge_into('two-file/file1.txt', 'dest/file1.txt')
3326
self.addCleanup(dest_wt.unlock)
3327
# The r1-lib1 revision should NOT be merged into this one
3328
self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
3329
self.assertTreeEntriesEqual(
3330
[('', 'dest-root-id'), ('file1.txt', 'two-file-file1.txt-id')],
3333
def test_no_such_source_path(self):
3334
"""PathNotInTree is raised if the specified path in the source tree
3337
dest_wt = self.setup_simple_branch('dest')
3338
two_file_wt = self.setup_simple_branch('src', ['dir/'])
3339
self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3340
'src/no-such-dir', 'dest/foo')
3342
self.addCleanup(dest_wt.unlock)
3343
# The dest tree is unmodified.
3344
self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
3345
self.assertTreeEntriesEqual([('', 'dest-root-id')], dest_wt)
3347
def test_no_such_target_path(self):
3348
"""PathNotInTree is also raised if the specified path in the target
3349
tree does not exist.
3351
dest_wt = self.setup_simple_branch('dest')
3352
two_file_wt = self.setup_simple_branch('src', ['file.txt'])
3353
self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3354
'src', 'dest/no-such-dir/foo')
3356
self.addCleanup(dest_wt.unlock)
3357
# The dest tree is unmodified.
3358
self.assertEqual(['r1-dest'], dest_wt.get_parent_ids())
3359
self.assertTreeEntriesEqual([('', 'dest-root-id')], dest_wt)
3362
class TestMergeHooks(TestCaseWithTransport):
3365
super(TestMergeHooks, self).setUp()
3366
self.tree_a = self.make_branch_and_tree('tree_a')
3367
self.build_tree_contents([('tree_a/file', b'content_1')])
3368
self.tree_a.add('file', b'file-id')
3369
self.tree_a.commit('added file')
3371
self.tree_b = self.tree_a.controldir.sprout('tree_b').open_workingtree()
3372
self.build_tree_contents([('tree_b/file', b'content_2')])
3373
self.tree_b.commit('modify file')
3375
def test_pre_merge_hook_inject_different_tree(self):
3376
tree_c = self.tree_b.controldir.sprout('tree_c').open_workingtree()
3377
self.build_tree_contents([('tree_c/file', b'content_3')])
3378
tree_c.commit("more content")
3380
def factory(merger):
3381
self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3382
merger.other_tree = tree_c
3383
calls.append(merger)
3384
_mod_merge.Merger.hooks.install_named_hook('pre_merge',
3385
factory, 'test factory')
3386
self.tree_a.merge_from_branch(self.tree_b.branch)
3388
self.assertFileEqual("content_3", 'tree_a/file')
3389
self.assertLength(1, calls)
3391
def test_post_merge_hook_called(self):
3393
def factory(merger):
3394
self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3395
calls.append(merger)
3396
_mod_merge.Merger.hooks.install_named_hook('post_merge',
3397
factory, 'test factory')
3399
self.tree_a.merge_from_branch(self.tree_b.branch)
3401
self.assertFileEqual("content_2", 'tree_a/file')
3402
self.assertLength(1, calls)