.gitディレクトリの中身を見てみる👀

`git init` をすると、 `.git` と言うディレクトリが作られます。
Gitに必要なファイルは全てこのディレクトリに含まれているのですが、普段はあまり意識しない人の方が多いと思います。
今回は `.git` ディレクトリの中身を少し探検してみましょう。

## .gitディレクトリの構成
まずは `git init` して `.git` ディレクトリを作成してみます。
`.git` の中身はこのようになっています。

```
.
├── HEAD
├── branches
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags
```

- **HEAD**:現在のブランチの参照
- **branches**: `git fetch`、`git pull`、および`git push`のURLを省略形を指定するために使用される、廃止予定
- **config**: リポジトリのGit設定、`git config`で設定するメールなど
- **description**: GitWeb(gitのデフォルトのWebUI)で使われる
- **hooks**: Gitの各コマンドを実行した時に呼び出されるスクリプトを設定できる(e.g. pre-commitなら`git commit`の前)
- **info**: このリポジトリに対する追加情報
    - **exclude**: `.gitignore`のようなもの
- **objects**:Gitの実体(オブジェクト)が保存される場所
    - **info**: オブジェクトに対する追加情報
    - **pack**: ランダムアクセスするためのインデックスファイルや、多数のオブジェクトを圧縮したファイル
- **refs**: Gitの各参照先が保存されている場所
    - **heads**: 参照先のブランチ(実体はコミットオブジェクト)
    - **tags**: 参照先のタグ名(実体はコミットオブジェクト)

## `git commit`してみる
`hoge.txt`というファイルを新規作成して、`git add`, `git commit`してみます

```
$ echo hoge > hoge.txt
$ git add hoge.txt
$ git commit -m "Add hoge.txt
```

`.git`配下はどうなっているでしょうか

```
.
├── COMMIT_EDITMSG
├── HEAD
├── branches
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 22
│   │   └── 62de0c121f22df8e78f5a37d6e114fd322c0b0
│   ├── 57
│   │   └── c089a02b22cb27920e14c256e9140c1b19dbb5
│   ├── 6a
│   │   └── 73751cba5a86b1ebe10ab2856b68404aab50fd
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags
```

何やらいくつか新しいファイルが生成されています。

`COMMIT_EDITMSG`にはコミットメッセージが入ります。

```
$ cat COMMIT_EDITMSG
Add hoge.txt
```

`index`は、`git add`されたコミットする前のステージング領域として利用されます。
`git commit`する時にコミットされるのは、作業ディレクトリではなくここの領域のものです。
バイナリファイルであり、中身は`git ls-files --stage`で確認出来ます。

```
$ git ls-files --stage
100644 2262de0c121f22df8e78f5a37d6e114fd322c0b0 0       hoge.txt
```

`logs`には参照に加えられた変更が残ります。

`logs/HEAD`は現在作業しているブランチの先頭のポインタです

```
$ cat logs/HEAD
0000000000000000000000000000000000000000 57c089a02b22cb27920e14c256e9140c1b19dbb5 tatane616 <メールアドレス> 1560681812 +0900      commit (initial): Add hoge.txt
```

ちなみに、Gitのコミットは自分のコミット番号と親のコミット番号を持っているのですが、ここで`0000000000000000000000000000000000000000`となっているのは、このコミットが最初のコミットで親を持たないからです。(`57c089a02b22cb27920e14c256e9140c1b19dbb5`はこのコミットの番号)

現在作業しているのは`master`ブランチなので、`logs/refs/heads/master`も同じコミットを参照していることになります。

```
$ cat logs/refs/heads/master
0000000000000000000000000000000000000000 57c089a02b22cb27920e14c256e9140c1b19dbb5 tatane616 <メールアドレス> 1560681812 +0900      commit (initial): Add hoge.txt
```

当然`refs/heads/master`も同じコミットを参照しています。

```
$ cat refs/heads/master
57c089a02b22cb27920e14c256e9140c1b19dbb5
```

`objects`の中にも何かできています。

