#!/bin/sh set -e usage() { cat < [refspec] $0 <--all|--mirror|--tags> [git-push options...] 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) [ "$NO_ACT" ] || die "Destination already exists: '$rsync_dest'";; *) die "rsync returned $? when checking for destination existence.";; esac) || 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 core.bare false 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= arg_count=$# for arg in "$@"; do case "$arg" in -h|--help|--usage) usage; exit;; --all|--mirror|--tags) REFSPEC_UNNEEDED=y;; --no-act) NO_ACT=y;; --) arg_count=$((arg_count - 1)); break;; -*) ;; *) break;; esac arg_count=$((arg_count - 1)) done 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"/