【ITニュース解説】Git Exposed, Part 1: Git Isn’t What You Think It Is
2025年09月30日に「Dev.to」が公開したITニュース「Git Exposed, Part 1: Git Isn’t What You Think It Is」について初心者にもわかりやすく解説しています。
ITニュース概要
Gitは単なるファイル保管でなく、ファイル内容をハッシュ値で管理する「コンテンツアドレス指定可能なファイルシステム」だ。Blobが内容、Treeが構造、Commitがスナップショットを記録。HEADとブランチで履歴を追う。従来のバージョン管理と異なり、効率的なコンテンツ追跡システムとして成功した。
ITニュース解説
Gitは、多くの人が最初に考えるような、単にローカルのファイルをGitHubのようなリモートサービスに送るだけのツールではない。GitとGitHubは連携して使われることが多いが、両者は直接的に比例する関係にあるわけではなく、Gitはもっと根本的な仕組みに基づいている。
Gitの真の姿は、その設計者であるLinus Torvaldsの言葉にヒントがある。「Gitは『愚かなコンテンツトラッカー』である」と彼は語っており、伝統的なソースコード管理システム(SCM)とは異なり、ファイルシステムに近い発想で設計された。特に重要なのは、「コンテンツアドレス指定型」のファイルシステムであるという点だ。
コンテンツアドレス指定とは、簡単に言えば、ファイルの中身そのものから生成されるユニークな「フィンガープリント」(ハッシュコードと呼ばれる)を使ってデータを管理する方式を指す。通常のファイルシステムがファイル名や保存場所でファイルを識別するのに対し、Gitはファイル名や日付、ファイル間の関連性といったメタデータには直接関心を持たない。代わりに、あるファイルの内容をGitに与えると、Gitはその内容からハッシュコードを生成し、そのハッシュコードを使って内容を保存する。後でそのハッシュコードをGitに渡せば、Gitは対応する内容を正確に取り出すことができる。たとえば、「Arma vs Code」という文字列を保存すると「a1b2c3d4...」のようなハッシュコードが生成され、次に「a1b2c3d4...」と指定すれば、Gitは「Arma vs Code」を返す。これにより、同じ内容であれば、たとえ異なるファイル名や異なるディレクトリに存在しても、Gitの内部では一度だけ保存され、ストレージが効率的に節約される。
Gitがどのようにコンテンツを保存し、プロジェクトの履歴を管理しているのか、その内部の仕組みを見てみよう。私たちが「コミットする」という操作を行う際、Gitの内部ではいくつかの重要なオブジェクトが生成され、連携して動作している。
まず「Blobオブジェクト」がある。これは、あなたのファイルの中身そのものを格納する。ファイル名やパーミッション、ディレクトリ構造といった情報は一切含まれず、純粋なデータだけが保存される。例えば、index.htmlというファイルの内容が「Hello, world!」であれば、「Hello, world!」という文字列がBlobとして保存され、それに固有のハッシュコードが付与される。同じ内容を持つファイルがプロジェクト内に複数存在する場合でも、GitはBlobを一度だけ保存するため、ストレージを効率的に節約できる。
次に「Treeオブジェクト」が登場する。Blobオブジェクトだけでは、どのファイルがどのディレクトリにあり、プロジェクト全体がどのような構造をしているのかを把握できない。そこでTreeオブジェクトは、ディレクトリの中身をリスト化する役割を果たす。一つのTreeオブジェクトは、ファイルやサブディレクトリ(これもまたTreeオブジェクト)への参照を含んでいる。各参照は、ファイル名や、対応するBlobオブジェクトまたはTreeオブジェクトのハッシュコード、そしてファイルの権限情報などから構成される。これにより、Treeオブジェクトはプロジェクトのある時点での「スナップショット」、つまりディレクトリ構造とファイル内容の全体像を表現する。もしプロジェクト内のファイルが変更されれば、新しいBlobオブジェクトが作成され、それに伴い新しいTreeオブジェクトも作成されるが、変更されていないファイルやディレクトリに対応するTreeやBlobはそのまま再利用される。この仕組みが、Gitの効率的なスナップショットベースのストレージを可能にしている。
そして、「Commitオブジェクト」が作成される。Commitオブジェクトは、プロジェクトの履歴における一つの「時点」を記録するものだ。これは、作成されたTreeオブジェクト(つまりプロジェクトのスナップショット)に加えて、そのコミットを行った作者の情報、コミットを行った時間、コミットメッセージ、そして一つ前のコミット(親コミット)へのポインタといったメタデータを含む。この親コミットへのポインタがあることで、Gitはコミット同士を連結させ、プロジェクトの変更履歴を辿れるようになる。Commitオブジェクトもまた、その内容に基づいてユニークなハッシュコードが付与され、Gitのオブジェクトデータベースに保存される。
コミットが作成されたら、Gitは次に「HEAD」を更新する。HEADは、Gitが現在どのコミット、またはどのブランチに「位置しているか」を示す非常に重要なポインタである。通常、HEADは特定のブランチ(例:refs/heads/main)を指している。新しいコミットが作成されると、このHEADが指しているブランチが、新しく作成されたコミットを指すように更新される。
最後のステップとして、「ブランチ参照の更新」が行われる。Gitにおけるブランチとは、実質的には特定のコミットを指す軽いポインタに過ぎない。ブランチの情報は.git/refs/heads/ディレクトリ内のファイルとして保存されており、そのファイルの中身は、ブランチが指し示すコミットのハッシュコードである。新しいコミットが作成され、HEADがそのブランチを指している場合、そのブランチの参照ファイルの内容が更新され、新しく作成されたコミットのハッシュコードを指すようになる。これにより、新しいコミットがプロジェクトの履歴に正式に組み込まれ、git logコマンドなどでその履歴を追跡できるようになる。この更新が行われないと、せっかく作成されたコミットはどのブランチからも参照されず、事実上「失われた」状態になってしまう。
コミットが完了すると、ステージングエリア(インデックスと呼ばれる)もリセットされ、次の変更を受け入れる準備が整う。
Gitがここまで成功したのは、伝統的なバージョン管理システムとしてではなく、ファイルシステムという視点から設計されたからだと言える。コンテンツをハッシュコードで管理し、スナップショットを効率的に保存するこの独特なアプローチが、Gitの強力な基盤となっている。