Skip to content

Instantly share code, notes, and snippets.

@dancysoft
Created February 23, 2021 19:35
Show Gist options
  • Save dancysoft/f81021bda23ed34215104490bbe2ccda to your computer and use it in GitHub Desktop.
Save dancysoft/f81021bda23ed34215104490bbe2ccda to your computer and use it in GitHub Desktop.
Push a single commit to Gerrit
#!/bin/bash
# Pushes a single commit to Gerrit.
# Contributors:
# Ahmon Dancy
# Kevin Layer
# Willem Broekema
set -ue
REMOTE=origin
git update-index -q --refresh
tmp="$(git diff-index --name-only HEAD --)"
if [ "$tmp" ]; then
echo "$(basename $0): uncommitted files: $tmp"
exit 1
fi
function usage {
cat <<EOF
Usage: $0 localref refs/for/BRANCH
For example, to push the second to last commit (one behind HEAD)
to branch 'master':
$0 HEAD~1 refs/for/master
EOF
exit 1
}
[ $# -ne 2 ] && usage
commit=$(git rev-parse -q --verify $1) || (echo "Invalid ref (no such commit): $1"; exit 1)
destref="$2"
tmpbranchPrefix="tmpbranch"
tmpbranch=${tmpbranchPrefix}$$
branch=$(echo $(git symbolic-ref -q HEAD||echo UNKNOWN)|sed 's,refs/heads/,,')
# Two attempts to find orgin branch:
# - either local branch name as-is, like 'v6.3.1',
# - or if branch contains '-', the part in front of it: 'v6.3.1-fix-foo' -> 'v6.3.1'
originBranchAttempt1=${branch}
originBranchAttempt2=$(echo ${originBranchAttempt1} | grep -Po '\K([^-]+)(?=-)' | head -1)
function main {
[[ "$branch" =~ "${tmpbranchPrefix}" ]] && fail_on_tmp_branch_already
if d git branch $tmpbranch origin/${originBranchAttempt1} >/dev/null 2>/dev/null; then
echo "Local branch ${branch} mapped as origin/${originBranchAttempt1}"
tmp_branch_success
else
if [ -n "${originBranchAttempt2}" ] && d git branch -q $tmpbranch origin/$originBranchAttempt2 2>/dev/null; then
echo "Local branch can be mapped (using its prefix): \"${branch}\" -> \"origin/${originBranchAttempt2}\"."
if askYN "Correct? [n] " ; then
tmp_branch_success
else
echo "You choose to cancel."
tmp_branch_fail
fi
else
echo -n "Error pushing to Gerrit: your local branch is called \"${branch}\" which does not exist in origin ("
echo -n "tried: \"origin/${originBranchAttempt1}\""
if [ -n "${originBranchAttempt2}" ]; then
echo -n " and \"origin/${originBranchAttempt2}\""
fi
echo ")"
echo "Tip: rename your branch to: <origin_branch> + \"-\" + <local_extra> e.g. \"v6.3.1-Bug17\" using \"git branch -m <NEWNAME>\""
tmp_branch_fail
fi
fi
}
function fail_on_tmp_branch_already {
cat <<EOF
You are currently on branch "${branch}" which suggests a previous push to Gerrit failed.
To clean this up, first checkout a normal branch:
git checkout <branch>
then delete this branch:
git branch -D ${branch}
EOF
exit 1
}
# This function optionally executes $* as a command.
# If there is a $debug variable and it is non-null, then it only
# prints the command (prefixed with "would do: "), other wise it
# prints the comand *and* executes it. This is handy for scripts
# that want to tell you what's going on, but "set -x" is too verbose.
# Why is it named `d'? Because we don't want to take up many columns
# when it's used. I don't know if any useful UNIX command named `d'
# that is shadowed by this function, so there's that.
function d {
if [ "${debug-}" ]; then
echo "would do: $*"
else
echo "+ $*"
fi
if [ -z "${debug-}" ]; then
"$@"
fi
}
function tmp_branch_success {
d git checkout -q $tmpbranch || unexpected_error
d git cherry-pick $commit || cherrypick_fail
d git push $REMOTE HEAD:$destref || push_gerrit_failed
d git checkout $branch
d git branch -D $tmpbranch
echo "SUCCESS pushing to Gerrit"
}
function tmp_branch_fail {
d git checkout ${branch}
exit 1
}
function unexpected_error {
echo "Unexpected error"
d git checkout ${branch}
exit 1
}
function cherrypick_fail {
cat <<EOF
Cherry picking failed. This commit cannot be sent individually.
You can either KEEP THE STATE of the temporary branch "${tmpbranch}",
or REVERT to your local branch "${branch}".
EOF
if askYN "Keep the state (y) or Revert (n)? [n] "; then
cat <<EOF
When you are done, you can go back to your initial branch by doing:
git reset --hard HEAD
git checkout $branch
git branch -d $tmpbranch
EOF
else
d git reset --hard HEAD
d git checkout ${branch}
d git branch -d $tmpbranch
fi
exit 1
}
function push_gerrit_failed {
cat <<EOF
Pushing to Gerrit failed
You can either KEEP THE STATE of the temporary branch "${tmpbranch}",
or REVERT to your local branch "${branch}"
EOF
if askYN "Keep the state (y) or Revert (n)? [n] "; then
cat <<EOF
When you are done, you can go back to your initial branch by doing:
git checkout $branch
git branch -d $tmpbranch
EOF
else
d git checkout ${branch}
d git branch -D $tmpbranch
fi
exit 1
}
# Ask a y/n question, returns 0 for input 'Y' or 'y' (with newline), 1 for anything else
# First argument (optional): prompt string.
function askYN {
if [ -n "$1" ]; then
echo -n "$1"
fi
read -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
return 0;
else
return 1;
fi
}
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment