67
72
return tree.inventory[params.file_id].name
69
74
def merge_text(self, params):
70
"""Float new changelog sections from other to the top of the changelog.
72
e.g. Given a changelog in THIS containing::
78
and a changelog in OTHER containing::
75
"""Merge changelog changes.
77
* new entries from other will float to the top
78
* edits to older entries are preserved
90
80
# Transform files into lists of changelog entries
91
81
this_entries = changelog_entries(params.this_lines)
92
82
other_entries = changelog_entries(params.other_lines)
93
83
base_entries = changelog_entries(params.base_lines)
94
# Determine which entries have been added by base
95
base_entries = frozenset(base_entries)
97
entry for entry in other_entries if entry not in base_entries]
98
# Prepend them to the entries in this
99
result_entries = new_in_other + this_entries
85
result_entries = merge_entries(
86
base_entries, this_entries, other_entries)
88
return 'not_applicable' # XXX: generating a nice conflict file
100
90
# Transform the merged elements back into real blocks of lines.
101
91
return 'success', entries_to_lines(result_entries)
94
class EntryConflict(Exception):
98
def merge_entries_old(base_entries, this_entries, other_entries):
99
# Determine which entries have been added by other (compared to base)
100
base_entries = frozenset(base_entries)
102
entry for entry in other_entries if entry not in base_entries]
103
# Prepend them to the entries in this
104
result_entries = new_in_other + this_entries
105
return result_entries
108
def default_guess_edits(new_entries, deleted_entries, entry_as_str=''.join):
109
# This algorithm does O(N^2 * logN) SequenceMatcher.ratio() calls, which is
110
# pretty bad, but it shouldn't be used very often.
111
deleted_entries_as_strs = [
112
entry_as_str(entry) for entry in deleted_entries]
113
new_entries_as_strs = [
114
entry_as_str(entry) for entry in new_entries]
115
result_new = list(new_entries)
116
result_deleted = list(deleted_entries)
118
sm = difflib.SequenceMatcher()
123
for new_entry in new_entries:
124
new_entry_as_str = entry_as_str(new_entry)
125
sm.set_seq1(new_entry_as_str)
126
for old_entry_as_str in deleted_entries_as_strs:
127
sm.set_seq2(old_entry_as_str)
130
if best_score is None or score > best_score:
131
best = new_entry_as_str, old_entry_as_str
134
del_index = deleted_entries_as_strs.index(best[1])
135
new_index = new_entries_as_strs.index(best[0])
137
(result_deleted[del_index], result_new[new_index]))
138
del deleted_entries_as_strs[del_index], result_deleted[del_index]
139
del new_entries_as_strs[new_index], result_new[new_index]
142
return result_new, result_deleted, result_edits
145
def merge_entries_new(base_entries, this_entries, other_entries,
146
guess_edits=default_guess_edits):
147
m3 = Merge3(base_entries, this_entries, other_entries,
151
for group in m3.merge_groups():
152
if 'changelog_merge' in debug.debug_flags:
153
mutter('merge group:\n%r', group)
154
group_kind = group[0]
155
if group_kind == 'conflict':
156
_, base, this, other = group
159
entry for entry in other if entry not in base]
162
entry for entry in base if entry not in other]
163
if at_top and deleted_in_other:
164
# Magic! Compare deletions and additions to try spot edits
165
new_in_other, deleted_in_other, edits_in_other = guess_edits(
166
new_in_other, deleted_in_other)
168
# Changes not made at the top are always preserved as is, no
169
# need to try distinguish edits from adds and deletes.
171
if 'changelog_merge' in debug.debug_flags:
172
mutter('at_top: %r', at_top)
173
mutter('new_in_other: %r', new_in_other)
174
mutter('deleted_in_other: %r', deleted_in_other)
175
mutter('edits_in_other: %r', edits_in_other)
176
# Apply deletes and edits
178
entry for entry in this if entry not in deleted_in_other]
179
for old_entry, new_entry in edits_in_other:
181
index = updated_this.index(old_entry)
183
# edited entry no longer present in this! Just give up and
184
# declare a conflict.
185
raise EntryConflict()
186
updated_this[index] = new_entry
187
if 'changelog_merge' in debug.debug_flags:
188
mutter('updated_this: %r', updated_this)
190
# Float new entries from other to the top
191
result_entries = new_in_other + result_entries
193
result_entries.extend(new_in_other)
194
result_entries.extend(updated_this)
195
else: # unchanged, same, a, or b.
197
result_entries.extend(lines)
199
return result_entries
202
merge_entries = merge_entries_new