penpot/tools/detect-target-branch
2026-07-09 13:12:41 +02:00

75 lines
2.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# detect-target-branch
#
# Determines the target branch for a PR of the current branch.
#
# Uses git name-rev to find the nearest ref (branch/tag) that is an ancestor of
# HEAD, falling back to a graph walk if name-rev returns the current branch.
#
# Pure local — no remote, no SSH, no network. Works even when the current
# branch shares its tip commit with the target (not yet diverged).
#
# Usage:
# tools/detect-target-branch # print branch name
# tools/detect-target-branch --verbose # print "branch~N"
#
# Exit status:
# 0 — target found
# 1 — no target found (orphan commit, no other branches)
verbose=false
for arg do
case "$arg" in
--verbose|-v) verbose=true ;;
--name-only) ;;
--help|-h)
sed -n '/^#/,/^$/p' "$0" | sed 's/^# //; s/^#$//'
exit 0
;;
*)
echo "Unknown option: $arg" >&2
echo "Usage: $(basename "$0") [--verbose|-v] [--name-only] [--help|-h]" >&2
exit 1
;;
esac
done
current=$(git rev-parse --abbrev-ref HEAD)
# Use name-rev to find the nearest ref — it has smart tie-breaking
raw=$(git name-rev --name-only HEAD 2>/dev/null)
target=${raw%%~[0-9]*} # strip ~N distance suffix
# If name-rev returned the current branch itself, walk backwards
if [ "$target" = "$current" ] || [ -z "$target" ]; then
target=$(git rev-list HEAD | while read commit; do
branch=$(git branch --points-at="$commit" --format='%(refname:short)' 2>/dev/null \
| grep -v "^$current$" \
| head -1 || true)
if [ -n "$branch" ]; then
echo "$branch"
break
fi
done || true)
if [ -z "$target" ]; then
echo "error: unable to determine target branch" >&2
exit 1
fi
fi
if [ "$verbose" = true ]; then
target_commit=$(git rev-parse "$target" 2>/dev/null || true)
if [ -n "$target_commit" ]; then
distance=$(git rev-list --count HEAD ^"$target_commit" 2>/dev/null || echo "0")
echo "${target}~${distance}"
else
echo "${target}~?"
fi
else
echo "$target"
fi