【その2】git をさらにもう一歩使いこなす!使えると生産性がぐっと高まる!

「git をもう一歩使いこなす(前回の記事)」でgitの基本のほんの少し先の機能を紹介しました。(ここでいう基本とは addしてcommitしてpushすることはできるというレベルを想定しています。)

前回の記事

【その1】git をもう一歩使いこなす!便利なオプションを紹介!

2017.03.29

この記事ではさらにそれよりも難しい(けれどとても便利な)gitの機能を紹介したいと思います。覚えなくてもなんとかなると言われればそれまでですが、覚えれば自分の生産性をぐっと高めることができるようになります。

そうやって作り出した時間で「賢くサボる」のが強いエンジニアだと思います。ここで紹介する機能は少し複雑ですがとても強力な機能です。

わからないからと最初から諦めてしまったらとても勿体ないのでこの機会に覚えてしまいましょう。

「git add -p」で行ごとに変更を add する

コミットを分ける意義

ファイルをコミットする場合コミットしたいファイルを「git add」します。しかし1ファイル内の変更を複数のコミットにわけたい場合もあります。

コミット履歴は後で何かあった時にその部分に戻ってくることになるので非常に重要な履歴です。それを「一旦コミット」とか「修正」などといったなんの役にも立たないコミットメッセージとともに大量の変更をコミットされてしまうのはそのリポジトリの負債といってもいいでしょう。そのため1コミットには可能な限り1つの意味を持たせてコミットするべきです。

しかし、開発中はそんなことを気にしてられないというのが実際のところだと思います。最初から完璧なプログラムを書くことは不可能ですので。

だからといって書き上げたプログラムをまとめて1つのコミットにするのは他人にも未来の自分にも不親切です。

そこで役に立つのが「git add -p」を使ったコミットする部分の選択機能です。

使い方

下記はファイルを2回に分割してコミットする例です。「git diff」をすると2行分の変更履歴が見れます。

// 元ファイル
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccc
dddddddddddddddddddd
eeeeeeeeeeeeeeeeeeee

// 変更後ファイル
aaaaaaaaaaaaaaaaaaaa1
bbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccc
dddddddddddddddddddd3
eeeeeeeeeeeeeeeeeeee

// git diff
-aaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaa1
 bbbbbbbbbbbbbbbbbbbb
 cccccccccccccccccccc
-dddddddddddddddddddd
+dddddddddddddddddddd3
 eeeeeeeeeeeeeeeeeeee

「git add -p」でファイルを指定すると「Stage this hunk [y,n,q,a,d,/,s,e,?]?」と聞かれます。最初の1回は普通に「git add」した場合と変わらない差分が表示されます。

分割して「git add」したい場合ここで「s」を入力します。

git add -p test.txt

diff --git a/test3.txt b/test3.txt
index 738ec26..2cec427 100644
--- a/test3.txt
+++ b/test3.txt
@@ -1,5 +1,5 @@
-aaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaa1
 bbbbbbbbbbbbbbbbbbbb
 cccccccccccccccccccc
-dddddddddddddddddddd
+dddddddddddddddddddd3
 eeeeeeeeeeeeeeeeeeee

 Stage this hunk [y,n,q,a,d,/,K,g,e,?]? s

すると今度は行ごとに「Stage this hunk [y,n,q,a,d,/,s,e,?]?」と聞かれます。これでコミットしたいところのみ「y」を入力し、コミットしたくない部分は「n」を入力します。

これでコミットしたい部分だけをステージングに乗せることができました。あとは普通に「git commit」コマンドでコミットすれば1ファイルの1部分のみをコミットできます。

// コミットしたい部分
Split into 2 hunks.
@@ -1,3 +1,3 @@
-aaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaa1
 bbbbbbbbbbbbbbbbbbbb
 cccccccccccccccccccc
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y

// コミットしたくない部分
@@ -2,4 +2,4 @@
 bbbbbbbbbbbbbbbbbbbb
 cccccccccccccccccccc
-dddddddddddddddddddd
+dddddddddddddddddddd3
 eeeeeeeeeeeeeeeeeeee
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? n

これ以外にも「git add -p」を利用した時に聞かれるオプションが複数あります。

「Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?」と聞かれた時に「a」を押すと「s」で行ごとに「git add」している状態を無視して、そのファイルの残りの変更部分を全て「git add」してくれます。

「d」はその逆で、そのファイルの残りの変更部分を全て「git add」しないという動作をします。「j,k」は「git add」するかどうかを決めずに次の差分を見に行くという挙動をしてくれます。

「e」を使うとその場で編集モードになり今の変更差分を自分で編集することができます。

このようにただ「git add」している時ではできなかった痒いところに手がとどく様々ことが「git add -p」でできるようになります。



「git rebase」でコミットをまとめる

コミットする前の変更は「git add -p」を使ってまとめたい情報ごとにコミットすることができます。しかしすでにコミットしてしまった情報をまとめたい場合もあります。

そんな時に使用するのが「git rebase -i」です。「git rebase」自体は他にも利用の機会がある便利なコマンドですが、ここでは”複数のコミットをまとめる”という場合に限定して紹介します。

下記のようなコミットがあるとします。「ファイル追加」と「ファイル追加2」という2つのコミットをした後で1つのコミットにまとめたくなったとします。

commit b967252e15273900df426b30035c4701ceaf3490
Author: author 
Date:   Mon Jan 9 23:32:51 2017 +0900

    ファイル追加2

commit 32fe808e5835dcd92526a476a13164d18e2b2a8e
Author: author 
Date:   Mon Jan 9 21:05:10 2017 +0900

    ファイル追加

commit 92958e487a95f7c427613e4df70112055372597f
Author: author 
Date:   Mon Jan 9 15:58:54 2017 +0900

    second commit

commit 96632032e41a360b040db710665770c8e02356d2
Author: author 
Date:   Mon Jan 9 15:58:20 2017 +0900

    first commit

その場合1つ前のコミットハッシュを指定します。

git rebase -i 92958e487a95f7c427613e4df70112055372597f

すると指定したコミットより新しい順に選択する画面が出てきます。「pick」というのはそのコミットはいじらないということを示しています。

pick 32fe808 ファイル追加
pick b967252 ファイル追加2

# Rebase 92958e4..b967252 onto 92958e4 (2 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

今やりたいことは2つのコミットを一つにまとめることなので使用するのは「squash」か「fixup」です。

今回は「squash」を使ってみます。「ファイル追加2」というコミットの右の「pick」を「squash」に書き換えます。

pick 32fe808 ファイル追加
squash b967252 ファイル追加2

「squash」と書いたら保存します(vimモードなので:wqですね)。すると下記のような画面が現れます。「ファイル追加」「ファイル追加2」の2つのコミットを1つにするのでそのコミットメッセージを求められている状態です。何もせずに保存するとそのまま「ファイル追加」「ファイル追加2」というコミットメッセージとなります。

コミットメッセージを書いたらまた保存します。

# This is a combination of 2 commits.
# The first commit's message is:

ファイル追加

# This is the 2nd commit message:

ファイル追加2

するとコミットが1になり下記のようなコミットログに変わります。

commit ed80192de7dc2706c5d93809993078360904ecf6
Author: author 
Date:   Mon Jan 9 16:05:01 2017 +0900

    ファイル追加

    ファイル追加2

commit 92958e487a95f7c427613e4df70112055372597f
Author: author 
Date:   Mon Jan 9 15:58:54 2017 +0900

    second commit

commit 96632032e41a360b040db710665770c8e02356d2
Author: author 
Date:   Mon Jan 9 15:58:20 2017 +0900

    first commit

「fixup」を使った場合はほとんど「squash」と同じですが、上記のようなコミットメッセージ編集画面が出ずに「pick」した側のコミットメッセージが使用されて1つのコミットにまとまります。

特に編集する必要がない場合、こちらを利用すると良いでしょう。

このようにコミットでも後から編集できる方法を知っておけば「まだ完全に整ってないけど現在の状態を保存しておきたい」という場合にガンガンコミットをすることができます。

コミットしておけば必ず履歴に残るので何かあった時に非常に重宝します。

まとめ

本記事ではわかりやすくコミットを分ける2つの方法を紹介しました。個人で使う分にはあまり気にしないかもしれませんが、プロジェクトで開発する時にはほぼ必須のスキルとなると思います。

わかりやすいコミットはプロジェクトのメンバーと未来の自分を救うことになります。是非この機会に覚えておきましょう。

プログラミングは楽しい!人生だって変わるかも!

この記事が気に入ったら
いいね!しよう

ABOUTこの記事をかいた人

ぽんぽこ

ぽんぽこです。数年前、会社に通いながらも頑張ってプログラミングスクールで学びました。私が利用したのはCodeCamp(コードキャンプ)です。なんとプログラミングを覚えたらころころと人生が変わっていって、現在は 外資系4大IT企業 に転職できました。今でも毎日勉強の日々ですが、このサイトで少しでも役立つ情報を提供できたらと思っています。プログラミングって難しそうだな...と思ってもとりあえずやってみることが大事!行動しないと現状は変わらない!▶自己紹介はこちら
話題のプログラミングスクールを厳選比較(真っ先に検討したいスクール)

2018年 最新版!話題のプログラミングスクールを厳選比較!