はじめに
今年の新入社員向けの研修でGitの担当になったのですが,研修で使う環境をどうするかで迷っていました.Git本来の基本を押さえやすいように通常のCLIでGitを教えるか,とっつきやすいように何らかのGUIツールでGitを教えるか悩ましいところです.ちなみに研修自体はWindowsで行いますが,業務ではLinuxデスクトップ環境で開発しているグループもあるので,どちらでも使えるつぶしのきく環境がベストです。
そんな中ネットをあさっていると**「Git for WindowsにTigが組み込まれたよ!」**との情報を見つけました.
Release Git for Windows 2.14.2 · git-for-windows/git
そこで今回はWindowsでもLinuxでも使えて,コミットツリーやDiff等の情報も見やすく,本来のGitコマンドともあわせて使いやすいCUIツールであるTigを採用しました.
Tigについて基本的な操作を一通りまとめていきます.
以降は[Emacs] magitチュートリアル - Qiitaの内容を参考にして,magitでの操作をTigに置き換えたものになります.
Tigとは
CUIのGitクライアントツールです.特徴として以下のようなものがあげられます.
- コミット履歴がツリー構造で確認しやすいです.
- ツリーからファイルの差分も表示できます.
- 情報表示だけでなく,add や commitなどの操作にも対応しています.
- Tig上で外部のコマンドの実行ができ,それにキーバインドを設定することもできます.

インストール
Windowsの場合は「Git for Windows」に同梱されています.インストーラをダウンロードしてインストールしてください.(Git for Windows 2.14.2以降のバージョンに組み込まれています.)
Git for Windows
Linuxの場合はパッケージマネージャからインストールしてください.以下はDebianの場合です.
$ sudo apt install tig
以降の内容はWindows環境でGit Bash上で動作を確認しながらまとめています.
- Windows 10 Pro
- git version 2.16.2.windows.1
- tig version 2.3.3
リポジトリ作成
Tigは基本的にGitのリポジトリに移動してから立ち上げて使います.そこでまずGit Bashからリポジトリを作成します.ここではリポジトリの初期化を行ったあとに,foo.txt
ファイルを作成して初回コミットを行っています.
$ mkdir tig_tut
$ cd tig_tut/
$ git init
$ touch foo.txt
$ git add foo.txt
$ git commit -m "初回コミット"
Tigへの入り口
Gitリポジトリができたらtig
コマンドでTigを起動します.対象のディレクトリに移動して操作してください.(上記からの流れだとそのままの場所でOKです.)
$ tig
Tigが立ち上がるとまずは「main view」が立ち上がってコミットツリーが表示されます.ここがTigへの入り口となります.

また,デフォルトでは「main view」にはチェックアウトしているブランチのみを表示しますが,--all
オプションを指定すると全てのブランチが表示されます.
$ tig --all
Tigを終了する場合はQキーを押します.また後述するようにTigでは様々なViewを表示することができますが,そのViewを閉じる(前のViewに戻る)にはqキーを押します.
hから始める
hキーを押すとショートカット一覧が表示されます.「迷ったらまずh」を覚えておくとよいです。

コミットしてみる
foo.txt
を開いて以下の編集を行います.
A
保存した後にsキーで「status view」を表示します.そこからカーソルを移動してfoo.txt
を選択しEnterキーで「diff view」が表示されます.

この状態でのカーソル移動まわりのキーバインドは以下のようになっています.ちなみに画面が1画面表示の場合はどちらのキーでもカーソル移動できます.
Key | 内容 |
---|---|
Ctrl + n | 画面左側のstatus viewのカーソルを下方向に移動 |
Ctrl + p | 画面左側のstatus viewのカーソルを上方向に移動 |
j | 画面右側のdiff viewのカーソルを下方向に移動 |
k | 画面右側のdiff viewのカーソルを上方向に移動 |
「status view」でfoo.txt
を選択しuキーでstagingされます.取り消す場合はもう一度uキーです.

stagingした状態からCキーでコミットメッセージを入力するためのエディタが起動します.

コミットメッセージの入力後Enterキーでコミットが完了します.
コミットログを見る
mキーで「main view」を表示すると先ほどのコミットログが確認できるはずです.コミットを選択してEnterキー押すと「diff view」が表示されます.

この状態でのカーソル移動まわりのキーバインドはコミット時のものと同じです.「main view」でのカーソルを移動すると「diff view」の表示も追従して更新されます.
Key | 内容 |
---|---|
Ctrl + n | 画面左側のmain viewのカーソルを下方向に移動 |
Ctrl + p | 画面左側のmain viewのカーソルを上方向に移動 |
j | 画面右側のdiff viewのカーソルを下方向に移動 |
k | 画面右側のdiff viewのカーソルを上方向に移動 |
ブランチを作成する
現在のmasterからブランチを作成してみます.ですがTigにはブランチを作成する機能は標準で用意されていないようです.そこでブランチの作成は通常のgitコマンドを使用します.ただしTig上から外部のコマンドの実行が可能なのでターミナルを切り替える必要はありません.:キーを押すと画面下部でコマンドを受け付けてくれます.:!<command>
の形式でコマンドを実行します.
:!git checkout -b <作成するブランチ名>

rキーで「refs view」を表示すると作成したブランチが確認できます.新しいブランチが表示されない場合はRキーで画面を更新してください.ここではnew-branch
ブランチを作成しています.

また.tigrc
ファイルに,上記のような操作に対してキーバインドを設定することができます.例えば以下のように設定すると「refs view」上で作成元となるブランチを選択している状態でnキーを押して新しいブランチ名を入力すると,その場所にブランチが作成されます.?git checkout
とすることで実行前に確認メッセージを出すようにしています.
.tigrcの置き場はWindowsの場合は%USERPROFILE%
,Linuxの場合は$HOME
になります.ファイルを作成した後にTigを再起動して設定を反映させてください.
bind refs n ?git checkout -b "%(prompt Enter new branch name: )" %(branch)
ブランチをマージする
前準備として作成したブランチ上でコミットを行います.ここでは2件コミットしています.
- foo.txtに
B
を追記(add B) - foo.txtに
C
を追記(add C)
mキーで「main view」を表示すると以下のようになっています.

この状態からmasterにnew-branchの内容をマージします.
まずはmasterに切り替えます.(チェックアウトします.)
rキーで「refs view」を表示して,masterを選択した状態でCキーを押してチェックアウトします.(本当に切り替えるかY/Nで聞かれるのでyキーを押します.)
切り替えに成功すると「refs view」でmasterが一番上に来ているはずです.(masterがチェックアウトされた状態です.)

マージ機能も標準では用意されていません.:キーから下記のコマンドを入力してマージします.
:!git merge new-branch
.tigrcに登録する場合は下記のようにするとよいです.「refs view」でMキーを押すと現在のブランチにカーソルが当たっているブランチをマージします.
bind refs M ?git merge %(branch)
コンフリクト等が起きなければこれでマージが完了します.mキーで「main view」を見るとマージできていることがわかります.

不要ブランチの削除
マージして不要になったnew-branch
ブランチの削除を行います.まずrキーで「refs view」を表示して削除するブランチを選択します.

その状態で!キーを押すと本当に削除するかY/Nで聞かれるので,yキーを押して削除します.また現在チェックアウトしているブランチの削除はできません.その場合はあらかじめCキーで別ブランチをチェックアウトしておく必要があります.
new-branch
を削除すると「main view」と「refs view」の表示は以下のようになります.更新されない場合はRキーを押して表示の更新を行ってください.
プッシュする
ここまでの作業をリモートのリポジトリにプッシュしてみます.今回はGitHubにtig_tut
リポジトリを作成したものとして話を進めます.またSSH keyの登録なども事前に完了しているものとします.リモートの追加は:キーからGitのコマンドを使って行います.ちなみにorigin
とは慣習的に使用されるリモートのデフォルトの名前です.
:!git remote add origin git@github.com:<アカウント名>/tig_tut.git
プッシュ機能も標準では用意されていません.:キーから下記のコマンドを入力してプッシュします.
:!git push -u origin master
また.tigrcに登録する例も載せておきます.rキーで「refs view」を表示して,Pキーを押すとカーソルが当たっているブランチをoriginにプッシュします.
bind refs P ?git push -u origin %(branch)
プッシュに成功すると,rキーの「refs view」にorigin/master
が追加されています.

