複数ファイルが立ち並ぶディレクトリにおいて、1つまたはいくつかの限られたファイルだけを`git add`したいという場合、いちいちそのファイル名を打ち込んでいくのがメンドイ。
こういう時、peco的なものでバッとリストを出して、インクリメントに対象を絞り込みつつ直感的に選択できないものか? と思っていた。
で、こういうのを作った。(コマンドは`ga`)
Perl製。
とくにリポジトリを公開したりしていないので、生コードはこんな感じで。
(.bashrcと組み合わせる)
# .bashrc function gi { local arg="$@" local pick=$(perl /path/to/gi.pl) #パスは任意の場所で if [ -z "$pick" ]; then echo Canceled. elif [ -z "$arg" ]; then echo Input a command. elif [ ! -z "$pick" ]; then git $arg $pick if [[ "$arg" == add ]]; then echo Add $pick fi fi } alias ga="gi add" alias gr="gi reset HEAD"
#!/usr/bin/env perl # # gi.pl use strict; use warnings; use feature 'say'; my $status = `git status`; my @msg = split /\n/, $status; my $msg; my %msg; my $marker = ''; for (@msg) { if ($_ =~ /\AChanges to be committed:/) { $marker = 'staged'; } elsif ($_ =~ /\AChanges not staged for commit:/) { $marker = 'not staged'; } elsif ($_ =~ /\AUntracked files:/) { $marker = 'untracked'; } if ($_ =~ /\A\t(.+)\z/) { $msg = $1; if ($marker eq 'staged') { if ($msg =~ /\A(.+):\s+(.+)\z/) { $msg{$2} = 'staged'; } } elsif ($marker eq 'not staged') { if ($msg =~ /\A(.+):\s+(.+)\z/) { $msg{$2} = $1; } } elsif ($marker eq 'untracked') { if ($msg =~ /(.+)\z/) { $msg{$1} = 'untracked'; } } } } my @result = (); while (1) { my @list = (); my $list = ''; for (sort keys %msg) { push @list, "$msg{$_}:\t$_"; } $list = join "\n", 'OK', @list; my $selected_line = `echo "$list" | peco`; chomp $selected_line; my $selected_status; my $selected_file = ''; if ($selected_line =~ /\* (.+):\t(.+)\z/) { $selected_status = $1; $selected_file = $2; shift @result; } elsif ($selected_line =~ /(.+):\t(.+)\z/) { $selected_status = '* '.$1; $selected_file = $2; push @result, $selected_file; } delete $msg{$selected_file}; $msg{$selected_file} = $selected_status; if ($selected_line =~ /\A(OK|)\z/) { if ($selected_line eq '' || ($selected_line eq 'OK' && scalar(@result) == 0)) { @result = (); } last; } } print "@result";
`gi`の後に何らかのgitコマンドを入れれば、選択したファイルに対してそれをする。
ここでは事前にエイリアスを設定して、`ga`と打てば`git add`、`gr`と打てば`git reset HEAD`になるようにしている。
何度も試しながら、結局半日ぐらいかかった気がするが、ひとまず「ん〜、いいじゃん、こういうのが欲しかったのだよ」と満足できる感じにはなった。
が、そこまで来てからふと、「しかし普通のプログラマーたちが毎日そんなメンドイことをしてるはずがないな……皆どうしてるんだろう?」と思った。
まあIDEや各種エディタ、あるいはGUIツールを使っている人なども少なくはないだろうけど、ターミナル操作で済ませている人も多いだろうしなあ、と。
そこでようやく、時々耳にするtigというツールを使えば似たようなことが出来るかも? と思い至り、簡単に調べてみた。
上記でほぼ把握。自分の手元でもやってみた。
最初に`tig`と打つと、`git log --oneline`的な画面が出てきて、そこで`S`(Shift+s)を打つと`git status`の情報が出てくる。
そこでjk(上下)移動しつつ目当てのファイルの上で`u`を押すと、さくさくステージ/アンステージしてくれる。
めっちゃお手軽!
Vimとほぼ同じ動かし方なので、直感的に使える。
pecoのようなインクリメントな絞り込みはできないけど、必要十分とは言えるだろう。
何より、上記ではaddだけやっているけど、このtigにはその他のgit系機能も盛りだくさんである。
先ほど`git blame`を試してみたけど、これもけっこう目覚ましい体験だった。
これがあるとわかっていれば、わざわざ冒頭のようなコードも書かなかったなあ……と一瞬思ったが、でもどうだろう。
初めからtigを使っていたら、tigの「使い方」には詳しくなったかもしれないけど、コードを書く技術は上がらなかっただろう。
ぼくが望むのは確かに「直感的に`git add`すること」だったけど、同時に「コードをより上手に書けること」も求めていて、言い換えると、ぼくが最終的に求めているのは道具の「使い方」ではなくて「作り方」を知ることなのだと思える。
最新のガジェットやそれらを「使いこなす」ことにあまり興味を持てないのもそのことと関係している気がする。
さらに突き詰めて考えるなら、ぼくが欲してるのは「自由」なのだとも思う。
最新のガジェットに興味がないとは書いたが、そうは言ってもiPhoneが初めて日本で発売されたとき(2008年の6月とかだった)、ぼくはまだソフトバンクで機種変更してから半年ぐらいしか経っていなかったけど、迷わずiPhoneを予約してすぐ交換した。
じゃあなんだ、新しいガジェットに興味あるじゃないか、とも言えるが、それを買ったのは「絶対的に未知の体験を死ぬ前に味わっておかなくてはならない」と思ったからで、それは「現在という束縛からの逃避・逸脱・自由」を求めてのことだったと思える。
他人が作った道具を使うというのは、結局どこまで行っても作り手の想定の範疇から抜け出せない行為のように感じられて、あまり強い欲求が生じないのかもしれない。
たとえば作り手がその大元のルールを変えてしまったら、せっかく覚えた使い方も無効になってしまう。その不自由さがいやなのだ。
それよりは、シンプルで小さな道具を自分なりに組み合わせて、他の誰も使わないかもしれないが自分に最適化された、自分にとって最高の道具を作り、それまでの人生で味わったことのない体験をしたい、という気持ちがある。
プログラミングにはそういう欲求を駆り立てるものがあるようで、「作ること」と「それを使って体験すること」とのバランスが絶妙に感じられる。
[PR] YAPC::Okinawa で発表します
その話と直接の関係はありませんが&次にいつブログ書けるかわからないのでこのまま書いてしまいますが、3/3に沖縄で開催されるYAPC::Okinawa 2018 ONNASON で登壇することになりました。
yapcjapan.org
http://yapcjapan.org/2018okinawa/talks.html#/detail/10
チケットは完売とのことですが(おめでとうございます!>各位)現地に行かれる皆さんにおきましては、ご興味ありましたらぜひお越しください!