動かして理解するGit | addとcommit編
この記事では、Gitのadd, commit コマンドについて深掘りした内容をまとめます。
addコマンド
addは選択したファイルだけをコミットするための仕組みです。
具体的には、以下のことをしています。
- ファイルの内容を圧縮してBlobオブジェクトを作成し
.git/objects/に保存。 - ステージングエリア(
.git/index)に登録。 
addの内部的な動作を追ってみる
下準備
以下のようにファイル・フォルダを配置し、a.txtに変更を加えました。
/folder1
├── /folder-1-1
| 	└── c.txt
└── b.txt
a.txt // ← ここを変更・addしてみる。
Tree・Blobオブジェクトの確認用。
ここは`a.txt`です。
+ 
+ ここを変更して、addの挙動を見てみる。
以下のコマンドでindexに登録されたファイルと、そのBlobオブジェクトのハッシュを見ることができます。
git ls-files --stage
addしていないときのindex
まずは、addしていない状態でindexを見てみます。
PS C:\Users\...\git-playground> git ls-files --stage
100644 d8038ab201a19d961c0be971a06479e1b857490a 0       a.txt
100644 6b2641653428bc52ebadf30821061fe6b148b039 0       folder1/b.txt
100644 b993a127140d50bab9c349674730d160a264f29a 0       folder1/folder1-1/c.txt
cat-file コマンドでa.txtの中身を見てみると、変更前の内容であることが分かります。
PS C:\Users\...\git-playground> git cat-file -p d8038ab201a19d961c0be971a06479e1b857490a
Tree・Blobオブジェクトの確認用。
ここは`a.txt`です。
addした後のindex
今度は、git add a.txt をした後のindexを見てみます。
PS C:\Users\...\git-playground> git ls-files --stage
- 100644 d8038ab201a19d961c0be971a06479e1b857490a 0       a.txt
+ 100644 8b36dadad0a62065b636af374e9c8d4c923924cb 0       a.txt
  100644 6b2641653428bc52ebadf30821061fe6b148b039 0       folder1/b.txt
  100644 b993a127140d50bab9c349674730d160a264f29a 0       folder1/folder1-1/c.txt
a.txtのハッシュが先ほどと変わっているのが分かります。
中身を見てみると、変更後の内容になっています。
PS C:\Users\...\git-playground> git cat-file -p  8b36dadad0a62065b636af374e9c8d4c923924cb
Tree・Blobオブジェクトの確認用。
ここは`a.txt`です。
ここを変更して、addの挙動を見てみる。
add をすると、選択したファイルの新しいBlobオブジェクトが作成され、indexに上書きされることが分かりました。
add しても、変更前のBlobオブジェクトは削除されずに.git/objects/にあり、過去のコミットから参照されます。
commitコマンド
コミットでは新しい変更履歴を作成します。
具体的には、以下のことをしています。
- indexを読み取り新しくTreeオブジェクトを作成する。
- スナップショットを作成・登録する。
 
 - Commitオブジェクトを作る。
 - HEADを新しいコミットを指すように進める。
 
コミットの内部的な動作を追ってみる
addコマンドのときに、ステージングした変更があるので、それをコミットしてみます。
下準備
現在はwrite-articleブランチにいるので、.git/HEADの中身は以下のようになっています。
ref: refs/heads/write-article
コミット前
write-articleブランチは最新のコミットを指しています。
c175cc4b295c544d3b80662f46cf06383171a191
コミット後
以下のようにコミットしてみました。
PS C:\Users\...\git-playground> git commit -m "コミットして動作確認"
[write-article fda604c] コミットして動作確認
 1 file changed, 3 insertions(+), 1 deletion(-)
コミット前後の.gitディレクトリを比較してみると、
何やら新しく2つのオブジェクトが作成されていました。

片方はCommitオブジェクト。
PS C:\Users\...\git-playground> git cat-file -p fda604c66974a2eb6352141305481eb268700996
tree fcfd7cb8d9db0f93c35aae39692800d13eb320af
parent c175cc4b295c544d3b80662f46cf06383171a191
author makim0939 <makim0939@gmail.com> 1757245797 +0900
committer makim0939 <makim0939@gmail.com> 1757245797 +0900
コミットして動作確認
もう片方はTreeオブジェクトです。
PS C:\Users\...\git-playground> git cat-file -p fcfd7cb8d9db0f93c35aae39692800d13eb320af
100644 blob 8b36dadad0a62065b636af374e9c8d4c923924cb    a.txt
040000 tree d8b465f99cbd8047e700b7a5843dccaf685efb77    folder1
write-articleブランチが新しいコミットを指すように変更されていました。
fda604c66974a2eb6352141305481eb268700996
.git/logsの現在のブランチに、新しいコミットが記録されていました。

中身は見れないですが、.git/indexも書き換わっていました。

- コミットすると、新しくTreeオブジェクトが作成され、この時点でのスナップショットが記録される。
 - そのスナップショット(ルートディレクトリのTreeオブジェクト)をもつCommitオブジェクトが新しく作成される。
 - HEADが指すブランチ(現在のブランチ)が指す先が今回のコミットに変更される。
 - logsにコミットの情報が追加される。
 - indexでおそらくステージングの情報が更新される。
 
Discussion