Git Advent Calendar / Jun. 22日目の記事です。21日目はgit最強のオプション filter-branchでした。
gst には怠惰さが足りない
さて、12日目に怠惰にgitを使うためのシェル活用術、またはgst一族という記事がありました。これ、僕に言わせればまだ怠惰さが足りません。 alias gst='git status'
などを設定すると便利という内容なのですが、 gst
は長い。3文字です。g を取って st
でいいじゃん。 gb
も g を取って b
でいいじゃん。
こう反論する向きもあるかもしれません。「自分は Mercurial や Subversion も使っていて alias hst='hg status'; alias sst='svn status'
と設定している。区別と統一性のために g が必要なのだ。てゆーか短いエイリアスってヤじゃね」
でもやっぱり gst
は長い。3文字です。カレントリディレクトリが Git リポジトリなのか Mercurial リポジトリなのかは明らかですから、人間様がプレフィクスをつけて gst
やら hst
やら使い分けなくても、コマンドの側が空気を読んでくれないものでしょうか。
たとえば、こんな st
コマンドを想像してみてください。Git リポジトリの中で st
と叩けば git status
になり、 Mercurial リポジトリの中で st
すれば hg status
になる。 Subversion リポジトリの中での st
は当然 svn status
を実行する。コンピュータと言えどそれくらいの融通は利いてくれてもいいと思いませんか。
思いませんか、と自分の MacBook に語り掛けてみてもヨシきたお前さんの言うとおりにしてやらあと袖まくりしてくれるわけではないので、てやんでいべらぼうめとか言いながら自分で Zsh スクリプトを書きました。
この csa.zsh を使うと上述の賢い st
が実現できます。
賢い st の使い方
賢い st
はこんな具合に賢いです。
# Git リポジトリに cd すると st == git status になる
$ cd ~/git-repo
$ st
## master
?? hoge
# 引数も普通に渡せる
$ st --ignored
## master
!! .DS_Store
?? hoge
# Mercurial リポジトリに cd すると st == hg status になる
$ cd ~/hg-repo
$ st
? fuga
# リポジトリでないディレクトリでは st は未定義
$ cd ~/not-a-repo
$ st
zsh: command not found: st
.zshrc の設定はだいたいこんな感じです。
# csa.zsh を読み込む
source csa.zsh
# 初期化する
# (csa_init 以降 alias コマンドが正しく動かなくなるので、
# これ以下の設定は .zshrc の終わりあたりに書くとよい)
csa_init
# エイリアスを定義する
# csalias <ctx> <alias> <cmd>
# ctx : コンテキストを表す任意の文字列
# alias: エイリアス名
# cmd : エイリアスに割り当てるコマンド
csalias ctx_git st 'git status -sb'
csalias ctx_hg st 'hg status'
csalias ctx_svn st 'svn status'
# ディレクトリを移動したときコンテキストが設定されるようにする
# (chpwd という名前の関数は、 cd したとき必ず呼ばれる)
function chpwd() {
# カレントディレクトリがどの種類のリポジトリか判別し、
# それに合わせてコンテキストを選ぶ
local -a ctx
if [[ -n `git rev-parse --is-inside-work-tree 2> /dev/null` ]]; then
ctx+=ctx_git
elif [[ -n `hg root 2> /dev/null` ]]; then
ctx+=ctx_hg
elif [[ -d .svn ]]; then
ctx+=ctx_svn
fi
# コンテキストを設定する
# csa_set_context [<ctx> [<ctx> …]]
# ctx: コンテキストを表す任意の文字列
csa_set_context $ctx
}
これで st
の2文字さえ覚えればどのリポジトリでもステータスを表示できるようになりました。 gst
と hst
と sst
の計9文字を使い分けていたのに比べると、覚えるべき文字数を78%も削減できたことになります。
僕は他にも以下のような設定をしています。1文字エイリアスこそ怠惰の極みです。
csalias ctx_git b 'git branch'
csalias ctx_git c 'git commit'
csalias ctx_git d 'git diff'
csalias ctx_git l 'git log --decorate'
csalias ctx_git m 'git merge --no-ff'
# hg と svn の設定は略
まだまだ怠惰さが足りない?
と、作るには作ってみましたが、他に活用できる場面を思いつきません。怠惰さが足りないのかもしれません。以前書いた記事を焼き直してこの記事を仕立てる程度の怠惰さでは不足なのでしょうか。
僕よりも怠惰な皆さんはこのスクリプトのうまい活用法をきっと見つけてくれるものと信じています。あるいは怠惰道を極め、念じるだけで st
できる手段を編み出してください。以上です。
追伸
ほとんど宣伝だし使い回しだし Git の話もしてないと書き上げてから気づいたので、適当に便利そうな小ネタをおまけしてお茶を濁すことにします。
insteadOf
.gitconfig に
[url "git@github.com:USERNAME/"]
insteadof = gh:
こう書いておくと gh:repo
で GitHub にある自分の repo
リポジトリにアクセスできます。 git clone gh:repo
とか git push gh:repo master
とか。便利ですね。
pushInsteadOf
GitHub にあるリポジトリには様々なプロトコルでアクセスできます:
https://github.com/USERNAME/repo.git
git://github.com/USERNAME/repo.git
git@github.com:USERNAME/repo.git
上の2つはリードオンリーなため、 push するとエラーが表示されます。
$ git push origin master # (origin == git://github.com/USERNAME/repo.git)
fatal: remote error:
You can't push to git://github.com/USERNAME/repo.git
Use git@github.com:USERNAME/repo.git
コミット権限のないリポジトリをリードオンリーな URL から clone したけれど、後からやっぱりコミッタになった、というときは origin の URL を設定し直さなければならず不便ですね。
ところが pushInsteadOf を使うと push 先の URL を書き換えることができます。
[url "git@github.com:"]
pushinsteadof = "https://github.com/"
pushinsteadof = "git://github.com/"
こう設定しておくだけで、 git://github.com/...
への push が git@github.com:...
への push に置き換わります。
$ git push origin master # (origin == git://github.com/USERNAME/repo.git)
Everything up-to-date
便利ですね。