rcmdnk's blog

Nendoroid Ghost Shell GitS Tachikoma PVC Figure GSC フィギュア 人形 おもちゃ (並行輸入)

Gitで長いコマンドをエイリアスにして短いコマンドにしておくと便利ですが、 ちょっと複雑なことまでやろうとした場合について。

Sponsored Links

git config alias

Gitでエイリアスを追加したい時は

$ git config --global alias.st status

などとすると、~/.gitconfig

[alias]
  st = status

という行が付け加えられ、今後

$ git st

とすると、git statusとした時と同じ結果が見られます。 --globalを除けば現在の作業リポジトリのみに反映される様に.git/configへ追加されます。

もちろん、~/.gitconfig等に直接[alias]下にコマンドを書いていってもOK。

複数の文字を与えても良くて

ci = commit -a

としておけばgit cigit commit -aが使えます。

外部コマンドを渡す

与えるコマンドが!から始まると、外部コマンドとしてそのコマンドを直接実行します。

~/.gitconfig

echo = !echo test

としてみると、

$ git echo
test

単にecho testを実行してるだけです 1

細かくやっていることを見て見るためには 環境変数のGIT_TRACE1にします。

常に有効にするなら

$ export GIT_TRACE=1

(戻すには0に)、一時的に行いたい時はコマンドの前に書いて

$ GIT_TRACE=1 git echo
trace: exec: 'git-echo'
trace: run_command: 'git-echo'
trace: run_command: 'echo test'
trace: exec: '/bin/sh' '-c' 'echo test' 'echo test'
test

こんな感じで、TRACEオプションを有効にすると エイリアスがどう解釈されてるかも表示してくれます。

git-echoが実行されて、それがecho testと解釈され 実行されてるのが分かります。

最後の行にecho testが二つありますが、 下を見ると分かりますがこの部分では引数の展開前後を示しています。

引数を渡す

さて、上のechoコマンドにそのまま引数を渡すと

$ GIT_TRACE=1 git echo hoge
trace: exec: 'git-echo' 'hoge'
trace: run_command: 'git-echo' 'hoge'
trace: run_command: 'echo test' 'hoge'
trace: exec: '/bin/sh' '-c' 'echo test "[email protected]"' 'echo test' 'hoge'
test hoge

単に後ろにくっついただけです。

引数を任意の場所で使うためには関数を作って渡すようにするのが簡単です。

~/.gitconfig

echo2 = "!f () { echo $1;};f"

と言ったエイリアスを加えて見ます。最初に!で外部コマンドであることを教えて、後は

1
2
3
4
f () {
  echo $1;
}
f

といった感じでechoするだけの関数を作って最後にその関数を実行してるだけです。

