788
790
self.active_hooks = [hook for hook in hooks if hook is not None]
789
791
with ui.ui_factory.nested_progress_bar() as child_pb:
790
792
for num, (file_id, changed, paths3, parents3, names3,
791
executable3) in enumerate(entries):
793
executable3, copied) in enumerate(entries):
795
# Treat copies as simple adds for now
796
paths3 = (None, paths3[1], None)
797
parents3 = (None, parents3[1], None)
798
names3 = (None, names3[1], None)
799
executable3 = (None, executable3[1], None)
792
802
trans_id = self.tt.trans_id_file_id(file_id)
793
803
# Try merging each entry
794
804
child_pb.update(gettext('Preparing file merge'),
859
869
executable3 = change.executable + (this_executable,)
861
871
(change.file_id, change.changed_content, paths3,
862
parents3, names3, executable3))
872
parents3, names3, executable3, change.copied))
864
874
def _entries_lca(self):
865
875
"""Gather data about files modified between multiple trees.
870
880
For the multi-valued entries, the format will be (BASE, [lca1, lca2])
872
:return: [(file_id, changed, paths, parents, names, executable)], where:
882
:return: [(file_id, changed, paths, parents, names, executable, copied)], where:
874
884
* file_id: Simple file_id of the entry
875
885
* changed: Boolean, True if the kind or contents changed else False
1039
1049
((base_ie.name, lca_names),
1040
1050
other_ie.name, this_ie.name),
1041
1051
((base_ie.executable, lca_executable),
1042
other_ie.executable, this_ie.executable)
1052
other_ie.executable, this_ie.executable),
1053
# Copy detection is not yet supported, so nothing is
1045
1058
def write_modified(self, results):
1302
1315
# This is a contents conflict, because none of the available
1303
1316
# functions could merge it.
1304
1317
file_group = self._dump_conflicts(
1305
name, (base_path, other_path, this_path), parent_id,
1306
file_id, set_version=True)
1318
name, (base_path, other_path, this_path), parent_id)
1319
for tid in file_group:
1320
self.tt.version_file(tid, file_id=file_id)
1307
1322
self._raw_conflicts.append(('contents conflict', file_group))
1308
1323
elif hook_status == 'success':
1309
1324
self.tt.create_file(lines, trans_id)
1315
1330
name = self.tt.final_name(trans_id)
1316
1331
parent_id = self.tt.final_parent(trans_id)
1317
1332
self._dump_conflicts(
1318
name, (base_path, other_path, this_path), parent_id, file_id)
1333
name, (base_path, other_path, this_path), parent_id)
1319
1334
elif hook_status == 'delete':
1320
1335
self.tt.unversion_file(trans_id)
1321
1336
result = "deleted"
1421
1436
self._raw_conflicts.append(('text conflict', trans_id))
1422
1437
name = self.tt.final_name(trans_id)
1423
1438
parent_id = self.tt.final_parent(trans_id)
1424
file_id = self.tt.final_file_id(trans_id)
1425
file_group = self._dump_conflicts(name, paths, parent_id, file_id,
1426
this_lines, base_lines,
1439
file_group = self._dump_conflicts(
1440
name, paths, parent_id,
1441
lines=(base_lines, other_lines, this_lines))
1428
1442
file_group.append(trans_id)
1430
1444
def _get_filter_tree_path(self, path):
1441
1455
# Skip the lookup for older formats
1444
def _dump_conflicts(self, name, paths, parent_id, file_id, this_lines=None,
1445
base_lines=None, other_lines=None, set_version=False,
1458
def _dump_conflicts(self, name, paths, parent_id, lines=None,
1446
1459
no_base=False):
1447
1460
"""Emit conflict files.
1448
1461
If this_lines, base_lines, or other_lines are omitted, they will be
1450
1463
or .BASE (in that order) will be created as versioned files.
1452
1465
base_path, other_path, this_path = paths
1467
base_lines, other_lines, this_lines = lines
1469
base_lines = other_lines = this_lines = None
1453
1470
data = [('OTHER', self.other_tree, other_path, other_lines),
1454
1471
('THIS', self.this_tree, this_path, this_lines)]
1455
1472
if not no_base:
1470
1485
name, parent_id, path, tree, suffix, lines,
1471
1486
filter_tree_path)
1472
1487
file_group.append(trans_id)
1473
if set_version and not versioned:
1474
self.tt.version_file(trans_id, file_id=file_id)
1476
1488
return file_group
1478
1490
def _conflict_file(self, name, parent_id, path, tree, suffix,
1520
1532
def cook_conflicts(self, fs_conflicts):
1521
1533
"""Convert all conflicts into a form that doesn't depend on trans_id"""
1522
content_conflict_file_ids = set()
1523
cooked_conflicts = transform.cook_conflicts(fs_conflicts, self.tt)
1524
fp = transform.FinalPaths(self.tt)
1525
for conflict in self._raw_conflicts:
1526
conflict_type = conflict[0]
1527
if conflict_type == 'path conflict':
1529
this_parent, this_name,
1530
other_parent, other_name) = conflict[1:]
1531
if this_parent is None or this_name is None:
1532
this_path = '<deleted>'
1534
parent_path = fp.get_path(
1535
self.tt.trans_id_file_id(this_parent))
1536
this_path = osutils.pathjoin(parent_path, this_name)
1537
if other_parent is None or other_name is None:
1538
other_path = '<deleted>'
1540
if other_parent == self.other_tree.path2id(''):
1541
# The tree transform doesn't know about the other root,
1542
# so we special case here to avoid a NoFinalPath
1546
parent_path = fp.get_path(
1547
self.tt.trans_id_file_id(other_parent))
1548
other_path = osutils.pathjoin(parent_path, other_name)
1549
c = _mod_conflicts.Conflict.factory(
1550
'path conflict', path=this_path,
1551
conflict_path=other_path,
1553
elif conflict_type == 'contents conflict':
1554
for trans_id in conflict[1]:
1555
file_id = self.tt.final_file_id(trans_id)
1556
if file_id is not None:
1557
# Ok we found the relevant file-id
1559
path = fp.get_path(trans_id)
1560
for suffix in ('.BASE', '.THIS', '.OTHER'):
1561
if path.endswith(suffix):
1562
# Here is the raw path
1563
path = path[:-len(suffix)]
1565
c = _mod_conflicts.Conflict.factory(conflict_type,
1566
path=path, file_id=file_id)
1567
content_conflict_file_ids.add(file_id)
1568
elif conflict_type == 'text conflict':
1569
trans_id = conflict[1]
1570
path = fp.get_path(trans_id)
1571
file_id = self.tt.final_file_id(trans_id)
1572
c = _mod_conflicts.Conflict.factory(conflict_type,
1573
path=path, file_id=file_id)
1575
raise AssertionError('bad conflict type: %r' % (conflict,))
1576
cooked_conflicts.append(c)
1578
self.cooked_conflicts = []
1579
# We want to get rid of path conflicts when a corresponding contents
1580
# conflict exists. This can occur when one branch deletes a file while
1581
# the other renames *and* modifies it. In this case, the content
1582
# conflict is enough.
1583
for c in cooked_conflicts:
1584
if (c.typestring == 'path conflict'
1585
and c.file_id in content_conflict_file_ids):
1587
self.cooked_conflicts.append(c)
1588
self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
1534
self.cooked_conflicts = list(self.tt.cook_conflicts(
1535
list(fs_conflicts) + self._raw_conflicts))
1591
1538
class WeaveMerger(Merge3Merger):
1642
1589
self._raw_conflicts.append(('text conflict', trans_id))
1643
1590
name = self.tt.final_name(trans_id)
1644
1591
parent_id = self.tt.final_parent(trans_id)
1645
file_id = self.tt.final_file_id(trans_id)
1646
file_group = self._dump_conflicts(name, paths, parent_id, file_id,
1648
base_lines=base_lines)
1592
file_group = self._dump_conflicts(name, paths, parent_id,
1593
(base_lines, None, None),
1649
1595
file_group.append(trans_id)