From f864cade7f6dcdb7aef4fbcdb6ac17c008a09e03 Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Tue, 11 Sep 2018 15:28:58 -0400 Subject: git-push-clone --- dot/local/bin/git-push-clone | 148 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100755 dot/local/bin/git-push-clone (limited to 'dot/local/bin/git-push-clone') diff --git a/dot/local/bin/git-push-clone b/dot/local/bin/git-push-clone new file mode 100755 index 0000000..06d045e --- /dev/null +++ b/dot/local/bin/git-push-clone @@ -0,0 +1,148 @@ +#!/bin/sh +set -e + +usage() +{ + cat < [refspec] +EOF +} + +die() { printf "Error: %s\n" "$*" >&2; exit 1; } + +loudly() { (set -x; "$@"); } + +quietly() { "$@" >/dev/null 2>&1; } + +bad_usage() +{ + usage >&2 + exit 1 +} + +check_existence() +{ + (set +e; + quietly rsync "$rsync_dest" + case $? in + 23) return 0;; + 0) die "Destination already exists: '$rsync_dest'";; + *) die "rsync returned $? when checking for destination existence.";; + esac) || [ "$NO_ACT" ] || exit +} + +git_init_push_checkout() +{ + TEMP_DIR=$(mktemp -d) + trap 'rm -rf "$TEMP_DIR"' EXIT + git init --bare "$TEMP_DIR"/.git + loudly git push "$TEMP_DIR" "$@" + + if [ "$CHECKOUT_BRANCH" -a ! "$BARE" ]; then + (cd "$TEMP_DIR"; + git config --unset core.bare + git checkout "$CHECKOUT_BRANCH") + fi +} + +go() +{ + check_existence + git_init_push_checkout "$@" +} + +# What we want to do is push a branch, and then checkout the branch we just +# pushed. Determining the branch to checkout requires parsing the branch out of +# the git push args in $@ + +check_source_ref() +{ + local src="$1" + git rev-parse --verify --quiet "$src^{commit}" >/dev/null +} + +pop=$(cat <<'EOF' +i=$#; +while [ $i -gt 0 ]; do + lastarg=$1; + shift; + i=$((i - 1)) + if [ $i -eq 0 ]; then + break; + else + set -- "$@" "$lastarg"; + fi +done +EOF +) + +parse_target() +{ + case "$1" in + ssh://*) rsync_dest=${1#ssh://} ;; + *:*) rsync_dest=$1 ;; + *) die "Unrecognized git URL: '$1'" ;; + esac + target=$1 + + case "$target" in + *[^/].git) BARE=y ;; + *) BARE= ;; + esac +} + +parse_refspec() +{ + local refspec="$1" + case "$refspec" in + *:*:*) die "expected : but got too many colons" ;; + ?*:?*) + check_source_ref "${1%:*}" || die "invalid source ref: $src" + CHECKOUT_BRANCH=${1#*:} + ;; + *) if check_source_ref "$1"; then CHECKOUT_BRANCH=$1; fi;; + esac +} + +current_branch() { git status -bs|sed -ne 's/^## //p'; } + +REFSPEC_UNNEEDED= +option_count=0 +for arg in "$@"; do + case "$arg" in + -h|--help|--usage) usage; exit;; + --all|--mirror|--tags) REFSPEC_UNNEEDED=y;; + --) option_count=$((option_count+1)); break;; + -*) ;; + *) break;; + esac + option_count=$((option_count+1)) +done +arg_count=$(($# - option_count)) + +case $arg_count in + 1) + eval "$pop" + parse_target "$lastarg" + b=$(current_branch) + parse_refspec "$b:$b" + if ! [ "$REFSPEC_UNNEEDED" ]; then + set -- "$@" "$b" + fi + ;; + 2) + eval "$pop" + refspec=$lastarg + parse_refspec "$refspec" + eval "$pop" + parse_target "$lastarg" + set -- "$@" "$refspec" + ;; + *) bad_usage;; +esac + +go "$@" + +loudly rsync ${NO_ACT:+-n} -zrlp --info=stats1 "$TEMP_DIR"/"${BARE:+.git/}" "$rsync_dest"/ + -- cgit v1.2.3