注意として、ワンライナーで書こうとするときに()の両側の空白を忘れたり、 {}内で;を忘れたり、{の後に空白を入れ忘れたり、 最後の関数後の;を忘れたりしないように。

実行結果はこんな感じ。

$ GIT_TRACE=1 git echo2 test1 test2
trace: exec: 'git-echo2' 'test1' 'test2'
trace: run_command: 'git-echo2' 'test1' 'test2'
trace: run_command: 'f () { echo $1;};f' 'test1' 'test2'
trace: exec: '/bin/sh' '-c' 'f () { echo $1;};f "[email protected]"' 'f () { echo $1;};f' 'test1' 'test2'
test1

最後のf関数に続いてそのまま引数が書かれるので、関数に直接引数を与えてることになります。 これで、第一引数のtest1だけがechoされてます。

また、shにコマンドとして渡す様に、こんな感じで書くことも出来ます。

echo3 = "!sh -c 'echo ${0}'"

ただし、この場合、引数が0から始まるので気をつけて下さい。

関数の場合と比べるためにこんなエイリアスを~/.gitconfigに書いて確かめてみます。

echo-f = "!f () { echo 0: ${0};echo 1: ${1};echo 2: ${2}};f"
echo-sh = "!sh -c 'echo 0: ${0};echo 1: ${1};echo 2: ${2}'"

echo-fの方は

$ GIT_TRACE=1 git echo-f a b c
trace: exec: 'git-echo-f' 'a' 'b' 'c'
trace: run_command: 'git-echo-f' 'a' 'b' 'c'
trace: run_command: 'f () { echo ${0};echo ${1};echo ${2};};f' 'a' 'b' 'c'
trace: exec: '/bin/sh' '-c' 'f () { echo ${0};echo ${1};echo ${2};};f "[email protected]"' 'f () { echo ${0};echo ${1};echo ${2};};f' 'a' 'b' 'c'
0: f () { echo ${0};echo ${1};echo ${2};};f
1: a
2: b

こんな感じに${0}には関数そのものが入っています。

一方、echo-shの方は

$ GIT_TRACE=1 git echo-sh a b c
trace: exec: 'git-echo-sh' 'a' 'b' 'c'
trace: run_command: 'git-echo-sh' 'a' 'b' 'c'
trace: run_command: 'sh -c '\''echo ${0};echo ${1};echo ${2}'\''' 'a' 'b' 'c'
trace: exec: '/bin/sh' '-c' 'sh -c '\''echo ${0};echo ${1};echo ${2}'\'' "[email protected]"' 'sh -c '\''echo ${0};echo ${1};echo ${2}'\''' 'a' 'b' 'c'
0: a
1: b
2: c

の様に${0}から詰まっています。

この辺の混乱を避ける為に(?)、shを使う時は

echo-sh = "!sh -c 'echo 0: ${0};echo 1: ${1};echo 2: ${2}' -"

こんな感じで1つ引数を予め渡して置いて、関数の場合と同じように${1}から始める 様にしておくのも結構見られます 2

実行されるディレクトリ

gitコマンドなので、リポジトリ内で実行する時、 必ずそのリポジトリのトップでコマンドが実行されます。

pwd = !pwd

みたいなコマンドで色んな所でgit pwdしてみれば確認出来ます。

より長いコマンド

git submoduleについてのメモ で書いたsubmoduleの追加と削除を1つのコマンドにしてみます。

長いコマンドを書く時は行末に \ を付けて改行して書いていくことが出来ます。

注意点としてはthendoまたelseの後に必ず空白を入れてから \ を付けること、 それ以外の場所で;を忘れないこと、等。

submoduleの追加については、レポジトリのpathを与えて、第二引数があれば その名前のディレクトリ下に配置する様に。

smad = "!f () {\
    if [ $# -lt 1 ];then \
      echo \"Usage: git smad git_repo_path [submodule parent path]\";\
      exit;\
    fi;\
    git_repo=${1};\
    repo_name=${git_repo#*/};\
    repo_name=${repo_name%.git};\
    echo git submodule add ${git_repo} ./${2}/${repo_name};\
    git submodule add ${git_repo} ./${2}/${repo_name};\
  };f"

リポジトリ名に.gitまで付けてた場合に取り除くこともしています。

$ git submodule add [email protected]:rcmdnk/evernote_mail.git ./submodules/evernote_mail

としていたのを

$ git smad [email protected]:rcmdnk/evernote_mail.git ./submodules

と出来ます(余り減ってないか。。。)。

submoduleの削除についてはこんな感じで。

smrm  = "!f () {\
    if [ $# -ne 1 ];then \
      echo \"Usage: git smrm path/to/submodule\";\
      exit;\
    fi;\
    sm=${1%/};\
    echo git config --remove-section submodule.${sm};\
    git config --remove-section submodule.${sm};\
    echo git config --file .gitmodules --remove-section submodule.${sm};\
    git config --file .gitmodules --remove-section submodule.${sm};\
    echo git rm --cached ${sm};\
    git rm --cached ${sm};\
    gitdir=./;\
    gitfile=.git;\
    while : ;do \
      if [ -f $gitfile ];then \
        gitfile=${gitdir}/$(awk '/gitdir/ {print $2}' ${gitfile});\
      else \
        gitdir=${gitfile};\
        break;\
      fi;\
    done;\
    if [ -n \"${gitdir}\" ];then \
      echo rm -rf ${gitdir}/modules/${sm};\
      rm -rf ${gitdir}/modules/${sm};\
    fi;\
    echo rm -rf ${sm};\
    rm -rf ${sm};\
  };f"

使い方は

$ git smrm ./submodules/evernote_mail

こんな感じで。modules以下を消しているのは

git submoduleについてのメモ

に追記してあるゴミを消すため。 最後にもう一度submoduleのディレクトリを直接消してますが、これもたまに ゴミが残っている時に対処するためです。

まとめ

こんな感じで関数を使えばシェルスクリプトがそのまま書けるので gitのコマンドになんでも加えられます。

単純にエイリアスで良ければ普通のaliasgit-*的なコマンドを 作ってしまっても良いかも知れませんが。 (そしてそれを更にgitのエイリアスにしたり。。。)

Sponsored Links
  1. なので普通に.bashrcなんかでalias git-echoみたいにして git-echoを実行するのと基本的に同じ。

  2. ここでの-はオプション展開とか関係ない、はず。。

Sponsored Links

« footnote-inline: Octopress用footnoteのプラグイン OctopressのRSSを部分配信にする »