Dev Study
← 解説「git add の正体 — ステージは「次のtreeの下書き」」に戻る

サンプルコードで身につける: git add の正体 — ステージは「次のtreeの下書き」

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

11つのファイルが3つの姿を持つ

addしたあとにさらに編集すると、作業ツリー・ステージ・最新コミットで中身が食い違います。git status がこれを2行に分けて表示する理由が、実際の出力で分かります。

$ printf 'version 2\n' > memo.txt
$ git add memo.txt              # ← ここでステージに version 2 が載る
$ printf 'version 3\n' > memo.txt   # ← さらに編集

$ git status
Changes to be committed:
        modified:   memo.txt      ← ステージにある version 2

Changes not staged for commit:
        modified:   memo.txt      ← 作業ツリーの version 3

# 同じファイルが2箇所に出るのは、姿が2つあるから
# この状態でcommitすると、記録されるのは version 2

2どの版がコミットされるかを確かめる

git diff は「作業ツリーとステージ」の差、git diff --staged は「ステージと最新コミット」の差を見ます。この2つを使い分けると、今コミットしたら何が入るかを事前に確認できます。

# ステージと作業ツリーの差(= まだコミットに入らない分)
$ git diff
-version 2
+version 3

# 最新コミットとステージの差(= 今コミットしたら入る分)
$ git diff --staged
-version 1
+version 2

# 最新の編集も入れたいなら、もう一度 add する
$ git add memo.txt
$ git diff              # 差が無くなった
$ git diff --staged
-version 1
+version 3

3addの時点でblobはもうDBに入っている

コミットを待たずに、addした瞬間オブジェクトが作られます。commitしていないのにcat-fileで中身が取り出せることを確認すると、addとcommitの分担がはっきりします。

$ printf 'まだコミットしていない内容\n' > draft.txt
$ git add draft.txt

# ステージの中身を見る(パス → blobハッシュ の一覧)
$ git ls-files --stage draft.txt
100644 9f8e7d6c5b4a39281706f5e4d3c2b1a098765432 0	draft.txt

# コミットしていないのに、もうオブジェクトDBから取り出せる
$ git cat-file -p 9f8e7d6c5b4a39281706f5e4d3c2b1a098765432
まだコミットしていない内容

# → add = blobを作ってDBへ入れ、ステージの一覧に載せる
#   commit = ステージの一覧から tree を組んで commit で包む

4ステージから tree が組まれる過程

commitがしていることは「ステージの一覧を階層に組み直してtreeを作る」ことです。パスの一覧からディレクトリ構造が復元される様子を追うと、なぜサブディレクトリの分だけtreeが増えるのか分かります。

Python

5「addを忘れてcommitした」が起きる理由

commitはステージだけを見て、作業ツリーを一切見ません。だからaddし忘れると、編集したはずの内容が入りません。よくある失敗を再現し、原因が仕様通りの動作だと理解します。

Python