公開:2025年4月14日

2分で読めます

20年にわたるGitの歴史をたどる

初めて行われたコミット、初期リリースのユニークな特徴、そしてgit-push(1)のデフォルト動作の変更によって生じた混乱について、一緒に振り返っていきましょう。

Gitプロジェクトはちょうど20周年を迎えました。20年の間にさまざまなことがありました。Gitの概念的なデザインは、その登場以来大きく変わってはいないものの、ユーザーによるGitの操作方法は大きく変化しました。GitLabは、この重要な技術をベースにサービスを構築し、その歴史の一部であることを誇りに思っています。

それでは、Gitの歴史をたどり、長年にわたってどのように進化を遂げてきたかを一緒に見ていきましょう。

初めてのコミット

初めてのコミットは、2005年4月7日にLinuxカーネルの生みの親であるLinus Torvalds氏によって行われました。e83c5163316 (Initial revision of "git", the information manager from hell, 2005-04-07)

ご覧のとおり、このコミットには多くのファイルは含まれていませんでした。

$ git ls-tree e83c5163316
100644 blob a6bba79ba1f46a1bbf7773449c3bd2bb9bf48e8b	Makefile
100644 blob 27577f76849c09d3405397244eb3d8ae1d11b0f3	README
100644 blob 98a32a9ad39883c6d05a000a68511d4b1ee2b3c7	cache.h
100644 blob 74a0a234dd346fff51c773aa57d82fc4b83a8557	cat-file.c
100644 blob 840307af0cfaab31555795ce7175d5e9c9f981a0	commit-tree.c
100644 blob 25dc13fe101b219f74007f3194b787dd99e863da	init-db.c
100644 blob c924a6e0fc4c36bad6f23cb87ee59518c771f936	read-cache.c
100644 blob 1b47742d8cbc0d98903777758b7b519980e7499e	read-tree.c
100644 blob b8522886a15db861508fb6d03d4d88d6de912a4b	show-diff.c
100644 blob 5085a5cb53ee52e1886ff6d46c609bdb2fc6d6cd	update-cache.c
100644 blob 921f981353229db0c56103a52609d35aff16f41b	write-tree.c

ビルドインフラストラクチャに加え、最初のコミットでは以下の7つのトップレベルコマンドが提供されていました。

  • init-db:新たなGitリポジトリを初期化
  • update-cache:インデックスにファイルを追加
  • write-tree:インデックスの内容を取得し、それをもとに新たなツリーを作成
  • read-tree:ツリーオブジェクトを読み込む
  • commit-tree:ツリーからコミットを作成
  • cat-file:特定のオブジェクトを一時ファイルに読み込む

なお、この時点では、gitコマンド自体存在していませんでした。代わりに、上記のコマンドを直接実行する必要がありました。

では、試しにリポジトリを新規作成してみましょう。

$ mkdir repo
$ cd repo
$ init-db
defaulting to private storage area
$ ls -a
.  ..  .dircache

みなさんにはまったくなじみがないと思います。.gitディレクトリではなく、.dircacheディレクトリが使用されていました。では、プライベートストレージ領域はどこでしょうか?

初期のGitのデザインでは、オブジェクトストレージ領域は「共有」と「プライベート」に分かれていました。このオブジェクトストレージ領域には、コミットやblobなども含め、あらゆるGitオブジェクトが格納されていました。

init-dbは、デフォルトではプライベートオブジェクトストレージ領域を作成します。これは、領域の作成先の管理ディレクトリ専用として使用されていました。一方、同じオブジェクトを二重に保存する必要がないように、「共有」オブジェクトストレージ領域を使用して、複数の管理ディレクトリ間でオブジェクトの内容を共有していました。

コミットを作成する

リポジトリの作成後は、どのようにコミットを作成していたのでしょうか?作成方法は、現在利用可能なgit add . && git commitほどシンプルではありませんでした。その代わりに、以下の方法で行っていました。

  1. 追加するファイルごとにupdate-cacheを呼び出してインデックスを更新する。
  2. write-treeを呼び出して新規ツリーを書き込む。インデックスに追加済みのすべての内容をもとに作成される。
  3. 環境変数を設定して、Gitにコミッターの情報を伝える。
  4. commit-treeを呼び出して、コミットオブジェクトを書き込む。

それでは、リポジトリにコミットを作成してみましょう。

