├── README.md └── git-merge-pr /README.md: -------------------------------------------------------------------------------- 1 | ## git-merge-pr: apply GitHub pull requests from command-line 2 | 3 | ## Usage 4 | 5 | git merge-pr [PRNUM][@REMOTE] [GIT-AM FLAGS...] 6 | 7 | `git merge-pr` will fetch the patch corresponding to pull request 8 | number PRNUM of REMOTE repository (defaults to your upstream) and 9 | applies it on top of the current tree, rewriting the commit message of 10 | the newest patch to close the pull request upon push (can be disabled 11 | by setting `merge-pr.autoclose` to false). 12 | 13 | Without PRNUM, all open pull requests of REMOTE will be listed. 14 | 15 | Useful `git am` flags: 16 | 17 | * `--signoff` 18 | * `--3way` 19 | * `--interactive` 20 | 21 | ## Why rebasing pull requests is preferable to merging them 22 | 23 | * Keeps a linear history without merge bubbles. 24 | * Properly keeps author and committer name as well as author and commit date. 25 | * Conflicts can easily be dealt with in an ad-hoc fashion. 26 | * Interactive mode allows for on-the-fly cherry picking. 27 | 28 | ## Requirements 29 | 30 | * Git 2.8.0 or newer 31 | * wget 32 | * jq (only for listing pull requests) 33 | 34 | ## Copyright 35 | 36 | git-merge-pr is in the public domain. 37 | 38 | To the extent possible under law, 39 | Leah Neukirchen 40 | has waived all copyright and related or 41 | neighboring rights to this work. 42 | 43 | http://creativecommons.org/publicdomain/zero/1.0/ 44 | -------------------------------------------------------------------------------- /git-merge-pr: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # git merge-pr [PRNUM][@REMOTE] [GIT-AM FLAGS...] - list or apply GitHub pull request from command-line 3 | set -e 4 | 5 | PR=$1 6 | REMOTE= 7 | case "$PR" in 8 | *@*) 9 | REMOTE=${PR#*@} 10 | PR=${PR%%@*} 11 | esac 12 | 13 | URL=$(git ls-remote --get-url $REMOTE) 14 | 15 | PROJECT=${URL%.git} 16 | PROJECT=${PROJECT##*:} 17 | PROJECT=${PROJECT#//github.com/} 18 | 19 | if command -v wget >/dev/null; then 20 | API_CMD="wget -q -O- --header" 21 | PATCH_CMD="wget -nv -O" 22 | elif command -v curl >/dev/null; then 23 | API_CMD="curl -s -H" 24 | PATCH_CMD="curl -Lo" 25 | else 26 | echo "Neither wget or curl could be found in PATH" 27 | exit 1 28 | fi 29 | 30 | if [ -z "$PR" ]; then 31 | $API_CMD 'Accept: application/json' \ 32 | "https://api.github.com/repos/${PROJECT}/pulls" | 33 | jq -r 'if length > 0 34 | then reverse[] | "\(.number) <\(.user.login)> \(.title)" 35 | else "No open pull requests." end' 36 | exit $? 37 | else 38 | shift 39 | fi 40 | 41 | PATCH="$(mktemp)" 42 | trap "rm -f $PATCH" INT TERM EXIT 43 | $PATCH_CMD "$PATCH" https://github.com/$PROJECT/pull/"$PR".patch 44 | WHITESPACE="$(git config --get merge-pr.whitespace || true)" 45 | git am --keep-cr "--whitespace=${WHITESPACE:-warn}" "$@" "$PATCH" 46 | 47 | if [ "$(git config --bool --get merge-pr.autoclose)" = false ]; then 48 | exit 0 49 | fi 50 | 51 | # Rewrite last commit message to close GitHub issue. 52 | GIT_EDITOR="git -c trailer.closes.ifExists=replace interpret-trailers \ 53 | --trailer 'Closes: #$PR [via git-merge-pr]' --in-place" \ 54 | git commit --quiet --amend 55 | --------------------------------------------------------------------------------