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のようなもの
-
exclude:
-
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...ですが、最初の2文字でサブディレクトリを作って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)が.gitディレクトリの内部でgit initして、.gitの差分をGitで確認してました、天才ですね🎉
皆さんも興味がわいたらGitのGitを作って遊んでみてください!