blob: ef7b5eff494fe3c02eb96bedfc044abecfcf9149 [file] [log] [blame]
#!/bin/bash
usage() {
cat <<EOF
Usage:
${0} Remerge all files with conflict markers in the git working tree
${0} [FILE...] Remerge the given files
Options:
-t, --tool {bcompare,meld,vimdiff}
Use the specified merge tool.
EOF
}
# shellcheck disable=SC2155
readonly BIN_DIR=$(dirname "${BASH_SOURCE[0]}")
readonly SPLIT3="${BIN_DIR}/split3.awk"
readonly CONFLICT_MARKER_BEGIN='^<{7}( .+)?$'
readonly CONFLICT_MARKER_BASE='^\|{7}( .+)?$'
TEMP_FILES=()
cleanup() {
rm -rf "${TEMP_FILES[@]}"
}
trap cleanup EXIT
xtrace() {
(
set -x
"${@}"
)
}
mergetool() {
local file="${1}"
local MERGED="$file"
local BASE="${file}:BASE"
local LOCAL="${file}:LOCAL"
local REMOTE="${file}:REMOTE"
TEMP_FILES+=("$BASE" "$LOCAL" "$REMOTE")
local has_base=false
if grep -qE "${CONFLICT_MARKER_BASE}" "$file"; then
has_base=true
fi
$has_base && awk -f "$SPLIT3" -v TARGET=BASE <"$file" >"$BASE"
awk -f "$SPLIT3" -v TARGET=LOCAL <"$file" >"$LOCAL"
awk -f "$SPLIT3" -v TARGET=REMOTE <"$file" >"$REMOTE"
case "$MERGETOOL" in
bc*)
if $has_base; then
xtrace bcompare "$LOCAL" "$REMOTE" "$BASE" -mergeoutput="$MERGED"
else
xtrace bcompare "$LOCAL" "$REMOTE" -mergeoutput="$MERGED"
fi
;;
meld)
if $has_base; then
xtrace meld "$LOCAL" "$BASE" "$REMOTE" -o "$MERGED"
else
xtrace meld "$LOCAL" "$MERGED" "$REMOTE"
fi
;;
vim*)
if $has_base; then
xtrace vimdiff -c '4wincmd w | wincmd J' "$LOCAL" "$BASE" "$REMOTE" "$MERGED"
else
xtrace vimdiff -c 'wincmd l' "$LOCAL" "$MERGED" "$REMOTE"
fi
;;
esac
}
#
# BEGIN
#
MERGETOOL=vimdiff
if [[ -n "$DISPLAY" ]]; then
if command -v bcompare; then
MERGETOOL=bcompare
elif command -v meld; then
MERGETOOL=meld
fi
fi >/dev/null
while [[ "$1" =~ ^- ]]; do
arg="${1}"
shift
case "$arg" in
--) break ;;
-t | --tool)
MERGETOOL="${1}"
shift
;;
-h | --help | --usage)
usage
exit 0
;;
*)
usage
exit 1
;;
esac
done
TRUST_EXIT_CODE=false
if git rev-parse >/dev/null 2>/dev/null; then
case "$MERGETOOL" in
bc* | vim*)
TRUST_EXIT_CODE=true
;;
esac
fi
readonly MERGETOOL
readonly TRUST_EXIT_CODE
FILES_UNFILTERED=()
if [[ "${#}" -eq 0 ]]; then
while IFS= read -r -d '' ARG; do
FILES_UNFILTERED+=("$ARG")
done < <(git -c grep.fallbackToNoIndex=true grep -zlE "$CONFLICT_MARKER_BEGIN" 2>/dev/null)
else
FILES_UNFILTERED+=("${@}")
fi
FILES=()
for file in "${FILES_UNFILTERED[@]}"; do
if ! [[ -f "$file" ]] || [[ -L "$file" ]]; then
echo "[SKIPPED] ${file}: not a regular file"
elif ! grep -qE "$CONFLICT_MARKER_BEGIN" "$file"; then
echo "[SKIPPED] ${file}: no conflict markers found"
else
FILES+=("$file")
fi
done
echo "Found files with conflict markers:"
printf ' %s\n' "${FILES[@]}"
for file in "${FILES[@]}"; do
echo
echo "Merging '${file}'"
mergetool "$file"
exit_code="$?"
if [[ "$exit_code" -ne 0 ]]; then
echo "Failed to merge '${file}'"
fi
if $TRUST_EXIT_CODE && [[ "$exit_code" -eq 0 ]]; then
if [[ -n "$(git ls-files "$file")" ]]; then
xtrace git add "$file"
fi
continue
fi
read -r -p "Continue merging other files? [Y/n]" -n 1 yn || exit
echo
[[ "$yn" == [nNqQ] ]] && exit "$exit_code"
done
exit 0