Back

Gitは内部でどのように動いているのか:オブジェクト、ツリー、そしてBlob

私たちは毎日git addgit commitgit pushを使っています。しかし、.gitフォルダの中を覗いたことはありますか?

Gitの内部を理解することで、「魔法」の謎が解け、はるかに自信を持って使えるようになります(そしてマージコンフリクトの解決も上手になります!)。

本質的に、Gitは**コンテンツアドレス指定ファイルシステム(Content-addressable filesystem)**です。理想的には、キーバリューストアです:

  • Key: コンテンツのSHA-1ハッシュ。
  • Value: コンテンツそのもの。

Gitが履歴を保存するために使用する3つの主要なオブジェクトを見てみましょう。

1. Blob(Binary Large Object)

ファイルをgit addすると、Gitはそのファイルの内容を取得し、Blobとして保存します。

  • ファイル名は保存せず、内容のみを保存します。
  • 2つのファイルの内容が完全に同じであれば、同じBlobを共有します(重複排除!)。

git cat-fileコマンドで確認できます:

$ echo "hello" | git hash-object -w --stdin ce013625030ba8dba906f756967f9e9ca394464a

そのハッシュ値がキーになります。

2. ツリー(Tree)

では、ファイル名はどこに保存されるのでしょうか?Treeオブジェクトの中です。

Treeはディレクトリのようなものです。以下の情報のリストを含んでいます:

  • ファイル権限(例:100644
  • オブジェクトタイプ(blobまたはtree
  • SHA-1ハッシュ
  • ファイル名

Treeをフォルダ構造のスナップショットと考えてください。

100644 blob ce0136...    hello.txt
100644 blob 9a8b7c...    README.md
040000 tree 3b1c2d...    src

3. コミット(Commit)

最後に、Commitオブジェクトがあります。

コミットは、特定のTree(その時点のプロジェクトのスナップショット)を指し示し、メタデータを追加する単なるラッパーです:

  • 作成者(Author)とコミッター(Committer)
  • 日付
  • コミットメッセージ
  • 親コミットハッシュ(Parent Commit hash)

各コミットが親を指し示すため、これらは連結リスト(正確には有向非巡回グラフ - DAG)を形成します。

tree 3b1c2d...
parent 8a9b0c...
author John Doe <[email protected]>
committer John Doe <[email protected]>

Initial commit

オブジェクト間の関係を可視化

graph TD subgraph Commit C[Commit Object] -->|Points to| T[Tree Object] C -->|Points to| P[Parent Commit] end subgraph Tree T -->|Contains| B1[Blob: hello.txt] T -->|Contains| B2[Blob: README.md] T -->|Contains| T2[Tree: src] end subgraph Blob B1 -->|Content| Content1["hello"] B2 -->|Content| Content2["# Readme"] end

すべてをまとめると

git commitを実行するときに起こること:

  1. Gitは変更されたファイルに対してBlobを作成します。
  2. Gitはプロジェクト構造を表すTreeを作成します。
  3. Gitは、そのTreeと以前のコミットを指すCommitオブジェクトを作成します。
  4. ブランチポインタ(例:main)がこの新しいコミットハッシュに移動します。

なぜこれが重要なのか

  • Gitは不変(Immutable)です: オブジェクトが一度作成されると、決して変更されません。履歴を「修正」することは、実際には新しいオブジェクトを作成することです。
  • 安価なブランチ作成: ブランチは、コミットハッシュを1つ含む40バイトの小さなファイルにすぎません。ブランチの作成にはコストがかかりません。
  • 完全性: SHA-1ハッシュのおかげで、データの1ビットでも変わればIDが変わります。検出されずに履歴を改ざんすることは不可能です。

次に「detached HEAD」状態に陥ったときは、思い出してください:あなたは単にブランチ名のラベルが付いていないコミットオブジェクトを指しているだけなのです!

TechGitVCSInternals

関連ツールを見る

Pockitの無料開発者ツールを試してみましょう