2019.09.12 追記
git-extras というのがあった。このレポジトリの中の git-pr
コマンドがまさにやりたかったことだ。皆さんにはぜひこれを使ってほしい。
以下は shell command を function で作るための参考記事として読んでほしい。
概要
以下のコマンド
$ git fetch <remote-name> pull/<ID>/head:<branch-name>
でプルリクエストを簡単に fetch して手元で実行できるらしい。
コマンドがわかりにくいし打ちにくいので、shell function にした。
ついでに、引数でID
やremote-name
を受け取れるようにした。
できたもの
(master) $ git-fetch-pull-request 42
Execute git fetch upstream pull/42/head:pull-request/42...
remote: Counting objects: ---, done.
remote: Compressing objects: 100% (---/---), done.
remote: Total --- (delta ---), reused --- (delta ---)
Unpacking objects: 100% (---/---), done.
From https://---
* [new branch] refs/pull/42/head -> pull-request/42
Checkout to pull-request/42...
Switched to branch 'pull-request/42'
(pull-request/42) $
(pull-request/42) $ git-fetch-pull-request --help
Usage: git-fetch-pull-request [-h,--help] [--upstream UPSTREAM] NUMBER
Fetch pull request to a new branch and checkout to it.
Positional arguments:
NUMBER (int): id for the target pull request.
Optional arguments:
-h, --help: show this message and exit.
--upstream UPSTREAM (str): remote upstream name. default: 'upstream'.
(pull-request/42) $
引数を処理する
やりたいこと
ID
は必須項目。remote-name
はdefaultを用意して、引数によって変えられるようにしたい。branch-name
は固定で良い。
せっかくなので、<function-name> --help
とか打ったら usage が出てほしい。
やったこと
http://dojineko.hateblo.jp/entry/2016/06/30/225113
ここを参考に、こんな感じで実装した。
git-fetch-pull-request() {
local function_name='git-fetch-pull-request'
local help="Usage: ${function_name} [-h,--help] [--upstream UPSTREAM] NUMBER
Fetch pull request to a new branch and checkout to it.
Positional arguments:
NUMBER (int): id for the target pull request.
Optional arguments:
-h, --help: show this message and exit.
--upstream UPSTREAM (str): remote upstream name. default: 'upstream'.
"
_git-fetch-pull-request() {
local number=$1
local upstream=$2
# ...
# メイン処理
# ...
return 0
}
show_help () {
echo $help
}
invalid_arguments () {
local argument=${1:-}
echo "${function_name}: illegal option ${argument}"
show_help
}
parameter_required () {
local argument=${1:-}
echo "${function_name}: argument required for ${argument}"
show_help
}
# Parse optional arguments
local upstream='upstream'
local PARAM=()
for OPT in "$@"; do
case $OPT in
'-h' | '--help' )
show_help
return 0
;;
'--upstream' )
if [[ -z $2 ]] || [[ $2 =~ ^-+ ]]; then
parameter_required '--upstream'
return 1
fi
upstream=$2
shift 2
;;
'--' | '-' )
# Treat following all arguments as positional arguments
shift
PARAM+=( "$@" )
break
;;
-* )
invalid_arguments $1
return 1
;;
* )
if [[ -n $1 ]] && [[ ! $1 =~ ^-+ ]]; then
PARAM+=( "$1" )
shift
fi
;;
esac
done
# Get positional arguments
number=$PARAM; PARAM=( "${PARAM[@]:1}" )
if [[ -z $number ]]; then
parameter_required 'NUMBER'
return 1
fi
# Check remains
if [[ -n "${PARAM[@]}" ]]; then
invalid_arguments $PARAM
return 1
fi
# Main
_git-fetch-pull-request ${number} ${upstream}
}
解説
来たオプションを前から順番に処理して、処理できないものはとりあえずPARAM
に詰めていく。
途中--upstream
のような追加引数のあるオプションが来た場合、その次まで読んで、その分2回SHIFT
する。
最後に残ったPARAM
を処理して終了。メイン処理(_git-fetch-pull-request()
)に入る。
Fetch
・Checkout
やりたいこと
前項で引数をいい感じに処理してID
とremote-name
を得たので、それを使ってgit fetch
する。
git repository 以外で叩いたときや、存在しないプルリクに対して実行したときには、適切にエラーを吐いて止まってくれるようにする。
やったこと
_git-fetch-pull-request() {
local number=$1
local upstream=$2
local branch_name="pull-request/${number}"
if [[ -z `git rev-parse --is-inside-work-tree 2> /dev/null` ]]; then
# Not a git repo
echo "Not a git repository (or any of the parent directories). Stop."
return 1
fi
if [[ -n `git branch --list ${branch_name}` ]]; then
# Branch exists
echo "Branch ${branch_name} already exists. Stop."
return 1
fi
if ! git remote show ${upstream} 2>&1 > /dev/null; then
# Upstream not exists
echo "Remote ${upstream} not exists. Stop."
return 1
fi
# Fetch
local fetching="git fetch upstream pull/${number}/head:${branch_name}"
echo "Execute ${fetching}..."
if ! eval ${fetching}; then
echo "Something went wrong. Stop."
return 1
fi
# Checkout
local checkout="git checkout ${branch_name}"
echo "Checkout to ${branch_name}..."
if ! eval ${checkout}; then
echo "Something went wrong. Stop."
return 1
fi
return 0
}
何が起こるかわからないので、丁寧にエラー処理をする。
Git の repository にいるかどうかの判定は、ここ ( https://stackoverflow.com/questions/2180270/check-if-current-directory-is-a-git-repository ) を参考にした。
せっかくなので、 checkout
もしてもらった。
最終的なコード
以下のようになった。
これを自分の.<shell-name>rc
に書き込んで完成。
git-fetch-pull-request() {
local function_name='git-fetch-pull-request'
local help="Usage: ${function_name} [-h,--help] [--upstream UPSTREAM] NUMBER
Fetch pull request to a new branch and checkout to it.
Positional arguments:
NUMBER (int): id for the target pull request.
Optional arguments:
-h, --help: show this message and exit.
--upstream UPSTREAM (str): remote upstream name. default: 'upstream'.
"
_git-fetch-pull-request() {
local number=$1
local upstream=$2
local branch_name="pull-request/${number}"
if [[ -z `git rev-parse --is-inside-work-tree 2> /dev/null` ]]; then
# Not a git repo
echo "Not a git repository (or any of the parent directories). Stop."
return 1
fi
if [[ -n `git branch --list ${branch_name}` ]]; then
# Branch exists
echo "Branch ${branch_name} already exists. Stop."
return 1
fi
if ! git remote show ${upstream} 2>&1 > /dev/null; then
# Upstream not exists
echo "Remote ${upstream} not exists. Stop."
return 1
fi
# Fetch
local fetching="git fetch upstream pull/${number}/head:${branch_name}"
echo "Execute ${fetching}..."
if ! eval ${fetching}; then
echo "Something went wrong. Stop."
return 1
fi
# Checkout
local checkout="git checkout ${branch_name}"
echo "Checkout to ${branch_name}..."
if ! eval ${checkout}; then
echo "Something went wrong. Stop."
return 1
fi
return 0
}
show_help () {
echo $help
}
invalid_arguments () {
local argument=${1:-}
echo "${function_name}: illegal option ${argument}"
show_help
}
parameter_required () {
local argument=${1:-}
echo "${function_name}: argument required for ${argument}"
show_help
}
# Parse optional arguments
local upstream='upstream'
local PARAM=()
for OPT in "$@"; do
case $OPT in
'-h' | '--help' )
show_help
return 0
;;
'--upstream' )
if [[ -z $2 ]] || [[ $2 =~ ^-+ ]]; then
parameter_required '--upstream'
return 1
fi
upstream=$2
shift 2
;;
'--' | '-' )
# Treat following all arguments as positional arguments
shift
PARAM+=( "$@" )
break
;;
-* )
invalid_arguments $1
return 1
;;
* )
if [[ -n $1 ]] && [[ ! $1 =~ ^-+ ]]; then
PARAM+=( "$1" )
shift
fi
;;
esac
done
# Get positional arguments
number=$PARAM; PARAM=( "${PARAM[@]:1}" )
if [[ -z $number ]]; then
parameter_required 'NUMBER'
return 1
fi
# Check remains
if [[ -n "${PARAM[@]}" ]]; then
invalid_arguments $PARAM
return 1
fi
# Main
_git-fetch-pull-request ${number} ${upstream}
}