【Git】「.git」の中をGitに管理させてみた

# はじめに
[Git Advent Calendar 2023 3日目](https://qiita.com/advent-calendar/2023/git)の投稿です。よろしくおねがいします。
[前々回](https://qiita.com/arubaito/private/607ebc3e6f87b50d5a68)、[前回](https://qiita.com/arubaito/private/d643222f712e5a04b234)を通じて以下のコマンドが使えるようになったと思います。
- `git init`
- `git add`
- `git commit`
- `git branch`
- `git checkout`
- `git merge`

内部でどんな動きがされてるか気になったので、今回は↑のコマンドを実行した時に`.git`フォルダの中がどのように変化するか観察したいと思います。

# やること
下の感じで進めていきます。

リポジトリ(A)を作成
⬇
リポジトリ(A)の`.git`内で`git init`して`.git`を管理するリポジトリ(B)を作成
⬇
リポジトリ(A)の方でGitのコマンドをいろいろ打ってリポジトリ(B)で差分を観察

Gitリポジトリを2つ使っているので、切り替えがわかりにくい箇所があると思いますがご容赦ください:bow:

# 準備
- 「git/sample2」フォルダを用意。
このフォルダでGitの各コマンドを打って`.git`の中身を観察していきます。

# git init
`git init`をしてリポジトリ(A)の`.git`フォルダを作ります。

```batch
~/Desktop/git/sample2
$ git init
Initialized empty Git repository in フォルダ名
```

- `.git`フォルダの構成
```
.git
│  config
│  description
│  HEAD
│
├─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
```

`.git`フォルダで`git init`してリポジトリ(B)を作ります。

```batch
~/Desktop/git/sample2/.git (GIT_DIR!)
$ git init
Initialized empty Git repository in フォルダ名

~/Desktop/git/sample2/.git (master)
$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        HEAD
        config
        description
        hooks/
        info/

nothing added to commit but untracked files present (use "git add" to track)

~/Desktop/git/sample2/.git (master)
$ git add .

~/Desktop/git/sample2/.git (master)
$ git commit -m"git init後"
[master (root-commit) a47036c] git init後
```

# git add
リポジトリ(A)上で「sample.txt」を作成します。

```batch
~/Desktop/git/sample2 (master)
$ echo "hello world" > sample.txt

~/Desktop/git/sample2 (master)
$ ls
sample.txt
```
ファイルを作成した直後の`.git`フォルダは特になにも変わりがありません。
```batch
~/Desktop/git/sample2/.git (master)
$ git status
On branch master
nothing to commit, working tree clean
```
`git add`してみます。
```batch
~/Desktop/git/sample2 (master)
$ git add .
```
`git add`後の`.git`フォルダ
```batch
~/Desktop/git/sample2/.git (master)
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        index
        objects/

nothing added to commit but untracked files present (use "git add" to track)
```
お、変化がありましたね。
- `.git`フォルダの現在の状況
```
C:.
│  config
│  description
│  HEAD
│  index　←←←新規
│
├─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
│  ├─3b　←←←新規
│  │      18e512dba79e4c8300dd08aeb37f8e728b8dad
│  │
│  ├─info
│  └─pack
└─refs
    ├─heads
    └─tags
```
`git init`後の`.git`フォルダと比較すると、新しく`index`と`objects`配下の`3b/18e512...`が作成されたことが分かります。`index`はバイナリファイルで差分の検証が難しいので、最後に[補足](#indexファイルについて補足)で説明書いてみます。

オブジェクトの中身を見てみましょう。
```batch
~/Desktop/git/sample2 (master)
$ git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
hello world
```
`git add`されたタイミングでブロブオブジェクトが生成されてます。
分かるのはこれくらいですね。

# git commit
次にコミットをしてみます。
```batch
~/Desktop/git/sample2 (master)
$ git commit -m"first commit"
[master (root-commit) 411124e] first commit
```
`.git`で`git status`してみます。
```batch
~/Desktop/git/sample2/.git (master)
$ 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:   index

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        COMMIT_EDITMSG
        logs/
        objects/02/
        objects/88/
        refs/

no changes added to commit (use "git add" and/or "git commit -a")
```
`.git`フォルダの中身が色々変わってきました。:arrow_down: `.git`フォルダ全体です。
```
.git
│  COMMIT_EDITMSG　←←新規
│  config
│  description
│  HEAD
│  index　←←更新
│
├─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
│
├─logs　←←新規
│  │  HEAD
│  │
│  └─refs
│      └─heads
│              master
│
├─objects
│  ├─02　←←新規
│  │      3bd50171db535beab09fed56bd5a9281b01555
│  │
│  ├─3b
│  │      18e512dba79e4c8300dd08aeb37f8e728b8dad
│  │
│  ├─88　←←新規
│  │      ccc6b27fbf7beb37e2b2dadceef08ed83a5716
│  │
│  ├─info
│  └─pack
└─refs
    ├─heads
    │      master　←←新規
    │
    └─tags
```
１つづつ見てみましょう。

まず、オブジェクトが２つ生成されてますので、中身をのぞいてみます。
```batch
~/Desktop/git/sample2 (master)
$ git cat-file -p 88ccc6b27fbf7beb37e2b2dadceef08ed83a5716
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad    sample.txt

~/Desktop/git/sample2 (master)
$ git cat-file -p 023bd50171db535beab09fed56bd5a9281b01555
tree 88ccc6b27fbf7beb37e2b2dadceef08ed83a5716
author hogehoge <fugafuga> 1699592736 +0900
committer hogehoge <fugafuga> 1699592736 +0900

first commit
```
ツリーオブジェクトとコミットオブジェクトが生成されていることが分かります。
ツリーはブロブへの、コミットはツリーへの参照を持っていることも分かりますね。

続いて`COMMIT_EDITMSG`をのぞいてみましょう。
```
$ cat COMMIT_EDITMSG
first commit
```
直前にコミットしたときのコミットメッセージですかね？いまのところ、これ以上分かりません。
あとで試しにもう一度コミットして変化するかどうか観察してみましょう。

続いて`refs/heads/master`をのぞいてみましょう。
```
$ cat refs/heads/master
023bd50171db535beab09fed56bd5a9281b01555
```
中身は`objects`フォルダに新規で作成されたコミットオブジェクトのハッシュ値ですね。

最後に`logs/HEAD`と`logs/refs/master`です。
```
$ cat logs/HEAD
0000000000000000000000000000000000000000 023bd50171db535beab09fed56bd5a9281b01555 hogehoge <fugafuga> 1699592736 +0900  commit (initial): first commit

$ cat logs/refs/heads/master
0000000000000000000000000000000000000000 023bd50171db535beab09fed56bd5a9281b01555 hogehoge <fugafuga> 1699592736 +0900  commit (initial): first commit
```
`logs`はその名の通りログを格納してますね。
それぞれ、`HEAD`がどのコミットからどのコミットへ移動したか、ブランチがどのコミットからどのコミットへ移動したのかを記録してます。
今回はブランチに対して特に操作をしていないので、同じ内容っぽいです。

`COMMIT_EDITMSG`の扱いが気になるので、もう１度コミットしてみましょう。
適当に「sample.txt」を更新して再度コミットします。
```batch
~/Desktop/git/sample2 (master)
$ echo "hello" > sample.txt

~/Desktop/git/sample2 (master)
$ git add .

~/Desktop/git/sample2 (master)
$ git commit -m"second commit"
[master 6cf4fd0] second commit
 1 file changed, 1 insertion(+), 1 deletion(-)
```
`.git`はどうなるでしょうか。`git status`をしてみます。
```batch
~/Desktop/git/sample2/.git (master)
$ 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:   COMMIT_EDITMSG
        modified:   index
        modified:   logs/HEAD
        modified:   logs/refs/heads/master
        modified:   refs/heads/master

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        objects/6c/
        objects/ce/
        objects/e3/

no changes added to commit (use "git add" and/or "git commit -a")
```
取り合えず見たかった`COMMIT_EDITMSG`ファイルの差分を見てみましょう。
```diff
~/Desktop/git/sample2/.git (master)
$ git diff COMMIT_EDITMSG
diff --git a/COMMIT_EDITMSG b/COMMIT_EDITMSG
index 44e4d81..2ff0cc0 100644
--- a/COMMIT_EDITMSG
+++ b/COMMIT_EDITMSG
@@ -1 +1 @@
-first commit
+second commit
```
どうやら、直前のコミットメッセージを保存しているファイルのようです。
もう察しがつくと思いますが、他のファイルも一応見ていきましょうか。
:arrow_down: 今の`.git`フォルダの様子です。
```
.git
│  COMMIT_EDITMSG　←←←更新
│  config
│  description
│  HEAD
│  index　←←←更新
│
├─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
│
├─logs
│  │  HEAD　←←←更新
│  │
│  └─refs
│      └─heads
│              master　←←←更新
│
├─objects
│  ├─02
│  │      3bd50171db535beab09fed56bd5a9281b01555
│  │
│  ├─3b
│  │      18e512dba79e4c8300dd08aeb37f8e728b8dad
│  │
│  ├─6c　←←←新規
│  │      f4fd07bd3d1f982045f50fae9dc070d6967f36
│  │
│  ├─88
│  │      ccc6b27fbf7beb37e2b2dadceef08ed83a5716
│  │
│  ├─ce　←←←新規
│  │      013625030ba8dba906f756967f9e9ca394464a
│  │
│  ├─e3　←←←新規
│  │      d14d7340059b5852f32f29574e98ff73eb3c47
│  │
│  ├─info
│  └─pack
└─refs
    ├─heads
    │      master
    │
    └─tags
```
- `objects`フォルダ配下の新規作成されたファイル３つ
```batch
~/Desktop/git/sample2 (master)
$ git cat-file -p ce013625030ba8dba906f756967f9e9ca394464a
hello

~/Desktop/git/sample2 (master)
$ git cat-file -p e3d14d7340059b5852f32f29574e98ff73eb3c47
100644 blob ce013625030ba8dba906f756967f9e9ca394464a    sample.txt

~/Desktop/git/sample2 (master)
$ git cat-file -p 6cf4fd07bd3d1f982045f50fae9dc070d6967f36
tree e3d14d7340059b5852f32f29574e98ff73eb3c47
parent 023bd50171db535beab09fed56bd5a9281b01555
author hogehoge <fugafuga> 1699594574 +0900
committer hogehoge <fugafuga> 1699594574 +0900

second commit
```
ブロブとツリーとコミットのオブジェクトですね。

- `logs`フォルダ配下の更新されたファイルの差分
```diff
~/Desktop/git/sample2/.git (master)
$ git diff logs/
diff --git a/logs/HEAD b/logs/HEAD
index 8783cde..58d6c1d 100644
--- a/logs/HEAD
+++ b/logs/HEAD
@@ -1,2 +1,2 @@
 0000000000000000000000000000000000000000 023bd50171db535beab09fed56bd5a9281b01555 hogehoge <fugafuga> 1699592736 +0900 commit (initial): first commit
+023bd50171db535beab09fed56bd5a9281b01555 6cf4fd07bd3d1f982045f50fae9dc070d6967f36 hogehoge <fugafuga> 1699594574 +0900 commit: second commit
diff --git a/logs/refs/heads/master b/logs/refs/heads/master
index 8783cde..58d6c1d 100644
--- a/logs/refs/heads/master
+++ b/logs/refs/heads/master
@@ -1,2 +1,2 @@
 0000000000000000000000000000000000000000 023bd50171db535beab09fed56bd5a9281b01555 hogehoge <fugafuga> 1699592736 +0900 commit (initial): first commit
+023bd50171db535beab09fed56bd5a9281b01555 6cf4fd07bd3d1f982045f50fae9dc070d6967f36 hogehoge <fugafuga> 1699594574 +0900 commit: second commit
```
ログはこうやって書かれていくんですね。

- `refs`配下の更新されたファイルの差分
```diff
~/Desktop/git/sample2/.git (master)
$ git diff refs/
diff --git a/refs/heads/master b/refs/heads/master
index f1bb341..ed3d53b 100644
--- a/refs/heads/master
+++ b/refs/heads/master
@@ -1 +1 @@
-023bd50171db535beab09fed56bd5a9281b01555
+6cf4fd07bd3d1f982045f50fae9dc070d6967f36
```
`master`ブランチの参照するコミットが移動しました。

# git branch
次に`git branch`でブランチを作ってみましょう。
`another`ブランチを作ります。
```batch
~/Desktop/git/sample2 (master)
$ git branch another
```
`.git`フォルダの中はどうなったのでしょうか。
```batch
~/Desktop/git/sample2/.git (master)
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        logs/refs/heads/another
        refs/heads/another

nothing added to commit but untracked files present (use "git add" to track)
```
２つファイルができました。見てみましょう。
```
$ cat refs/heads/another
6cf4fd07bd3d1f982045f50fae9dc070d6967f36

$ cat logs/refs/heads/another
0000000000000000000000000000000000000000 6cf4fd07bd3d1f982045f50fae9dc070d6967f36 hogehoge <fugafuga> 1699595907 +0900  branch: Created from master
```
`refs/heads/another`は`master`と同じコミットを指しています。
ログを見て確認しておきます。
```batch
~/Desktop/git/sample2 (master)
$ git log --oneline
6cf4fd0 (HEAD -> master, another) second commit
023bd50 first commit
```

# git checkout
では、`git checkout`してブランチを移動してみましょう。
`another`ブランチに移動してみます。
```batch
~/Desktop/git/sample2 (master)
$ git checkout another
Switched to branch 'another'

~/Desktop/git/sample2 (another)
```

さて`.git`はどうなるでしょうか。
```batch
~/Desktop/git/sample2/.git (master)
$ 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:   HEAD
        modified:   logs/HEAD

no changes added to commit (use "git add" and/or "git commit -a")
```
見ての通り、`HEAD`と`logs/HEAD`が更新されてますね。
差分とってみます。
- `HEAD`ファイル
```diff
~/Desktop/git/sample2/.git (master)
$ git diff HEAD -- HEAD
diff --git a/HEAD b/HEAD
index cb089cd..b64c555 100644
--- a/HEAD
+++ b/HEAD
@@ -1 +1 @@
-ref: refs/heads/master
+ref: refs/heads/another
```
先ほど作成された`refs/heads/another`ファイルを参照するように更新されてました。そりゃそうなんですが、`HEAD`が`another`を参照するように更新されてますね。
再掲にはなりますが、`refs/heads/another`は以下の内容でした。
```
$ cat refs/heads/another
6cf4fd07bd3d1f982045f50fae9dc070d6967f36
```
このことから`HEAD` :arrow_right: `another`ブランチ :arrow_right: コミット`6cf4fd..`へと参照が続くように更新されてます。

- `logs/HEAD`ファイル

```diff
~/Desktop/git/sample2/.git (master)
$ git diff HEAD -- logs/HEAD
diff --git a/logs/HEAD b/logs/HEAD
index 58d6c1d..9bb9ffe 100644
--- a/logs/HEAD
+++ b/logs/HEAD
@@ -1,2 +1,3 @@
 0000000000000000000000000000000000000000 023bd50171db535beab09fed56bd5a9281b01555 hogehoge <fugafuga> 1699592736 +0900 commit (initial): first commit
 023bd50171db535beab09fed56bd5a9281b01555 6cf4fd07bd3d1f982045f50fae9dc070d6967f36 hogehoge <fugafuga> 1699594574 +0900 commit: second commit
+6cf4fd07bd3d1f982045f50fae9dc070d6967f36 6cf4fd07bd3d1f982045f50fae9dc070d6967f36 hogehoge <fugafuga> 1699596443 +0900 checkout: moving from master to another
```
これは`HEAD`参照のログですね。こういう風にログをとってるんですね。

# git merge
最後に`git merge`して`.git`フォルダを観察してみます。

## 準備
（準備が長いので準備と観察する節をわけました）
まず準備として`another`ブランチへ適当なコミットを作ります。
```batch
~/Desktop/git/sample2 (another)
$ echo "another" > sample.txt

~/Desktop/git/sample2 (another)
$ git add .

~/Desktop/git/sample2 (another)
$ git commit -m"third commit"
[another cbc33d4] third commit
 1 file changed, 1 insertion(+), 1 deletion(-)
```
`.git`フォルダの状態。特筆すべきところは特にないので、とりあえずコミットしておきます。
```batch
~/Desktop/git/sample2/.git (master)
$ 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:   COMMIT_EDITMSG
        modified:   index
        modified:   logs/HEAD
        modified:   logs/refs/heads/another
        modified:   refs/heads/another

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        objects/11/
        objects/9b/
        objects/cb/

no changes added to commit (use "git add" and/or "git commit -a")

~/Desktop/git/sample2/.git (master)
$ git add .

~/Desktop/git/sample2/.git (master)
$ git commit -m"git commit 1回目@another"
[master d25acad] git commit 1回目@another
```

今度は`master`ブランチに`git checkout`します。
```batch
~/Desktop/git/sample2 (another)
$ git checkout master
Switched to branch 'master'
```
`.git`フォルダの状態。
```batch
~/Desktop/git/sample2/.git (master)
$ 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:   HEAD
        modified:   index
        modified:   logs/HEAD

no changes added to commit (use "git add" and/or "git commit -a")

~/Desktop/git/sample2/.git (master)
$ git add .

~/Desktop/git/sample2/.git (master)
$ git commit -m"git checkout後another→master"
[master c15e8de] git checkout後another→master
 3 files changed, 2 insertions(+), 1 deletion(-)
```
`another`ブランチでファイルを更新した状態から元の`master`ブランチに戻ってきたので、「index」も更新されてますね。

## マージ
さあ、`another`ブランチを`master`ブランチへ`git merge`してみます。
```batch
~/Desktop/git/sample2 (master)
$ git merge --no-ff another
Merge made by the 'recursive' strategy.
 sample.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
```
`.git`フォルダの状態。
```batch
~/Desktop/git/sample2/.git (master)
$ 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:   index
        modified:   logs/HEAD
        modified:   logs/refs/heads/master
        modified:   refs/heads/master

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        ORIG_HEAD
        objects/bb/

no changes added to commit (use "git add" and/or "git commit -a")
```
新しく`ORIG_HEAD`とオブジェクトが生成されましたね。
見てみましょう。
```
$ cat ORIG_HEAD
6cf4fd07bd3d1f982045f50fae9dc070d6967f36
```
何のハッシュ値だっけこれ…。ということで、ログを確認してみます。
```batch
~/Desktop/git/sample2 (master)
$ git cat-file -p 6cf4fd07bd3d1f982045f50fae9dc070d6967f36
tree e3d14d7340059b5852f32f29574e98ff73eb3c47
parent 023bd50171db535beab09fed56bd5a9281b01555
author hogehoge <fugafuga> 1699594574 +0900
committer hogehoge <fugafuga> 1699594574 +0900

second commit

~/Desktop/git/sample2 (master)
$ git log --oneline --graph
*   bb54bb5 (HEAD -> master) Merge branch 'another'
|\
| * cbc33d4 (another) third commit
|/
* 6cf4fd0 second commit
* 023bd50 first commit
```
`ORIG_HEAD`が参照する`6cf4fd`は`master`ブランチの直前のコミットですね。
だから`git reset ORIG_HEAD --hard`でマージ前の状況に戻れるわけですね。

新たに生成されたオブジェクトはどういう内容でしょうか。
```batch
MINGW64 ~/Desktop/git/sample2 (master)
$ git cat-file -p bb54bb567fc99a85750561362e2dcd892f179434
tree 11982c20951b8249234a871fb7fce5983fe1465f
parent 6cf4fd07bd3d1f982045f50fae9dc070d6967f36
parent cbc33d4a1844fddc6f4dad5918897d00a6eaa80f
author hogehoge <fugafuga> 1699598971 +0900
committer hogehoge <fugafuga> 1699598971 +0900

Merge branch 'another'
```
2つの親のオブジェクトを参照するマージコミットのオブジェクトですね。

他の全ファイル見てるとくどくなるので、個人的な興味で`logs`配下のファイルだけ見てみます。
```diff
~/Desktop/git/sample2/.git (master)
$ git diff logs/
diff --git a/logs/HEAD b/logs/HEAD
index c43111f..eb4e0a5 100644
--- a/logs/HEAD
+++ b/logs/HEAD
@@ -4,3 +4,4 @@
 6cf4fd07bd3d1f982045f50fae9dc070d6967f36 6cf4fd07bd3d1f982045f50fae9dc070d6967f36 hogehoge <fugafuga> 1699596443 +0900 checkout: moving from master to another
 6cf4fd07bd3d1f982045f50fae9dc070d6967f36 cbc33d4a1844fddc6f4dad5918897d00a6eaa80f hogehoge <fugafuga> 1699598305 +0900 commit: third commit
 cbc33d4a1844fddc6f4dad5918897d00a6eaa80f 6cf4fd07bd3d1f982045f50fae9dc070d6967f36 hogehoge <fugafuga> 1699598624 +0900 checkout: moving from another to master
+6cf4fd07bd3d1f982045f50fae9dc070d6967f36 bb54bb567fc99a85750561362e2dcd892f179434 hogehoge <fugafuga> 1699598971 +0900 merge another: Merge made by the 'recursive' strategy.
diff --git a/logs/refs/heads/master b/logs/refs/heads/master
index 58d6c1d..1256f4a 100644
--- a/logs/refs/heads/master
+++ b/logs/refs/heads/master
@@ -1,2 +1,3 @@
 0000000000000000000000000000000000000000 023bd50171db535beab09fed56bd5a9281b01555 hogehoge <fugafuga> 1699592736 +0900 commit (initial): first commit
 023bd50171db535beab09fed56bd5a9281b01555 6cf4fd07bd3d1f982045f50fae9dc070d6967f36 hogehoge <fugafuga> 1699594574 +0900 commit: second commit
+6cf4fd07bd3d1f982045f50fae9dc070d6967f36 bb54bb567fc99a85750561362e2dcd892f179434 hogehoge <fugafuga> 1699598971 +0900 merge another: Merge made by the 'recursive' strategy.

```
ログはそう書かれるわけね。

## 競合

では競合させるとどうなるのでしょう。
`master`と`another`で同じファイルを適当に編集してみます。

```batch
説明：masterブランチでファイルを編集
~/Desktop/git/sample2 (master)
$ echo "master" > sample.txt

~/Desktop/git/sample2 (master)
$ git add .

~/Desktop/git/sample2 (master)
$ git commit -m"fourth commit"
[master 61dd6f5] fourth commit
 1 file changed, 1 insertion(+), 1 deletion(-)

説明：anotherブランチでファイルを編集
~/Desktop/git/sample2 (master)
$ git checkout another
Switched to branch 'another'

~/Desktop/git/sample2 (another)
$ echo "another branch" > sample.txt

~/Desktop/git/sample2 (another)
$ git add .

~/Desktop/git/sample2 (another)
$ git commit -m"fifth commit"
[another bd61df7] fifth commit
 1 file changed, 1 insertion(+), 1 deletion(-)

説明：masterブランチでマージするので移動
~/Desktop/git/sample2 (another)
$ git checkout master
Switched to branch 'master'
```
今こんな感じです。
```batch
~/Desktop/git/sample2 (master)
$ git log --graph --oneline --all
* bd61df7 (another) fifth commit
| * 61dd6f5 (HEAD -> master) fourth commit
| *   bb54bb5 Merge branch 'another'
| |\
| |/
|/|
* | cbc33d4 third commit
|/
* 6cf4fd0 second commit
* 023bd50 first commit
```

さあ、マージして競合させてみます。
```batch
~/Desktop/git/sample2 (master)
$ git merge --no-ff another
Auto-merging sample.txt
CONFLICT (content): Merge conflict in sample.txt
Automatic merge failed; fix conflicts and then commit the result.
```
競合しました。`.git`フォルダはどうなっているのでしょうか。
```batch
 ~/Desktop/git/sample2/.git (master)
$ 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:   ORIG_HEAD
        modified:   index

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        MERGE_HEAD
        MERGE_MODE
        MERGE_MSG
        objects/7a/

no changes added to commit (use "git add" and/or "git commit -a")
```
先ほどのマージの際にはなかったファイルが生成されてますね。
`MERGE_HEAD`、`MERGE_MODE`、`MERGE_MSG`の中身を見ていきましょう。
```
$ cat MERGE_HEAD
bd61df78647648b52c932db29880eda9f0e6b72f

$ cat MERGE_MODE
no-ff

$ cat MERGE_MSG
Merge branch 'another'

# Conflicts:
#       sample.txt
```
`MERGE_HEAD`はマージされるブランチの最新コミットですね（今回で言うと`another`ブランチの最新コミット）。

`7a`から始まるオブジェクトも生成されていますので中身を見てみます。
```batch
~/Desktop/git/sample2 (master)
$ git cat-file -p 7a67e00d6b7bee74c0f0ebdd167f22400ea80f9b
<<<<<<< HEAD
master
=======
another branch
>>>>>>> another
```
競合するとdiff形式のオブジェクトが生成されるんですね。

次に更新された`ORIG_HEAD`です。
先ほどと同じく、マージする直前のコミットを指してます。
```diff
~/Desktop/git/sample2/.git (master)
$ git diff ORIG_HEAD
diff --git a/ORIG_HEAD b/ORIG_HEAD
index ed3d53b..6eb6ba5 100644
--- a/ORIG_HEAD
+++ b/ORIG_HEAD
@@ -1 +1 @@
-6cf4fd07bd3d1f982045f50fae9dc070d6967f36
+61dd6f56c96d9a3edef66cce619cf2915d76205e
```
`--abort`すると`.git`フォルダはどうなるんですかね。
`--abort`した後の`.git`フォルダの状況は以下のようになります。
```batch
~/Desktop/git/sample2/.git (master)
$ git status
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    MERGE_HEAD
        deleted:    MERGE_MODE
        deleted:    MERGE_MSG
        modified:   index
        modified:   logs/HEAD

no changes added to commit (use "git add" and/or "git commit -a")
```
消えました。これらのファイルはマージが完了されても消えそうですね。

先ほどの競合を再現した後に、今度は競合を解消してマージしてみます。
競合の再現は省略します。↓競合発生後から競合解消までです。
```batch
~/Desktop/git/sample2 (master|MERGING)
$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   sample.txt

no changes added to commit (use "git add" and/or "git commit -a")

~/Desktop/git/sample2 (master|MERGING)
$ git diff
diff --cc sample.txt
index 1f7391f,5d70384..0000000
--- a/sample.txt
+++ b/sample.txt
@@@ -1,1 -1,1 +1,5 @@@
++<<<<<<< HEAD
 +master
++=======
+ another branch
++>>>>>>> another

~/Desktop/git/sample2 (master|MERGING)
$ echo "master" > sample.txt

~/Desktop/git/sample2 (master|MERGING)
$ git add .

~/Desktop/git/sample2 (master|MERGING)
$ git commit
[master 8254984] Merge branch 'another'
```
競合を解消して、マージコミットができました。

「.git」フォルダの状況はこうなります。
```batch
 ~/Desktop/git/sample2/.git (master)
$ git status
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   COMMIT_EDITMSG
        deleted:    MERGE_HEAD
        deleted:    MERGE_MODE
        deleted:    MERGE_MSG
        modified:   index
        modified:   logs/HEAD
        modified:   logs/refs/heads/master
        modified:   refs/heads/master

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        objects/82/

no changes added to commit (use "git add" and/or "git commit -a")
```
`--abort`と同じく`MERGE_HEAD`、`MERGE_MODE`、`MERGE_MSG`は消えました。

また、マージコミットのオブジェクトが生成されています。
```batch
~/Desktop/git/sample2 (master)
$ git cat-file -p 8254984b906f193d27c2e4f55ee75991155bcedf
tree d9fd303cfe17e779b06fe84c97c970ba22253c4b
parent 61dd6f56c96d9a3edef66cce619cf2915d76205e
parent bd61df78647648b52c932db29880eda9f0e6b72f
author hogehoge <fugafuga> 1699601812 +0900
committer hogehoge <fugafuga> 1699601812 +0900

Merge branch 'another'
```


以上で、「.git」を「Git」に管理させてみる回は終わりです。
最後まで読んでくれた方ありがとうございました。

# 「index」ファイルについて補足
ここでかたくなに無視し続けた`index`ファイルについて補足です。
`index`ファイルはバイナリファイルなので観察するのをやめてましたが、名前の通り`index`ファイルはインデックスの状態を表現するバイナリファイルです。ツリーオブジェクトと同じく、ツリーの参照とファイル名とブロブの対応を持つイメージです。

`index`ファイルの作成と更新をまとめると以下のようになります。
- [`git add`](#git-add)：初回のみ`index`作成、その後は更新
- [`git commit`](#git-commit)：`index`更新
- [`git branch`](#git-branch)：なにもなし
- [`git checkout`](#git-checkout)：なにもなし
- [`git merge`](#git-merge)：`index`更新

`git add`すると`index`ファイルが更新されて`git commit`時にツリーオブジェクトが作成されることから、言い換えると`git add`時はツリーオブジェクトは作成されず`index`ファイルの更新のみが行われます。`git commit`でツリーオブジェクトが作成されてインデックスの内容も差し替えられます。
こうすることで、`git add`した際は作業ディレクトリとインデックスの内容が一致して、`git commit`時は作業ディレクトリとインデックスとリポジトリが一致してますね。

# さいごに
コマンドを打ちながら記事を書きましたが、途中間違えたりして一部コマンド結果を改変して載せてますので、細かい箇所に間違いがあったらごめんなさい:pray: