Created
February 23, 2021 19:35
-
-
Save dancysoft/f81021bda23ed34215104490bbe2ccda to your computer and use it in GitHub Desktop.
Push a single commit to Gerrit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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