Gitのステージとは?ファイル選別だけではなかった、もう一つの目的とは

ファイルを編集後、git addでステージ状態にします(ステージに移動する、とも表現します)。ステージ状態のファイル群をgit commitでコミットします。

ステージに移動して、コミットする、という2段階操作なんだね。

ステージをなくして、いきなりコミットでもいいんじゃないの?

ステージって何のためにあるの?

Gitのステージについて、考えてみました。

ステージの目的

GUIクライアントでは、
コミットしたいファイルを選別するため。

ステージの本来の目的

編集したらすぐにステージに移動することで、
編集した内容、確認した内容を実ファイルの変更から保護します。

実ファイルは、Git管理外のファイルであり、誰でも変更できてしまいます。

ステージは、Git管理内の領域なので、実ファイルの変更から保護します。

「編集から確認するまで」と「確認してからコミットするまで」に分けて見ていきます。

編集してから確認するまで、編集内容を保護するため

ステージがなく、1日の作業終わりにコミットする場合

朝にファイルAを編集しました。ファイルAは実ファイルのままなので、夕方のコミットまでの間に変更できてしまいます。

コミット直前の確認で、変更に気づくことができるしれませんが、朝の編集直後の状態に戻さなければいけません。

変更に気づかなければ、間違った内容でコミットしてしまうことになります。

ステージがあり、1日の作業終わりにコミットする場合

朝にファイルAを編集して、編集直後にステージに移動しました。編集内容は夕方のコミットまで保護されます。

ファイルを編集したら、他のファイルの編集を待たずに、すぐにステージに移動するのが正しい使い方です。

確認してからコミットするまで、確認内容を保護するため

ステージがなく、実ファイルを確認してから、コミットする場合

実ファイルを確認して、実ファイルをコミットするとしましょう。

確認してからコミットするまでの間に、実ファイルが変更されてしまったら、どうなるでしょうか。誤操作などで気づかずに実ファイルを変更してしまい、予期しない内容でコミットしてしまいます。

ステージを確認して、コミットする場合

ステージはGit管理内の領域です。実ファイルの変更から保護されます。

確認してからコミットするまでの間に、実ファイルを変更しても、確認した内容は変更されません。確実に、確認した内容でコミットできます。

git addはファイルを選別するため?

「Git ステージとは」で検索してみると、次の理由に集約できます。

複数ファイルを編集してしまった。しかし、編集した全ファイルをコミットするのではなく、一部のファイルだけをコミットしたい。そこで、コミットしたいファイルだけを選別して、ステージに移動して、コミットするため。

筆者もこのような使い方をしています。

ある作業をしているとき、いろいろなファイルを編集します。「変更あり」のファイルを確認して、ファイルを選別してステージに移動します。

  • このファイルはログ表示を追加しただけだから、コミットしなくてもいい。(たぶん、元に戻す)
  • このファイルの変更は、今回のIssueとは関係ないから、別のIssueでコミットしたほうがいい。
  • このファイルは、今回のIssueでコミットするから、ステージに移動しよう。

などを判断しながら、コミットしたいファイルをステージに移動しています。

git commitでもファイル指定できる

確かに、git gui や SourcetreeなどのGUIクライアントでは、ステージに移動するときにファイル選別し、ステージ一括してコミットします。ファイル選別のタイミングはステージに移動するときだけです。

ところが、ターミナルのコマンドでコミットするとき、git commit はファイルを指定できるんですね。a.txt、b.txt、c.txtをステージに移動して、a.txt、b.txtだけをコミットできます。次のようにして確認できます。

a.txt、b.txt、c.txtを編集して、git statusを表示します。a.txt、b.txt、c.txtとも未ステージのmodifiedです。

$ git status
ブランチ master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   a.txt
	modified:   b.txt
	modified:   c.txtCode language: Bash (bash)

全てを git addして、git statusを表示します。a.txt、b.txt、c.txtともステージに移動しました。

$ git add --all

$ git status
ブランチ master
コミット予定の変更点:
  (use "git restore --staged <file>..." to unstage)
	modified:   a.txt
	modified:   b.txt
	modified:   c.txtCode language: Bash (bash)

a.txt、b.txtの2ファイルをコミットして、git statusを表示します。c.txtはステージに残っていますね。

$ git commit -m "#2" a.txt b.txt
[master 9d322e7] #2
 2 files changed, 6 insertions(+)

$ git status
ブランチ master
コミット予定の変更点:
  (use "git restore --staged <file>..." to unstage)
	modified:   c.txt
Code language: Bash (bash)

ステージの目的はファイル選別だけではないかもしれません。

ステージの不思議な機能

ステージの不思議というか、おもしろいと感じている機能があります。

 『ステージに移動した後、実ファイルを変更しても、ステージは変更されない。』

次のようにして、確認できます。

(1)a.txtを "Hello" と編集します。
(2)ステージに移動します。
(3)a.txtを "Good bye" と編集します。
(4)git status は、次のように表示されます。

$ git status
ブランチ master
コミット予定の変更点:
  (use "git restore --staged <file>..." to unstage)
	modified:   a.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   a.txt
Code language: HTML, XML (xml)

(5)コミットします。ステージに移動した(2)の時点の a.txt "Hello"がコミットされます。(3)の編集内容 "Good bye" はコミットされません。

ステージがないと

ステージがないと、何か困ったことになるのでしょうか?次のような状況を考えました。

ステージがないと、

  • a.txt を "Hello" と編集しました。"Hello" でコミットするつもりです。
  • キーボードの誤操作で、気づかないうちに、a.txtが "HelloXXX" になってしまいました。
  • コミットを実行しました。"HelloXXX"でコミットしてしまいました。

ステージがあると、

  • a.txt を "Hello" と編集しました。"Hello" でコミットするつもりです。
  • a.txtをステージに移動しました。
  • キーボードの誤操作で、気づかないうちに、a.txtが "HelloXXX" になってしまいました。
  • 幸いなことに、ステージの内容は、a.txt "Hello" のままです。
  • コミットを実行しました。予定どおり、"Hello"でコミットしました。

「キーボード誤操作」の部分は、次のケースも考えられます。

  • テキストエディターその他のクラッシュで、a.txt ファイル内容が壊れた。
  • 自動formatterが走って、a.txtのインデントが変更された。インデント変更途中の不完全な状態のときが、コミットのタイミングだった。

まとめ

以上のことから、筆者は次のように考えました。

ステージの目的は、

編集したらすぐにステージに移動することで、編集した内容、確認した内容を実ファイルの変更から保護します。

実ファイルは、Git管理外のファイルであり、誰でも変更できてしまいます。ステージはGit管理内の領域なので、ステージに移動することで、実ファイルの変更から保護します。

湊川 あい シーアンドアール研究所 2017/4
タイトルとURLをコピーしました