Last active
January 5, 2023 22:38
-
-
Save philpennock/04c22c0e2bc74f87fb651b61c39eef6f to your computer and use it in GitHub Desktop.
Revisions
-
philpennock revised this gist
Apr 18, 2022 . 1 changed file with 84 additions and 17 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -36,21 +36,17 @@ get_version_information() { cd_to_toplevel get_version_information # Should this respect core.hooksPath ? HOOKS_DIR="$(git rev-parse --git-dir)/hooks" readonly HOOKS_DIR # Remove all GIT_PDP_* variables, don't inherit any. # Provide a cleaner env for our hooks. unset "${!GIT_PDP_@}" should_force=true should_set_upstream=0 should_find_upstream=0 is_trunk=false disable_force_because_trunk() { @@ -75,37 +71,66 @@ if [[ -z "${FORCE_TRUNK:-}" ]]; then fi fi if ! upstream="$(git for-each-ref --format='%(upstream:remotename)' "$full_branch")" || [[ -z "$upstream" ]] then stderr "missing a current upstream" should_set_upstream=1 should_find_upstream=1 if "$is_trunk"; then die "this is a trunk branch, not hunting for an upstream" fi fi bad_upstream='' if [[ -n "$upstream" ]]; then pu="$(git config --local --get "remote.$upstream.pushurl" || true )" need_new_up=0 case "${pu,,}" in (- | -- | nonexistant | non-existant | nonexistent | non-existent | no | none | off | 0 | disable | disabled | readonly | read-only | ro) need_new_up=1 ;; esac if (( need_new_up )); then stderr "upstream ${upstream@Q} unacceptable for push [${pu@Q}]" bad_upstream="$upstream" upstream='' # Do *NOT* _set_ upstream, just find it. # Upstream should be left on the repo which is read-only to us should_set_upstream=0 should_find_upstream=1 fi unset pu need_new_up fi if (( should_find_upstream )) && [[ -z "$upstream" ]]; then if candidate="$(git config --local --get remotes.push)"; then stderr "picking upstream ${candidate@Q} because is remotes.push" upstream="$candidate" fi fi if (( should_find_upstream )) && [[ -z "$upstream" ]]; then for B in main master; do candidate="$(git for-each-ref --format='%(upstream:remotename)' "refs/heads/$B")" [ "$candidate" != "" ] || continue stderr "picking upstream ${candidate@Q} as per branch ${B@Q}" upstream="$candidate" break done fi if [[ -n "$bad_upstream" ]] && [[ "$upstream" == "$bad_upstream" ]]; then stderr "oops, re-picked ${upstream@Q} after rejecting it" die "if repo declares our upstream as invalid to push to, repo should set 'remotes.push' too" fi # This is why we went to bash: when 2 conditionals & 4 patterns, was willing to stick to sh. # With three conditionals and 8 invocation patterns, time to use an array. declare -a subcmd=('push') if (( should_set_upstream )); then subcmd+=( -u "$upstream" "$branch" ) elif (( should_find_upstream )); then subcmd+=( "$upstream" "$branch" ) fi if $should_force; then # this checks that our ref for the remote matches @@ -119,4 +144,46 @@ if $should_force; then fi fi # Side-effect: sets cleanliness information set_hook_expensive() { if [[ -n "${GIT_PDP_DIRTY:-}" ]] || [[ -n "${GIT_PDP_CLEAN:-}" ]]; then return 0 fi local status status="$(git status --porcelain=v2)" if [[ "$status" == "" ]]; then declare -gxr GIT_PDP_CLEAN=true else declare -gxr GIT_PDP_DIRTY=true fi } # For the hooks run_checker() { local check_cmd="${1:?need a command to run}" declare -xr GIT_PDP_BRANCH="$branch" declare -xr GIT_PDP_UPSTREAM="$upstream" if (( should_set_upstream )); then declare -xr GIT_PDP_SET_UPSTREAM=true else declare -xr GIT_PDP_SET_UPSTREAM=false fi declare -xr GIT_PDP_FORCE_PUSH=$should_force set_hook_expensive "$check_cmd" } # TBD: should I set up anything like an easy way to get exactly the tree being pushed? # At this time, the tree visible to the hook is the current working tree, which might be bogus. # Or perhaps missing, if bare. found_check=false for checker in "$HOOKS_DIR/pdp.prepush"; do if [ -x "$checker" ]; then found_check=true stderr "invoking checker: $checker" run_checker "$checker" fi done if $found_check; then stderr "checkers complete"; fi git "${subcmd[@]}" "$@" -
philpennock created this gist
Oct 5, 2021 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,122 @@ #!/usr/bin/env bash set -euo pipefail # # git pb: push branch # implicit: force with lease, set upstream if needed, etc # # Path coercion for platforms where git might be in multiple places and I can't # mess with the ordering "normally" but want to explicitly pick up newer git # here. [ -d /opt/local/bin ] && PATH="/opt/local/bin:$PATH" [ -d /opt/git/bin ] && PATH="/opt/git/bin:$PATH" # shellcheck disable=SC2034 SUBDIRECTORY_OK=true set +eu # shellcheck source=/dev/null . "$(git --exec-path)/git-sh-setup" set -eu # I like prefices in front of messages, so we also stomp of the normal git die() progname="$(basename "$0" .sh)" stderr() { printf >&2 '%s: %s\n' "$progname" "$*"; } die() { stderr "$@"; exit 1; } get_version_information() { local v="$( git --version )" v="${v#git version }" v="${v%% *}" git_version_full="$v" git_version_major="${v%%.*}" v="${v#*.}" git_version_minor="${v%%.*}" } cd_to_toplevel get_version_information HOOKS_DIR="$(git rev-parse --git-dir)/hooks" readonly HOOKS_DIR found_check=false for checker in "$HOOKS_DIR/pdp.prepush"; do if [ -x "$checker" ]; then found_check=true stderr "invoking checker: $checker" "$checker" fi done if $found_check; then stderr "checkers complete"; fi should_force=true should_set_upstream=false is_trunk=false disable_force_because_trunk() { stderr "force disabled for branch: ${1:?need a branch name}" should_force=false is_trunk=true } full_branch="$(git symbolic-ref HEAD)" || die "not on a branch, not pushing" branch="${full_branch#refs/heads/}" if [[ -z "${FORCE_TRUNK:-}" ]]; then case "$branch" in main | master | dev | v? ) disable_force_because_trunk "$branch" ;; esac if $should_force && protected_s="$(git config --local --get pdp.protect-branches)"; then for b in $protected_s; do [[ "$branch" == "$b" ]] || continue disable_force_because_trunk "$branch" break done; unset b fi fi if ! upstream="$(git for-each-ref --format='%(upstream:remotename)' "$full_branch")" || [ -z "$upstream" ] then stderr "missing a current upstream" should_set_upstream=true if "$is_trunk"; then die "this is a trunk branch, not hunting for an upstream" fi fi if $should_set_upstream && [[ -z "$upstream" ]]; then if candidate="$(git config --local --get remotes.push)"; then stderr "picking upstream '${candidate}' because is remotes.push" upstream="$candidate" fi fi if $should_set_upstream && [[ -z "$upstream" ]]; then for B in main master; do candidate="$(git for-each-ref --format='%(upstream:remotename)' "refs/heads/$B")" [ "$candidate" != "" ] || continue stderr "picking upstream '${candidate}' as per branch '${B}'" upstream="$candidate" break done fi # This is why we went to bash: when 2 conditionals & 4 patterns, was willing to stick to sh. # With three conditionals and 8 invocation patterns, time to use an array. declare -a subcmd=('push') if $should_set_upstream; then subcmd+=( -u "$upstream" "$branch" ) fi if $should_force; then # this checks that our ref for the remote matches subcmd+=( --force-with-lease ) if [[ $git_version_major -gt 2 ]] || [[ $git_version_major -eq 2 && $git_version_minor -ge 30 ]]; then # this guards against something else doing `git remote update` # and having matched our ref for the remote, by requiring that # the ref for the remote be reachable from a reflog entry for # the current branch. subcmd+=( --force-if-includes ) fi fi git "${subcmd[@]}" "$@"