gitの作業ディレクトリでの移動が捗るgit_cdを作りました

11月19日にオラクルでSCMBootCamp in Tokyo 2というイベントがあり、そこでgitの講師してました。
周りの方のgitの質問に答えたり、コマンドの説明をしていたり。

濃い話が聞けたり、普段触らない他DVCSの話も聞けて楽しかったです。
あと、花映塚はやっぱり面白いですね!

そんな#scmbcの演習中

@1syoさんからの質問(質問だったと思う)で「gitの作業ディレクトリのrootに簡単に移動するコマンドとかないですか?」というものがありました。

具体的に書くと、こんな感じ。
例えば、下記のようなフォルダ構成だったとして。

~/rails_proj/.git
~/rails_proj/app/controllers

こういう時に

$ pwd
/home/sinsoku/rails_proj/app/controllers
$ git cd
$ pwd
/home/sinsoku/rails_proj/

こういう事ができないか?と。

言われるまで気づかなかったけど、便利そうなので調べてみましたが、標準には無いようです。
じゃあ、作りましょう!

仕様

仕様は上述した通りなのですが、もう一つ便利そうなのでこんな仕様を加えてみました。

$ pwd
/home/sinsoku/rails_proj/app/controllers
$ git cd spec
$ pwd
/home/sinsoku/rails_proj/spec

こんな感じのスクリプトの作り方を考えてみました。

迷走

少し調べると、gitのリポジトリは簡単に取得できましたが、ディレクトリの移動が難しい。

の2種類の方法を調べて試してみましたが、上手くcdできない。

bashrcに関数作った

出来れば"git cd"にしたかったけど、難しそうなので諦めて"git_cd"にして.bashrcに関数作りました。

git_cd() {
    path="`git rev-parse --git-dir`/../$1"
    ret=$?
    if [ ${ret} -eq 0 ]; then cd ${path}; fi
}
alias gcd="git_cd"

これでGitの作業ディレクトリでの移動が楽になって、作業が捗りますね!

何、勘違いしているんだ…まだ、俺のバトルフェイズは終了してないぜ…!!

追加スクリプト発動!

#!bash
#
# version: 0.2
# license: New BSD License
#
# how to install
# ===============
#
# * Ubuntu
# $ cp git_cd.bash /etc/bash_completion.d/git_cd

_git_cd()
{
    if [ ${COMP_CWORD} -lt 2 ]; then
        git_repo="`git rev-parse --git-dir 2>/dev/null`"
        ret=$?
        if [ ${ret} -ne 0 ]; then return 0; fi
        if [ "${git_repo}" == ".git" ]; then
            git_root="`pwd`/"
        else
            git_root="${git_repo%.git}"
        fi

        cur="${COMP_WORDS[COMP_CWORD]}"
        if [ -n "${cur##*/}" ]; then
            cur_d="${cur}/"
        else
            cur_d="${cur}"
        fi

        dist="${git_root}${cur_d}"
        if [ -d "${dist}" ]; then
            subs=`compgen -d "${dist}"`
            opts=`for s in ${subs}; do echo ${s#${git_root}}; done`
        else
            subs=`compgen -d "${dist%/[^/]*}/"`
            opts=`for s in ${subs}; do echo ${s#${git_root}}; done`
        fi

        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
    fi
}
complete -F _git_cd git_cd
complete -F _git_cd gcd

このスクリプトを入れておくと、bash-completionでディレクトリの補完が効くようになります。
たぶんgit_cdの関数本体よりもこの補完スクリプトの方が時間かかってる。