LoginSignup
0
0

More than 1 year has passed since last update.

Gitの裏側の仕組み

Posted at

Gitの仕組みを理解するために、gitの操作に応じて.gitディレクトリの中身がどのように変化するかをまとめました。

Gitの用語や基本操作を理解していることは前提。

git init

git initを実行すると実行したディレクトリ内のファイルがGitの追跡状態になり、.gitディレクトリが作られる。

.gitディレクトリ

.gitディレクトリの中身は以下のようになっている。

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

主なディレクトリの役割

  • HEAD - 現在のブランチの参照を表すファイル
  • config - リモートブランチなどの情報が書かれたファイル
  • objects - コミットなどのオブジェクトを保存するディレクトリ
  • refs- ブランチの情報を保存するディレクトリ
    • heads - ブランチの情報を保存する
    • tag - タグの情報を保存する

ステージに追加

git addで変更をステージに追加する。

ステージに追加したときの.gitディレクトリの中身

.git
├── HEAD
├── config
├── description
├── hooks
│   ├── 省略
├── index
├── info
│   └── exclude
├── objects
│   ├── e6
│   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags
  • index - インデックスの情報を保存するファイル

ステージに追加するとindexファイルとobjectsディレクトリの中にファイルが作られている。

objectsの中に作られたe6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391というファイルは変更対象の圧縮ファイルであり、blobオブジェクトという。blobとはかたまりという意味。

圧縮ファイルのファイル名はファイル内容などをハッシュ関数で40文字の英数字に変換したハッシュIDであり、先頭の2文字をディレクトリ名にして、残り38文字をファイル名として保存される。

このハッシュIDは一意のものであり、ファイルの内容が同じなら全く同じものとなる。

indexファイルには、変更したファイルのblobオブジェクトと対応するファイルの名前が記録されている。

コミット

ステージに追加した変更をコミットする。

コミット時の.gitディレクトリの中身は以下のようになる。

.git
├── COMMIT_EDITMSG
├── HEAD
├── config
├── description
├── hooks
│   ├── 省略
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 09
│   │   └── b5607d0c6afa2cc0067699e6b25807c7c8659c
│   ├── e6
│   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│   ├── f9
│   │   └── 3e3a1a1525fb5b91020da86e44810c87a2d7bc
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags

COMMIT_EDITMSGlogsrefs/heads/masterobjectsの中にいくつかのディレクトリとファイルが作られている。

重要なのがobjectsの中の3つのファイルである。1つはステージに追加した時の圧縮ファイルであり、残りがツリーファイルとコミットファイルである。

ツリーファイル

ツリーファイルはファイル名とファイル構造を保存するファイルで、treeオブジェクトという。

圧縮ファイルにはファイル名が保存されていないので、ツリーファイルによって圧縮ファイルとファイル名を対応させる。

コミットファイル

コミットファイルはいつ、誰が、何を、何のために変更したかが保存されるファイルで、commitオブジェクトという。

コミットファイルにはtree、parent、author、committer、コミットメッセージの5つの情報が保存される。

treeはコミットした時点のプロジェクトの一番上のディレクトリのツリーファイルが保存される。このツリーファイルを保存することでスナップショットを記録している。

parentは親のコミット、つまり1つ前のコミットを保存している。これによってコミットの履歴を辿ることができる。一番最初のコミットには親のコミットを持たないのでparentは保存されない。

authorとcommitterはコミットした人のユーザー名とメールアドレスが保存される。

プッシュ

リモートリポジトリにローカルの内容を保存するためにgit pushを行う。git pushを行うとremotesディレクトリが作られる。このremotesディレクトリの直下にあるディレクトリがリモート追跡ブランチになる。

.git
├── COMMIT_EDITMSG
├── HEAD
├── config
├── description
├── hooks
│   ├── 省略
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 09
│   │   └── b5607d0c6afa2cc0067699e6b25807c7c8659c
│   ├── e6
│   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│   ├── f9
│   │   └── 3e3a1a1525fb5b91020da86e44810c87a2d7bc
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    ├── remotes
    │   └── origin
    │       └── main    
    └── tags

refs/remotes/origin/mainの中身をcatコマンドで確認すると、プッシュした時点での最新のコミットのハッシュが保存されている。

09b5607d0c6afa2cc0067699e6b25807c7c8659c

ブランチを作成

git branchでブランチを作成すると、refs/headsの中に作成したブランチ名のファイルが作られ、その中身には最新のコミットのハッシュが記述されている。

git branch featureを実行する。

.git
├── COMMIT_EDITMSG
├── HEAD
├── config
├── description
├── hooks
│   ├── 省略
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 09
│   │   └── b5607d0c6afa2cc0067699e6b25807c7c8659c
│   ├── e6
│   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│   ├── f9
│   │   └── 3e3a1a1525fb5b91020da86e44810c87a2d7bc
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   ├── feature
    │   └── master
    └── tags

作られたrefs/heads/featureの中身をcat .git/refs/heads/featureで確認する。

09b5607d0c6afa2cc0067699e6b25807c7c8659c

これによってブランチは最新のコミットを参照していることがわかる。

ブランチの切り替え

git checkoutでブランチを切り替えると、HEADファイルの中身に切り替えたブランチ名が記述される。

masterブランチにいる状態でgit checkout featureを実行し、cat .git/HEADを実行して中身を確認する。

ref: refs/heads/feature

これによってHEADが今いるブランチを指していて、そのブランチは最新のコミットを指しているということがわかる。

タグ

git tag [タグ名]でタグを作成すると、refs/tagsの中に作成したタグ名のファイルが作られ、

ここではgit tag v1.0と実行する。

.git
├── COMMIT_EDITMSG
├── HEAD
├── config
├── description
├── hooks
│   ├── 省略
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           ├── feature
│           └── master
├── objects
│   ├── 09
│   │   └── b5607d0c6afa2cc0067699e6b25807c7c8659c
│   ├── e6
│   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│   ├── f9
│   │   └── 3e3a1a1525fb5b91020da86e44810c87a2d7bc
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   ├── feature
    │   └── master
    └── tags
        └── v1.0

cat .git/refs/tags/v1.0で中身を確認する。

09b5607d0c6afa2cc0067699e6b25807c7c8659c

これによって作成したタグがコミットを指していることがわかる。

まとめ

  • git initでGitの初期化を行うと、.gitディレクトリが作られる
  • git addで変更をステージに追加すると、ファイルの内容を圧縮した圧縮ファイルが作られる
  • 圧縮ファイルには一意の名前を設定するためにハッシュIDが使われる
  • git commitで変更をコミットすると、ファイル名とファイル構造を保存するツリーファイルと、いつ、誰が、何を、何のために変更したかを保存するコミットファイルが作られる

参考

https://github.com/kaityo256/github/blob/main/internals/README.md
https://zenn.dev/kaityo256/articles/inside_the_index

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0