Gitの仕組みを垣間見てみる（確認編）

下記記事の内容を実際にコマンドを実行しながら確認してみる。

https://qiita.com/Crawd/items/eae3e88d7cba9875ab2f

### 1. `git init`を実行

```
mkdir gittest
cd gittest/.git

.
├── HEAD
├── branches
├── config
├── description
├── hooks
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags
```

### 2. ファイルを作って`git add`

```
❯ echo "First" > file1.txt
❯ git add file1.txt
 
.
├── HEAD
├── branches
├── config
├── description
├── hooks
├── index
├── info
│   └── exclude
├── objects
│   ├── 48
│   │   └── 56fe236dbdd59db2741de0235ae7db63c27a1a
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags
```

新たに作成されたのは`4856fe2`のGitオブジェクトとindexファイル。
`4856fe2`のGitオブジェクトの内容を`git cat-file`で確認する。

```
❯ git cat-file -p 4856fe2
First
```

`file1.txt`の内容を格納しているblobオブジェクトが作成されている。
indexファイルの内容を`git ls-files`で確認する。

```
❯ git ls-files --stage
100644 4856fe236dbdd59db2741de0235ae7db63c27a1a 0       file1.txt
```

現在のステージングエリアの内容が格納されていることがわかる。

### 3. `git commit`する
```
❯ git commit -m "First Commit"

.git/
├── COMMIT_EDITMSG
├── HEAD
├── branches
├── config
├── description
├── hooks
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 18
│   │   └── e184c65133446831504aa14212765cfaaf84be
│   ├── 48
│   │   └── 56fe236dbdd59db2741de0235ae7db63c27a1a
│   ├── 6c
│   │   └── 7213511f6852e357e27c220ec8c2199bcd7ec9
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags
```

作成されたファイルの中身を確認する。

```
❯ git cat-file -p 18e184c
100644 blob 4856fe236dbdd59db2741de0235ae7db63c27a1a    file1.txt
```

`gittest`ディレクトリに対応するtreeオブジェクトが作成されている。

```
❯ git cat-file -p 6c72135
tree 18e184c65133446831504aa14212765cfaaf84be
author Crawd <test@example.com> 1729080682 +0900
committer Crawd <test@example.com> 1729080682 +0900

First Commit
```

1回目のコミットに対応するcommitオブジェクトが作成されている。

```
❯ cat .git/refs/heads/master
6c7213511f6852e357e27c220ec8c2199bcd7ec9
```

最初のコミットを作成したことでmasterブランチが作成され、1回目のコミットを指し示している。


### 4. 新たにディレクトリを作成し、そのディレクトリ内にファイルを１つ作成する。作成したファイルを`git add`する。

```
❯ mkdir dir1
❯ cd dir1
❯ echo "Second" > file2.txt
❯ git add dir1/file2.txt

.
├── dir1
│   └── file2.txt
└── file1.txt


.git/
├── COMMIT_EDITMSG
├── HEAD
├── branches
├── config
├── description
├── hooks
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 18
│   │   └── e184c65133446831504aa14212765cfaaf84be
│   ├── 48
│   │   └── 56fe236dbdd59db2741de0235ae7db63c27a1a
│   ├── 49
│   │   └── 5a7e948fe261472b6a1b15d76ffdd69a68eff0
│   ├── 6c
│   │   └── 7213511f6852e357e27c220ec8c2199bcd7ec9
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags
```

作成されたGitオブジェクトの内容を確認する。

```
❯ git cat-file -p 495a7e9
Second
```

file2.txtに対応するblobオブジェクトが作成されている。

```
❯ git ls-files --stage
100644 495a7e948fe261472b6a1b15d76ffdd69a68eff0 0       dir1/file2.txt
100644 4856fe236dbdd59db2741de0235ae7db63c27a1a 0       file1.txt
```

indexファイルの内容。
更新されていないfile1.txtの内容も保持していることがわかる。

### 5. `git commit`する。
```
❯ git commit -m "Second Commit"

.git/
├── COMMIT_EDITMSG
├── HEAD
├── branches
├── config
├── description
├── hooks
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 18
│   │   └── e184c65133446831504aa14212765cfaaf84be
│   ├── 1b
│   │   └── 6d6d37524dde2b5a035d7a3a659ff92faba1e6
│   ├── 48
│   │   └── 56fe236dbdd59db2741de0235ae7db63c27a1a
│   ├── 49
│   │   └── 5a7e948fe261472b6a1b15d76ffdd69a68eff0
│   ├── 6c
│   │   └── 7213511f6852e357e27c220ec8c2199bcd7ec9
│   ├── a4
│   │   └── e270851b8580efbca3b478583c2530eb60267a
│   ├── e4
│   │   └── fe49647f2e9533250257bb0b4a4877ca80856e
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags
```

新たに作成されたgitオブジェクトの内容を確認する。

```
❯ git cat-file -p 1b6d6d3
tree a4e270851b8580efbca3b478583c2530eb60267a
parent 6c7213511f6852e357e27c220ec8c2199bcd7ec9
author Crawd <test@example.com> 1729081669 +0900
committer Crawd <test@example.com> 1729081669 +0900

Second Commit
```

2回目のコミットに対応するcommitオブジェクトが作成されている。

```
❯ git cat-file -p a4e2708
040000 tree e4fe49647f2e9533250257bb0b4a4877ca80856e    dir1
100644 blob 4856fe236dbdd59db2741de0235ae7db63c27a1a    file1.txt
```

`gittest`ディレクトリに対応するtreeオブジェクトが作成されている。

```
❯ git cat-file -p e4fe496
100644 blob 495a7e948fe261472b6a1b15d76ffdd69a68eff0    file2.txt
```

`dir1`ディレクトリに対応するtreeオブジェクトが作成されている。

```
❯ cat .git/refs/heads/master
1b6d6d37524dde2b5a035d7a3a659ff92faba1e6
```

`master`ブランチは、2回目のコミットを指し示している。


### 6. `git tag`でタグを作成する。

```
❯ git tag testtag1
❯ git tag -a testtag2 -m "Annotation Tag"


.git/
├── COMMIT_EDITMSG
├── HEAD
├── branches
├── config
├── descriptihttps://qiita.com/growsic/items/ed67e03fda5ab7ef9d08on
├── hooks
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 18
│   │   └── e184c65133446831504aa14212765cfaaf84be
│   ├── 19
│   │   └── 00f7f12f43f52261100f03f60ad6c523c18607
│   ├── 1b
│   │   └── 6d6d37524dde2b5a035d7a3a659ff92faba1e6
│   ├── 48
│   │   └── 56fe236dbdd59db2741de0235ae7db63c27a1a
│   ├── 49
│   │   └── 5a7e948fe261472b6a1b15d76ffdd69a68eff0
│   ├── 6c
│   │   └── 7213511f6852e357e27c220ec8c2199bcd7ec9
│   ├── a4
│   │   └── e270851b8580efbca3b478583c2530eb60267a
│   ├── e4
│   │   └── fe49647f2e9533250257bb0b4a4877ca80856e
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags
        ├── testtag1
        └── testtag2
```

作成されたGitオブジェクトを確認する。

```
❯ git cat-file -p 1900f7f
object 1b6d6d37524dde2b5a035d7a3a659ff92faba1e6
type commit
tag testtag2
tagger Crawd <test@example.com> 1729082183 +0900

Annotation Tag
```

`testtag2`に対応するtagオブジェクト。
タグが参照しているコミット(2回目のコミット)の情報も格納されている。

```
❯ cat .git/refs/tags/testtag1
1b6d6d37524dde2b5a035d7a3a659ff92faba1e6

❯ cat .git/refs/tags/testtag2
1900f7f12f43f52261100f03f60ad6c523c18607
```
軽量タグ(`testtag1`)は2回目のコミットを直接参照しており、注釈付きタグ(`testtag2`)は先程のtagオブジェクトを参照している。


