240
240
assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
241
241
'killed-both'), \
245
def plan_merge(file, version_a, version_b):
246
"""Return pseudo-annotation indicating how the two versions merge.
248
This is computed between versions a and b and their common
251
Weave lines present in none of them are skipped entirely.
253
inc_a = set(file.get_ancestry([version_a]))
254
inc_b = set(file.get_ancestry([version_b]))
255
inc_c = inc_a & inc_b
257
for lineno, insert, deleteset, line in file.walk([version_a, version_b]):
258
if deleteset & inc_c:
259
# killed in parent; can't be in either a or b
260
# not relevant to our work
261
yield 'killed-base', line
262
elif insert in inc_c:
263
# was inserted in base
264
killed_a = bool(deleteset & inc_a)
265
killed_b = bool(deleteset & inc_b)
266
if killed_a and killed_b:
267
yield 'killed-both', line
269
yield 'killed-a', line
271
yield 'killed-b', line
273
yield 'unchanged', line
274
elif insert in inc_a:
275
if deleteset & inc_a:
276
yield 'ghost-a', line
280
elif insert in inc_b:
281
if deleteset & inc_b:
282
yield 'ghost-b', line
286
# not in either revision
287
yield 'irrelevant', line
289
yield 'unchanged', '' # terminator
292
def weave_merge(plan):
293
"""Yield merged sequence of lines based on merge plan."""
299
for state, line in plan:
300
if state == 'unchanged' or state == 'killed-both':
301
# resync and flush queued conflicts changes if any
302
if not lines_a and not lines_b:
304
elif ch_a and not ch_b:
306
for l in lines_a: yield l
307
elif ch_b and not ch_a:
308
for l in lines_b: yield l
309
elif lines_a == lines_b:
310
for l in lines_a: yield l
313
for l in lines_a: yield l
315
for l in lines_b: yield l
322
if state == 'unchanged':
325
elif state == 'killed-a':
328
elif state == 'killed-b':
331
elif state == 'new-a':
334
elif state == 'new-b':
338
assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
339
'killed-both'), state