2018年以降の記事はGitHub Pagesに移行しました

Git add, commitをした時、中でどんな事が起こっているのか

あらすじ

いまだにファイルがどういうタイミングでリポジトリ、インデックス、ワーキングエリア間を行き来しているのかわからんので色々調べてみることに。

やってみる

まずは、空のREADMEファイルを作ってFirst commit。

$ git init
Initialized empty Git repository in /tmp/repos/.git/
$ touch README
$ git add README
$ git commit -m "first commit"
[master (root-commit) b0d99b3] first commit
 0 files changed
 create mode 100644 README
$ git log --graph --date-order --all --date=short -C -M --pretty=format:"%h"\ %t\ %ad\ %Cblue%cn%Creset\ %Cgreen%d%Creset\ %s
* b0d99b3 543b9be 2012-08-29 kk_Ataka  (HEAD, master) first commit
色々確認

※確認スクリプト

  • rev-parse コマンドはタグとかブランチとかHEAD、HEAD^^など指定したらそのコミットのSHA1が返ってくる
    • 最初のコミットオブジェクトは b0d99b3
  • cat-file コマンドでコミットオブジェクトの内容を確認できる
    • コミットオブジェクトに関連付いているツリーオブジェクトは 543b9be
  • ls-tree コマンドで上のツリーオブジェクトにぶら下がっているツリーオブジェクト、ブロブオブジェクトを確認できる
    • 今は直下にREADMEファイルしかないから e69de29
  • コミットすると.git/objects/の下に3つファイルが作られる
    • SHA1の先頭2桁でディレクトリを作り、その下に残りのSHA1でファイル作成
ゆっくり

READMEを編集してみる。

$ echo "このリポジトリはテスト用です" > README 
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   README
#
no changes added to commit (use "git add" and/or "git commit -a")

で、色々確認。まだ作業エリア(ワーキングツリー)内での変更しかしてない(git addしてない)ので特に変わらず。

addしたとき

ここでREADMEファイルをadd。

$ git add README
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   README
#

再度、色々確認。

リポジトリにコミットされているREADME e69de29 の他にde7e855 というオブジェクトがステージされている。おまけに.git/objectsの下にも。

  • addした時点で、gitリポジトリの中に入れ込まれている?
  • リセットしても.git/objectsの下からは消えない(ただ、この状態でほったらかしておくとどこからも参照されないからいずれgcされて死ぬ?)
$ git reset HEAD README
Unstaged changes after reset:
M	README
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   README
#
no changes added to commit (use "git add" and/or "git commit -a")
$ find .git/objects
.git/objects/de/7e8558a365886d75f9c1ac6861693be19bdc53
.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
.git/objects/54/3b9bebdc6bd5c4b22136034a95dd097a57d3dd
.git/objects/b0/d99b34f1e2497a78619de4fde08b62f2d851f7
$ git cat-file -p de7e8558
このリポジトリはテスト用です
commitしたとき

もう一回addして今度はコミット。

$ git add README
$ git commit -m "README追記"
[master 30eab42] README追記
 1 file changed, 1 insertion(+)

すごい見づらいけど、.git/objectsの下に2つファイルが増えている。追ってみると今回のコミットオブジェクト 30eab42 ツリーオブジェクト 9dd7dd4 …の下にさっきaddした時にできていたREADME(de7e855)

まとめ

以上の事から

  • addした時、既にリポジトリにファイルは追加されている。また、インデックスの参照先も更新されている
初期状態

addした後

  • commitした時、インデックスがそのまま次のコミットのツリーオブジェクトになる。そのツリーオブジェクトを取りまとめるコミットオブジェクトを作成し、tree情報とparent情報を持たせる
  • で、ブランチの参照先を更新してやる
commitした後

でいいのかなあ。