### 7. `git branch`でブランチを作成する。

ブランチ作成前の状態を確認しておく。

```
❯ cat .git/HEAD
ref: refs/heads/master

❯ cat .git/refs/heads/master
1b6d6d37524dde2b5a035d7a3a659ff92faba1e6
```

`.git/HEAD`は現在のブランチの参照を格納している。
現在は`master`ブランチにいることがわかる。

`.git/refs/heads/master`は`master`ブランチが現在指し示しているコミットの参照が格納されている。
`master`ブランチは2回目のコミットを参照していることがわかる。

`git branch`で`testbranch`ブランチを作成する。
```
❯ git branch testbranch

gittest on  master
❯ tree .git/
.git/
├── COMMIT_EDITMSG
├── HEAD
├── branches
├── config
├── description
├── hooks
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           ├── master
│           └── testbranch
├── objects
│   ├── 18
│   │   └── e184c65133446831504aa14212765cfaaf84be
│   ├── 19
│   │   └── 00f7f12f43f52261100f03f60ad6c523c18607
│   ├── 1b
│   │   └── 6d6d37524dde2b5a035d7a3a659ff92faba1e6
│   ├── 48
│   │   └── 56fe236dbdd59db2741de0235ae7db63c27a1a
│   ├── 49
│   │   └── 5a7e948fe261472b6a1b15d76ffdd69a68eff0
│   ├── 6c
│   │   └── 7213511f6852e357e27c220ec8c2199bcd7ec9
│   ├── a4
│   │   └── e270851b8580efbca3b478583c2530eb60267a
│   ├── e4
│   │   └── fe49647f2e9533250257bb0b4a4877ca80856e
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   ├── master
    │   └── testbranch
    └── tags
        ├── testtag1
        └── testtag2
```

作成されたファイルの内容を確認する。

```
❯ cat .git/refs/heads/testbranch
1b6d6d37524dde2b5a035d7a3a659ff92faba1e6
```

`testbranch`が2回目のコミットを参照していることがわかる。

### 8. `git switch`でブランチを切り替える
```
❯ git switch testbranch
Switched to branch 'testbranch'

❯ cat .git/HEAD
ref: refs/heads/testbranch

❯ cat .git/refs/heads/testbranch
1b6d6d37524dde2b5a035d7a3a659ff92faba1e6
```

`.git/HEAD`の内容から`testbranch`に切り替わっていることがわかる。
`testbranch`は2回目のコミットを指し示している。

### 9. file1.txtを更新し、`git add`、`git commit`する。
```
gittest on  testbranch
❯ echo "Third" >> file1.txt

gittest on  testbranch [!]
❯ git add file1.txt

gittest on  testbranch [+]
❯ git commit -m "Third Commit"
[testbranch eab02bb] Third Commit
 1 file changed, 1 insertion(+)
```

`.git/HEAD`ファイルと`.git/refs/head`ファイルの内容を確認する。

```
❯ cat .git/HEAD
ref: refs/heads/testbranch

❯ cat .git/refs/heads/testbranch
eab02bb70abe497f074016605ff6e2c353caf7da

❯ cat .git/refs/heads/master
1b6d6d37524dde2b5a035d7a3a659ff92faba1e6
```

`.git/HEAD`の内容から`testbranch`にいることがわかる。
`testbranch`ブランチは3回目のコミットを指し示している。
`master`ブランチは2回目のコミットを指し示したまま。

## まとめ
- Gitは4つのオブジェクトで変更を管理している。
- `git add`はblobオブジェクトを作成する。
- `git commit`はtreeオブジェクトとcommitオブジェクトを作成する。
- タグは特定のコミットへの参照。
- HEADはブランチへの参照。今いるブランチを示す。
- ブランチは特定のコミットへの参照。