##// END OF EJS Templates
mergetools: add new conflict marker format with diffs in...
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

File last commit:

r45714:10f48720 default
r46724:bdc2bf68 default
Show More
diffhelper.py
83 lines | 2.3 KiB | text/x-python | PythonLexer
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 # diffhelper.py - helper routines for patch
#
# Copyright 2009 Matt Mackall <mpm@selenic.com> and others
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
from .i18n import _
from . import (
error,
Gregory Szorc
global: use pycompat.xrange()...
r38806 pycompat,
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 )
Rodrigo Damazio Bovendorp
diff: move no-eol text constant to a common location...
r45714 MISSING_NEWLINE_MARKER = b'\\ No newline at end of file\n'
Augie Fackler
style: run a patched black on a subset of mercurial...
r43345
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 def addlines(fp, hunk, lena, lenb, a, b):
"""Read lines from fp into the hunk
The hunk is parsed into two arrays, a and b. a gets the old state of
the text, b gets the new state. The control char from the hunk is saved
when inserting into a, but not b (for performance while deleting files.)
"""
while True:
todoa = lena - len(a)
todob = lenb - len(b)
num = max(todoa, todob)
if num == 0:
break
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(num):
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 s = fp.readline()
if not s:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b'incomplete hunk'))
Rodrigo Damazio Bovendorp
diff: move no-eol text constant to a common location...
r45714 if s == MISSING_NEWLINE_MARKER:
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 fixnewline(hunk, a, b)
continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if s == b'\n' or s == b'\r\n':
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 # Some patches may be missing the control char
# on empty lines. Supply a leading space.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 s = b' ' + s
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 hunk.append(s)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if s.startswith(b'+'):
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 b.append(s[1:])
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif s.startswith(b'-'):
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 a.append(s)
else:
b.append(s[1:])
a.append(s)
Augie Fackler
style: run a patched black on a subset of mercurial...
r43345
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 def fixnewline(hunk, a, b):
"""Fix up the last lines of a and b when the patch has no newline at EOF"""
l = hunk[-1]
# tolerate CRLF in last line
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if l.endswith(b'\r\n'):
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 hline = l[:-2]
else:
hline = l[:-1]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if hline.startswith((b' ', b'+')):
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 b[-1] = hline[1:]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if hline.startswith((b' ', b'-')):
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 a[-1] = hline
hunk[-1] = hline
Augie Fackler
style: run a patched black on a subset of mercurial...
r43345
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 def testhunk(a, b, bstart):
"""Compare the lines in a with the lines in b
a is assumed to have a control char at the start of each line, this char
is ignored in the compare.
"""
alen = len(a)
blen = len(b)
if alen > blen - bstart or bstart < 0:
return False
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(alen):
Yuya Nishihara
diffhelper: rename module to avoid conflicts with ancient C module (issue5846)...
r37821 if a[i][1:] != b[i + bstart]:
return False
return True