っていうポストが流れてきて、正直「またか」と思った。
Gitはむずかしい、わかりにくい、サブコマンドの名前がイケてない、っていうのは定期的に上がる話題だ。
一方で、ではなぜGitはわかりにくいのか?という話を掘り下げている記事が意外とないということに気がついたので、この機にまとめておこうと思ったのだった1。
自分が思うに、Gitがわかりにくい理由は、ほとんどの人がそもそもGitが作られた目的と違う使い方をしている ことにあると思う。
これを理解するには、Gitがどこから来たのか、歴史を知らねばならない。
Git誕生前夜:図書館のメタファーと中央集権の時代
「checkout」や「commit」といったなんだか仰々しい言葉は、どこから来たのだろうか?
もちろんこれらはGitが発明した言葉ではない。歴史を紐解くと、1970年代のSCCSや、1980年代のRCSといった、はるか昔のバージョン管理システムにまで遡る。
当時のバージョン管理は「排他制御(ロック)」が基本だった。誰かがファイルを編集している間は、他の人は触れないようにする。つまり、図書室から本を 「借り出し(Checkout)」 て、終わったら 「返却する(Checkin)」 という図書館のメタファーがそのまま使われていたのだ。
その後、1990年代に登場したCVS(Concurrent Versions System)や、2000年代のSubversion(SVN)といったシステムによって、「中央のサーバーにあるファイルを全員で共有し、あとでマージする」という、いわゆる 中央管理型(集中型) の開発スタイルが定着した。
この頃に、RCSの「checkout」という言葉を引き継ぎつつ、変更を中央サーバーに「確定させる」という意味で 「commit(コミット)」 という言葉が広く使われるようになった。
この時代、バージョン管理といえば「サーバーにアクセスして最新のコードをもらい(checkout/update)、手元で直してサーバーに送り返す(commit)」というシンプルなものだった。
この開発フローは、おそらく現代でも多くの人にとって直感的にわかりやすく、また馴染み深いものではないだろうか。
実際、現代の我々もこの時代の開発フローのメタファーを多く引き継いでいる。
しかし、 Gitはこのような中央集権的な開発フローとは全く異なるところから生まれた。
泥臭い手作業と、ある天才の「予言」
一方その頃、Linuxカーネルの開発現場では全く違う課題に直面していた。
世界中に散らばった何千人ものハッカーたちが、Linuxカーネルという巨大なコードベースをいじり回していたのだ。
全員が中央サーバーにアクセスするようなことをしていたらスケールしないのは目に見えているし、他の人がファイルを編集している間そのファイルに触れないとか、あるいはコンフリクトしないように気をつけながら編集するなんて論外だ。
そういうこともあってか、当初Linus Torvaldsはバージョン管理システム(VCS)そのものへの嫌悪感を公言し、「コードを書く邪魔をするもの」として毛嫌いしていた。
では、彼らはどうやってコードを管理していたのか?
それは、驚くほど泥臭い「人間系」の信頼ネットワークだった。
- 手元でソースコードをいじって
diffしてパッチファイルを作る。 - それをメーリングリストにメールで送る。
- Linusの右腕と言えるサブメンテナたちがメールを読んで見極め、最終的にLinusが自分の手元のコードに
patchコマンドで取り込む。
中央サーバーのアクセス権(コミット権)なんかなくても、誰でもコードを送って貢献できるし、なんならそのまま自分でフォークして勝手に開発を進めることだってできる。
これぞ 『伽藍とバザール』 でいうところの「バザール」モデルの真骨頂だ。
しかし、1日何百通ものパッチメールを手動で処理するのには限界がある。
1998年、OSパフォーマンス解析ツールのLMbenchを通じてLinusと交流のあったLarry McVoyという凄腕エンジニアが、 「A solution for growing pains」というタイトルのメールをLinuxカーネルMLに投稿した。
彼はその中で 「Linuxがこのまま巨大化すれば、今の『パッチをメールで送る』やり方は必ず破綻する。解決策は分散型VCSしかない」 と予言した。
Larryは、自身の開発した画期的な 分散型 バージョン管理システム「BitKeeper」2をLinux開発に使わせようと、Linusの説得を試みた。しかし、VCS嫌いのLinusはこれを数年間も突っぱね続けたのである。
「Linus doesn't scale」問題とBitKeeperの導入
Larryの予言がついに的中したのは2002年のことだった。
カーネル開発者が増えすぎた結果、Linusの受信トレイがパンクし、彼自身がパッチをさばききれずに開発が停滞する「Linus doesn't scale」という問題に直面したのだ。
これまでのやり方を続けることはできないと悟ったLinusは、ついにBitKeeperを試すことにした。
使ってみたLinusは驚いた。
BitKeeperは「中央サーバーを置かず」「自分が取り込みたいときだけパッチを取り込む(プル型)」という、彼がこれまで手作業でやっていた汚い仕事をそのままシステム化したようなツールだったからだ。
それもそのはず、Larryは最初から 「Linusの究極の職人芸を極限まで最適化するためのツール」 としてBitKeeperを設計し、彼がギブアップするのを何年も待ち構えていたからだ。
オープンソース界隈から「商用ツールを使うのか!」と猛烈な批判を浴びながらも、LinusはBitKeeperの導入に踏み切り、カーネル開発は劇的に効率化した。
ちなみに、現在もGitのサブコマンドでおなじみの clone、pull、push といった分散型の用語は、このBitKeeperが発明したものだ。
「やるしかなかった」怒りの10日間とGitの誕生
ところが2005年、Linuxコミュニティの一部がBitKeeperをリバースエンジニアリングしようとしたことで、BitKeeper側が「無料ライセンスを廃止する」とブチギレてしまう。
いわゆるBitKeeper事件である。
Linuxカーネルの開発が人質に取られたような状態になり、Linusは途方に暮れた。
しかし既存のツール(CVSやSubversion、当時の分散VCS候補だったMonotoneなど)に対しては「設計がクソだ」「Linuxの規模には遅すぎて使い物にならない」という強い確信があった。
「まともな代替品がないなら、俺が書く。それまでカーネル開発は一切休止する」
Linusはそう宣言した。
2025年のGit20周年インタビューで、彼は当時の心境をこう振り返っている。
「Linuxカーネルをやるのは、OSのカーネルが面白いからだ。でも、Gitをやったのは『やらざるを得なかった(I had to)』からだ。」
つまり、Gitの開発は彼にとって「趣味」ではなく、「大好きなカーネル開発を再開するために、嫌々ながらも完璧に片付けなければならない忌々しい障害物」 だったのである。
凄まじい怒りと切実な必要性に迫られ、彼はなんと わずか10日ほど でGitの原型となるコードを書き上げてしまった。バケモノかな?
なぜそんな短期間で完成に漕ぎ着けたのか。理由は明白で、コードを書くまでに要したのが2週間だっただけで、システム設計のシミュレーションはBitKeeperを使っていた数年の間に彼の中で完全に終わっていたからだ。
彼はBitKeeperの素晴らしさを深く理解しつつも、内部でSCCS(ファイル単位の管理)を引きずっていることによる欠点(ファイル名変更の追跡が苦手など)を見抜いていた。
だから「ファイル単位ではなくツリー全体のスナップショットで管理する」「データが壊れないよう整合性をハッシュ(SHA-1)で担保する」というGitの強固なアーキテクチャの根幹について、開発初日から迷わず設計を進めることができたのである。
Gitは「パッチをこねくり回すツール」である
さて、ここまでの歴史を整理すると、Gitがなぜこれほど「わかりにくい」のか、その最大の理由が見えてくる。
Gitは、CVSやSubversionのような「ファイルの変更履歴を蓄積するためのツール」として生まれたのではない。
メーリングリストでパッチを送り合うという、超絶・分散型のプロセスを極限まで効率化することに特化して作られたツール なのだ。
実際、Linuxカーネルの開発では今でもメーリングリスト方式が現役で使われており、Gitには git format-patch(コミットをメール用パッチに変換)、git send-email(メール送信)、git am(メールからパッチを取り込む)といったコマンドが標準で備わっている。
つまり、Gitというのは本質的に、「手元でパッチをこねくり回して他人に送りつけ、あるいは他所から受け取ったパッチを自分のソースツリーに取り込むためのツール」 なのである。
認識ギャップ:「ユニバース」と「マルチバース」
もうおわかりだろう。Gitがわかりにくいと言われる理由は、 「Linuxカーネル開発という超絶分散型開発のために作られたツールを、まるでSVNのような中央集権型のツールとして使おうとしているから」 だ。
CVSやSVNのような中央集権型の世界では、「正しい歴史は中央サーバーにある1つだけ」という ユニバース(単一宇宙) の世界観だった。対してGitは、作業者全員が完全なリポジトリのコピーを持ち、それぞれのローカルで歴史が分岐していく マルチバース(多元宇宙) の世界観である3
しかし今やほとんどの人が、マルチバースのツールであるGitを、GitHubやGitLabといった「中央リポジトリ」を前提としたユニバースの運用に無理やり押し込めている。
彼らはこう思っている:
「よし、サーバーからコードを取ってきて(えっと、checkoutじゃなくてcloneだっけ?)、変更したら保存して(commitして)、ん?なんでサーバーに反映されないの?ああ、pushしなきゃいけないの?めんどくさ!」
SVN時代なら「commit=サーバーへの送信」だった。
しかしGitでは、commit とはあくまで 「ただ自分のローカル宇宙の歴史にパッチを記録しただけ」 である。
他人のリポジトリ(GitHubなど)に向かって「ねえねえ、俺の書いたパッチを受け取ってよ!」と push しなければ、ローカルの「オレオレ歴史」が共有されることはない。
さらに初心者殺しなのが ステージングエリア(Index) の存在だ。
SVNなら「変更したファイルをコミット」で終わりだった。
しかしGitでは add をしてステージングしなければならない。
これは「ファイルAとBを直したけど、今はAの修正だけを1つ目のパッチとしてまとめたい」というような、 「コミット(パッチ)をきれいに練り上げるための準備スペース」 だからだ。
「とりあえず手元の変更を全部サーバーに上げたいだけ」のユーザーからすれば、この多段階構造(ワークツリー → ステージング → コミット → リモート)は無駄に複雑で煩わしいものにしか見えない。
だが、Gitがパッチをやり取りするために作られたツールであることがわかれば、この多段階構造こそがGitの真価であると理解できるだろう。
そして、そもそも背景にあるコンセプトが違うのだから、CVSやSVNのような中央集権型のツールとサブコマンドの体系が違うのも当たり前のことなのだ4。
rebase の恐怖と、パッチこねくり回しの真骨頂
ここで、初心者が震え上がるサブコマンド筆頭である git rebase についても触れておこう。
SVNのような中央管理型の世界観にどっぷり浸かっていると、「一度コミットした歴史(過去)を改変するなんて、とんでもないご法度だ!」と感じるはずだ。しかし、Gitの世界ではむしろ rebase(特に履歴を編集するインタラクティブモードの rebase -i)は日常茶飯事であり、必須のテクニックである。
なぜなら、Gitにおけるローカルのコミット履歴とは、他人に送りつける前の「パッチの下書き」に過ぎない からだ。
メーリングリストにパッチを投稿して世界中の凄腕ハッカーたちにレビューしてもらうなら、そのパッチ群は「1コミット=1つの論理的な変更」として、誰が読んでも意図が伝わる美しい物語(ストーリー)になっていなければならない。「あ、タイポ直した」とか「とりあえず帰るから保存」みたいなゴミコミットが混ざったままの汚い歴史を、そのままパッチの束として送りつけるのはマナー違反なのだ。
だからこそ、手元で何度コミットしようとも、最後に他人に送りつける(push する、あるいはパッチをメールで送る)直前に、rebase -i を使って複数のコミットを綺麗にまとめ上げたり(squash)、順番を入れ替えたりして、他人が読みやすいように歴史を清書してあげる必要がある。
「Gitは手元でパッチをこねくり回すためのツール」だという元々の思想を知っていれば、なぜ歴史を書き換えるような一見危険なコマンドがあれほど強力に備わっているのかが、ストンと腑に落ちるはずだ。
Gitのエレガントさと、裏にあるオブジェクトストレージ
Gitが本来想定していた「手元で歴史を自在に操る」という使い方を知った上で、その裏側にあるデータ構造を理解すると、Gitの設計がいかにエレガントであるかがわかる。
Gitは差分ではなく、ファイルの状態(スナップショット)やツリー構造、コミット情報などをすべて、ファイルそれ自身のSHA-1ハッシュ値で管理する Content-addressable storage として保存している。ブランチの実態なんて、単に特定のコミットハッシュを指し示す「ただのテキストファイル(ポインタ)」に過ぎない。だからブランチの作成や切り替えが爆速なのだ。
この堅牢で極めて洗練された「オブジェクトストレージとしてのGit」は、単なるVCSの枠を超えて様々なツールのバックエンドとして利用されている。
たとえば、最近話題のJujutsu (jj) も、Gitのオブジェクトストレージという強力な基盤の上に乗ることで、Gitとの互換性も維持している。
他にも、Wikiの裏側でGitをDBとして使う Gollum や、Gitのオブジェクトストレージとそのファイルフォーマットであるpackfileをバックアップシステムに利用している Bup など、例を挙げればキリがない。
みんなが口を揃えて「Gitはわかりにくい」と文句を言いながらも、結局Gitを使わざるを得ないのは、天才が作ったこのコアシステムが、あまりにも優れているからなのだ。
これはまた、天才の優れたアイデアがいかに常人には理解し難いのか、ということの証左でもある。
まとめ:巨人の肩の上に乗っていることを思い出そう
Gitのコマンドが直感的でないのは確かだ。歴史的な経緯で checkout がブランチ切り替えとファイル復元の両方を担わされたり(今は switch と restore に改善されたが)、内部構造(DAG)の理解を強要してきたりするのも事実である。
だが、「SVNみたいに簡単にサーバーに保存させろよ!Gitイケてねー!」と吠えるのは、Gitの思想と設計意図を無視した的外れな批判 なのだ。
もし次にGitを触って「なんだこの挙動は💢」とイラッとしたら思い出してほしい。
今自分が叩いているそのコマンドは、世界中の名だたるハッカーたちが、海を越え、膨大なパッチをメーリングリストで投げ合いながら、分散化された知の結晶(Linuxカーネル)を紡ぎ出すために最適化された、超硬派なツールの名残なのだということを。
そう思えば、少しはGitのことも愛せるようになる……かもしれない。(知らんけど)
参考文献
- Daniel Smith (2023). A Brief History of the Pull Request
- 「pullリクエストはどこから生まれたのか」という視点で分散VCSの歴史を追った記事。今回の記事の執筆に当たって、大いに参考にさせてもらいました。
- 濱野 純 (Junio C Hamano) (2009). 入門Git. 秀和システム.
- Gitのメンテナー本人によるGitの哲学と設計思想を日本語で(翻訳ではなく)原文を読める名著。絶版状態なので、中古で見つけたら買っとけ!
- Larry McVoy (1998). A solution for growing pains. Linux Kernel Mailing List.
- 「Linus doesn't scale」問題を提起して、今日のGitへの道筋を作った歴史の転換点。
- Taylor Blau (2025). Git turns 20: A Q&A with Linus Torvalds. GitHub Blog.
- Git誕生20周年に当たってのLinus Torvaldsへのインタビュー記事。BitKeeper事件からGitに至る経緯が本人の口から語られている。
-
ちなみに自分が読んだこの手の考察の中で、しっくりきたのはこの記事くらいだった: https://eel3.hatenablog.com/entry/2021/08/31/191810 ↩
-
もちろんBitKeeperも、何もないところから突然アイデアが降って湧いたわけではない。Larryはサン・マイクロシステムズに勤務しており、サンで開発されていたTeamWareという分散VCSの存在を知っていたのだ。つまり、Gitの祖先はTeamWareということになる。 ↩
-
普通の人にとっては、マルチバースのような並行世界の概念を直感的に理解するのはただでさえ難しいのだから、Gitで戸惑うのは当然なのだ。 ↩
-
まぁこれには、LinusがCVSとかSVNを嫌っていたから、わざと違う名前をつけたというのもある。 ↩