$ echo content-1 >file-a
$ update-cache file-a
$ echo content-2 >file-b
$ update-cache file-b
$ write-tree
3f143dfb48f2d84936626e2e5402e1f10c2050fb
$ export COMMITTER_NAME="Patrick Steinhardt"
$ export [email protected]
$ echo "commit message" | commit-tree 3f143dfb48f2d84936626e2e5402e1f10c2050fb
Committing initial tree 3f143dfb48f2d84936626e2e5402e1f10c2050fb
5f8e928066c03cebe5fd0a0cc1b93d058155b969

人間工学的な方法とは言えないものの、コミットは作成されます。それでは、生成されたコミットを見てみましょう。

$ cat-file 5f8e928066c03cebe5fd0a0cc1b93d058155b969
temp_git_file_rlTXtE: commit
$ cat temp_git_file_rlTXtE
tree 3f143dfb48f2d84936626e2e5402e1f10c2050fb
author Patrick Steinhardt <[email protected]> Wed Mar 26 13:10:16 2025
committer Patrick Steinhardt <[email protected]> Wed Mar 26 13:10:16 2025

commit message

注目していただきたいのは、cat-fileは内容を直接表示せず、まずは一時ファイルに書き出す点です。しかしながら、ファイルの内容は、まさに現代的なコミットと同じように見えます。

変更を加える

ファイルの作成後、どのようにステータスを確認していたのでしょうか?おそらくお察しのとおりで、show-diffを使用していました。

$ show-diff
file-a: ok
file-b: ok

$ echo modified-content >file-a
$ show-diff
--- -	2025-03-26 13:14:53.457611094 +0100
+++ file-a	2025-03-26 13:14:52.230085756 +0100
@@ -1 +1 @@
-content-1
+modified-content
file-a:  46d8be14cdec97aac6a769fdbce4db340e888bf8
file-b: ok

驚くべきことに、すでにshow-diffでは、変更されたファイルの新旧の状態を比較して差分を取得していました。しかも面白いことに、GitではこれをUNIXのdiff(1)ツールを使用するという簡単な方法で実現していました。

つまり、これらはすべて必要最低限の機能しか備えていなかったものの、履歴の追跡に必要なすべての役割を果たしていました。以下のように制限は多数ありました。

  • あるコミットから別のコミットへ簡単に切り替える方法がなかった。
  • ログを表示できなかった。
  • ブランチやtag、また参照すら存在しなかった。そのため、ユーザーは手動でオブジェクトIDを追跡しなければならなかった。
  • 2つのリポジトリを相互に同期させる方法がなかった。代わりに、ユーザーがrsync(1)を使用して、.dircacheディレクトリを同期させる必要があった。
  • マージの実行方法がなかった。

Git 0.99

Gitの最初のテストリリースは、バージョン0.99でした。バージョン0.99は、最初のコミットからわずか2か月後にリリースされたものの、すでに1,076件のコミットが追加されていました。約50人のデベロッパーが開発に携わっており、最も頻繁にコミットを行っていたのはTorvalds氏自身でした。トーバルズ氏に続く勢いで、コミット件数の多かったコミッターは、現在メンテナーを務めている濱野純氏でした。

最初のコミット以降、多数の変更が加えられました。

  • Gitは参照を使って複数の開発ブランチを追跡するようになりました。その結果、大抵の場合、手作業でオブジェクトIDを追跡せずに済むようになりました。
  • 新たにリモートプロトコルが導入され、2つのリポジトリ間で相互にオブジェクトを交換できるようになりました。
  • .dircacheディレクトリの名前が.gitに変更されました。
  • 個々のファイルを相互にマージできるようになりました。

ただし、最も重要かつ顕著な変化は、トップレベルのgitコマンドとそのサブコマンドが導入されたことでした。興味深いことに、「配管(plumbing)」コマンドと「磁器(porcelain)」コマンドの概念が導入されたのも、このリリースです。

  • 「配管」ツールは、基盤となるGitリポジトリにアクセスして低レベルな処理を行うコマンドです。
  • 「磁器」ツールは、「配管」コマンドをラップして、高レベルでよりユーザーフレンドリーなユーザーインターフェイスを提供するShellスクリプトです。

Gitには現在でもこの分け方は採用されており、git(1)にも概説されています。しかしながら、「磁器」ツールの大半はShellスクリプトからC言語に書き直されたため、これらの2つのカテゴリ間の境界線はかなり曖昧になってきています。

Torvalds氏、メンテナーの役割を濱野氏に引き継ぐ

Torvalds氏がGitに取り掛かった理由は、バージョン管理システムが好きだったためではなく、Linuxカーネル開発のためにBitKeeperの代替ツールを必要としていたためです。そのため、Gitのメンテナンスをずっと続けるつもりはなく、信頼できる人が現れるまで、メンテナーを務めようと考えていました。

その条件に当てはまったのが、濱野純氏でした。濱野氏は、Torvalds氏が最初のコミットを行った約1週間後にGitの開発に参加しました。Git 0.99のリリース後にはすでに数百件のコミットを行っていました。そのため、2005年7月26日に、Torvalds氏は濱野氏をGitプロジェクトの新たなメンテナーに任命しました。Torvalds氏は引き続きGitにコントリビュートしているものの、徐々にGitプロジェクトへの関わりは薄れていきました。これは、Linuxプロジェクトの責任者として多忙を極めているため、当然のことでした。

現在でも、引き続き濱野氏がGitプロジェクトを率いています。

Git 1.0

濱野氏は、2025年12月21日にGitの最初のメジャーリリースを行いました。興味深いことに、バージョン0.99から1.0の間には、34回もリリースが行われました(0.99.1~0.99.7、0.99.7a~0.99.7d、0.99.8~0.99.8g、0.99.9~0.99.9n)。

0.99以降の特に重要なマイルストーンのひとつは、おそらく2つのツリーを相互にマージできるgit-merge(1)コマンドの追加でしょう。それまでは基本的にファイルのマージを行う場合、ファイルごとにスクリプトを作成する必要があったことを考えると、非常に対照的です。

リモート

もう1つの大きな変化は、リモートリポジトリの省略記法が導入されたことです。すでにGitからリモートリポジトリを操作することはできましたが、変更をフェッチする際に毎回、対象のリポジトリのURLを指定する必要がありました。通常は同じリモートリポジトリと何度もやり取りを行うため、これはユーザーにとってかなり使い勝手の悪い仕様でした。

現在のremoteコマンドの仕組みはご存知だと思いますが、当時の仕組みはまだ大きく異なっていました。リモートリポジトリを管理するためのgit-remote(1)コマンドはまだ存在しておらず、リモートリポジトリの情報は.git/configファイルに保存すらされていませんでした。実のところ、バージョン0.99.2でremoteコマンドが最初に導入された際、Gitにはconfigファイル自体ありませんでした

代わりに、.git/branchesに直接ファイルを書き込んでリモートリポジトリの設定を行う必要がありました。今となってはあまり直感的な方法とは思えません。しかしながら、この仕組みは今でも動作します。

$ git init repo --
Initialized empty Git repository in /tmp/repo/.git/
$ cd repo
$ mkdir .git/branches
$ echo https://gitlab.com/git-scm/git.git >.git/branches/origin
$ git fetch origin refs/heads/master

それだけではなく、その直後にGitバージョン0.99.5でディレクトリ名が「remotes」に変更されたため、現在のGitクライアントではリモートリポジトリの設定方法が全部で3つあります。

おそらく多くの方は、.git/branches.git/remotesも使ったことがないと思います。これらはそれぞれ、2005年、および2011年以降、非推奨化されています。また、最終的にこれらのディレクトリは、Git 3.0で削除される予定です。

Gitのブランディング

2007年に、Gitの最初のロゴが作成されました。これは、単に3つの緑のプラス記号の上に3つの赤いマイナス記号が配置された構成(git diffの出力がどのように見えるかを表していた)だったため、ロゴと呼べるかどうかについては、議論の余地があります。

の出力がどのように見えるかを表し、3つの緑のプラス記号の上に3つの赤いマイナス記号が配置されている

それから少し経った2008年に、ウェブサイトgit-scm.comが公開されました。

2026年時点でのgit-scm.comのランディングページ

2012年に、Scott Chacon氏とJason Long氏によって、Gitのウェブサイトはリニューアルされました。ご覧のように現在の外観にかなり近くなりました。

2012年にリニューアルされたGitウェブサイト

再デザインされたウェブサイトには、Jason Long氏がデザインし、今でも使用されている新しい赤橙色のロゴが目立つように掲載されていました。

Gitロゴ

Git 2.0

Git 1.0のリリース時点で、すでに現在のGitとかなり同じようになってきたため、ここでGitの歴史をたどる旅の歩みをGit 2.0まで進めます。Git 1.0の約10年後にリリースされたこのバージョンは、中央ワークフローに後方互換性のない変更を意図的に含めた最初のリリースでした。

