Skip to content

Instantly share code, notes, and snippets.

@jakecraige
Last active April 11, 2019 22:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jakecraige/51f9583f1f55cc0ecccfaa11e7a7c6b1 to your computer and use it in GitHub Desktop.
Save jakecraige/51f9583f1f55cc0ecccfaa11e7a7c6b1 to your computer and use it in GitHub Desktop.
Script to allow for some fancy git workflows as described in https://wchargin.github.io/posts/managing-dependent-pull-requests/. I've extended it to also have features to support a style described in https://jg.gg/2018/09/29/stacked-diffs-versus-pull-requests/ via the --pick(-p) option
#!/bin/sh
#
# git-push-to-target: Force push with lease this commit to a branch specified in its
# commit description like `branch: jake/my-branch`:
#
# `git-push-to-target --query` will do a dry run and print the branch name it detects
# `git-push-to-target --pick` will pick this to it's own branch off master and push.
#
# Usage within an interative rebase, add this after each commit that contains the branch directive:
# > exec git push-to-target
#
# Copyright (c) 2017 William Chargin. Released under the MIT license.
# https://wchargin.github.io/posts/managing-dependent-pull-requests/
# Modified by Jake Craige for:
# - Mac sed support
# - Remove branch prefix
# - Support sha argument
# - Support pick option to move to it's own branch
set -eu
DIRECTIVE='branch'
target_branch() {
source="$1"
directive="$( \
git show "$source" --pretty='%B' \
| sed -n 's/^'"${DIRECTIVE}"': \([A-Za-z0-9_./-]*\)$/\1/p' \
; )"
if [ -z "${directive}" ]; then
printf >&2 'error: missing "%s" directive\n' "${DIRECTIVE}"
return 1
fi
if [ "$(printf '%s\n' "${directive}" | wc -l)" -gt 1 ]; then
printf >&2 'error: multiple "%s" directives\n' "${DIRECTIVE}"
return 1
fi
printf '%s\n' "${directive}"
}
main() {
arg="${1:-none}"
case $arg in
-q|--query)
branch="$(target_branch "${2:-HEAD}")"
echo $branch
return
;;
-p|--pick)
source="${2:-HEAD}"
head_sha="${2:-$(git rev-parse --verify HEAD)}"
branch="$(target_branch "$source")"
set -x
git fetch
# create new branch from master, expect it to not exist or fail. tmp prefix needed to not
# accidentally checkout an existing remote branch with same name.
git checkout -b "tmp/$branch" origin/master
# cherry-pick commit over and push the branch
git cherry-pick "$head_sha"
git push --force-with-lease origin HEAD:refs/heads/"$branch"
# Clean up. Go back to where we came from. If specific commit sha given, assume no rebase and
# go back to last branch. If not, we assume rebase and go back to the sha.
# Then delete tmp branch from local git
if [ -n "${2:-}" ]; then # non-empty value
git checkout -
else
git checkout "$head_sha"
fi
git branch -D "tmp/$branch"
;;
*)
source="${1:-HEAD}"
branch="$(target_branch "$source")"
git push --force-with-lease origin "$source":refs/heads/"$branch"
;;
esac
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment