git の仕組み - git オブジェクト (git LT vol.2)

社内 LT に使ったものを少し改編しています
主に git 初級者の方 (私もですが) に向けた内容だと思います
Qiita スライドモードでご覧頂ければ幸いです

---

# git の内側を知れば git がもっと楽しくなる!

---

今回は git の使い方ではなく、仕組みの方になります
知っていればトラブルに対処しやすいと思います！ :smile:

---

## git オブジェクト

git は下記を組み合わせてリポジトリやブランチ等を表現しています
.git/objects に格納されています

- 1. blob
- 2. tree
- 3. commit
- 4. tag

---

## git オブジェクト

こんなイメージ

- 1. blob (ファイル)
- 2. tree (ディレクトリ構造)
- 3. commit (コミット情報)
- 4. tag (タグ情報)

今回は 1-3 を見ていきます！

---

## では早速 .git/objects を見ていきましょう！

---

## 空のリポジトリを作成した時点ではオブジェクトは作成されません

~~~
$ mkdir explore-dot-git && cd explore-dot-git && git init
$ find .git/objects -type f
$
~~~

---

## git add 前も何も作成されません

~~~
$ echo "Alchol 9%" > strong.txt
$ find .git/objects -type f
$
~~~

---

## git add すると何やらオブジェクトが作成されます

~~~
$ git add strong.txt
$ find .git/objects -type f
.git/objects/29/032df2fd6343ea93bc2c00cd5e93128ba1997f
~~~

---

## blob オブジェクト

### git cat-file するとオブジェクトを調べられます

cat-file -t (タイプ)

~~~
$ git cat-file -t 29032df2fd6343ea93bc2c00cd5e93128ba1997f
blob
~~~

cat-file -p (中身)

~~~
$ git cat-file -p 29032df2fd6343ea93bc2c00cd5e93128ba1997f
Alchol 9%
~~~

blob オブジェクトにファイルの中身が格納されていますね！
という訳で blob オブジェクトはファイルの中身のようです

---

## git commit してみます

~~~
$ git commit -m 'add strong.txt'
[master (root-commit) 5fd34a2] add strong.txt
 1 file changed, 1 insertion(+)
 create mode 100644 strong.txt
~~~

オブジェクトが 2 つ増えました

~~~
$ find .git/objects -type f
.git/objects/29/032df2fd6343ea93bc2c00cd5e93128ba1997f
.git/objects/48/b394fb164aeb3b5482f3123721df65e208d309
.git/objects/5f/d34a2411bf91577399fd6b9647e37d59e31fa8
~~~

---

増えたオブジェクト 2 つを見てきましょう！

---

## commit オブジェクト (1st commit)

cat-file -t (タイプ)

~~~
$ git cat-file -t 5fd34a2411bf91577399fd6b9647e37d59e31fa8
commit
~~~

cat-file -p (中身)

~~~
$ git cat-file -p 5fd34a2411bf91577399fd6b9647e37d59e31fa8
tree 48b394fb164aeb3b5482f3123721df65e208d309
author sunadorinekop <sunadorinekop@example.com> 1543332950 +0900
committer sunadorinekop <sunadorinekop@example.com> 1543332950 +0900

add strong.txt
~~~

commit オブジェクトは commit 情報のようですね！

---

## tree オブジェクト (1st commit)

cat-file -t (タイプ)

~~~
$ git cat-file -t 48b394fb164aeb3b5482f3123721df65e208d309
tree
~~~

cat-file -p (中身)

~~~
$ git cat-file -p 48b394fb164aeb3b5482f3123721df65e208d309
100644 blob 29032df2fd6343ea93bc2c00cd5e93128ba1997f    strong.txt
~~~

tree オブジェクトはディレクトリ構造の情報のようですね！
このディレクトリは strong.txt がありそのハッシュ値は 29032d... であることがわかりますね！

---

## ここまでのイメージ

こんな感じ？

commit > tree > blob

---

では、更に commit してみます

---

## 更に commit

