scripts/detect-base-branch.sh
scripts/detect-base-branch.shBrowse 2 files
949 tokens
3,169 bytes
Token encoding: o200k_base
Snapshot bf83086
← Back to SKILL.md
1#!/usr/bin/env bash2# Pick the remote branch the current branch most likely forked from.3# Supports telescoped/stacked PRs by scanning all origin/* ancestors.4set -euo pipefail5 6current_branch=$(git branch --show-current)7head_tip=$(git rev-parse HEAD)8best=""9min_commits=99999910best_is_feature=011 12is_trunk() {13 case "$1" in14 main | next) return 0 ;;15 *) return 1 ;;16 esac17}18 19normalize_branch_name() {20 local name=$121 name=${name#origin/}22 name=${name#remotes/origin/}23 echo "${name}"24}25 26branch_exists_on_origin() {27 git rev-parse --verify "origin/$1" >/dev/null 2>&128}29 30is_valid_base() {31 local branch=$132 local ref="origin/${branch}"33 34 [ "${branch}" != "${current_branch}" ] || return 135 branch_exists_on_origin "${branch}" || return 136 37 local branch_tip38 branch_tip=$(git rev-parse "${ref}")39 [ "${branch_tip}" != "${head_tip}" ] || return 140 git merge-base --is-ancestor "${ref}" HEAD 2>/dev/null41}42 43commit_count_since() {44 git rev-list --count "origin/$1..HEAD"45}46 47consider() {48 local branch=$149 local commit_count=$250 51 if ! is_valid_base "${branch}"; then52 return53 fi54 55 local is_feature=056 is_trunk "${branch}" || is_feature=157 58 if [ -z "${best}" ]; then59 best=${branch}60 min_commits=${commit_count}61 best_is_feature=${is_feature}62 return63 fi64 65 if [ "${commit_count}" -lt "${min_commits}" ]; then66 best=${branch}67 min_commits=${commit_count}68 best_is_feature=${is_feature}69 return70 fi71 72 if [ "${commit_count}" -gt "${min_commits}" ]; then73 return74 fi75 76 # Tie: prefer telescoped parent over trunk, then next over main.77 if [ "${best_is_feature}" -eq 0 ] && [ "${is_feature}" -eq 1 ]; then78 best=${branch}79 best_is_feature=${is_feature}80 return81 fi82 83 if [ "${best_is_feature}" -eq 1 ] && [ "${is_feature}" -eq 0 ]; then84 return85 fi86 87 if [ "${branch}" = "next" ]; then88 best=${branch}89 best_is_feature=${is_feature}90 fi91}92 93try_explicit_base() {94 local branch=$195 branch=$(normalize_branch_name "${branch}")96 consider "${branch}" "$(commit_count_since "${branch}")"97}98 99# 1. Tracked upstream (set when branching with -u or --track)100if upstream=$(git rev-parse --abbrev-ref '@{upstream}' 2>/dev/null); then101 try_explicit_base "${upstream}"102fi103 104# 2. Reflog: most recent branch we checked out from105if [ -z "${best}" ]; then106 while IFS= read -r line; do107 if [[ ${line} =~ checkout:\ moving\ from\ (.+)\ to\ ${current_branch} ]]; then108 try_explicit_base "${BASH_REMATCH[1]}"109 break110 fi111 if [[ ${line} =~ branch:\ Created\ from\ (.+) ]]; then112 try_explicit_base "${BASH_REMATCH[1]}"113 break114 fi115 done < <(git reflog show --format='%gs' "${current_branch}" 2>/dev/null || true)116fi117 118# 3. Scan all origin/* branches — closest strict ancestor wins119while IFS= read -r ref; do120 branch=${ref#origin/}121 122 if [ "${branch}" = "HEAD" ] || [ "${branch}" = "${current_branch}" ]; then123 continue124 fi125 126 if ! is_valid_base "${branch}"; then127 continue128 fi129 130 consider "${branch}" "$(commit_count_since "${branch}")"131done < <(git for-each-ref --format='%(refname:short)' refs/remotes/origin/)132 133if [ -z "${best}" ]; then134 best="next"135fi136 137echo "${best}"138 