Gitのような複雑なシステムは使い方が難しい。 ある程度使い方を知っている場合でも、あまり一般的でない機能を使うのは難しい。 たとえば以下のような場合はどういうコマンドを使えばいいだろうか?
- ひとつ前のバージョンの
README.md
からの変更を見たい README.md
は3日前からどう変わった?package.json
にcoffee
という名前が入ったのはいつ?- ここ1週間ぐらい変更されてないファイルは?
- 最近大量に修正したファイルはどれだっけ?
最初の例について考えてみる。
Gitでは「HEAD^
」「HEAD^^
」のような表現で昔のコミットを参照できるので
$ git diff HEAD^ README.md
のようにすればひとつ前のコミットのREADME.md
との比較ができるが、
最近のコミットでREADME.md
を編集していなかった場合は
このコマンドを起動しても何も出力されない。
$ git log README.md
とすればREDME.md
の編集履歴を調べられるので、
ひとつ前のバージョンのコミットIDを使えば
$ git diff (ひとつ前のバージョンのコミットID) README.md
のようにして
ひとつ前のバージョンのREADME.md
からの変更を見ることができる。
しかしこれはなかなか面倒な話である。
手動でコミットIDを手打ちかコピペしなければならない
し、当然ながら
git log
やgit diff
といったコマンドの存在と動作を知っていなければならない。
git log
やgit diff
はよく使うコマンドなので
Gitユーザなら誰でも知っているだろうが、
このような簡単な仕事でもコピペのような面倒な操作が必要なのは嫌である。
実はGitにはrev-list
というコマンドがあり、
編集があったコミットのIDをリストすることができる。
これを利用すると、ひとつ前の編集のコミットIDは
$ git rev-list HEAD -- README.md | head -2 | tail -1
で取得できる。
この結果を利用すると、
「ひとつ前のバージョンのREADME.md
からの変更を見たい」
という要求は
git diff $(git rev-list HEAD -- README.md | head -2 | tail -1) -- README.md
のようなコマンドで実行できることになる。
($(...)
というのはbash
の記法で、コマンド実行結果を文字列として扱うものである。
--
というのは、その後に続く文字列がオプションではなくてファイル名等だということを示すもの。)
rev-list
の-n
オプションを利用すると
git diff $(git rev-list -n 1 HEAD -- README.md)^ -- README.md
のように書くこともできる。
それにしても、このように
無駄知識が大量に必要
だったり
単純な要求の実行が大変
だったりするのは嫌すぎる。
上のような工夫によって、
ひとつ前のバージョンのREADME.md
からの変更は調べられるようになったわけだが、
こういった要求は無限にあるわけで、
そのたびにいろんなGit機能を調べたり思い出したりしなければならないのだろうか。
「README.md
は最後にどこ変えたっけ?」
のような自然な質問を簡単に
Gitコマンドに翻訳する方法が欲しい。
こういった要望に対して最近は人工知能的に解決しようとするアプローチが人気かもしれない。
しかしそのためには高度な自然言語処理が必要で、
ちょっと違った表現を許したり内容を変えたりすることは簡単ではない。
大阪弁で質問できる日が来るとは思えない。
また、「longfilename
」のようなものを指定しようとして
「logfile
」のように間違って入力しても動くようにするには
単純な予測/補完/誤り修正機能などを使った方が良いだろう。
逆引き辞典などでは
「ひとつ前のバージョンのファイルとの違いを知る」
のようなエントリはあるかもしれないが、
それを調べた後で「README.md
」のような名前を指定してコマンドを起動する必要がある。
こういった二度手間も減らしたいものである。
Macのヘルプで「時間 セット」と入力しても時間をセットする方法は出てこないし、ヘルプを自分で追加することはできない。
時間をセットする方法がヘルプに書いてあったとしても、
時間を4時にセットするためには自分で「4時」という値を指定してから「時間のセット」機能を実行しなければならない。
マニュアルやヘルプを書くのは面倒なものである。 システムのドキュメントやマニュアルやヘルプシステムを独立に開発するのは 面倒すぎるし齟齬も起きやすいだろう。 ユーザをサポートするシステムがひとつにまとまっていて、 誰でも情報を足したり修正したりできたら嬉しいだろう。
GitHelpのアプローチ
GitHelpは、以下のような方針で上のような課題をすべて解決しようというものである。
- ユーザのあらゆる曖昧な表現にマッチするようにヘルプ文字列を正規表現で表現し、 Gitコマンドに変換する - ExpandHelp(ソース / 論文)を利用
- データをすべてクラウド上に置いて編集可能にすることにより、 誰でもデータを追加/修正できるようにする - Scrapboxを利用
- ユーザが指定したパラメタはそのまま利用して実行に使う - ユーザが「4」「時間」などと指定すると「時刻を4時にセットする」のようなものを提案して実行可能にする
- 多少の誤入力を許す
利用例
Gitに関連するタスクのキーワードやパラメタを指定してgithelp
を起動すると
候補のリストが表示され、
カーソルで選択すると実行される。
$ githelp 2 README
のように引数を指定して起動すると以下のような候補リストが提示される- カーソルで選択してリターンを押すと実行される
インストール
% gem install githelp
実装
- re_expand という正規表現展開ライブラリを利用
- Scrapboxにあらゆる情報を書いておく
- 様々なタスクの説明と実際の操作を組にして記述しておき、 ユーザが与えたキーワードやパラメタにマッチするものを リストして実行可能にする
- 行頭に
$
がある行でタスクの説明を記述し、行頭に%
がある行で実行コマンドを示す - ファイル名にマッチする引数(e.g.
READM
)や 数字にマッチする引数(e.g.2
)が指定されると$
の行に記述された#{filename}
や#{number}
に マッチする - マッチしたときは、マッチした文字列が
$1
などで参照/展開されてGitコマンド文字列になる - ワンライナーでは難しい場合は
exe
の下にヘルパーコマンドを用意して利用する (e.g.githelp-changed
) )
考察
- 生活の中ではこういった言い換えをいつも行なっているかもしれない。 たとえば「部屋暗くして」と頼まれたら 部屋の入口にある電灯スイッチを操作するかもしれないが、 この場合は頭の中で 「部屋を暗くする」⇒「電灯を消す」⇒「電灯のスイッチを切る」 という翻訳が行なわれていることになる。 こういうことは生活で非常に多いので 翻訳作業があまり気にならないものなのかもしれないが、 そういう「翻訳」は少ない方が良いのは確かだろう。
- そういえば先日「らくらくホン」画面に出てくる鬱陶しい「羊」を消す方法が全くわからなかったのだが、
あれは「マチキャラ」と呼ばれるものなので
「マチキャラ」を消すという操作が必要だった。
お前はMSのイルカか。
「羊 消す」とか「消す」とかで消せるべきだろう。
githelpでは
$ githelp 削除
と入力すれば削除関連で何ができるのかわかるだろうし、(鬱陶しい|不快な)羊を(消す|殺す)
のようなエントリをユーザが足すこともできるだろう。 - というわけでGitは単なる適用例であり、広い範囲で使いたいと思っている。
注意
githelp
コマンドはGitリポジトリのディレクトリで実行する必要がある- re_expandの実装が富豪的なので 大きなリポジトリだと不具合があるかも
- データが全然足りない... 特にリモートリポジトリ関連のデータは皆無だが、 ローカルだけでも充分複雑なのでとりあえずローカル処理の情報を充実させたい
- 旧版はこちら
関連システム
- AnyCode
- 自然言語キーワードからJavaスニペットを検索する
copy fileA fileB
みたいなキーワードからFileUtil.copyFile(new File(fileA), new File(fileB))
みたいなコード候補を生成する- 文芸的プログラミング