Edited at

対話的に指定ファイルの指定部分のみステージする[git]

More than 1 year has passed since last update.

自分は短気で面倒くさがりなので、よくgit add .としてしまう

まあgitマスターからすると発狂ものかもしれないが、おそらくこんなgitの操作をやっているのは自分だけじゃない気がする

しかし作業ツリーにあるファイル全てステージなんてことをしちまうと以下のようなことが起きる可能性がある(いや実際よく起きていた)


  • エディタで自動的に整形した内容がコミット(いいか悪いかは環境によるけど)

  • キーボードに肘が当たったかなんかで予期せぬ文字がいつの間にか入っていて、気づかずコミット

  • tmpで作ったはずのテストファイルなんかが一緒にコミット

...などなど

エディタの自動整形がdiffに入っちゃうと、GUIでdiffを見ている人からするととっても見づらいログになっちゃうし(CUIだとdiffに-bという素晴らしいオプションが付いている)、自分が予期せぬコードとかファイルがログに残っていると、何より恥ずかしい

ということで以下はファイルやファイル内の変更行を対話的に細かくコミットするやり方


やりたいこと


  • file : second commitという行のみコミット

  • new-file : ステージするファイル

  • tmp-file : ステージしないファイル

$ git status --short

M file
?? new-file
?? tmp-file

$ git diff
diff --git a/file b/file
index c74fa01..fa0600c 100644
--- a/file
+++ b/file
@@ -2,8 +2,9 @@ first commit
first commit
first commit
first commit
-
+
first commit
+second commit
first commit
first commit
first commit
@@ -15,3 +16,4 @@ first commit
first commit
first commit
first commit
+


作業

対話シェルモードに移行する

$ git add -i

staged unstaged path
1: unchanged +2/-2 file

*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now>

それぞれのコマンドはスペルの頭文字をタイプするか、番号をタイプすることで実行する


  • 1: status : ステータスを確認

  • 2: update : ファイルをステージする

  • 3: revert : ステージを取り消す

  • 4: add untracked : トラッキングされていないファイル(新規ファイル)をステージする

  • 5: patch : 部分的にステージ

  • 6: diff : ステージしたファイルの差分を確認(作業ツリーのファイルではない)

           staged     unstaged path

1: unchanged +3/-1 file

*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> p #部分的にステージする
staged unstaged path
1: unchanged +3/-1 file
Patch update>> 1 #fileを選択する
staged unstaged path
* 1: unchanged +3/-1 file # 選択された状態
Patch update>> #Enterでファイル選択を終了する
diff --git a/file b/file
index c74fa01..fa0600c 100644
--- a/file
+++ b/file
@@ -2,8 +2,9 @@ first commit
first commit
first commit
first commit
-
+
first commit
+second commit
first commit
first commit
first commit
Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]?

patchを実行すると対話モードに移行する。


  • y : ステージする

  • n : ステージしない

  • q : 対話モードを終了する

  • a : そのファイルの以降を全てステージする

  • d : そのファイルの以降を全てスキップする

  • g : 指定のハンクへ移動

  • K : 前のハンクへ移動

  • J : 次のハンクへ移動

  • e : ハンクを編集

diff --git a/file b/file

index c74fa01..fa0600c 100644
--- a/file
+++ b/file
@@ -2,8 +2,9 @@ first commit
first commit
first commit
first commit
-
+
first commit
+second commit
first commit
first commit
first commit
Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]? s #ハンクを分割する
Split into 2 hunks.
@@ -2,5 +2,5 @@

first commit
first commit
first commit
-
+
first commit
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? n #自動整形された行はステージしない
@@ -6,4 +6,5 @@

first commit
+second commit
first commit
first commit
first commit
Stage this hunk [y,n,q,a,d,/,K,j,J,g,e,?]? y #ステージする
@@ -15,3 +16,4 @@
first commit
first commit
first commit
first commit
+
Stage this hunk [y,n,q,a,d,/,K,g,e,?]? n #自動整形された行はステージしない

それ以上ハンクがない場合、自動的に対話モードが終了する

*** Commands ***

1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> a #トラッキングされていないファイルをステージするモードに移行
1: new-file
2: tmp-file
Add untracked>> 1 #new-fileのみをステージ
* 1: new-file
2: tmp-file
Add untracked>> #Enterで終了
added one path

*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> s #ステータスを確認
staged unstaged path
1: +1/-0 +2/-1 file
2: +0/-0 nothing new-file

*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> q #終了

以上の操作で以下のようなステータスになる

$ git status --short

MM file
A new-file
?? tmp-file
$ git diff
diff --git a/file b/file
index 928e9ef..fa0600c 100644
--- a/file
+++ b/file
@@ -2,7 +2,7 @@ first commit
first commit
first commit
first commit
-
+
first commit
second commit
first commit
@@ -16,3 +16,4 @@ first commit
first commit
first commit
first commit
+
$ git diff --cached
diff --git a/file b/file
index c74fa01..928e9ef 100644
--- a/file
+++ b/file
@@ -4,6 +4,7 @@ first commit
first commit

first commit
+second commit
first commit
first commit
first commit
diff --git a/new-file b/new-file
new file mode 100644
index 0000000..e69de29

あとは普通にコミットするだけだが、癖でgit commit -aとか間違えてやっちゃわないこと!


おまけ

ちなみにSourceTreeで上記の内容を操作するのはとっても簡単だった。

コミットしたい行を選択する

選択した行をステージへ移動を選択する

追加したい新規ファイルのみを選択する

以上