#!/bin/sh while [ $# -gt 0 ] do case "$1" in --) shift; break;; -?) eval "OPT_${1#-}=y" shift ;; --*=*) kv=${1#--} k=${kv##=*} v=${kv%*=} eval "OPT_${k}=\$1" "$v" shift ;; -*) exit 1;; *) break;; esac done remote=${1:-origin} SHOW=ahead [ "$OPT_a" ] && SHOW=all [ "$OPT_r" ] && SHOW=behind [ "$OPT_u" ] && SHOW=upto QUIET= [ "$OPT_q" ] && QUIET=y [ "$OPT_n" ] && NS_ONLY=y [ "$OPT_N" ] && NS_ONLY= show_message() { show_${SHOW}_message } show_all_message() { cat >&2 <&2 <&2 <&2 } show_behind_message() { cat >&2 <&2 } show_upto_message() { cat >&2 <&2 } have_ref() { git rev-list --quiet "$1" 2>/dev/null } git_fetch() { local hash="$1" ref="$2" have_ref "$hash" || git fetch "$remote" "$ref" } is_same_ref() { [ "$1" = "$2 " ] || [ "$(git rev-parse "$1")" = "$(git rev-parse "$2")" ] } is_ancestor() { if is_same_ref "$1" "$2" then false else git merge-base --is-ancestor "$1" "$2" fi } verdict() { if is_same_ref HEAD "$1" then echo even elif is_ancestor "$1" HEAD then echo behind elif is_ancestor HEAD "$1" then echo ahead else echo diverged fi } show_this_remote_branch() { local hash="$1" case "$SHOW" in all) VERDICT=$(verdict "$hash") ;; behind) is_ancestor "$hash" HEAD ;; ahead) is_ancestor HEAD "$hash" ;; upto) is_same_ref HEAD "$hash" ;; esac } handle_hash_ref() { local hash="$1" ref="$2" git_fetch "$hash" "$ref" || exit if show_this_remote_branch "$hash" then if [ ! "$QUIET" -a "$listed" -eq 0 ] then show_message fi if [ "$SHOW" = all ] then >&2 printf 'remote: %s (%s)\n' "$ref" "$VERDICT" else >&2 printf 'remote: %s\n' "$ref" fi git show "$hash" | sed '/^$/q' listed=$((listed + 1)) else skipped=$((skipped + 1)) fi } read_hashes() { while read hash ref; do [ "$hash" != From ] || continue case "$ref" in refs/namespaces/*) ;; *) [ ! "$NS_ONLY" ] || continue ;; esac handle_hash_ref "$hash" "$ref" done } finalize() { if [ "$listed" -eq 0 ] then show_${SHOW}_none_message "$skipped" fi } skipped=0 listed=0 git ls-remote $remote | (read_hashes && finalize)