mキーの「main view」でもorigin/master
が確認できます.

ログからのブランチ作成
「main view」のツリーの任意のコミットにブランチを作成します.そのための前準備としてコミットIDが表示されるように設定します.
set main-view = id date author commit-title:graph=yes,refs=yes
各コミットの左にコミットIDが表示されるようになります.

:キーから下記のコマンドを入力して任意の場所に新しいブランチを作成できます.
:!git checkout -b <作成するブランチ名> <ブランチを作成する場所のID>
また「main view」で選択している行のコミットIDをクリップボードにコピーするキーバインドの設定例を載せておきます.xキーに割り当ててあります.
bind main x @bash -c "echo -n '%(commit)' | clip"
下図ではnew-branch-2
ブランチを133f12d
に作成しています.表示されない場合はRキーを押して表示の更新を行ってください.

マージコンフリクトに対処する
new-branch-2
ブランチでfoo.txt
の内容を更新してコミットします.この編集内容が「add C」コミットとコンフリクトします.
A
B
D
mキーから「main view」を表示すると,以下のようになっているはずです.

master
に戻ってnew-branch-2
ブランチをマージしてみます.
rキーで「refs view」を表示して,masterを選択した状態でCキーを押してチェックアウトします.
チェックアウトに成功すると「refs view」でmasterが一番上に来ているはずです.(masterがチェックアウトされた状態です.)

ここから,前回と同様に:キーから下記のコマンドを入力するか,「refs view」でnew-branch-2
ブランチを選択し,登録したキーバインドであるMキーを押します.
:!git merge new-branch-2
するとコンフリクトしたとのメッセージが表示されます.

Enterキーを押してTigに戻り,sキーで「status view」を表示するとfoo.txtファイルにU
の目印がついておりマージが完了していない状態です.foo.txtファイルを選択した状態でEnterキーを押すと「diff view」が表示されコンフリクトしている様子が見えます.

foo.txtファイルを選択した状態でMキーを押すとマージツールが起動します.またはeキーを押すとエディタが立ち上がります.お好みのほうを立ち上げて手動で修正を行いコンフリクトを解消します.
今回は以下のように修正します.


修正後「status view」に戻ってfoo.txtファイルを選択しuキーでstagingします.

stagingした状態からCキーでコミットを行います.

mキーから「main view」を表示するとマージされていることが確認できます.

編集の一部をコミット
masterで作業を続けます.foo.txt
の内容を下記のように更新します.
A
HOGE
PIYO
B
C
D
E
sキーで「status view」を表示します.foo.txtを選択してEnterキーを押して「diff view」を表示します.

「diff view」でコミットしたい行(今回は+E)を選択した状態で1キーを押すとその行のみがstagingされます.

この状態でCキーでコミットすると「E」の追加だけをコミットすることができます.

不要な編集を戻す
先ほどコミットしなかった編集を削除します.sキーで「status view」を表示し,foo.txtを選択してEnterキーを押して「diff view」を表示します.コミットしなかった「HOGE」と「PIYO」が残っています.

この状態で!キーを押すと本当に破棄するかY/Nで聞かれるので,yキーを押して変更を戻します.
sキーで「status view」を表示すると,編集内容が破棄されています.

ここまでをプッシュ
ここまでの状態を前回と同様の手順でプッシュしておきます..tigrc
にキーバインドを登録していれば,rキーで「refs view」を表示して,Pキーを押すとカーソルが当たっているブランチをoriginにプッシュします.

フェッチ・プルする
前準備としてGitHub側でファイルの編集を行います.GitHubでfoo.txt
を表示して鉛筆マークから編集画面を開いて,最終行にF
を追加します.

フェッチ機能はTigには標準では用意されていません.:キーから下記のコマンドを入力してフェッチします.
!git fetch
.tigrcに登録する場合は下記のようにするとよいです.「main view」でFキーを押すとフェッチします.
bind generic F none
bind main F ?git fetch
GitHubでの変更が取り込まれています.この時点ではローカルのmaster
ブランチには反映されていません.

