#! /bin/sh DRY_RUN= # Check whether argument is actually a local branch name # (i.e., names something in refs/heads/). Return the # cleaned-up branch name, i.e., HEAD becomes master or # whatever branch you're on. check_branch_name() { local full branch fail fail=true full=$(git rev-parse -q --verify --symbolic-full-name "${1}") && case "$full" in refs/heads/*) branch=${full#refs/heads/} fail=false ;; esac $fail && { echo "'$1': not a branch name or equivalent" 1>&2 return 1 } echo "$branch" return 0 } # Compute gerrit name for given branch, with optional error message # (and nonzero status) if there is none. Supply remote if known # with -r, quiet errors with -q. gerrit_name() { local branch track remote theirref local errmsg print_err local result print_err=true while true; do case "$1" in -q) print_err=false;; -r) remote="$2"; shift;; -r*) remote=${1#-r};; *) break;; esac shift done branch="$1" case "$remote" in "") remote=$(git config --get branch."$1".remote);; esac errmsg="" case "$remote" in "") errmsg="branch '$branch' is not tracking anything";; ".") errmsg="branch '$branch' is tracking a local branch";; *) theirref=$(git config --get branch."$branch".merge) case "$theirref" in "") errmsg="branch '$branch' is not tracking anything";; refs/heads/*) ;; *) errmsg="branch '$branch': don't grok '$theirref'";; esac ;; esac [ -n "$errmsg" ] && { $print_err && echo "$errmsg" 1>&2 return 1 } echo "refs/for/${theirref#refs/heads/}" return 0 } # Push current branch to gerrit name, or print error # and return nonzero if there is no such name review_current() { local branch remote target branch=$(git symbolic-ref -q --short HEAD) || { echo "you seem to be in detached HEAD mode" 1>&2 return 1 } remote=$(git config --get branch."$branch".remote) target=$(gerrit_name -r "$remote" "$branch") || return 1 $DRY_RUN git push "$remote" "$branch:$target" } # Push all local branches to gerrit names (use with care!) # Skips non-viable names quietly. review_all() { local branch remote target git for-each-ref --format='%(refname:short)' refs/heads | while read branch; do remote=$(git config --get branch."$branch".remote) || continue target=$(gerrit_name -r "$remote" -q "$branch") || continue $DRY_RUN git push "$remote" "$branch:$target" done } # Push given name(s) to gerrit names. review_given() { local arg branch remote target # Two passes: first check everything, then actually do it. for arg do branch=$(check_branch_name "$arg") || return 1 gerrit_name "$branch" > /dev/null || return 1 done for arg do branch=$(check_branch_name "$arg") || return 1 remote=$(git config --get branch."$branch".remote) target=$(gerrit_name -r "$remote" "$branch") || return 1 $DRY_RUN git push "$remote" "$branch:$target" done } usage() { cat <<- EOF Usage: $(basename $0) [-h | --help] [--dry-run] [branch ...] With no arguments, push current branch to gerrit for review. With branch name arguments, push given branch(es) for review. Options: -n, --dry-run: show what this would do, without actually doing it -h, --help: show this usage EOF } main() { while true; do case "$1" in -h|--help) usage; exit 0;; -n|--dry-run) DRY_RUN=echo;; --) break;; -*) usage 1>&2; exit 1;; *) break;; esac shift done case $# in 0) review_current;; *) review_given "$@";; esac } main "$@"