git commit を実行したとき、内部で何が起きているかを可視化します。
git commit は、ステージングエリア(index)の内容を「スナップショット」として Git の履歴に確定するコマンドです。git add が「買い物かごに入れる」操作だったのに対し、git commit は「レジで精算して受領書(コミット)をもらう」操作です。
一度コミットすれば、その時点のプロジェクト全体の状態がいつでも復元できるようになります。 ファイルを壊してしまっても、過去のコミットに戻ればいいので安心です。
コミットは「差分」ではなくプロジェクト全体のスナップショットです。 ただし Git は賢く、変更のないファイルは前のコミットの blob をそのまま再利用するので、容量は最小限で済みます。
「コミットする」の裏側では何が起きているのでしょうか? 具体的に覗いてみましょう。 Git は git commit を実行すると、内部で3つの処理を行っています。
上のツールでステップを進めると、この3つの処理が順番に行われる様子を確認できます。
commit オブジェクトはシンプルなテキストです。git cat-file -p [コミットハッシュ] で確認できます。
treeroot tree のハッシュ(プロジェクト全体のスナップショット)parent親コミットのハッシュ(初回コミットにはなし。マージコミットは2つ)author変更を書いた人 + タイムスタンプ(rebase しても変わらない)committerコミットを実行した人 + タイムスタンプ(rebase で変わる)可視化の中に出てくる f7e8d9c のような文字列がハッシュです。 Git はオブジェクト(blob・tree・commit)の中身をSHA-1 というアルゴリズムで計算し、40文字の英数字に変換します。
ハッシュはオブジェクトの「指紋」のようなもので、中身が同じなら必ず同じハッシュになり、1文字でも違えばまったく別のハッシュになります。 Git はこのハッシュを使ってオブジェクトを管理・参照しています。
.git/refs/ は、ブランチやタグの情報が保存されているフォルダです。 例えば main ブランチなら .git/refs/heads/main、feature ブランチなら .git/refs/heads/feature というファイルが対応します。
中身を開くと驚くほどシンプルで、commit のハッシュが1行だけ書かれたテキストファイルです。
もうひとつ重要なファイルが .git/HEAD です。 これは refs/ の中ではなく .git/ 直下にあり、「今どのブランチで作業しているか」を記録しています。 git commit すると、Git はまず HEAD を見て「今 main ブランチにいる」と判断し、.git/refs/heads/main を新しい commit のハッシュに書き換えます。
つまりHEAD → main → f7e8d9c → t4e5f6a(root tree)という参照チェーンでプロジェクト全体にたどり着けます。
.git/HEAD今チェックアウトしているブランチ名を記録(例: ref: refs/heads/main).git/refs/heads/mainmain ブランチが指す commit のハッシュ(git commit のたびに書き換わる).git/refs/heads/feature他のブランチも同じ構造。ブランチ作成 = ファイルを1つ作るだけ.git/refs/tags/v1.0タグも同様にハッシュを記録したファイル(こちらは書き換わらない)git commit -m "[メッセージ]"指定したメッセージでコミットする(エディタを開かない)git commitエディタが開き、コミットメッセージを入力してコミットするgit commit -a -m "[メッセージ]"追跡済みファイルの変更を自動で git add してからコミットするgit commit --amend直前のコミットをやり直す(メッセージ変更やファイル追加)git commit --allow-empty変更がなくても空のコミットを作成する(CI トリガー等に使用)git commit -vエディタに diff を表示し、変更内容を確認しながらコミットする