プル機能もTigには標準では用意されていません.:キーから下記のコマンドを入力してプルします.
!git pull origin master
.tigrcに登録する場合は下記のようにするとよいです.「refs view」で対象のブランチを選択した状態でLキーを押すとプルします.
bind refs L ?git pull origin %(branch)
GitHubでの変更がローカルのmaster
に取り込まれました.

今回はフェッチしてプルという流れでしたが,フェッチせずいきなりプルしても問題ありません.
おまけ
ここまでの手順で出てこなかったものについて付け加えておきます.
クローンする
今回の説明ではローカルで作成したリポジトリをリモートのGitHubにプッシュする流れでしたが,すでにリモートに存在しているリポジトリをローカルにクローンすることもできます.
$ git clone git@github.com:<アカウント名>/tig_tut.git
ツリービューを見る
tキーで「tree view」を表示することができます.「main view」から移動する場合は,選択しているコミット時点のディレクトリ・ファイルの一覧が表示されます.

「tree view」でファイルを選択している状態からeキーを押すとテキストエディタでファイルを開くことができます.
各行ごとの変更履歴を見る
tキーの「tree view」でファイルを選択している状態からbキーを押すと「blame view」が表示されます.ファイルの各行ごとのコミット履歴を確認することができます.

スタッシュする
作業中のファイルの編集を,いったん退避しておきたいときにスタッシュが便利です.
foo.txt
の内容を下記のように「G」を追加して更新します.
A
B
C
D
E
F
G
スタッシュする機能は標準では用意されていません.:キーから下記のコマンドを入力してスタッシュします.
:!git stash save <スタッシュコメント>
.tigrcに登録する場合は下記のようにするとよいです.「main view」でSキーを押すと現在の作業中の内容がスタッシュされます.
bind main S ?git stash save "%(prompt Enter stash comment: )"

「status view」を確認すると編集途中のファイルがなくなっています.

スタッシュされている中身はyキーから「stash view」を表示して確認することができます.

「stash view」からは以下の操作が標準で用意されています.
Key | 内容 |
---|---|
A | 選択しているスタッシュを適用します.(git stash apply) |
P | 選択しているスタッシュを適用して削除します.(git stash pop) |
! | 選択しているスタッシュを削除します.(git stash drop) |
直前のコミットを取り消す
コミットを取り消す系の操作はいろいろとありますが,ここでは直前のコミットIDに対してgit reset
することで,最新のコミットを取り消す方法を例にあげます.
以下の.tigrc
の設定は,ファイルの編集などはそのままでstagingされたプッシュする直前の状態に戻すキーバインドを「main view」に!キーで割り当てています.
bind main ! ?git reset --soft HEAD^
Gitの管理から特定のファイルを除外する
除外設定を行いたいディレクトリ(トップでもOK)に.gitignore
という名前でファイルを作成し,除外したいファイルの情報を記述しておくと,Gitの管理対象から外れます.
.gitignore自体をコミットしておくと,複数人で開発をするときに同じ除外ルールでGit管理をすることができます.
設定パターンの一例を下記に載せておきます.
# 拡張子が.exeのファイルを除外する
*.exe
# Bin/binディレクトリとその中身を除外する
[Bb]in/
# 特定のディレクトリの中の特定の拡張子のファイルを除外する
out/*.log
Tigのカスタマイズ
ここまでの説明で出てきた.tigrc
の設定内容についてまとめておきます.
bind generic F none
bind main F ?git fetch
bind main S ?git stash save "%(prompt Enter stash comment: )"
bind main ! ?git reset --soft HEAD^
bind main x @bash -c "echo -n '%(commit)' | clip"
bind refs n ?git checkout -b "%(prompt Enter new branch name: )" %(branch)
bind refs M ?git merge %(branch)
bind refs P ?git push -u origin %(branch)
bind refs L ?git pull origin %(branch)
set main-view = id date author commit-title:graph=yes,refs=yes
おわりに
Git(Tig)を使いながら開発を進めるにあたって,必要最小限の操作は説明できたと思います.また.tigrc
をカスタマイズすることでさらに便利に使っていけるようになると思うので,ぜひ使いこなしていってほしいです.