Subject: automerge implementation ideas for Windows

From: Seth House <[email-masked]>
Date: Wed, 20 Jan 2021 16:24:47 -0700

On Fri, Jan 15, 2021 at 09:24:59PM -0700, Seth House wrote:
> The autocrlf test is breaking because the sed that ships with some mingw
> versions (and also some minsys and cygwin versions) will *automatically*
> remove carriage returns:

So the mingw builds of both sed and awk change carriage returns. So far
I haven't found documentation on it so I'm not aware of a portable way
to disable the behavior. Instead I've been playing with alternate
approaches. The two patches below work and pass the autocrlf test on
Windows, however they are first-draft implementations. Feedback welcome.

One other point of discussion: I would like to change the name of this
feature. "Automerge" is a bit of an overloaded term and, IMO, doesn't
describe this feature very well. Several of the GUI diff programs have
a feature that they call "automerge" or "auto merge", and there's a flag
for Meld already in Git called "mergetool.meld.useAutoMerge" which could
cause confusion.

Instead, I'd like to propose "mergetool.hideResolved" or the more
verbose "mergetool.hideResolvedConflicts" as the name. We're not really
merging anything (Git aleady did that before the mergetool is invoked),
but rather we're just not showing any conflicts that Git was already
able to resolve.

---------->8--------->8--------->8--------->8-------

#1: Use POSIX read and a while loop to emulate an awk-like approach:

diff --git a/git-mergetool.sh b/git-mergetool.sh
index 246d6b76fc..94728dd518 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -240,19 +240,46 @@ checkout_staged_file () {
 }
 
 auto_merge () {
+	C0="<<<<<<< "
+	C1="||||||| "
+	C2="======="
+	C3=">>>>>>> "
+	inl=0
+	inb=0
+	inr=0
+
 	git merge-file --diff3 --marker-size=7 -q -p "$LOCAL" "$BASE" "$REMOTE" >"$DIFF3"
+
 	if test -s "$DIFF3"
 	then
-		C0="^<<<<<<< "
-		C1="^||||||| "
-		C2="^=======$(printf '\015')\{0,1\}$"
-		C3="^>>>>>>> "
-
-		sed -e "/$C0/,/$C1/d" -e "/$C2/,/$C3/d" "$DIFF3" >"$BASE"
-		sed -e "/$C1/,/$C3/d" -e "/$C0/d" "$DIFF3" >"$LOCAL"
-		sed -e "/$C0/,/$C2/d" -e "/$C3/d" "$DIFF3" >"$REMOTE"
+		touch "$LCONFL" "$BCONFL" "$RCONFL"
+
+		while read -r line
+		do
+			case $line in
+				$C0*) inl=1; continue ;;
+				$C1*) inl=0; inb=1; continue ;;
+				$C2*) inb=0; inr=1; continue ;;
+				$C3*) inr=0; continue ;;
+			esac
+
+			case 1 in
+				$inl) printf '%s\n' "$line" >>"$LCONFL" ;;
+				$inb) printf '%s\n' "$line" >>"$BCONFL" ;;
+				$inr) printf '%s\n' "$line" >>"$RCONFL" ;;
+				*)
+					printf '%s\n' "$line" >>"$LCONFL"
+					printf '%s\n' "$line" >>"$BCONFL"
+					printf '%s\n' "$line" >>"$RCONFL"
+				;;
+			esac
+		done < "$DIFF3"
+
+		mv -- "$LCONFL" "$LOCAL"
+		mv -- "$BCONFL" "$BASE"
+		mv -- "$RCONFL" "$REMOTE"
+		rm -- "$DIFF3"
 	fi
-	rm -- "$DIFF3"
 }
 
 merge_file () {
@@ -295,8 +322,11 @@ merge_file () {
 	DIFF3="$MERGETOOL_TMPDIR/${BASE}_DIFF3_$$$ext"
 	BACKUP="$MERGETOOL_TMPDIR/${BASE}_BACKUP_$$$ext"
 	LOCAL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_$$$ext"
+	LCONFL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_LCONFL_$$$ext"
 	REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
+	RCONFL="$MERGETOOL_TMPDIR/${BASE}_REMOTE_RCONFL_$$$ext"
 	BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
+	BCONFL="$MERGETOOL_TMPDIR/${BASE}_BASE_BCONFL_$$$ext"
 
 	base_mode= local_mode= remote_mode=

---------->8--------->8--------->8--------->8-------

#2: Call merge-file twice:

A much simpler implementation but probably less performant. Is it enough
to matter?

diff --git a/git-mergetool.sh b/git-mergetool.sh
index 246d6b76fc..1cb45a7437 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -240,19 +240,10 @@ checkout_staged_file () {
 }
 
 auto_merge () {
-	git merge-file --diff3 --marker-size=7 -q -p "$LOCAL" "$BASE" "$REMOTE" >"$DIFF3"
-	if test -s "$DIFF3"
-	then
-		C0="^<<<<<<< "
-		C1="^||||||| "
-		C2="^=======$(printf '\015')\{0,1\}$"
-		C3="^>>>>>>> "
-
-		sed -e "/$C0/,/$C1/d" -e "/$C2/,/$C3/d" "$DIFF3" >"$BASE"
-		sed -e "/$C1/,/$C3/d" -e "/$C0/d" "$DIFF3" >"$LOCAL"
-		sed -e "/$C0/,/$C2/d" -e "/$C3/d" "$DIFF3" >"$REMOTE"
-	fi
-	rm -- "$DIFF3"
+	git merge-file --ours -q -p "$LOCAL" "$BASE" "$REMOTE" >"$LCONFL"
+	git merge-file --theirs -q -p "$LOCAL" "$BASE" "$REMOTE" >"$RCONFL"
+	mv -- "$LCONFL" "$LOCAL"
+	mv -- "$RCONFL" "$REMOTE"
 }
 
 merge_file () {
@@ -295,7 +286,9 @@ merge_file () {
 	DIFF3="$MERGETOOL_TMPDIR/${BASE}_DIFF3_$$$ext"
 	BACKUP="$MERGETOOL_TMPDIR/${BASE}_BACKUP_$$$ext"
 	LOCAL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_$$$ext"
+	LCONFL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_LCONFL_$$$ext"
 	REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
+	RCONFL="$MERGETOOL_TMPDIR/${BASE}_REMOTE_RCONFL_$$$ext"
 	BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
 
 	base_mode= local_mode= remote_mode=