Skip to content

Instantly share code, notes, and snippets.

@unphased
Created May 19, 2017 21:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save unphased/b71816c8679cec0c7c5c84b56afe8f0d to your computer and use it in GitHub Desktop.
Save unphased/b71816c8679cec0c7c5c84b56afe8f0d to your computer and use it in GitHub Desktop.
A command line (shell) tool for grokking git merge conflicts
#!/bin/bash
# set -x
# PS4='{$LINENO}+'
if [ -t 1 ]; then
# echo "in terminal"
RED=""
RESET=""
BLUE=""
YELLOW=""
GREEN=""
MAGENTA=""
COLORFLAG=" --color=always"
# else
# echo "in script"
fi
# this is a simple script to help handle a git conflict situation. It is intended for use in conjunction with the merge.conflictStyle = diff3 git configuration setting. We split a conflicted file as produced by git into two pairs of "temporary" buffers to pipe to sift for each conflicting chunk. This way we can view for each chunk what the original was, what local changes were, and what remote changes were, this shall usually be sufficient to inform the changes we need to make, and so far we have been in dire need of an interface for this.
# Unfortunately git's own behavior falls short of what we'd consider reasonable
# and I cant just let this be triggered from the ext diff. But I can just run
# this and it will:
# 1. check that the cwd's repo is in conflict state, quit otherwise
# 2. determine which files are in conflict, for each of them:
# 3. for each of their conflict chunks:
# 4. run sift of each side from the original content.
if [[ -n $(git ls-files --unmerged) ]]; then
files=$(git diff --name-only --diff-filter=U)
for file in $files; do
echo ${MAGENTA}Looking at ${file}...$RESET
# sanity check for <<< ||| === >>>
markers=$(grep -n '^[<=>|]\{7\}' $file)
linecount=$(wc -l <<< "$markers")
[[ $(($linecount % 4)) -ne 0 ]] && echo "marker count not multiple of 4" && exit 1;
matches1=0
result1=$(grep '<' -n <<< "$markers")
result11=$(cut -d':' -f1 <<< "$result1")
while read idx; do
((matches1++))
if [[ $(($idx % 4)) -ne 1 ]]
then echo "bad marker index < $idx"; exit 1;
fi
done <<< "$result11"
matches2=0
result2=$(grep '|' -n <<< "$markers")
result22=$(cut -d':' -f1 <<< "$result2")
while read idx; do ((matches2++)); if [[ $(($idx % 4)) -ne 2 ]]
then echo "bad marker index | $idx"; exit 1; fi; done <<< "$result22"
matches3=0
result3=$(grep '=' -n <<< "$markers")
result33=$(cut -d':' -f1 <<< "$result3")
while read idx; do ((matches3++)); if [[ $(($idx % 4)) -ne 3 ]]
then echo "bad marker index = $idx"; exit 1; fi; done <<< "$result33"
matches4=0
result4=$(grep '>' -n <<< "$markers")
result44=$(cut -d':' -f1 <<< "$result4")
while read idx; do ((matches4++)); if [[ $(($idx % 4)) -ne 0 ]]
then echo "bad marker index > $idx"; exit 1; fi; done <<< "$result44"
if [[ $matches4 -ne $matches1 || $matches4 -ne $matches2 || $matches4 -ne $matches3 ]]
then echo "not same marker counts $matches1 $matches2 $matches3 $matches4"; exit 1; fi
echo "found $matches4 chunks"
IFS=$'\n'
result111=($(cut -d':' -f2 <<< "$result1"))
result222=($(cut -d':' -f2 <<< "$result2"))
result333=($(cut -d':' -f2 <<< "$result3"))
result444=($(cut -d':' -f2 <<< "$result4"))
i=0
while read res4; do
echo "${BLUE}============ chunk $i ours ${result111[$i]} ${result222[$i]} theirs ${result333[$i]} ${result444[$i]} original ${result222[$i]} ${result333[$i]}${RESET}"
orig=$(sed -n $((${result222[$i]}+1)),$((${result333[$i]}-1))p $file)
ours=$(sed -n $((${result111[$i]}+1)),$((${result222[$i]}-1))p $file)
theirs=$(sed -n $((${result333[$i]}+1)),$((${result444[$i]}-1))p $file)
# printf "orig is $orig, ours is $ours"
echo ${YELLOW}OURS$RESET
sift -R "$orig" "$ours"
echo ${YELLOW}THEIRS$RESET
sift -R "$orig" "$theirs"
((i++))
done <<< "$result4"
done
else
echo "Current repo not unmerged"
fi
@unphased
Copy link
Author

unphased commented May 23, 2017

This has a dependency on the program sift which is not included here. It is a program that works just like diff. It uses the -R flag with it as well. That flag takes the two arguments themselves as the text to diff (rather than treating them as filenames).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment