1#!/bin/bash 2 3usage() { 4 cat <<EOF 5Usage: 6 ${0} Remerge all files with conflict markers in the git working tree 7 ${0} [FILE...] Remerge the given files 8 9Options: 10 -t, --tool {bcompare,meld,vimdiff} 11 Use the specified merge tool. 12EOF 13} 14 15# shellcheck disable=SC2155 16readonly BIN_DIR=$(dirname "${BASH_SOURCE[0]}") 17readonly SPLIT3="${BIN_DIR}/split3.awk" 18readonly CONFLICT_MARKER_BEGIN='^<{7}( .+)?$' 19readonly CONFLICT_MARKER_BASE='^\|{7}( .+)?$' 20 21TEMP_FILES=() 22cleanup() { 23 rm -rf "${TEMP_FILES[@]}" 24} 25trap cleanup EXIT 26 27xtrace() { 28 ( 29 set -x 30 "${@}" 31 ) 32} 33 34mergetool() { 35 local file="${1}" 36 local MERGED="$file" 37 local BASE="${file}:BASE" 38 local LOCAL="${file}:LOCAL" 39 local REMOTE="${file}:REMOTE" 40 TEMP_FILES+=("$BASE" "$LOCAL" "$REMOTE") 41 42 local has_base=false 43 if grep -qE "${CONFLICT_MARKER_BASE}" "$file"; then 44 has_base=true 45 fi 46 47 $has_base && awk -f "$SPLIT3" -v TARGET=BASE <"$file" >"$BASE" 48 awk -f "$SPLIT3" -v TARGET=LOCAL <"$file" >"$LOCAL" 49 awk -f "$SPLIT3" -v TARGET=REMOTE <"$file" >"$REMOTE" 50 51 case "$MERGETOOL" in 52 bc*) 53 if $has_base; then 54 xtrace bcompare "$LOCAL" "$REMOTE" "$BASE" -mergeoutput="$MERGED" 55 else 56 xtrace bcompare "$LOCAL" "$REMOTE" -mergeoutput="$MERGED" 57 fi 58 ;; 59 meld) 60 if $has_base; then 61 xtrace meld "$LOCAL" "$BASE" "$REMOTE" -o "$MERGED" 62 else 63 xtrace meld "$LOCAL" "$MERGED" "$REMOTE" 64 fi 65 ;; 66 vim*) 67 if $has_base; then 68 xtrace vimdiff -c '4wincmd w | wincmd J' "$LOCAL" "$BASE" "$REMOTE" "$MERGED" 69 else 70 xtrace vimdiff -c 'wincmd l' "$LOCAL" "$MERGED" "$REMOTE" 71 fi 72 ;; 73 esac 74} 75 76# 77# BEGIN 78# 79 80MERGETOOL=vimdiff 81if [[ -n "$DISPLAY" ]]; then 82 if command -v bcompare; then 83 MERGETOOL=bcompare 84 elif command -v meld; then 85 MERGETOOL=meld 86 fi 87fi >/dev/null 88 89while [[ "$1" =~ ^- ]]; do 90 arg="${1}" 91 shift 92 case "$arg" in 93 --) break ;; 94 -t | --tool) 95 MERGETOOL="${1}" 96 shift 97 ;; 98 -h | --help | --usage) 99 usage 100 exit 0 101 ;; 102 *) 103 usage 104 exit 1 105 ;; 106 esac 107done 108 109TRUST_EXIT_CODE=false 110if git rev-parse >/dev/null 2>/dev/null; then 111 case "$MERGETOOL" in 112 bc* | vim*) 113 TRUST_EXIT_CODE=true 114 ;; 115 esac 116fi 117 118readonly MERGETOOL 119readonly TRUST_EXIT_CODE 120 121FILES_UNFILTERED=() 122if [[ "${#}" -eq 0 ]]; then 123 while IFS= read -r -d '' ARG; do 124 FILES_UNFILTERED+=("$ARG") 125 done < <(git -c grep.fallbackToNoIndex=true grep -zlE "$CONFLICT_MARKER_BEGIN" 2>/dev/null) 126else 127 FILES_UNFILTERED+=("${@}") 128fi 129 130FILES=() 131for file in "${FILES_UNFILTERED[@]}"; do 132 if ! [[ -f "$file" ]] || [[ -L "$file" ]]; then 133 echo "[SKIPPED] ${file}: not a regular file" 134 elif ! grep -qE "$CONFLICT_MARKER_BEGIN" "$file"; then 135 echo "[SKIPPED] ${file}: no conflict markers found" 136 else 137 FILES+=("$file") 138 fi 139done 140 141echo "Found files with conflict markers:" 142printf ' %s\n' "${FILES[@]}" 143 144for file in "${FILES[@]}"; do 145 echo 146 echo "Merging '${file}'" 147 148 mergetool "$file" 149 exit_code="$?" 150 if [[ "$exit_code" -ne 0 ]]; then 151 echo "Failed to merge '${file}'" 152 fi 153 154 if $TRUST_EXIT_CODE && [[ "$exit_code" -eq 0 ]]; then 155 if [[ -n "$(git ls-files "$file")" ]]; then 156 xtrace git add "$file" 157 fi 158 continue 159 fi 160 read -r -p "Continue merging other files? [Y/n]" -n 1 yn || exit 161 echo 162 [[ "$yn" == [nNqQ] ]] && exit "$exit_code" 163done 164 165exit 0 166