~~~
$ echo "Alchol 3%" > horoyoi.txt
$ git add horoyoi.txt
$ git commit -m 'add horoyoi'
~~~

object が増えました

~~~
$ find .git/objects -type f
.git/objects/29/032df2fd6343ea93bc2c00cd5e93128ba1997f
.git/objects/47/50679d8ca0d3379dae8abaed6b6013583de548
.git/objects/48/b394fb164aeb3b5482f3123721df65e208d309
.git/objects/5f/d34a2411bf91577399fd6b9647e37d59e31fa8
.git/objects/8b/9e22d4a872621a5ae50655f4ef45bbc6b3e6fc
.git/objects/a5/91a631eaff52e0f32df02d4ba80bc17dd044a1
~~~

---

増えたオブジェクト 3 つを見てきましょう！

---

## blob オブジェクト (2nd commit)

cat-file -t (タイプ)

~~~
$ git cat-file -t a591a631eaff52e0f32df02d4ba80bc17dd044a1
blob
~~~

cat-file -p (中身)

~~~
$ git cat-file -p a591a631eaff52e0f32df02d4ba80bc17dd044a1
Alchol 3%
~~~

こちらは 1st commit と同じ感じなので問題無いと思います

---

## commit オブジェクト  (2nd commit)

cat-file -t (タイプ)

~~~
$ git cat-file -t 8b9e22d4a872621a5ae50655f4ef45bbc6b3e6fc
commit
~~~

cat-file -p (中身)

~~~
$ git cat-file -p 8b9e22d4a872621a5ae50655f4ef45bbc6b3e6fc
tree 4750679d8ca0d3379dae8abaed6b6013583de548
parent 5fd34a2411bf91577399fd6b9647e37d59e31fa8
author sunadorinekop <sunadorinekop@example.com> 1543333980 +0900
committer sunadorinekop <sunadorinekop@example.com> 1543333980 +0900

add horoyoi
~~~

1st commit のオブジェクトと違うのは
parent 5fd34a ...
でしょうか

これは 2nd commit オブジェクトの parent は　5fd34a... であることを示しています
つまり、**commit オブジェクトは自分より一つ前の commit オブジェクトのみ知っています**

---

## commit オブジェクトのポイント

git は
1st commit <- 2nd commit <- 3rd commit ...
と、**git オブジェクトが自分より一つ前の commit を指し示すことによって commit の歴史を表現しています**

これが壊れると commit を辿れなって壊れるよ!! (git push -f とか)

---

## tree オブジェクト  (2nd commit)

cat-file -t (タイプ)

~~~
$ git cat-file -t 4750679d8ca0d3379dae8abaed6b6013583de548
tree
~~~

cat-file -p (中身)

~~~
$ git cat-file -p 4750679d8ca0d3379dae8abaed6b6013583de548
100644 blob a591a631eaff52e0f32df02d4ba80bc17dd044a1    horoyoi.txt
100644 blob 29032df2fd6343ea93bc2c00cd5e93128ba1997f    strong.txt
~~~

先ほど commit した horoyoi.txt が増えました
tree オブジェクトはそのディレクトリのファイル等の一覧情報であることがわかります

---

## 以上

blob, commit, tree が何となくイメージ頂けましたでしょうか？ :thinking: 

---

## おまけ問題

strong.txt に "double lemon" を追記して 3rd commit した場合どうなるでしょうか？

1. 既存の strong.txt の blob オブジェクトに "double lemon" が追加される
2. 既存の strong.txt の blob オブジェクトとの差分が新しい blob オブジェクトとして作成される
3. 全く新しい blob オブジェクトが作成される

---

## おまけ問題

strong.txt に "double lemon" を追記して 3rd commit した場合どうなるでしょうか？

1. ~~既存の strong.txt の blob オブジェクトに "double lemon" が追加される~~
2. 既存の strong.txt の blob オブジェクトとの差分が新しい blob オブジェクトとして作成される
3. ~~全く新しい blob オブジェクトが作成される~~