Skip to content

Instantly share code, notes, and snippets.

@canton7
Last active January 3, 2016 09:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save canton7/8440936 to your computer and use it in GitHub Desktop.
Save canton7/8440936 to your computer and use it in GitHub Desktop.
#!/bin/bash
SVN="svn/trunk"
MASTER="master"
die() { echo "$@" >&2; exit 1; }
ensure_clean()
{
if ! git diff --no-ext-diff --ignore-submodules --quiet --exit-code; then
die ">> FATAL: Working tree contains unstaged changes. Aborting"
elif ! git diff-index --cached --quiet --ignore-submodules HEAD --; then
die ">> FATA: Index contains uncommitted changes. Aborting"
fi
}
ensure_review_branch()
{
parse_review_branch
[[ -z "$REVIEW_BRANCH" ]] && die ">> FATAL: Current branch must be in the form 'review/xx'"
}
parse_review_branch()
{
REVIEW_BRANCH=$(git symbolic-ref HEAD | grep -E 'refs/heads/review/[[:digit:]]' | cut -d '/' -f 3-)
REVIEW_ID=$(echo "$REVIEW_BRANCH" | cut -d '/' -f 2)
}
label_branch()
{
local review_id="$1"
(
cd "$(git rev-parse --show-toplevel)";
git filter-branch -f --msg-filter "sed -e \"/Review-ID:/d\" | sed -e :a -e \"/^\\n*$/N;/\\n$/ba\" && echo \"\\nReview-ID: $review_id\"" $SVN..HEAD
)
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
}
branch_name_for_hash()
{
local hash="$@"
echo $(git for-each-ref --format="%(objectname)%09%(refname:short)" refs/heads/ | grep "$hash" | cut -f 2 | head -n 1)
}
find_revision_range()
{
# If they passed --revision-range, we don't add our own
if [[ "$@" == *--revision-range=* ]]; then
REVISION_RANGE=""
return
fi
local head=$(git rev-parse HEAD)
local latest_review=$(git for-each-ref --format="%(objectname)" refs/heads/review/* | { \
while read commit; do
# Is it a direct ancestor, but not the current commit?
if [[ "$commit" != "$head" && "$(git merge-base HEAD "$commit")" = "$commit" ]]; then
# Is it newer than the previously-stored revision branch?
if [[ -z "$latest_review" || "$(git merge-base "$commit" "$latest_review")" = "$latest_review" ]]; then
latest_review="$commit"
fi
fi
done
echo $latest_review
})
if [[ -z "$latest_review" ]]; then
REVISION_RANGE=""
else
echo ">> Found revision range: $(branch_name_for_hash "$latest_review")"
REVISION_RANGE="--revision-range=$latest_review"
fi
}
cmd_label()
{
ensure_clean
# They can either pass the review ID as the first arg, or be on a review branch
local review_id="$1"
[[ -z "$review_id" ]] && parse_review_branch && review_id="$REVIEW_ID"
[[ -z "$review_id" ]] && die ">> FATAL: Either be on a review branch, or pass in the review ID as an argument"
label_branch $review_id
}
cmd_submit()
{
ensure_clean
parse_review_branch
find_revision_range "$@"
# If it's not an update, and they're on a review branch, it's probably a mistake
[[ -n "$REVIEW_ID" ]] && die ">> FATAL: It looks like you're on a review branch. This is probably a mistake?"
local regex="Review request #([[:digit:]]+) posted"
local full_output=$(post-review "$REVISION_RANGE" "$@")
echo "$full_output"
local filtered_output=$(echo "$full_output" | grep -E "$regex")
[[ "$filtered_output" =~ $regex ]] || die ">> FATAL: Could not find review ID in output"
local review_id=${BASH_REMATCH[1]}
local review_branch="review/$review_id"
git show-ref --verify --quiet refs/heads/$review_branch
[[ $? -eq 0 ]] && die ">> Review branch $review_branch already exists, so not touching it"
git checkout -b $review_branch
}
cmd_update()
{
ensure_clean
ensure_review_branch
find_revision_range "$@"
post-review -r "$REVIEW_ID" --diff-only --no-guess-fields --no-guess-summary --no-guess-description "$REVISION_RANGE" "$@"
}
cmd_commit()
{
ensure_clean
ensure_review_branch
# Make sure we're up to date
git svn fetch
[[ "$(git merge-base $SVN HEAD)" != "$(git rev-parse $SVN)" ]] && die ">> FATAL: Rebase onto $SVN"
git checkout $MASTER
git merge --ff-only $REVIEW_BRANCH || die ">> FATAL: Failed to fast-forward master to $REVIEW_BRANCH"
git branch -d $REVIEW_BRANCH
label_branch $REVIEW_ID
git svn dcommit
rbt close --close-type=submitted $REVIEW_ID
}
cmd_apply()
{
ensure_clean
local review_id="$1"
[[ -z "$review_id" ]] && parse_review_branch && review_id=$REVIEW_ID
[[ -z "$review_id" ]] && die ">> FATAL: Either be on a review branch, or pass in the review ID as an argument"
local review_branch="review/$review_id"
if [[ -n "$REVIEW_BRANCH" ]]; then
echo ">> Branch $REVIEW_BRANCH already exists. Resetting to $SVN"
[[ "$(git symbolic-ref HEAD)" != "refs/heads/$REVIEW_BRANCH" ]] && git checkout $REVIEW_BRANCH
git reset --hard $SVN
else
local review_branch="review/$review_id"
echo ">> Creating $review_branch, based on $SVN"
git checkout -b $review_branch $SVN
fi
rbt patch --px=0 $review_id
}
usage()
{
echo "Usage: $0 [submit|label|update|commit|apply] [review-id] [args]"
echo
echo "The commands label,update,apply will grab the review ID from the current branch"
echo "name if avaible, or from the first argument (e.g. 'git review label 23')."
echo
echo "The commit command requires that you're on a review/xx branch, and submit doesn't"
echo "care what branch you're on."
echo
echo "The submit and update commands will automatically determine the --revision-range"
echo "argument (the latest review/xx branch that's an ancestor of HEAD) if this argument"
echo "isn't given"
echo
echo "Subcommands:"
echo " submit: Runs 'post-review $@' on the current branch, and retrieves the ID,"
echo " then creates a branch review/xx at the current point."
echo " label: For each commit between HEAD and $SVN, adds a Review-ID: xx line."
echo " update: Updates the review with a new patch, derived from the current HEAD."
echo " commit: Requires you to be on a review/xx branch. Merges the branch into master,"
echo " and dcommits, then closes the related review."
echo " apply: If you're on a review/xx branch, resets it to $SVN and applies the latest"
echo " patch from ReviewBoard. If you're not, creates such a branch based on $SVN"
echo " and applies the patch. Useful to checking someone else's patch."
}
SUBCOMMAND="$1"
shift
case "$SUBCOMMAND" in
submit)
cmd_submit "$@"
;;
label)
cmd_label "$@"
;;
update)
cmd_update "$@"
;;
commit)
cmd_commit "$@"
;;
apply)
cmd_apply "$@"
;;
*)
usage
exit 1
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment