公開日: 2023年9月17日

Git 第3回: Git にテキスト UI をもたらす Tig の恩恵にあずかろう

今回はテキストユーザーインターフェース (TUI) をもつ Git クライアント (というよりもリポジトリビューアと呼んだほうが正確かもしれません) である Tig を紹介したいと思います。TUI なだけあって軽量、シンプル、マウス?なにそれ、そのうえ Windows でも利用可能というまさに最凶なツールです。

スクリーンショット

図 1 (a): main ビュー & diff ビュー
図 1 (a): main ビュー & diff ビュー
図 1 (b): status ビュー & stage ビュー
図 1 (b): status ビュー & stage ビュー
図 1 (c): log ビュー
図 1 (c): log ビュー

インストール

Tig は ports/packages からインストールすることができます。tigrc(5) のマニュアルを閲覧するのに groff が必要ということなので、一緒にインストールしています。
# pkg install -y tig groff

閲覧したいリポジトリを用意しよう

Tig 自身は基本的にビューアに徹するスタンスのため、リポジトリを作ったり clone したりする機能はありません。リポジトリがなければ起動すらしません。そこでまずは閲覧したいリポジトリを用意します。 手動でgit initするか、GitHub 等から閲覧したいリポジトリを fork して clone してきてください。ここでは第 1 回で作った hp-example を clone して適当なディレクトリに展開します。
% cd ~/temp/
% git clone git@github.com:mijinco0/hp-example.git hp-example
さて、このサンプルプロジェクトは実際は私が 1 人で作ったのですが、チームでやっている感を出すために、アメリカ人のマイクとケイトが 2 人で担当しているという体で進めようと思います。前々回はマイクにやってもらったので、今回はケイトの出番です。「そこはアリスとボブだろ」とか「ジャックとベティだろ」といった異論はナシでお願いします。私が中学生の頃の英語のテキストではマイクとケイトでした (でも "Mike" と "Kate" は文字数がどちらも 4 文字なので、git log --onelineしたときにぱっと見で区別がつかないんだよなあ、ジャックとベティにしておくんだった…と思い始めています)。というわけで、clone してきたこのリポジトリの user.name を Kate に設定します。
% cd ~/temp/hp-example/
% git config user.name "kate"
準備ができたので、リポジトリに移動して Tig を起動します。
% cd ~/temp/hp-example/
% tig

main ビューで慣れる

各ビューにだいたい共通の操作

Tig を起動すると、最初に main ビューが表示されます (図 2 (a))。Tig では、ビューによってキーの割り当てが変わる場合がありますが、次の操作はだいたい共通です。main ビューで Tig の基本操作に慣れておきましょう。

キー機能
j

カーソル移動: 下

k

カーソル移動: 上

:<n>

カーソル移動: n 行目

Ctrl-u

半ページアップ

Ctrl-d

半ページダウン

Space

ページダウン

Enter

画面分割して別のビューを表示 (図 2 (b))

Tab

画面分割されているとき、ビューを移動する

Q

Tig 終了

q

現在のビューを閉じる

: (コロン)

プロンプトを開く (後述)

R

ビューの再読み込み

h

読みやすい親切なヘルプ

/

順方向検索 ([n] で次へ、[N] で前へ)

?

逆方向検索 ( 〃 )

図 2 (a)
図 2 (a)
図 2 (b)
図 2 (b)

ビューの切り替え

他のビューに切り替えてみましょう。いま自分がいるビューは画面左下に表示されます。

キービューコメント
m

main

起動して最初に表示されるビュー

d

diff

差分を表示する

l

log

ログを表示する

L

reflog

操作履歴を表示する

t

tree

エクスプローラーみたいにディレクトリをたどる

f

blob

ファイルの中身を表示する

b

blame

ファイルの各行がどのコミットで変更されたかを表示する

r

refs

ブランチとタグの一覧を表示する、ブランチを切り替える

p

pager

必要なときに表示される。積極的に使う機会はあまりない

h

help

ヘルプを表示する

s

status

ステージエリアの状態を表示する、コミットする

c

stage

個人的にはあまり使わない

y

stash

スタッシュの一覧を表示する、スタッシュを操作する

g

grep

リポジトリ内を grep した結果を表示する

素の Git コマンドを実行する

Tig 単体で実行できない機能については、素の Git コマンドでカバーします。実行するには [:] キーでプロンプトを開き、[!] と入力してから Git コマンドを入力します (図 3 (a))。実行した結果、Git が標準出力に何かしら出力していれば、Tig がそれを受け取って pager ビューに表示してくれます (図 3 (b))。pager ビューを抜けるのも [q] キーです。また、プロンプトを開いた状態では [Ctrl-p] [Ctrl-n] キーでコマンド履歴を前後にたどることができます。

図 2 (a)
図 3 (a)
図 2 (b)
図 3 (b)

リポジトリにあるディレクトリやファイルを表示する (tree ビュー、blob ビュー)

tree ビュー (図 4 (a)) と blob ビューでは、エクスプローラーのようにディレクトリをたどって、選択したファイルの中身を表示することができます。
キー機能
ディレクトリにカーソルを合わせて Enter

そのディレクトリに移動する

ファイルにカーソルを合わせて Enter

画面分割してそのファイルを blob ビューで表示する (図 4 (b))

ファイルにカーソルを合わせて f

そのファイルを blob ビューで表示する

ファイルにカーソルを合わせて e

そのファイルをエディタで開く

, (カンマ)

親ディレクトリに移動する

図 4 (a)
図 4 (a)
図 4 (b)
図 4 (b)

「この行はどのコミットで変更したんだっけ?」を調べる (blame ビュー)

ソースコードを眺めていて「あれ、ここの処理これでいいんだっけ、いつからこうなってんだ?」と思うことが往々にしてあります。そんなときは blame ビュー (批判的に眺める) がうってつけです。
キー機能
d

カーソルがある行のコミットの差分を表示する

, (カンマ)

カーソルがある行のコミットを親コミットの状態まで戻して表示する

tree ビューなどにいるときに (図 4 (a))、批判的に眺めたいファイルにカーソルを合わせて [b] キーを押すと blame ビューに切り替わります (図 5 (a))。図のように、どの行がいつ、誰の、どのコミットで変更されたかが一目瞭然です。さらに、詳しく確認したい行にカーソルを合わせて [d] キーを押せば diff ビューに切り替わり (図 5 (b))、そのコミットの差分を確認することもできます。

このように、キー 1 発でビューを縦横無尽に切り替えて、いろいろな視点から変更点を眺められるのが Tig の優れた点だと思います。

図 5 (a)
図 5 (a)
図 5 (b)
図 5 (b)

ログや差分を確認する (log ビュー、diff ビュー)

キー機能
d

カーソルがある行の差分を表示する

Enter

画面分割してカーソルがある行の差分を表示する

f

ファイルを blob ビューで表示する

b

ファイルを blame ビューで表示する

e

ファイルをエディタで開く

  1. [l] キーで log ビューに切り替える (図 6 (a))。差分を確認したいコミットがあれば、そのコミットに含まれる行のどこかにカーソルを合わせて、[d] キーまたは [Enter] を押す (Enter の場合は画面分割される)。
  2. diff ビューに切り替わり、そのコミットで行われた変更の差分が表示される。ファイルの内容を確認したければ、そのファイルの差分情報が含まれる行のどこかにカーソルを合わせて、[f] キーを押す (図 6 (b))。
  3. blob ビューに切り替わり、ファイルの内容を確認することができる (図 6 (c))。

手順 2 で [f] キーの代わりに [b] キーを押せば、すでに紹介した blame ビューで表示させることもできます。また、[e] キーを押せば、エディタが起動して編集することもできます。

図 6 (a)
図 6 (a)
図 6 (b)
図 6 (b)
図 6 (c)
図 6 (c)

ブランチの操作 (refsビュー)

キー / コマンド機能
git branch

ブランチを作成する

C

ブランチを切り替える (チェックアウト)

git merge

ブランチをマージする

!

ブランチを削除する

ブランチを作る

Tig にはブランチを作る機能がないので、[:] キーでプロンプトを開き、直接 Git コマンドを打ちます (図 7 (a))。
:!git branch issue1
このとき pager ビューに切り替わりますが、何も表示されません。気にせず [q] キーで抜けます。

ブランチの一覧表示

[r] キーで refs ビューに切り替えます (図 7 (b))。いま作った issue1 ブランチが表示されていることがわかります。もし見当たらなければ [R] キーでビューを再読み込みしてみましょう。

ブランチの切り替え

refs ビューにて切り替えたいブランチにカーソルを合わせ、[C] キーを押します。本当に切り替えるか訊かれるので、y と入力します (図 7 (c))。

ブランチのマージ

これもキー割り当てがありません。例えば、issue1 ブランチを master ブランチにマージしたければ、master ブランチに切り替えてからプロンプトを開き、次のコマンドを実行します。
:!git merge issue1

ブランチの削除

削除したいブランチにカーソルを合わせて [!] キーを押します。本当に削除するか訊かれるので、よければ y と入力します。
図 7 (a)
図 7 (a)
図 7 (b)
図 7 (b)
図 7 (c)
図 7 (c)

