#!/usr/bin/env bash # resolve-go-lint-base prints the local remote-tracking ref used by the # pre-commit Go lint hook. Fork worktrees conventionally retain the canonical # repository as `upstream`; do not silently fall back when the base is unclear. set -euo pipefail canonical_repository='kdlbs/kandev' comparison_base=false die() { echo "Go lint base resolution failed: $*" >&2 exit 1 } case "${1:-}" in '') ;; --comparison-base) comparison_base=true; shift ;; *) die "unknown argument: $1" ;; esac [ "$#" -eq 0 ] || die "unexpected arguments: $*" normalize_remote_ref() { local ref=$1 remote_branch remote branch normalized case "$ref" in refs/remotes/*) remote_branch=${ref#refs/remotes/} ;; refs/*) die "base ref must be a remote-tracking ref: $ref" ;; */*) remote_branch=$ref ;; *) die "base ref must be a remote-tracking ref: $ref" ;; esac remote=${remote_branch%%/*} branch=${remote_branch#*/} [ -n "$remote" ] && [ "$branch" != "$remote_branch" ] && [ -n "$branch" ] || die "base ref must name a remote and branch: $ref" normalized="refs/remotes/$remote/$branch" git check-ref-format "$normalized" >/dev/null 2>&1 || die "base ref does not have a safe ref format: $ref" printf '%s\n' "$normalized" } is_canonical_remote() { local url=$1 path case "$url" in https://github.com/*) path=${url#https://github.com/} ;; http://github.com/*) path=${url#http://github.com/} ;; ssh://git@github.com/*) path=${url#ssh://git@github.com/} ;; git+ssh://git@github.com/*) path=${url#git+ssh://git@github.com/} ;; git://github.com/*) path=${url#git://github.com/} ;; git@github.com:*) path=${url#git@github.com:} ;; *) return 1 ;; esac path=${path%.git} path=${path%/} [ "$(printf '%s' "$path" | tr '[:upper:]' '[:lower:]')" = "$canonical_repository" ] } configured_refs=() while IFS= read -r ref; do [ -n "$ref" ] && configured_refs+=("$ref") done < <(git config --get-all kandev.lintBaseRef || true) if [ "${#configured_refs[@]}" -gt 1 ]; then die "ambiguous kandev.lintBaseRef values: ${configured_refs[*]}" fi if [ "${#configured_refs[@]}" -eq 1 ]; then base_ref=$(normalize_remote_ref "${configured_refs[0]}") else origin_head=$(git symbolic-ref --quiet refs/remotes/origin/HEAD || true) [ -n "$origin_head" ] || die "origin's default branch is unavailable; set kandev.lintBaseRef to a local remote-tracking ref" case "$origin_head" in refs/remotes/origin/*) base_branch=${origin_head#refs/remotes/origin/} ;; *) die "origin's default ref is invalid: $origin_head" ;; esac [ -n "$base_branch" ] || die "origin's default ref is invalid: $origin_head" if upstream_url=$(git remote get-url upstream 2>/dev/null); then is_canonical_remote "$upstream_url" || die "upstream remote is not the canonical $canonical_repository repository" base_ref=$(normalize_remote_ref "refs/remotes/upstream/$base_branch") git rev-parse --verify --quiet "$base_ref^{commit}" >/dev/null || die "canonical upstream ref does not resolve locally: $base_ref" else base_ref=$(normalize_remote_ref "$origin_head") fi fi git rev-parse --verify --quiet "$base_ref^{commit}" >/dev/null || die "base ref does not resolve locally: $base_ref" if [ "$comparison_base" = true ]; then merge_head=$(git rev-parse --verify MERGE_HEAD 2>/dev/null || true) base_tip=$(git rev-parse --verify --quiet "$base_ref^{commit}") if [ -n "$merge_head" ] && [ "$merge_head" = "$base_tip" ]; then printf '%s\n' "$merge_head" else merge_base=$(git merge-base HEAD "$base_ref" 2>/dev/null) || die "base ref has no merge base with HEAD: $base_ref" printf '%s\n' "$merge_base" fi else printf '%s\n' "$base_ref" fi