#!/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