Mercurial > hg-stable
diff mercurial/simplemerge.py @ 46109:bdc2bf68f19e
mergetools: add new conflict marker format with diffs in
I use 3-way conflict markers. Often when I resolve them, I manually
compare one the base with one side and apply the differences to the
other side. That can be hard when the conflict marker is large. This
patch introduces a new type of conflict marker, which I'm hoping will
make it easier to resolve conflicts.
The new format uses `<<<<<<<` and `>>>>>>>` to open and close the
markers, just like our existing 2-way and 3-way conflict
markers. Instead of having 2 or 3 snapshots (left+right or
left+base+right), it has a sequence of diffs. A diff looks like this:
```
------- base
+++++++ left
a
-b
+c
d
```
A diff that adds one side ("diff from nothing") has a `=======` header
instead and does not have have `+` prefixed on its lines. A regular
3-way merge can be viewed as adding one side plus a diff between the
base and the other side. It thus has two ways of being represented,
depending on which side is being diffed:
```
<<<<<<<
======= left
contents
on
left
------- base
+++++++ right
contents
on
-left
+right
>>>>>>>
```
or
```
<<<<<<<
------- base
+++++++ left
contents
on
-right
+left
======= right
contents
on
right
>>>>>>>
```
I've made it so the new merge tool tries to pick a version that has
the most common lines (no difference in the example above).
I've called the new tool "mergediff" to stick to the convention of
starting with "merge" if the tool tries a regular 3-way merge.
The idea came from my pet VCS (placeholder name `jj`), which has
support for octopus merges and other ways of ending up with merges of
more than 3 versions. I wanted to be able to represent such conflicts
in the working copy and therefore thought of this format (although I
have not yet implemented it in my VCS). I then attended a meeting with
Larry McVoy, who said BitKeeper has an option (`bk smerge -g`) for
showing a similar format, which reminded me to actually attempt this
in Mercurial.
Differential Revision: https://phab.mercurial-scm.org/D9551
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Tue, 08 Dec 2020 16:45:13 -0800 |
parents | a771ffc378a8 |
children | 59fa3890d40a |
line wrap: on
line diff
--- a/mercurial/simplemerge.py Thu Dec 10 14:39:22 2020 -0800 +++ b/mercurial/simplemerge.py Tue Dec 08 16:45:13 2020 -0800 @@ -455,6 +455,68 @@ return ctx.node() != nodemod.nullid +def _mergediff(m3, name_a, name_b, name_base): + lines = [] + conflicts = False + for group in m3.merge_groups(): + if group[0] == b'conflict': + base_lines, a_lines, b_lines = group[1:] + base_text = b''.join(base_lines) + b_blocks = list( + mdiff.allblocks( + base_text, + b''.join(b_lines), + lines1=base_lines, + lines2=b_lines, + ) + ) + a_blocks = list( + mdiff.allblocks( + base_text, + b''.join(a_lines), + lines1=base_lines, + lines2=b_lines, + ) + ) + + def matching_lines(blocks): + return sum( + block[1] - block[0] + for block, kind in blocks + if kind == b'=' + ) + + def diff_lines(blocks, lines1, lines2): + for block, kind in blocks: + if kind == b'=': + for line in lines1[block[0] : block[1]]: + yield b' ' + line + else: + for line in lines1[block[0] : block[1]]: + yield b'-' + line + for line in lines2[block[2] : block[3]]: + yield b'+' + line + + lines.append(b"<<<<<<<\n") + if matching_lines(a_blocks) < matching_lines(b_blocks): + lines.append(b"======= %s\n" % name_a) + lines.extend(a_lines) + lines.append(b"------- %s\n" % name_base) + lines.append(b"+++++++ %s\n" % name_b) + lines.extend(diff_lines(b_blocks, base_lines, b_lines)) + else: + lines.append(b"------- %s\n" % name_base) + lines.append(b"+++++++ %s\n" % name_a) + lines.extend(diff_lines(a_blocks, base_lines, a_lines)) + lines.append(b"======= %s\n" % name_b) + lines.extend(b_lines) + lines.append(b">>>>>>>\n") + conflicts = True + else: + lines.extend(group[1]) + return lines, conflicts + + def simplemerge(ui, localctx, basectx, otherctx, **opts): """Performs the simplemerge algorithm. @@ -499,9 +561,15 @@ extrakwargs[b'name_base'] = name_base extrakwargs[b'minimize'] = False - lines = m3.merge_lines( - name_a=name_a, name_b=name_b, **pycompat.strkwargs(extrakwargs) - ) + if mode == b'mergediff': + lines, conflicts = _mergediff(m3, name_a, name_b, name_base) + else: + lines = list( + m3.merge_lines( + name_a=name_a, name_b=name_b, **pycompat.strkwargs(extrakwargs) + ) + ) + conflicts = m3.conflicts # merge flags if necessary flags = localctx.flags() @@ -519,5 +587,5 @@ else: localctx.write(mergedtext, flags) - if m3.conflicts and not mode == b'union': + if conflicts and not mode == b'union': return 1