ステージに上げてコミットする (status ビュー)

キー機能
u

変更されたファイルや未追跡ファイルをステージに上げる / 下ろす

C

コミットする

!

ファイルの変更を破棄する

status ビューの説明をするために、適当なブランチを作ってコミットしてみます。hp-example の sass ディレクトリには、なんでもやりたい放題の debug レイヤというものを用意してあります (本来の FLOCSS にはそんなものありません、念のため)。ふだんはコメントアウトしてありますが、これを有効にしてみましょう。有効にすると図 8 のように HTML の各要素を色分けして表示することができます。ブラウザの「検証ツール」を知らなかった頃は、これを使ってちまちまとデバッグしていました…。

図 8
図 8

まず debug ブランチを作って debug ブランチに切り替えましょう。やり方はすぐ上の refs ビューの説明を参照してください。その後の手順は次のとおりです。

  1. エディタで src/sass/style.scss を開いて、一番下の行のコメントアウトを解除する (図 9 (a))。
  2. この変更をコミットするために [s] キーで status ビューに切り替える。"Changes not staged for commit:" と書かれた行の下にいま変更したファイルが表示されている (図 9 (b))。
    ファイルにカーソルを合わせて [Enter] キーを押すと、画面が分割されて差分を確認することができる (図 9 (c))。
  3. 変更をステージに上げる。ステージに上げたいファイルにカーソルを合わせて [u] キーを押すと、そのファイルが "Changes to be committed:" の下に移動する (図 9 (d))。
    ステージから下ろしたい場合は、そのファイルにカーソルを合わせて再度 [u] キーを押す。
    変更を破棄したい場合は、そのファイルにカーソルを合わせ、[!] キーを押す (ステージングエリアのファイルはステージから下ろしておく必要がある)。
  4. コミットするには、ステージに上げた状態で [C] キーを押す。エディタが起動するので、コミットメッセージを入力する (図 9 (e))。
    '#' から始まる行は無視され、メッセージには含まれない。保存してエディタを終了させると、コミットが実行される。
  5. main ビューに戻ると、たった今のコミットが追加されていることがわかる (図 9 (f))。もし表示されていなければ [R] キーで再読み込みしてみる。
図 9 (a)
図 9 (a)
図 9 (b)
図 9 (b)
図 9 (c)
図 9 (c)
図 9 (d)
図 9 (d)
図 9 (e)
図 9 (e)
図 9 (f)
図 9 (f)

チェリーピック

チェリーピックも Tig 上で行えます。ただしやり方にちょっとクセがあるかもしれません。
ビューキー機能
refs

C

チェリーピック元のブランチに切り替える

refs

Enter

チェリーピック先のブランチの main ビューを表示する

main

C

カーソル位置のコミットをチェリーピックする

ケイトが debug ブランチでいろいろやっている間に、マイクがリモートリポジトリを更新しました。それを debug ブランチにもチェリーピックして取り込みましょう。まずはリモートリポジトリを pull します。

:!git pull origin master

履歴を確認すると、タグ v0.1 以降で 2 件のコミットがあります (図 10 (a))。これらのコミットを debug ブランチに取り込むには、次のように操作します。

  1. チェリーピック先のブランチ (ここでは debug ブランチ) に切り替える。[r] キーで ref ビューに切り替え、切り替えたいブランチにカーソルを合わせて [C] キーを押す。切り替えてもよいか訊かれるので、y と入力する (図 10 (b))。
  2. 引き続き ref ビューでチェリーピック元のブランチ (ここでは master ブランチ) にカーソルを合わせて [Enter] キーを押す。画面が分割されて master ブランチの main ビューが表示される (図 10 (c))。
  3. master ブランチの main ビューでチェリーピックしたいコミットにカーソルを合わせ、[C] キーを押す。本当に実行するか訊かれるので、y と入力する (図 10 (d))。
  4. いまは main ビューに master ブランチの情報が表示されている状態。debug ブランチがどうなったか確認したいので、[TAB] キーで上段の refs ビュー移り、debug ブランチにカーソルを合わせて [Enter] キーを押す。
  5. 下段の main ビューが debug ブランチの表示になった (図 10 (e))。master ブランチからのコミットが ab8691d というハッシュ値でチェリーピックされたことがわかる。

引き続き他のコミットをチェリーピックする場合は、この手順を繰り返します。

なお、チェリーピック時のコミットメッセージは、デフォルトではチェリーピック元と同じになります。~/.tigrc に次のように書いておく (-x オプションを付ける) と、図 10 (f) のようにチェリーピック元のコミットハッシュを自動で付記してくれるので便利です。

~/.tigrc
bind main C ?git cherry-pick -x %(commit)
図 10 (a)
図 10 (a)
図 10 (b)
図 10 (b)
図 10 (c)
図 10 (c)
図 10 (d)
図 10 (d)
図 10 (e)
図 10 (e)
図 10 (f)
図 10 (f)

pull と push

Tig 自体に pull や push を行う機能はないので、プロンプトを開いて直接コマンドを打ちます。例えば origin から master への pull、master から origin への push は次のようにします。
:!git pull origin master
:!git push origin master
オプションを付けたり、タグを push する場合も同様です。例えば、上流ブランチを設定してローカルブランチを push する場合。
:!git push -u origin issue1
タグを push する場合。
:!git push origin v1.0

いま取り組んでいる作業を一時中断して別の作業に取り組む (stash ビュー)

そういうときは git stash です。いまやっている作業を脇に押しのけて、リポジトリを一時的にクリアな状態に戻してくれます。押しのけた作業は stash ビューからいつでも復帰させることができます。
キー / コマンド機能
git stash -u

現在の作業を退避する (-uを付けると未追跡ファイルも退避する)

A (apply)

カーソル位置のスタッシュを復旧する。スタッシュはリストに残る

P (pop)

カーソル位置のスタッシュを復旧する。スタッシュはリストから削除される

!

スタッシュを削除する
  1. debug ブランチで何らかの作業をしていて、ステージに上げたファイルが 1 個と、まだ上げていないファイルが 1 個ある状態 (図 11 (a))。
  2. stash にはデフォルトではキーが割り当てられていないので、プロンプトを開いてコマンドを直接打つ (図 11 (b))。-uオプションを付けると、追跡していないファイルも含めて退避する。
    :!git stash -u
    
  3. pager ビューにスタッシュの概要が表示される (図 11 (c))。ちなみに「WIP」は「Work In Progress」の略。
  4. status ビューで確認する (図 11 (d))。リポジトリがクリアな状態になった。[R] キーで再読み込みしないと表示が更新されていないかも。
  5. [y] キーで stash ビューに切り替える。今しがた退避した作業が一番上に表示されている (図 11 (e))。他にも退避した作業があれば、それらもまとめて一覧表示される。
    画面下のステータスバーに「stash@{0}」と表示されているが、これがスタッシュを識別する番号になる。1 行目のスタッシュはstash@{0}、2 行目のスタッシュはstash@{1}、... という具合。
  6. スタッシュを復旧するときは、まず復旧させたいブランチ (この例では debug ブランチ) に切り替える。
  7. stash ビューで復旧したいスタッシュにカーソルを合わせて [A] キー (apply) を押す。status ビューに切り替えて確認すると、退避前はステージに上がっていた _header.scss がステージから下ろされた状態で戻ってしまった (図 11 (f)) が、これはこういう仕様。
手順 7 について補足です。
  • ステージエリアの状態も含めて復旧したいときは、--indexオプションを付けます。
    :!git stash apply stash@{0} --index
    
    こうすると図 11 (a) の状態に戻ります。
  • 復旧してもスタッシュは残ります。不要なら破棄しましょう。stash ビューで、破棄したいスタッシュにカーソルを合わせて [!] キーを押します。
  • 復旧と同時にスタッシュを破棄することもできます。[A] (apply) キー の代わりに [P] (pop) キーを押します。
図 11 (a)
図 11 (a)
図 11 (b)
図 11 (b)
図 11 (c)
図 11 (c)
図 11 (d)
図 11 (d)
図 11 (e)
図 11 (e)
図 11 (f)
図 11 (f)

Tig 起動時にブラウズする範囲をしぼり込む

本稿で説明に用いているサンプルプロジェクト程度ならいいんですが、実際の実用的なプロジェクトではコミット件数がものすごいことになると思います。FreeBSD のカーネルソースや ports などもそうです。そんなときは Tig を起動するときにブラウズする範囲をしぼり込むことができます。いくつか例を挙げます。

ファイル名でしぼり込む。

% tig src/ejs/template.ejs

期間でしぼり込む。

% tig --after="2023-08-11" --before="2023-08-13"

コミットハッシュでしぼり込む。

% tig 1bea5e3..4095817

HEAD の 10 個前から HEAD まで。

% tig HEAD~10..
その他にも Git 界隈で通用する範囲指定方法はたいてい使えると思うので、いろいろ試してみてください。

今回はここまで

今回見たように、いくつかの機能は Tig には実装されておらず、Git コマンドを直接入力する必要があります。ただこれは、自分で何らかのキーにバインドすることが可能です。次回はそのあたりのカスタマイズに関する話をしたいと思います。