ファイルを編集後、git addでステージ状態にします(ステージに移動する、とも表現します)。ステージ状態のファイル群をgit commitでコミットします。
ステージに移動して、コミットする、という2段階操作なんだね。
ステージをなくして、いきなりコミットでもいいんじゃないの?
ステージって何のためにあるの?
Gitのステージについて、考えてみました。
ステージの目的
ステージの本来の目的
実ファイルは、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.txt
Code 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.txt
Code 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管理内の領域なので、ステージに移動することで、実ファイルの変更から保護します。