diff --git a/.devtools/check-pr-size.sh b/.devtools/check-pr-size.sh index f0442f8..049009f 100755 --- a/.devtools/check-pr-size.sh +++ b/.devtools/check-pr-size.sh @@ -6,7 +6,8 @@ # Two callers, one script. ci-pr passes both ends of the pull request. The # `pr-size` pre-commit hook passes nothing, so the base is worked out here: # $PR_BASE, then the branch below this one in a gh stack, then the remote's -# default branch. +# default branch. A default branch that was worked out is read from origin, since +# the local copy only moves on a pull. # # Either way a stacked branch is measured against the layer below it, so each # layer is counted on its own rather than accumulating the whole stack. On a @@ -65,17 +66,36 @@ resolve() { fi } +resolve_remote_first() { + if git rev-parse --verify --quiet "origin/$1" >/dev/null; then + printf 'origin/%s' "$1" + else + resolve "$1" + fi +} + # A base nobody named is a guess, and a wrong guess must not block a push: CI # measures the real pull request. A base somebody named has to resolve. guessed=false +default="" if [[ -z "${base}" ]]; then guessed=true + default="$(default_branch)" + [[ -n "${default}" ]] || default="main" base="$(stack_parent)" - [[ -n "${base}" ]] || base="$(default_branch)" - [[ -n "${base}" ]] || base="main" + [[ -n "${base}" ]] || base="${default}" fi -base_ref="$(resolve "${base}")" +# Nobody commits to the default branch locally, so the local copy is as old as +# the last pull. A branch cut from a fresher origin then counts every file the +# default branch gained in between as its own. origin's copy is what the pull +# request is measured against, so it wins here; a stack's parent and a named +# base stay local-first. +if [[ "${guessed}" == true && "${base}" == "${default}" ]]; then + base_ref="$(resolve_remote_first "${base}")" +else + base_ref="$(resolve "${base}")" +fi head_resolved="$(resolve "${head_ref}")" if [[ -z "${base_ref}" || -z "${head_resolved}" ]]; then diff --git a/.devtools/test-hooks.sh b/.devtools/test-hooks.sh index 94c90f5..3a663ba 100755 --- a/.devtools/test-hooks.sh +++ b/.devtools/test-hooks.sh @@ -85,6 +85,13 @@ expect fail "blocks 21 files, explicit refs" "${size}" --base main --head feat/b expect pass "measures a layer against PR_BASE" env PR_BASE=feat/small "${size}" expect pass "measures a layer against --base" "${size}" --base feat/small +# A stale local default branch: origin has since gained what feat/small added, so +# only the one file from feat/big is this branch's own. +git update-ref refs/remotes/origin/main feat/small +expect pass "prefers origin for a worked-out default branch" "${size}" +expect fail "keeps a named base local" "${size}" --base main +git update-ref -d refs/remotes/origin/main + expect fail "fails when a named base is missing" "${size}" --base no-such-branch git branch --quiet -m main trunk expect pass "skips when a guessed base is missing" "${size}"