Put this in your PATH
. Requires git-hub
Last active
February 5, 2018 13:43
-
-
Save mildred/b85295738b054c504080fed3eb9c9188 to your computer and use it in GitHub Desktop.
Pull requests from Git CLI
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 | |
SUBDIRECTORY_OK=1 | |
USAGE="REMOTE [[BASE] [+]REF[:BRANCH]] - Open a Pull request for BASE..REF" | |
LONG_USAGE="ARGUMENTS | |
BASE | |
defaults to REF^ | |
+ | |
Implies -f | |
REF | |
defaults to HEAD | |
BRANCH | |
name of the branch on the remote (replaces -n BRANCH) | |
OPTIONS | |
-n NAME | |
Name of the branch on the remote | |
-f Force push | |
--dry-run | |
" | |
. "$(git --exec-path)/git-sh-setup" | |
op_name= | |
op_base= | |
op_ref= | |
op_remote=origin | |
op_force= | |
op_force_plus= | |
op_dry_run=false | |
while true; do | |
case "$1" in | |
-h|--help) | |
usage | |
exit 0 | |
;; | |
-n) | |
shift | |
op_name="$1" | |
;; | |
-f) | |
op_force=-f | |
;; | |
--dry-run) | |
op_dry_run=true | |
;; | |
*) | |
break | |
;; | |
esac | |
shift | |
done | |
case $# in | |
1) | |
op_remote="$1" | |
op_ref=HEAD | |
;; | |
2) | |
op_remote="$1" | |
op_ref="$2" | |
;; | |
3) | |
op_remote="$1" | |
op_base="$2" | |
op_ref="$3" | |
;; | |
*) | |
shift 3 | |
echo "Error: extra command-line arguments: $*" >&2 | |
exit 1 | |
esac | |
if [[ -z "$op_base" ]] && [[ "${op_remote%%/*}" != "$op_remote" ]]; then | |
op_base="$op_remote" | |
op_remote="${op_remote%%/*}" | |
fi | |
if [[ "$op_remote" == '-' ]]; then | |
op_remote=origin | |
elif [[ "$op_remote" != origin ]]; then | |
die "Error: remote $op_remote not supported. Pleaase use 'origin'" | |
fi | |
if [[ -z "$op_name" ]] && [[ "${op_ref//:/}" != "$op_ref" ]]; then | |
op_name="${op_ref##*:}" | |
fi | |
if [[ "${op_ref#+}" != "$op_ref" ]]; then | |
op_force_plus=+ | |
fi | |
op_ref="${op_ref%%:*}" | |
op_ref="${op_ref#+}" | |
note-pr(){ | |
git notes show "$2" 2>/dev/null | egrep "^Pull-Request: $1 " | cut -d' ' -f3- | |
} | |
note-branch(){ | |
git notes show "$2" 2>/dev/null | egrep "^Pull-Request-Branch: $1 " | cut -d' ' -f3- | |
} | |
if [[ -z "$op_name" ]]; then | |
op_name="$(note-branch "$op_remote" "$op_ref")" | |
fi | |
if [[ -z "$op_name" ]]; then | |
op_name="$(git log -1 --format=%D "$op_ref" | tr -d , | xargs -n 1 | sed -n "s:^$op_remote/::p" | grep -v '^HEAD$')" | |
if [[ -n "$op_name" ]] && [[ $(wc -w <<<"$op_name") -gt 1 ]]; then | |
echo "Commit $op_name corresponds to multiple remote branches:" | |
echo "$op_name" | xargs -n 1 printf "\t%s/%s\n" "$op_remote" | |
op_name="$(head -n 1 <<<"$op_name")" | |
fi | |
fi | |
if [[ -z "$op_name" ]]; then | |
if name="$(git symbolic-ref --short "$op_ref")"; then | |
op_name="$name" | |
if [[ $op_name == master ]]; then | |
op_name= | |
fi | |
fi | |
fi | |
if [[ -z "$op_name" ]]; then | |
op_name="$op_ref" | |
fi | |
if [[ -z "$op_name" ]]; then | |
die "Error: you need to specify a remote branch name using -n flag" | |
fi | |
pr_url="$(note-pr "$op_remote" "$op_ref")" | |
pr_branch="$(note-branch "$op_remote" "$op_ref")" | |
if [[ -z "$op_base" ]]; then | |
n=1 | |
op_base="$op_ref^" | |
if [[ -n "$pr_url" ]]; then | |
while [[ "$pr_url" = "$(note-pr "$op_remote" "$op_base")" ]] && [[ "$pr_branch" == "$(note-branch "$op_remote" "$op_base")" ]]; do | |
n=$(($n+1)) | |
op_base="$op_ref~$n" | |
done | |
fi | |
echo "Using base commit $op_base" | |
fi | |
log(){ | |
if [ "a$1" = a-always ]; then | |
shift | |
elif $op_dry_run; then | |
echo "- $*" >&2 | |
return 0 | |
fi | |
(set -x; "$@") | |
} | |
if [[ "${op_base#$op_remote/}" != "$op_base" ]]; then | |
base_branch="${op_base#$op_remote/}" | |
else | |
base_branch="$(note-branch "$op_remote" "$op_base")" | |
if [[ -z "$base_branch" ]]; then | |
base_branch="$(git log -1 --format=%D "$op_base" | tr -d , | xargs -n 1 | sed -n "s:^$op_remote/::p" | grep -v '^HEAD$')" | |
if [[ -z "$base_branch" ]]; then | |
die "Error: base commit $op_base corresponds to no remote branch." | |
elif [[ $(wc -w <<<"$base_branch") -gt 1 ]]; then | |
echo "Base commit $op_base corresponds to multiple remote branches:" | |
echo "$base_branch" | xargs -n 1 printf "\t%s/%s\n" "$op_remote" | |
base_branch="$(head -n 1 <<<"$base_branch")" | |
fi | |
fi | |
if [[ -z "$base_branch" ]]; then | |
die "Error: base commit $op_base does not corresponds to a branch in $op_remote. Submit a pull request for that commit first." | |
fi | |
fi | |
echo "Using base branch for pull request $op_remote/$base_branch" | |
if [[ "$(git rev-parse "refs/remotes/$op_remote/$op_name" -- 2>/dev/null)" != "$(git rev-parse "$op_ref")" ]]; then | |
echo | |
if ! log git push $op_force "$op_remote" "$op_force_plus$op_ref:refs/heads/$op_name"; then | |
exit 1 | |
fi | |
fi | |
echo | |
for commit in $(git rev-list "$op_base..$op_ref"); do | |
( git notes show "$commit" 2>/dev/null | egrep -v "^(Pull-Request-Branch): $op_remote " | |
echo "Pull-Request-Branch: $op_remote $op_name" | |
) | ( git notes add -f -F /dev/stdin "$commit" 2>/dev/null ) | |
log git notes show "$commit" | cat | |
done | |
for commit in $(git rev-list "$op_base..$op_ref"); do | |
git log -1 --format=%B "$commit" | |
echo | |
echo | |
done >"$GIT_DIR/PULLREQ_EDITMSG" | |
UPDIR="$(git rev-parse --show-cdup)" | |
if [ -f "${UPDIR}.github/PULL_REQUEST_TEMPLATE.md" ]; then | |
( | |
cat "${UPDIR}.github/PULL_REQUEST_TEMPLATE.md" | |
echo | |
echo | |
) >>"$GIT_DIR/PULLREQ_EDITMSG" | |
fi | |
git log "$op_base..$op_ref" | sed 's/^/## /' >>"$GIT_DIR/PULLREQ_EDITMSG" | |
git_editor "$GIT_DIR/PULLREQ_EDITMSG" | |
sed -i '/^## /d' "$GIT_DIR/PULLREQ_EDITMSG" | |
if ! [[ -s "$GIT_DIR/PULLREQ_EDITMSG" ]]; then | |
die "Abort: no message specified" | |
fi | |
if false && [[ -n "$pr_url" ]]; then | |
echo | |
if ! log hub pull-request -i "$pr_url" -F "$GIT_DIR/PULLREQ_EDITMSG" -b "$base_branch" -h "$op_name"; then | |
exit 1 | |
fi | |
else | |
echo | |
if ! pr_url="$(log hub pull-request -f -F "$GIT_DIR/PULLREQ_EDITMSG" -b "$base_branch" -h "$op_name")"; then | |
echo "$pr_url" | |
exit 1 | |
fi | |
echo "$pr_url" | |
fi | |
echo | |
for commit in $(git rev-list "$op_base..$op_ref"); do | |
( git notes show "$commit" 2>/dev/null | egrep -v "^(Pull-Request): $op_remote " | |
echo "Pull-Request: $op_remote $pr_url" | |
) | ( git notes add -f -F /dev/stdin "$commit" 2>/dev/null ) | |
log git notes show "$commit" | cat | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment