rcmdnk's blog
Last update

【Amazon.co.jp限定】エイリアス DVDコンプリートBOX

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

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 "$@"' '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 "$@"' '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 "$@"' '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}'\'' "$@"' '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のディレクトリを直接消してますが、これもたまに ゴミが残っている時に対処するためです。

サブコマンドを作る

追記: 2017/10/01

エイリアスで複雑なことをしようとすると上みたいに結構面倒ですが、 複雑なことをする場合にはサブコマンドを作ってしまうのも1つの手です。

PATHが通ったディレクトリにgit-mycommandといったgit-と付いた実行ファイルを入れておくと

$ git mycommand

と実行できる様になります。 この実行ファイルはシェルスクリプトでもC++で書いてコンパイルしたものでも なんでも構いません。 Gitに全く関係ないものでも使えます。

このコマンドはそのままgit-mycommandを実行したのと同じ様なものなので 引数等もそのまま使えます。

上のエイリアスをサブコマンドにするなら、

git-smad
1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env bash

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}

みたいなシェルスクリプトをPATHが通ったディレクトリに置いておけば エイリアスのときと同様に使えます。

追記ここまで

まとめ

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

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

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

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

Sponsored Links

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

}