13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
71
71
class Merger(object):
72
72
def __init__(self, this_branch, other_tree=None, base_tree=None,
73
this_tree=None, pb=None, change_reporter=None,
73
this_tree=None, pb=DummyProgress(), change_reporter=None,
74
74
recurse='down', revision_graph=None):
75
75
object.__init__(self)
76
76
self.this_branch = this_branch
538
532
winner_idx = {"this": 2, "other": 1, "conflict": 1}
539
533
supports_lca_trees = True
541
def __init__(self, working_tree, this_tree, base_tree, other_tree,
535
def __init__(self, working_tree, this_tree, base_tree, other_tree,
542
536
interesting_ids=None, reprocess=False, show_base=False,
543
537
pb=DummyProgress(), pp=None, change_reporter=None,
544
538
interesting_files=None, do_merge=True,
800
794
content_changed = True
801
795
if kind_winner == 'this':
802
796
# No kind change in OTHER, see if there are *any* changes
803
if other_ie.kind == 'directory':
797
if other_ie.kind == None:
798
# No content and 'this' wins the kind, so skip this?
801
elif other_ie.kind == 'directory':
804
802
if parent_id_winner == 'this' and name_winner == 'this':
805
803
# No change for this directory in OTHER, skip
807
805
content_changed = False
808
elif other_ie.kind is None or other_ie.kind == 'file':
806
elif other_ie.kind == 'file':
809
807
def get_sha1(ie, tree):
810
808
if ie.kind != 'file':
1074
1072
if name_winner == "conflict":
1075
1073
trans_id = self.tt.trans_id_file_id(file_id)
1076
self._raw_conflicts.append(('name conflict', trans_id,
1074
self._raw_conflicts.append(('name conflict', trans_id,
1077
1075
this_name, other_name))
1078
1076
if parent_id_winner == "conflict":
1079
1077
trans_id = self.tt.trans_id_file_id(file_id)
1080
self._raw_conflicts.append(('parent conflict', trans_id,
1078
self._raw_conflicts.append(('parent conflict', trans_id,
1081
1079
this_parent, other_parent))
1082
1080
if other_name is None:
1083
# it doesn't matter whether the result was 'other' or
1081
# it doesn't matter whether the result was 'other' or
1084
1082
# 'conflict'-- if there's no 'other', we leave it alone.
1086
1084
# if we get here, name_winner and parent_winner are set to safe values.
1113
1111
self.tt.unversion_file(trans_id)
1114
1112
if file_id in self.this_tree:
1115
1113
self.tt.delete_contents(trans_id)
1116
file_group = self._dump_conflicts(name, parent_id, file_id,
1114
file_group = self._dump_conflicts(name, parent_id, file_id,
1117
1115
set_version=True)
1118
1116
self._raw_conflicts.append(('contents conflict', file_group))
1123
1121
base_pair = contents_pair(self.base_tree)
1124
1122
other_pair = contents_pair(self.other_tree)
1126
this_pair = contents_pair(self.this_tree)
1127
lca_pairs = [contents_pair(tree) for tree in self._lca_trees]
1128
winner = self._lca_multi_way((base_pair, lca_pairs), other_pair,
1129
this_pair, allow_overriding_lca=False)
1131
if base_pair == other_pair:
1134
# We delayed evaluating this_pair as long as we can to avoid
1135
# unnecessary sha1 calculation
1136
this_pair = contents_pair(self.this_tree)
1137
winner = self._three_way(base_pair, other_pair, this_pair)
1138
if winner == 'this':
1139
# No interesting changes introduced by OTHER
1141
trans_id = self.tt.trans_id_file_id(file_id)
1142
if winner == 'other':
1143
# OTHER is a straight winner, so replace this contents with other
1144
file_in_this = file_id in self.this_tree
1146
# Remove any existing contents
1147
self.tt.delete_contents(trans_id)
1148
if file_id in self.other_tree:
1149
# OTHER changed the file
1150
create_from_tree(self.tt, trans_id,
1151
self.other_tree, file_id)
1152
if not file_in_this:
1153
self.tt.version_file(file_id, trans_id)
1156
# OTHER deleted the file
1157
self.tt.unversion_file(trans_id)
1160
# We have a hypothetical conflict, but if we have files, then we
1161
# can try to merge the content
1162
if this_pair[0] == 'file' and other_pair[0] == 'file':
1123
if base_pair == other_pair:
1124
# OTHER introduced no changes
1126
this_pair = contents_pair(self.this_tree)
1127
if this_pair == other_pair:
1128
# THIS and OTHER introduced the same changes
1131
trans_id = self.tt.trans_id_file_id(file_id)
1132
if this_pair == base_pair:
1133
# only OTHER introduced changes
1134
if file_id in self.this_tree:
1135
# Remove any existing contents
1136
self.tt.delete_contents(trans_id)
1137
if file_id in self.other_tree:
1138
# OTHER changed the file
1139
create_from_tree(self.tt, trans_id,
1140
self.other_tree, file_id)
1141
if file_id not in self.this_tree:
1142
self.tt.version_file(file_id, trans_id)
1144
elif file_id in self.this_tree.inventory:
1145
# OTHER deleted the file
1146
self.tt.unversion_file(trans_id)
1148
#BOTH THIS and OTHER introduced changes; scalar conflict
1149
elif this_pair[0] == "file" and other_pair[0] == "file":
1163
1150
# THIS and OTHER are both files, so text merge. Either
1164
1151
# BASE is a file, or both converted to files, so at least we
1165
1152
# have agreement that output should be a file.
1207
1195
def iter_merge3(retval):
1208
1196
retval["text_conflicts"] = False
1209
for line in m3.merge_lines(name_a = "TREE",
1210
name_b = "MERGE-SOURCE",
1197
for line in m3.merge_lines(name_a = "TREE",
1198
name_b = "MERGE-SOURCE",
1211
1199
name_base = "BASE-REVISION",
1212
start_marker=start_marker,
1200
start_marker=start_marker,
1213
1201
base_marker=base_marker,
1214
1202
reprocess=self.reprocess):
1215
1203
if line.startswith(start_marker):
1224
1212
self._raw_conflicts.append(('text conflict', trans_id))
1225
1213
name = self.tt.final_name(trans_id)
1226
1214
parent_id = self.tt.final_parent(trans_id)
1227
file_group = self._dump_conflicts(name, parent_id, file_id,
1215
file_group = self._dump_conflicts(name, parent_id, file_id,
1228
1216
this_lines, base_lines,
1230
1218
file_group.append(trans_id)
1232
def _dump_conflicts(self, name, parent_id, file_id, this_lines=None,
1220
def _dump_conflicts(self, name, parent_id, file_id, this_lines=None,
1233
1221
base_lines=None, other_lines=None, set_version=False,
1234
1222
no_base=False):
1235
1223
"""Emit conflict files.
1237
1225
determined automatically. If set_version is true, the .OTHER, .THIS
1238
1226
or .BASE (in that order) will be created as versioned files.
1240
data = [('OTHER', self.other_tree, other_lines),
1228
data = [('OTHER', self.other_tree, other_lines),
1241
1229
('THIS', self.this_tree, this_lines)]
1242
1230
if not no_base:
1243
1231
data.append(('BASE', self.base_tree, base_lines))
1316
1304
conflict_args = conflict[2:]
1317
1305
if trans_id not in name_conflicts:
1318
1306
name_conflicts[trans_id] = {}
1319
unique_add(name_conflicts[trans_id], conflict_type,
1307
unique_add(name_conflicts[trans_id], conflict_type,
1321
1309
if conflict_type == 'contents conflict':
1322
1310
for trans_id in conflict[1]:
1408
1396
self._raw_conflicts.append(('text conflict', trans_id))
1409
1397
name = self.tt.final_name(trans_id)
1410
1398
parent_id = self.tt.final_parent(trans_id)
1411
file_group = self._dump_conflicts(name, parent_id, file_id,
1399
file_group = self._dump_conflicts(name, parent_id, file_id,
1413
1401
file_group.append(trans_id)
1599
1587
for record in self.vf.get_record_stream(keys, 'unordered', True):
1600
1588
if record.storage_kind == 'absent':
1601
1589
raise errors.RevisionNotPresent(record.key, self.vf)
1602
result[record.key[-1]] = osutils.chunks_to_lines(
1603
record.get_bytes_as('chunked'))
1590
result[record.key[-1]] = osutils.split_lines(
1591
record.get_bytes_as('fulltext'))
1606
1594
def plan_merge(self):