14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19
from bzrlib import (
19
20
branch as _mod_branch,
594
593
'other_tree': self.other_tree,
595
594
'interesting_ids': self.interesting_ids,
596
595
'interesting_files': self.interesting_files,
597
'pp': self.pp, 'this_branch': self.this_branch,
596
'this_branch': self.this_branch,
598
597
'do_merge': False}
599
598
if self.merge_type.requires_base:
600
599
kwargs['base_tree'] = self.base_tree
618
617
if self._is_criss_cross and getattr(self.merge_type,
619
618
'supports_lca_trees', False):
620
619
kwargs['lca_trees'] = self._lca_trees
621
return self.merge_type(pb=self._pb,
620
return self.merge_type(pb=None,
622
621
change_reporter=self.change_reporter,
697
696
def __init__(self, working_tree, this_tree, base_tree, other_tree,
698
697
interesting_ids=None, reprocess=False, show_base=False,
699
pb=progress.DummyProgress(), pp=None, change_reporter=None,
698
pb=None, pp=None, change_reporter=None,
700
699
interesting_files=None, do_merge=True,
701
700
cherrypick=False, lca_trees=None, this_branch=None):
702
701
"""Initialize the merger object and perform the merge.
712
711
:param: reprocess If True, perform conflict-reduction processing.
713
712
:param show_base: If True, show the base revision in text conflicts.
714
713
(incompatible with reprocess)
715
:param pb: A Progress bar
716
715
:param pp: A ProgressPhase object
717
716
:param change_reporter: An object that should report changes made
718
717
:param interesting_files: The tree-relative paths of files that should
745
744
# making sure we haven't missed any corner cases.
746
745
# if lca_trees is None:
747
746
# self._lca_trees = [self.base_tree]
750
747
self.change_reporter = change_reporter
751
748
self.cherrypick = cherrypick
753
self.pp = progress.ProgressPhase("Merge phase", 3, self.pb)
752
warnings.warn("pp argument to Merge3Merger is deprecated")
754
warnings.warn("pb argument to Merge3Merger is deprecated")
757
756
def do_merge(self):
758
757
operation = OperationWithCleanups(self._do_merge)
759
operation.add_cleanup(self.pb.clear)
760
758
self.this_tree.lock_tree_write()
761
759
operation.add_cleanup(self.this_tree.unlock)
762
760
self.base_tree.lock_read()
768
766
def _do_merge(self, operation):
769
self.tt = transform.TreeTransform(self.this_tree, self.pb)
767
self.tt = transform.TreeTransform(self.this_tree, None)
770
768
operation.add_cleanup(self.tt.finalize)
772
769
self._compute_transform()
774
770
results = self.tt.apply(no_conflicts=True)
775
771
self.write_modified(results)
781
777
def make_preview_transform(self):
782
778
operation = OperationWithCleanups(self._make_preview_transform)
783
operation.add_cleanup(self.pb.clear)
784
779
self.base_tree.lock_read()
785
780
operation.add_cleanup(self.base_tree.unlock)
786
781
self.other_tree.lock_read()
1025
1017
raise AssertionError('unhandled kind: %s' % other_ie.kind)
1026
# XXX: We need to handle kind == 'symlink'
1028
1019
# If we have gotten this far, that means something has changed
1029
1020
result.append((file_id, content_changed,
1120
1111
def _three_way(base, other, this):
1121
#if base == other, either they all agree, or only THIS has changed.
1122
1112
if base == other:
1113
# if 'base == other', either they all agree, or only 'this' has
1124
1116
elif this not in (base, other):
1117
# 'this' is neither 'base' nor 'other', so both sides changed
1125
1118
return 'conflict'
1126
# "Ambiguous clean merge" -- both sides have made the same change.
1127
1119
elif this == other:
1120
# "Ambiguous clean merge" -- both sides have made the same change.
1129
# this == base: only other has changed.
1123
# this == base: only other has changed.
1177
1170
# only has an lca value
1180
# At this point, the lcas disagree, and the tips disagree
1173
# At this point, the lcas disagree, and the tip disagree
1181
1174
return 'conflict'
1177
@deprecated_method(deprecated_in((2, 2, 0)))
1184
1178
def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
1185
1179
"""Do a three-way test on a scalar.
1186
1180
Return "this", "other" or "conflict", depending whether a value wins.
1236
1230
parent_id_winner = "other"
1237
1231
if name_winner == "this" and parent_id_winner == "this":
1239
if name_winner == "conflict":
1240
trans_id = self.tt.trans_id_file_id(file_id)
1241
self._raw_conflicts.append(('name conflict', trans_id,
1242
this_name, other_name))
1243
if parent_id_winner == "conflict":
1244
trans_id = self.tt.trans_id_file_id(file_id)
1245
self._raw_conflicts.append(('parent conflict', trans_id,
1246
this_parent, other_parent))
1233
if name_winner == 'conflict' or parent_id_winner == 'conflict':
1234
# Creating helpers (.OTHER or .THIS) here cause problems down the
1235
# road if a ContentConflict needs to be created so we should not do
1237
trans_id = self.tt.trans_id_file_id(file_id)
1238
self._raw_conflicts.append(('path conflict', trans_id, file_id,
1239
this_parent, this_name,
1240
other_parent, other_name))
1247
1241
if other_name is None:
1248
1242
# it doesn't matter whether the result was 'other' or
1249
1243
# 'conflict'-- if there's no 'other', we leave it alone.
1251
# if we get here, name_winner and parent_winner are set to safe values.
1252
trans_id = self.tt.trans_id_file_id(file_id)
1253
1245
parent_id = parents[self.winner_idx[parent_id_winner]]
1254
1246
if parent_id is not None:
1255
parent_trans_id = self.tt.trans_id_file_id(parent_id)
1247
# if we get here, name_winner and parent_winner are set to safe
1256
1249
self.tt.adjust_path(names[self.winner_idx[name_winner]],
1257
parent_trans_id, trans_id)
1250
self.tt.trans_id_file_id(parent_id),
1251
self.tt.trans_id_file_id(file_id))
1259
1253
def _do_merge_contents(self, file_id):
1260
1254
"""Performs a merge on file_id contents."""
1540
1534
def cook_conflicts(self, fs_conflicts):
1541
1535
"""Convert all conflicts into a form that doesn't depend on trans_id"""
1543
1536
self.cooked_conflicts.extend(transform.cook_conflicts(
1544
1537
fs_conflicts, self.tt))
1545
1538
fp = transform.FinalPaths(self.tt)
1546
1539
for conflict in self._raw_conflicts:
1547
1540
conflict_type = conflict[0]
1548
if conflict_type in ('name conflict', 'parent conflict'):
1549
trans_id = conflict[1]
1550
conflict_args = conflict[2:]
1551
if trans_id not in name_conflicts:
1552
name_conflicts[trans_id] = {}
1553
transform.unique_add(name_conflicts[trans_id], conflict_type,
1555
if conflict_type == 'contents conflict':
1541
if conflict_type == 'path conflict':
1543
this_parent, this_name,
1544
other_parent, other_name) = conflict[1:]
1545
if this_parent is None or this_name is None:
1546
this_path = '<deleted>'
1548
parent_path = fp.get_path(
1549
self.tt.trans_id_file_id(this_parent))
1550
this_path = osutils.pathjoin(parent_path, this_name)
1551
if other_parent is None or other_name is None:
1552
other_path = '<deleted>'
1554
parent_path = fp.get_path(
1555
self.tt.trans_id_file_id(other_parent))
1556
other_path = osutils.pathjoin(parent_path, other_name)
1557
c = _mod_conflicts.Conflict.factory(
1558
'path conflict', path=this_path,
1559
conflict_path=other_path,
1561
elif conflict_type == 'contents conflict':
1556
1562
for trans_id in conflict[1]:
1557
1563
file_id = self.tt.final_file_id(trans_id)
1558
1564
if file_id is not None:
1565
1571
c = _mod_conflicts.Conflict.factory(conflict_type,
1566
1572
path=path, file_id=file_id)
1567
self.cooked_conflicts.append(c)
1568
if conflict_type == 'text conflict':
1573
elif conflict_type == 'text conflict':
1569
1574
trans_id = conflict[1]
1570
1575
path = fp.get_path(trans_id)
1571
1576
file_id = self.tt.final_file_id(trans_id)
1572
1577
c = _mod_conflicts.Conflict.factory(conflict_type,
1573
1578
path=path, file_id=file_id)
1574
self.cooked_conflicts.append(c)
1576
for trans_id, conflicts in name_conflicts.iteritems():
1578
this_parent, other_parent = conflicts['parent conflict']
1579
if this_parent == other_parent:
1580
raise AssertionError()
1582
this_parent = other_parent = \
1583
self.tt.final_file_id(self.tt.final_parent(trans_id))
1585
this_name, other_name = conflicts['name conflict']
1586
if this_name == other_name:
1587
raise AssertionError()
1589
this_name = other_name = self.tt.final_name(trans_id)
1590
other_path = fp.get_path(trans_id)
1591
if this_parent is not None and this_name is not None:
1592
this_parent_path = \
1593
fp.get_path(self.tt.trans_id_file_id(this_parent))
1594
this_path = osutils.pathjoin(this_parent_path, this_name)
1596
this_path = "<deleted>"
1597
file_id = self.tt.final_file_id(trans_id)
1598
c = _mod_conflicts.Conflict.factory('path conflict', path=this_path,
1599
conflict_path=other_path,
1580
raise AssertionError('bad conflict type: %r' % (conflict,))
1601
1581
self.cooked_conflicts.append(c)
1602
1582
self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)