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を作って遊んでみてください!