はじめに
2年目のエンジニアになりました、FM_Harmonyです。 Rakus Developers Blogでは4回目の投稿です。
↓前回の記事はこちら tech-blog.rakus.co.jp
さて、弊社ではビアバッシュというイベントを行っています。(ビアバッシュ・・・?という方はコチラ)
今回はその際に私が発表したことについて、補足も踏まえつつまとめたいと思います。
テーマはtigでcommitをきれいに!です。
tigとは?
Tig is an ncurses-based text-mode interface for git.
--- https://github.com/jonas/tig より
tigとはターミナル上で動作するgitリポジトリのブラウザ・・・と言えます。 logの閲覧や、commit操作などを簡単に行うことができる便利なツールです。 動作も高速なので、慣れてしまえばストレスなくcommit操作を行うことができます。
準備
今回はwindowsで実演を行いますが、macでのインストール方法も記載しておきます。
tigのインストール(windowsの場合)
Git for Windows の v2.14.2から、tigが付属するようになりました。 なので、windowsではtigを特別にインストールする必要はありません。
tigのインストール(macOSの場合)
Homebrewを使うことで簡単にインストールすることができます*1。 tigをmacOSにインストールするためには、ターミナルから以下のコマンドを実行します。
$ brew install tig
commitをきれいにする理由
実演の前に、commitを分割する理由を説明します。 開発を行っていると、どうしても commit が大きくなったり、適切に commit が分けられていないという状況に出くわすと思います。
このcommitをそのままにすると何が良くないのかというと、
- レビュワーがpushされたcommit一つ一つについて規模を把握しづらい。
- 後から変更に問題があることが発覚した際に、問題が発生した commitを追いづらくなる.
といった事が挙げられます。
なので、上記の問題が発生しないようにcommitを適切な大きさに分割するとよいです。 その際に、tigを利用すると高速かつ簡単にcommitの分割を行うことができます。
使ってみる
実演の前準備
実際にtigを使って、commitの分割を行ってみましょう。
現在、sampleReposというリポジトリが以下のような構成になっているとします。
SampleRepos/ │ └ NewFile.java
NewFile.java の内容は以下の通りです。
public class NewFile { public static void main(String args[]){ System.out.println("Hello World!"); } }
この状態で一度 commit したとします。
$ git add NewFile.java $ git commit -m "first commit"
機能を追加して、まとめてcommitする
さて、この状態からNewFile.javaにmethodA
とmethodB
というメソッドを追加することになりました。
public class NewFile { public static void main(String args[]){ System.out.println("Hello World!"); methodA(); methodB(); } public static void methodA(){ System.out.println("methodA!"); } public static void methodB(){ System.out.println("methodB!"); } }
とりあえず今回の変更をまとめてcommitします。
$ git add NewFile.java $ git commit -m "add methodA and methodB to NewFile"
git rebase -i で、commitの分割を始める
では、早速commitを分割してみましょう。
今回は、先程のadd methodA and methodB to NewFile
というcommitを
add methodA to NewFile
add methodB to NewFIle
という二つのcommitにします。
まず、以下のgitコマンドを実行します。
$ git rebase -i HEAD~1 // HEAD~の後の数字は適宜変更する.
すると、以下のような文章がテキストエディタで表示されるはずです*2。
pick 1599a7b add methodA and methodB to NewFile # Rebase 9276511..1599a7b onto 9276511 (1 command) // 以下省略.
次に、add methodA and methodB to NewFile
のcommitを分割したいので、その横にある pick を edit へ変更してエディタを保存して閉じます。
その後、以下のコマンドを実行します。
$ git reset HEAD~1
こうして問題のcommitによる変更がunstagedな状態になりました。 ( = 変更がaddされる前の状態)
tigを使ってcommitを分割する - その1
さて、この状態で以下のコマンドを実行します。
$ tig
すると、次のような画面が表示されるはずです。
yyyy-mm-dd Unknown o Unstaged changes yyyy-mm-dd Committer I [HEAD] first commit
これがtigのMain Viewと呼ばれる画面です。 この状態で、Shift + S
キーを押すと、次のような画面へ遷移します。
Interactive rebase master Changes to be committed: (no files) Changes not staged for commit: M NewFile.java Untracked files: (no files)
これがtigのStatus Viewと呼ばれる画面です*3。
この状態で、Changes not staged for commit:
以下の M NewFile.java
にカーソルを合わせてEnter
キーを押すと、次のような画面が画面下(もしくは画面右)に分割されて表示されます。
NewFile.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/NewFile.java b/NewFile.java index 9fde6b2..01487f8 100644 --- a/NewFile.java +++ b/NewFile.java @@ -1,5 +1,15 @@ public class NewFile { public static void main(String args[]){ System.out.println("Hello World!"); + methodA(); + methodB(); + } + + public static void methodA(){ + System.out.println("methodA!"); + } + + public static void methodB(){ + System.out.println("methodB!"); } }
これが、Diff Viewと呼ばれる画面です。
長くなりましたが、この画面上でstaged / unstagedする部分を指定できます。
tigを使ってcommitを分割する - その2
それでは、main
メソッド内の
methodA();
と、methodA
メソッドの定義である
public static void methodA(){ System.out.println("methodA!"); }
という変更が含まれたcommitを作成しましょう。
今回は1行ずつstagedにしていきます。 Diff Viewで、commitに含める変更箇所にカーソルを合わせて1
キーを押すことで1行づつ staged にすることができます。 ただし、変更内容が表示された画面でのカーソル移動には j
, k
キーを使うことに注意します。
完了したら、Status Viewが
Interactive rebase master Changes to be committed: M NewFile.java Changes not staged for commit: M NewFile.java Untracked files: (no files)
となっているはずです。 つまり、同じファイルに対する変更をcommitに含むものと含まないものに分けることができたということです。
念のため、Changes to be committedの下にあるNewFile.javaのDiff Viewが以下のようになっていることを確認してください。
NewFile.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NewFile.java b/NewFile.java index 9fde6b2..becff04 100644 --- a/NewFile.java +++ b/NewFile.java @@ -1,5 +1,10 @@ public class NewFile { public static void main(String args[]){ System.out.println("Hello World!"); + methodA(); + } + + public static void methodA(){ + System.out.println("methodA!"); } }
q
キーを押してDiff Viewを閉じてからStatus View上で Shift + C
キーを押すと、git commit
コマンド入力時と同じ画面が現れます。 そこで、 commit messageを add methodA to NewFile.java.
としてcommitを行います。
こうして、methodA
の追加に関する変更のみを含んだcommitが完成しました。
tigを使ってcommitを分割する - その3
その後、画面にはEnter
キーを押すようにメッセージが出ているはずなので、それに従います。
すると、Main Viewに画面が戻るはずです。 ここで先程のadd methodA to NewFile.
というcommitが表示されていない場合は、Shift + R
キーでMain Viewを更新します。
では、同じようにmethodB
の追加に関する変更のみを含んだcommitを作成しましょう。Status Viewから、unstagedなNewFile.java
に関するDiff Viewを開きます。
NewFile.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NewFile.java b/NewFile.java index becff04..01487f8 100644 --- a/NewFile.java +++ b/NewFile.java @@ -2,9 +2,14 @@ public class NewFile { public static void main(String args[]){ System.out.println("Hello World!"); methodA(); + methodB(); } public static void methodA(){ System.out.println("methodA!"); } + + public static void methodB(){ + System.out.println("methodB!"); + } }
また1行ずつstagedにしても良いのですが、今度は全ての変更をcommitに含みます。 なので、U
キー を押してファイルの変更全てを一括でstagedにします。
Status Viewを確認すると、以下のようになるはずです。
Changes to be committed: M NewFile.java Changes not staged for commit: (no files) Untracked files: (no files)
そして、Diff Viewを閉じた状態でShift + C
キーを押してgit commit
を行います。 commit messageはadd methodB to NewFile.java.
とします。
commitが完了してMain Viewへ戻った後、q
キー を押してtigを終了します。
その後、以下のコマンドを実行して、add methodA and methodB to NewFile
というcommitへの編集を終了します。
$ git rebase --continue
その後、再びtig
コマンドを実行すると、確かにcommitが分割されたことが分かります。
yyyy-mm-dd hh:ss Committer o [master] add methodB to NewFile.java yyyy-mm-dd hh:ss Committer o add MethodA to NewFile. yyyy-mm-dd hh:ss Committer I first commit
まとめ
いかがでしたでしょうか。 ちょっと伝わりづらい部分もあったと思いますが、実際にtig
を使ってみるとその便利さが分かると思います。
commitを見やすく分割するのは、レビュアーの負担を減らすだけでなく、将来の自分が変更内容を理解するのに役立ちます。 なので、この記事をご覧いただいた方が、tig を使うことで少しでも楽に commit をきれいにできれば幸いです。
最後に、以下の言葉を引用してこの記事のまとめとします。
コードは他の人が最短時間で理解できるように書かなければならない
--- リーダブルコード - より良いコードを書くためのシンプルで実践的なテクニック P.3 より
(本来はコードの可読性に関する言葉ですが、commitをきれいにするということは上記の考え方と同じものが根底にあると感じました)
参考資料
- VS Code の統合ターミナルで Tig が標準装備された Git for Windows を使う
- gitクライアント「tig」を使いこなすための最低限覚えるべき6つのコマンド
- 発表に利用したスライド(一部編集済)