Go
Git
ワンライナー
rebase

最速でGitのコミットをまとめるコマンドqs(quick squash)を作ってみた

はじめに

皆さんはGitを使っていますか?私はGit歴1年ほどです。
それまではバージョン管理はSVNを使っていましたが、とあるプロジェクトで初めてGitに触れました。
当時SVNに慣れていた私はGitに戸惑いましたが、コミット(セーブポイント)さえすればあとからログを奇麗にしたり、無かったことにしたり出来るので、今ではGit最高!もうSVNには戻りたくありません。
ですが、ビビって細かくコミットをしすぎると後から「あーこれまとめられるやん!」ってことが日常茶飯事!

通常だと以下の手順でrebaseしてコミットをまとめます

  • git rebase -iでHEADから対象の過去コミットまで指定
  • GIT EDITOR(vimとかnanoとか)が立ち上がり1つ1つのコミットに対して「pick -> squash」に書きかえる
  • GIT EDITORを閉じる

でも、毎回この手順踏むのしんどいなあということでコマンドを作りました。

NOTE: @chaspy と一緒に作成しました。本記事ではqsコマンド作るまでの試みや過程、技術的な要素は割愛します。もちろん、学びもあったので別の記事できちんと掲載します。一緒に協力しながらなんとか作れました。とても感謝しています:grinning:

どんなコマンド

ワンライナーで複数のコミットを1つにまとめるコマンドです。
コマンドの書式例では、 HEAD~iからHEAD~(j-1)HEAD~jにコミットメッセージを「quick squash」にして1つにまとめます。

$ qs -n i..j -m"quick squash" -f

例を示してみましょう。
fileを追加したコミットがたくさんあります。

$ git log --oneline
becd1ab (HEAD -> master) Add file-10   // HEAD~0
e5cc748 Add file-9                     // HEAD~1
bbd11e8 Add file-8                     // HEAD~2
f93742b Add file-7                     // HEAD~3  ここから
5ca590b Add file-6                     // HEAD~4  ~
ea8ba99 Add file-5                     // HEAD~5  ~
39f4b30 Add file-4                     // HEAD~6  ここまでのコミットを
632e3ee Add file-3                     // HEAD~7  このコミットに含めたい
ce882d0 Add file-2                     // HEAD~8
3ab0e11 Add file-1                     // HEAD~9
3105266 Add file-0                     // HEAD~10
f712ef6 Initial commit

いろいろあって、「Add file-7」から「Add file-4」までを「Add file-3」のコミットに含めたくなったとします。
また、含まれたコミットのコミットメッセージをなんだか「Hello QS」にしたくなりました。
ちなみに-dはデバッグオプションです。大量のログが出ます。

$ ./qs -n 3..7 -d -m "Hello QS"
INFO[0000] [11] pickup -> pickup  f712ef6 Initial commit 
INFO[0000] [10] pickup -> pickup  3105266 Add file-0 
INFO[0000] [ 9] pickup -> pickup  3ab0e11 Add file-1 
INFO[0000] [ 8] pickup -> pickup  ce882d0 Add file-2 
INFO[0000] [ 7] pickup -> pickup  632e3ee Add file-3 
INFO[0000] [ 6] pickup -> squash  39f4b30 fixup! Add file-3 
INFO[0000] [ 5] pickup -> squash  ea8ba99 fixup! Add file-3 
INFO[0000] [ 4] pickup -> squash  5ca590b fixup! Add file-3 
INFO[0000] [ 3] pickup -> squash  f93742b fixup! Add file-3 
INFO[0000] [ 2] pickup -> pickup  bbd11e8 Add file-8 
INFO[0000] [ 1] pickup -> pickup  e5cc748 Add file-9 
INFO[0000] [ 0] pickup -> pickup  becd1ab Add file-10 
Do you squash the following commits?(y/n)
y
…ログ…
$ git log --oneline
665635b (HEAD -> master) Add file-10
25a658c Add file-9
446f5ad Add file-8
da0197d Hello QS
ce882d0 Add file-2
3ab0e11 Add file-1
3105266 Add file-0
f712ef6 Initial commit

$ git show da0197d  // HelloQSのコミット内容を見る
commit da0197d24cffbac543ba1911cf05dac967f6fb8e
Author: git fixup <git-fixup@example.com>
Date:   Fri Aug 24 23:19:10 2018 +0900

    Hello QS

diff --git a/file-3 b/file-3
new file mode 100644
index 0000000..6d0cc47
--- /dev/null
+++ b/file-3
@@ -0,0 +1 @@
+file-3
diff --git a/file-4 b/file-4
new file mode 100644
index 0000000..e53a9ad
--- /dev/null
+++ b/file-4
@@ -0,0 +1 @@
+file-4
diff --git a/file-5 b/file-5
new file mode 100644
index 0000000..079a298
--- /dev/null
+++ b/file-5
@@ -0,0 +1 @@
+file-5
diff --git a/file-6 b/file-6
new file mode 100644
index 0000000..e01f1fc
--- /dev/null
+++ b/file-6
@@ -0,0 +1 @@
+file-6
diff --git a/file-7 b/file-7
new file mode 100644
index 0000000..f8b15d3
--- /dev/null
+++ b/file-7
@@ -0,0 +1 @@
+file-7

想定どおりにコミットがまとまっていると思います^^
このとき-fオプションを使うと確認画面を出さずシュッと実行します。

$ qs -n 3..7 -m "Hello QS" -f
Successfully rebased and updated refs/heads/master.

$ git log --oneline
585f9ee (HEAD -> master) Add file-10
9a5edfc Add file-9
9ec94c3 Add file-8
a469db8 Hello QS
f101a87 Add file-2
c5bccf9 Add file-1
4d40f78 Add file-0
1c945c1 Initial commit

めっちゃQuickです。でも、qsって打つのしんどいと思うのでAliasして下さい:persevere:

インストール方法

Go言語で書いてますので、go getを使います。Go言語がデフォルトでクロスコンパイルに対応しており、Windows,Mac,Linuxに対応できるところも強みです。(Windowsは文字の色付けが文字化けしたままになっています。次期リリースで修正します)

Go言語の環境構築(Goをインストール、環境変数の設定)は完了していることが前提です。

$ go get github.com/kamontia/qs

これだけでqsコマンドが使えます。

ちなみに、ソースコードは以下に置いています。
Github::qs::Code

バイナリはReleaseページに置いています。(MacOS,Windows,Linux)
Github::qs::Release

今後

リリースバージョン0.1.0ということで、ソースコードが超絶スパゲティです。そしてGoは初めて触りましたし、なんならコード書き終わった後に「リファクタが必要すぎる&Goっぽさが出てるのか??」ということでGoの本を購入しました。OS依存の部分についてさっそく有効な情報も見つけたので対応を進めていきたいと思っています。
0.1.0はとりあえず動くものなので気にせず進めましたし、0.2.0に向けてのモチベーション維持のために投稿させて頂きました。まだまだ改善の余地しかないヤツですが、皆さんの協力も得やすいようなコードに修正していくので暖かく見守って頂ければと思います。