```
├── objects
│   ├── 22
│   │   └── 62de0c121f22df8e78f5a37d6e114fd322c0b0
│   ├── 57
│   │   └── c089a02b22cb27920e14c256e9140c1b19dbb5
│   ├── 6a
│   │   └── 73751cba5a86b1ebe10ab2856b68404aab50fd
│   ├── info
│   └── pack
```

`objects/57/c089a02b22cb27920e14c256e9140c1b19dbb5`は、いまHEADが参照しているコミットです。
コミット番号は57c089a02b2...ですが、最初の２文字でサブディレクトリを作って`objects/57/c089a02b2...`のように作られます。
オブジェクトは、このようにコミット番号(SHA1)の最初の2文字を使用して256個のサブディレクトリに配置され、オブジェクト自体のディレクトリエントリ数を管理可能な数に保ちます。

3つオブジェクトが追加されていますが、これは、1つはコミットした時のスナップショットで、2つ目はコミット情報、3つ目がコミット自体を圧縮したものです。(理解が怪しいので詳しい方いらっしゃったらコメントもらえると嬉しいです…)
`git cat-file -p`で見ることが出来ます。

```
$ git cat-file -p 2262de
hoge
$ git cat-file -p 57c08
tree 6a73751cba5a86b1ebe10ab2856b68404aab50fd
author tatane616 <メールアドレス> 1560681812 +0900
committer tatane616 <メールアドレス> 1560681812 +0900

Add hoge.txt
$ git cat-file -p 6a737
100644 blob 2262de0c121f22df8e78f5a37d6e114fd322c0b0    hoge.txt
```

## 別ブランチを作ってみる
`master`以外にもブランチを作ってみましょう。

```
$ git checkout -b feature
$ echo foobar > foobar.txt
$ git add foobar.txt
$ git commit -m "Add foobar.txt" 
```

`.git`ディレクトリの中をみてみます。

```
.
├── COMMIT_EDITMSG
├── HEAD
├── branches
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           ├── feature
│           └── master
├── objects
│   ├── 22
│   │   └── 62de0c121f22df8e78f5a37d6e114fd322c0b0
│   ├── 32
│   │   ├── 3fae03f4606ea9991df8befbb2fca795e648fa
│   │   └── 7935c13bb7741628e9edd2bb423c759f513300
│   ├── 57
│   │   └── c089a02b22cb27920e14c256e9140c1b19dbb5
│   ├── 6a
│   │   └── 73751cba5a86b1ebe10ab2856b68404aab50fd
│   ├── db
│   │   └── 7d1a982785cdb9eb8c9b4ac2ae72d1f62f20ee
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   ├── feature
    │   └── master
    └── tags
```

いくつかの部分に`feature`が増えていますね。
`HEAD`を見ると、しっかり`feature`に変わっています。

```
$ cat HEAD 
ref: refs/heads/feature
```

ちなみに、現在の`git log`はこのようになっています。

```
commit db7d1a982785cdb9eb8c9b4ac2ae72d1f62f20ee (HEAD -> feature)
Author: tatane616 <メールアドレス>
Date:   Sun Jun 16 22:15:07 2019 +0900

    Add foobar.txt

commit 57c089a02b22cb27920e14c256e9140c1b19dbb5 (master)
Author: tatane616 <メールアドレス>
Date:   Sun Jun 16 19:43:32 2019 +0900

    Add hoge.txt
```

`refs/heads/feature`を見ると、現在のHEADに合致しています。

```
$ cat refs/heads/feature
db7d1a982785cdb9eb8c9b4ac2ae72d1f62f20ee
```

## もっと詳しく探検したい人は
友人([@zawawahoge](https://twitter.com/zawawahoge))が`.git`ディレクトリの内部で`git init`して、`.git`の差分をGitで確認してました、天才ですね🎉
皆さんも興味がわいたらGitのGitを作って遊んでみてください！

## 参考文献
- https://www.siteground.com/tutorials/git/directory-structure/
- https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain
- https://yakst.com/ja/posts/3811