git-push(1)のデフォルトの動作

このリリースで最も混乱を招いたのは、間違いなくgit-push(1)のデフォルトの動作が変更されたことです。

リモートリポジトリへのプッシュ時に何をプッシュするかを具体的に指定しなかった場合、Gitは以下のいずれかのアクションを取る可能性がありました。

  • Gitは何も実行せず、何をプッシュするか具体的な情報をユーザーに要求する。
  • その時点でチェックアウトされているブランチをプッシュする。
  • その時点でチェックアウトされているブランチをプッシュする(ただし、リモート側に対応するブランチがあることを確認できた場合に限る)。
  • リモート側に対応するブランチが存在する全ブランチをプッシュする。

現在のGitは、いわゆる「シンプル」な手法を採用しており、上記の3番目のアクションを行います。しかしながら、Git 2.0より前のバージョンにおけるデフォルトの動作は、「マッチング」手法を使用した上記の最後のアクションでした。

「マッチング」手法は、現在採用されている手法と比べて、はるかにリスクがありました。プッシュする前に毎回、リモート側に対応するブランチがあるすべてのローカルブランチをプッシュしても問題がないか確認する必要がありました。そうしないと、意図せずに変更がプッシュされてしまう可能性がありました。そこでリスクを軽減し、Gitを使い始めたばかりのユーザーにとって使い勝手をよくするために「シンプル」手法が採用されました。

git-add(1)

もう1つの大きな変化は、削除された追跡済みファイルに対するgit-add(1)のデフォルトの動作が変更されたことです。Git 2.0より前のバージョンでは、git-add(1)は削除済みのファイルを自動的にステージングしませんでした。そのため、コミットに含めるには、git-rm(1)を使用して削除済みのファイルを1つずつ手動で追加する必要がありました。Git 2.0ではこの動作が変更され、git-add(1)を実行すると、削除済みのファイルもインデックスに追加されるようになりました。

Gitコミュニティの業績を称えよう

おそらくみなさんGitを日々活用しているかと思いますので、Gitの現在の仕組みについて細かくはここでは取り上げません。まだ活用していない方向けには、利用開始に役立つチュートリアルが多数用意されています。現在の仕組みについて説明する代わりに、Git誕生から20年経った今でも機能するように取り組んでくださったGitコミュニティの業績を称えたいと思います。

Gitは、その歴史の中で以下の実績を達成してきました。

  • Git 2.49のリリース時点での累計コミット件数、56,721件
  • 2,000人の個人のコントリビューターによるコントリビュート
  • 公開されたメジャーリリース件数、60件

また、GitプロジェクトはGoogle Summer of Code(GSOC)Outreachyにも参加しており、新たなコントリビューターが着実に増えています。このような新たなコントリビューターのおかげで、Gitプロジェクトは長期的に健全な状態を保てます。

この場を借りて、すべてのコントリビューターに心からお礼申し上げます。みなさんのコントリビュートのおかげで、Gitが実現しました。

今後の展開

Gitがバージョン管理システムの競争で事実上勝利を収めたと言っても、異論はほとんどないでしょう。Gitは大きな市場シェアを占めており、Git以外のバージョン管理システムを使用しているオープンソースプロジェクトはほとんどありません。そう考えると、Gitが多くのことを正しく成し遂げてきたことは明らかです。

とは言っても、Gitの開発は完結したわけではなく、依存として多くの課題が残されています。その例が、以下のような技術的な課題です。

  • 古くなったコードベースのモダナイゼーション
  • 拡大し続けるモノレポのサイズに合わせたスケーリング
  • サイズの大きいバイナリファイルの処理の改善

それとは別に、以下のような社会的な課題もあります。

  • Gitの使いやすさの向上
  • 長期にわたってプロジェクトの健全性を確保することを目的とした、Gitコミュニティの育成

取り組むべき作業は常にあります。次の20年もGitが素晴らしいバージョン管理システムであり続けられるよう、私たちGitLabもコントリビュートできることを誇りに思います。

Git関連のその他のリソース

ご意見をお寄せください

このブログ記事を楽しんでいただけましたか?ご質問やフィードバックがあればお知らせください。GitLabコミュニティフォーラムで新しいトピックを作成して、ご意見をお聞かせください。

フォーチュン100企業の50%以上がGitLabを信頼

より優れたソフトウェアをより速く提供

インテリジェントなDevSecOpsプラットフォームで

チームの可能性を広げましょう。