Suggestion: pure Python 3-way merge tool
Marcos Chaves
marcos.nospam at gmail.com
Thu Nov 23 03:03:40 UTC 2006
Hi,
just in case this can be useful to someone, below (and attached) is
the pure Python 3-way merge tool that I'm using with hg on windows. Of
course on unix, it may not make any sense.
My problem with the diffutils ports or merge tools that we can easily
find on the web is that they like to change the EOL style of the files
and I think they should never be changed by the tools if the user
doesn't want that (my case). I also had enough problems trying to
recompile binutils on windows, so I gave up.
If this works well, maybe this could be included in the windows
package as a 'minimal' merge tool. Maybe a version using
mercurial.mdiff could be more appropriate for this. Or maybe this
could be part of a future hgmerge.py. What do you guys think?
Comments? Bugs? Suggestions?
-Marcos
- - - - - 8< - - - - -
#!/usr/bin/env python
# mymerge.py - simple 3-way merge tool for Mercurial
#
# Copyright 2006 Marcos Chaves <marcos.nospam at gmail.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
import difflib
import sys
def read_file(filename):
try:
f = open(filename, 'rb')
l = f.readlines()
f.close()
except IOError, err:
print "can't open file '" + filename + "'. aborting."
sys.exit(-1)
else:
return l
def drop_inline_diffs(diff):
r = []
for t in diff:
if not t.startswith('?'):
r.append(t)
return r
def merge_files(a, x, b):
dxa = difflib.Differ()
dxb = difflib.Differ()
xa = drop_inline_diffs(dxa.compare(x, a))
xb = drop_inline_diffs(dxb.compare(x, b))
m = []
index_a = 0
index_b = 0
had_conflict = 0
while (index_a < len(xa)) and (index_b < len(xb)):
# no changes or adds on both sides
if (xa[index_a] == xb[index_b] and
(xa[index_a].startswith(' ') or xa[index_a].startswith('+ '))):
m.append(xa[index_a][2:])
index_a += 1
index_b += 1
continue
# removing matching lines from one or both sides
if ((xa[index_a][2:] == xb[index_b][2:])
and (xa[index_a].startswith('- ') or xb[index_b].startswith('- '))):
index_a += 1
index_b += 1
continue
# adding lines in A
if xa[index_a].startswith('+ ') and xb[index_b].startswith(' '):
m.append(xa[index_a][2:])
index_a += 1
continue
# adding line in B
if xb[index_b].startswith('+ ') and xa[index_a].startswith(' '):
m.append(xb[index_b][2:])
index_b += 1
continue
# conflict - list both A and B, similar to GNU's diff3
m.append("<<<<<<< A\n")
while (index_a < len(xa)) and not xa[index_a].startswith(' '):
m.append(xa[index_a][2:])
index_a += 1
m.append("=======\n")
while (index_b < len(xb)) and not xb[index_b].startswith(' '):
m.append(xb[index_b][2:])
index_b += 1
m.append(">>>>>>> B\n")
had_conflict = 1
# append remining lines - there will be only either A or B
for i in range(len(xa) - index_a):
m.append(xa[index_a + i][2:])
for i in range(len(xb) - index_b):
m.append(xb[index_b + i][2:])
return had_conflict, m
if len(sys.argv) < 4:
print 'mymerge.py my base other [merged]'
sys.exit(-1)
a = read_file(sys.argv[1])
x = read_file(sys.argv[2])
b = read_file(sys.argv[3])
had_conflict, m = merge_files(a, x, b)
try:
if len(sys.argv) == 5:
f = open(sys.argv[4], 'wb')
else:
f = open(sys.argv[1], 'wb')
for line in m:
f.write(line)
f.close()
except IOError, err:
print "can't write to merged file. aborting."
sys.exit(-1)
if had_conflict:
sys.exit(-1)
# ~
-------------- next part --------------
A non-text attachment was scrubbed...
Name: mymerge.py
Type: text/x-python
Size: 3083 bytes
Desc: not available
URL: <http://lists.mercurial-scm.org/pipermail/mercurial-devel/attachments/20061123/002a3975/attachment.py>
More information about the Mercurial-devel
mailing list