横着で神経質な私とあなたに贈るgit add -p

  • 539
    いいね
  • 3
    コメント
この記事は最終更新日から1年以上が経過しています。

特技はgit commit -a -m いろいろ修正です!

なんて奴は今すぐredbullの角に頭ぶつけて詫びるべき。
しかしこまめにコミットするのは面倒臭いですよね。でもrebaseやらrevertやらcherry-pickするにもコミットログは綺麗にしたい。そんなズボラで凝り性なあなたはgit add -pでコミットを整えるといいと思います。

members
1: 島村卯月
2: 渋谷凛
3: 本田未央
4: 前川みく
5: 高森藍子

なんだかよくわからない名簿があったとします。どういう因果か1コミットにつき1行の変更するのが決まりとしましょう。

下のように変更してみました。

-1: 島村卯月
+1: 福山舞
 2: 渋谷凛
 3: 本田未央
 4: 前川みく
-5: 高森藍子
+5: 日野茜

さてこのままgit addすると両方の変更が一度にステージングされてしまいます。
そこでgit add -pすると以下のようなdiffがコンソールに表示されます。

console
diff --git a/members b/members
index 15440ce..1f43716 100644
--- a/members
+++ b/members
@@ -1,5 +1,5 @@
-1: 島村卯月
+1: 福山舞
 2: 渋谷凛
 3: 本田未央
 4: 前川みく
-5: 高森藍子
+5: 日野茜
Stage this hunk [y,n,q,a,d,/,s,e,?]?  

変更のひとかたまり(ハンク)を表示し、そのハンクに対しての指示を待ち受けます。サンプルが小さいのですべて表示されますが、ハンクの間が7行以上空くと別のハンクとして扱われます。

変更を分割する

コミットを分割するのが目的なのでここでsを入力します。

console
Split into 2 hunks.
@@ -1,4 +1,4 @@
-1: 島村卯月
+1: 福山舞
 2: 渋谷凛
 3: 本田未央
 4: 前川みく
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? 

するとハンクが2つに分割され、diffの表示される範囲が狭まり一人だけの変更になりました。yでこの変更をステージングしましょう。
さらに次のハンクが表示されステージングするか否か選べますがひとまずnでスキップするかqで終了します。

git-status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   members
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   members
#

git statusを見るとmembersステージされているにもかかわらずワークスペースに残った状態になってますね。
ここでgit diffを見ると

git-diff
@@ -2,4 +2,4 @@
 2: 渋谷凛
 3: 本田未央
 4: 前川みく
-5: 高森藍子
+5: 日野茜

最終行の変更だけが残っています。
そしてステージングされた状態を確認するためにgit diff --cachedすると

git-diff-cached
@@ -1,4 +1,4 @@
-1: 島村卯月
+1: 福山舞
 2: 渋谷凛
 3: 本田未央
 4: 前川みく

先頭の変更だけがステージングされていますね。
これで1つの変更だけどコミットすることができます。

さらに分割する

ハンク同士が1行以上空いている場合はsで分割できますが、連続する行の場合はそうはいきません。

console
@@ -1,5 +1,5 @@
 1: 島村卯月
-2: 渋谷凛
-3: 本田未央
+2: 双葉杏
+3: 諸星きらり
 4: 前川みく
 5: 高森藍子
Stage this hunk [y,n,q,a,d,/,e,?]? 

このように変更した行が連続する場合sが使えません。もしこの状態を分割したい場合はeで直接編集する必要があります。

edit
# Manual hunk edit mode -- see bottom for a quick guide
@@ -1,5 +1,5 @@
 1: 島村卯月
-2: 渋谷凛
-3: 本田未央
+2: 双葉杏
+3: 諸星きらり
 4: 前川みく
 5: 高森藍子
# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged.

eを押すとエディタが立ち上がり、diffの編集画面になります。元の状態からの差分を記述することでその変更を適用することができます。

edit/2行目だけを適用する場合
# Manual hunk edit mode -- see bottom for a quick guide
@@ -1,5 +1,5 @@
 1: 島村卯月
-2: 渋谷凛
+2: 双葉杏
 3: 本田未央
 4: 前川みく
 5: 高森藍子
# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged.

上記のように内容を変更し保存するとステージングとして適用されます。ちなみにdiffとして正しければ何を書いてもいいのでこの画面で変更をさらに変更することもできます。フォーマットがおかしかったり差分が解決できない場合はステージングされません。

git-diff-cached
@@ -1,5 +1,5 @@
 1: 島村卯月
-2: 渋谷凛
+2: 双葉杏
 3: 本田未央
 4: 前川みく
 5: 高森藍子

これで連続した行の変更を分割してステージングすることができました。

これで綺麗なコミットログがつくれるね!

とは言うものの変更が増えた後にいちいちハンクを分割してコミットだのやってられないので作業のスコープをちゃんとしぼってこまめにコミットしたほうがいいと思います。ちなみに僕の特技はgit commit -a -m いろいろ修正です!

おまけ:git add -pした時のコマンド一覧

  • y - このハンクをステージングする
  • n - スキップする
  • q - 終了する
  • a - 以降のハンクをすべてステージングする
  • d - 以降のハンクをすべてスキップする
  • g - 指定したハンクへ移動
  • / - 正規表現によるハンクの検索
  • j - 未確定な前のハンクへ移動する
  • J - 前のハンクへ移動する
  • k - 未確定な次のハンクへ移動する
  • K - ハンクへ移動する
  • s - ハンクを分割する
  • e - 手動で現在のハンクを修正する
  • ? - ヘルプを表示する
この投稿は Git Advent Calendar 2012 / Jun26日目の記事です。