yu nkt’s blog

nkty blog

I'm an enterprise software and system architecture. This site dedicates sharing knowledge and know-how about system architecture with me and readers.

Squash commit

背景

APIの仕様をRAMLで記述する際に、api-designerを使っていますが、そのContribution guideに以下のような記述がありました。

Before submitting a pull request, you need to execute squash commits. Submit the pull request afterwards to have them considered for merging into the main API Designer repo;

プルリクエストを送信する前に、スカッシュコミットを実行する必要があります。 その後にプルリクエストを送信すれば、それらの変更がAPI Designerのメインのリポジトリへマージして良いか検討されます。

あまり会社内の普段の開発では使わないのですが、OSSの開発に携わったりする場合は、api-designerのようにPull request前にsquash commitを要求するケースも多々あるので、知っておいた方が良いでしょう。

Squash commitとは

端的に言うと、複数回のコミットを一つにまとめるための手段です。

コミットの粒度は諸説あり、問題が起きた時に戻せられる粒度、細かい機能が完成した(テストが通る)粒度などがありますが、往々にして一つのコミットは一回のPull requestに対し細かすぎることがあります。 そうすると、複数回のコミットが詰まったPull requestを提出することになると思いますが、それではOSSのメンテナーはPull requestのレビューがしにくくなります。

そのため、OSSのメンテナーの要望として、複数回のコミットを一回のコミットにまとめてほしいという場合があります。 ここでSquash commitを使います。 Squash commitを行い、複数回のコミットを一回のコミットにまとめた上で、Pull requestを提出するのです。

Squash commitのやり方

あるブランチに複数回のコミットがなされていたとして、別のブランチにその複数回のコミットを一回のコミットとしてマージします。

下が、squash commitの例です。

~/dev $ mkdir squash-practice

~/dev $ cd squash-practice/

~/dev/squash-practice $ git init
Initialized empty Git repository in /Users/y-nakata/dev/squash-practice/.git/

~/dev/squash-practice [master #]$ touch README.md

~/dev/squash-practice [master #%]$ git add .

~/dev/squash-practice [master +]$ git commit -m "Add README"
[master (root-commit) 6466ea0] Add README
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.md

~/dev/squash-practice [master]$ git checkout -b readme
Switched to a new branch 'readme'

~/dev/squash-practice [readme]$ echo "This is a project for practice of squash commit" > README.md

~/dev/squash-practice [readme *]$ git commit -am "Add description"
[readme faaa0b4] Add description
 1 file changed, 1 insertion(+)

~/dev/squash-practice [readme]$ echo "This is second line" >> README.md

~/dev/squash-practice [readme *]$ git commit -am "Add second line"
[readme 2559fc0] Add second line
 1 file changed, 1 insertion(+)

~/dev/squash-practice [readme]$ git checkout -b create-readme master
Switched to a new branch 'create-readme'

~/dev/squash-practice [create-readme]$ git merge --squash readme
Updating 6466ea0..2559fc0
Fast-forward
Squash commit -- not updating HEAD
 README.md | 2 ++
 1 file changed, 2 insertions(+)

~/dev/squash-practice [create-readme +]$ git commit -m "Create README to show description"
[create-readme 61e0a68] Create README to show description
 1 file changed, 2 insertions(+)

~/dev/squash-practice [create-readme]$ git log
commit 61e0a685984d8a233da4a75d3b2c579ea8e422f2 (HEAD -> create-readme)
Date:   Thu Jan 3 23:27:58 2019 +0900

    Create README to show description

commit 6466ea08ede8f3478919945136926431e4681d3c (master)
Date:   Thu Jan 3 23:22:10 2019 +0900

    Add README

README.mdを作成してから、readmeブランチをmasterブランチから作成しています。

readmeブランチでは、README.mdを2回修正しており、それぞれの修正に対してコミットが行われています。 つまり、2回コミットが行われています。

その後、create-readmeブランチをmasterブランチから作成し、readmeブランチの内容をcreate-readmeブランチにマージします。 この時のgit merge--squashオプションをつけています。

最後にgit commitをし、git logで履歴を確認しています。 create-readmeブランチでは一回のコミットがあったように見えています。

まとめ

長期的に開発されるOSSや大規模なプロジェクトでは、コミットログも他人から見られるので、わかりやすい履歴をつけておきましょう。 今回は、そのための主要な方法であるsquash commitについて、メモしておきました。