137 lines
3.8 KiB
Bash
137 lines
3.8 KiB
Bash
set -euo pipefail
|
|
|
|
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ] || [ $# -ge 2 ]; then
|
|
declare -r ARGV0=$(basename "${0:-git-switch-interactive.sh}")
|
|
printf 'Usage: %s [BRANCH]\n' "$ARGV0" >&2
|
|
cat <<'EOF' >&2
|
|
Interactively choose a local branch, remote branch, or tag to switch to.
|
|
|
|
Options:
|
|
-h, --help Show this help and exit
|
|
BRANCH If a single branch/tag/ref is provided, the script will
|
|
run git switch BRANCH and exit.
|
|
EOF
|
|
exit 0
|
|
fi
|
|
|
|
if [ $# -eq 1 ]; then
|
|
git switch "$1"
|
|
exit 0
|
|
fi
|
|
|
|
declare -r BRANCH_COLOR=$'\033[32m'
|
|
declare -r REMOTE_COLOR=$'\033[33m'
|
|
declare -r ORIGIN_COLOR=$'\033[31m'
|
|
declare -r TAG_COLOR=$'\033[35m'
|
|
declare -r RESET_COLOR=$'\033[0m'
|
|
|
|
if [ -n "${NO_COLOR:-}" ]; then
|
|
declare -r BRANCH_COLOR=''
|
|
declare -r REMOTE_COLOR=''
|
|
declare -r ORIGIN_COLOR=''
|
|
declare -r TAG_COLOR=''
|
|
declare -r RESET_COLOR=''
|
|
fi
|
|
|
|
declare -r STRIP_ANSI_SED='s/\x1B\[[0-9;]*[a-zA-Z]//g'
|
|
declare -r TRIM_SPACE_SED='s/^[[:space:]]+//; s/[[:space:]]+$//'
|
|
declare -r STRIP_SURROUNDING_QUOTES_SED="s/^[[:space:]\"'[]+//; s/[[:space:]\"'\\]]+\$//; s/^\"\\[+//; s/^\\[+//; s/\\]+$//"
|
|
|
|
sanitize_ref_from_git() {
|
|
sed -E \
|
|
-e "$STRIP_ANSI_SED" \
|
|
-e "$TRIM_SPACE_SED" \
|
|
-e "$STRIP_SURROUNDING_QUOTES_SED" <<<"$1"
|
|
}
|
|
|
|
declare -a VISIBLE_ITEMS=()
|
|
|
|
while IFS= read -r ref; do
|
|
[ -z "$ref" ] && continue
|
|
VISIBLE_ITEMS+=("${BRANCH_COLOR}(branch)${RESET_COLOR} $(sanitize_ref_from_git "$ref")")
|
|
done < <(git for-each-ref --sort=-committerdate --format='%(refname:short)' refs/heads 2>/dev/null)
|
|
|
|
while IFS= read -r ref; do
|
|
[ -z "$ref" ] && continue
|
|
case "$ref" in
|
|
*/*)
|
|
remote_part="$(sanitize_ref_from_git "${ref%%/*}")"
|
|
branch_part="$(sanitize_ref_from_git "${ref#*/}")"
|
|
if [ -z "$branch_part" ] || [ "$branch_part" = "HEAD" ]; then
|
|
continue
|
|
fi
|
|
VISIBLE_ITEMS+=("${REMOTE_COLOR}(remote)${RESET_COLOR} ${ORIGIN_COLOR}${remote_part}${RESET_COLOR}/${branch_part}")
|
|
;;
|
|
*)
|
|
continue
|
|
;;
|
|
esac
|
|
done < <(git for-each-ref --format='%(refname:short)' refs/remotes 2>/dev/null)
|
|
|
|
while IFS= read -r ref; do
|
|
[ -z "$ref" ] && continue
|
|
VISIBLE_ITEMS+=("${TAG_COLOR}(tag)${RESET_COLOR} $(sanitize_ref_from_git "$ref")")
|
|
done < <(git for-each-ref --format='%(refname:short)' refs/tags 2>/dev/null)
|
|
|
|
if [ ${#VISIBLE_ITEMS[@]} -eq 0 ]; then
|
|
echo "No branches or tags found." >&2
|
|
exit 1
|
|
fi
|
|
|
|
declare -r SKIM_PREVIEW_COMMAND_RAW=$(cat <<'EOF'
|
|
raw=$(printf "%s" {} | sed -r 's/\x1B\[[0-9;]*[A-Za-z]//g')
|
|
ref=$(printf "%s" "$raw" | sed -n -E 's/^[[:space:]]*\([^)]*\)[[:space:]]*(.*)$/\1/p')
|
|
git show --color "$ref" 2>/dev/null || git log -n 50 --color "$ref"
|
|
EOF
|
|
)
|
|
|
|
declare -r SKIM_PREVIEW_COMMAND_=${SKIM_PREVIEW_COMMAND_RAW//$'\n'/'; '}
|
|
declare -r SKIM_PREVIEW_COMMAND=${SKIM_PREVIEW_COMMAND_%'; '}
|
|
|
|
CHOSEN=$(printf '%s\n' "${VISIBLE_ITEMS[@]}" | sk --ansi --reverse --info=inline --preview "$SKIM_PREVIEW_COMMAND")
|
|
|
|
[ -z "${CHOSEN:-}" ] && exit 0
|
|
|
|
declare -r CLEAN=$(sed -E \
|
|
-e "$STRIP_ANSI_SED" \
|
|
-e "$TRIM_SPACE_SED" \
|
|
<<<"$CHOSEN"
|
|
)
|
|
|
|
declare -r PARSED=$(sed -n -E "s/^[[:space:]]*\(([^)]*)\)[[:space:]]*(.*)$/\1|\2/p" <<<"$CLEAN") || true
|
|
if [ -z "$PARSED" ]; then
|
|
echo "Failed to parse selection: $CLEAN" >&2
|
|
exit 2
|
|
fi
|
|
|
|
declare -r TYPE=${PARSED%%|*}
|
|
declare -r RAW_REF=${PARSED#*|}
|
|
declare -r REF=$(sanitize_ref_from_git "$RAW_REF")
|
|
|
|
case "$TYPE" in
|
|
tag)
|
|
git switch --detach "$REF"
|
|
;;
|
|
remote)
|
|
SWITCH_NAME="$REF"
|
|
for R in $(git remote 2>/dev/null); do
|
|
if [ "${REF#"${R}"/}" != "$REF" ]; then
|
|
CAND=${REF#"${R}"/}
|
|
if git show-ref --verify --quiet "refs/heads/$CAND"; then
|
|
SWITCH_NAME="$CAND"
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
git switch "$SWITCH_NAME"
|
|
;;
|
|
branch)
|
|
git switch "$REF"
|
|
;;
|
|
*)
|
|
git switch "$REF"
|
|
;;
|
|
esac
|
|
|
|
exit 0
|