Git入門:バージョン管理のきほん

バージョン管理とは何か

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部
commit はセーブポイント

このレッスンで分かること

  • ファイルをコピーして名前で管理する方法が、なぜ数日で破綻するのか
  • Git が記録しているのは「差分の履歴」ではなく「その時点のプロジェクト全体」であること
  • コミットという単位を覚えると、開発中の不安がどう減るのか

学習のはじめに壊れるのは、たいてい動いていたコード

プログラミングを始めて数週間経つと、多くの人が同じ場所でつまずきます。昨日まで動いていたコードが、今日いじったら動かなくなる。しかもどこをいじったのか自分でも分からない。エディタの取り消し操作を連打しても、途中で保存してしまっているので戻りきりません。

このとき失われているのは、コードそのものではありません。「動いていた状態」という情報です。ファイルは目の前にあるのに、それが動く形だった瞬間を再現できない。バージョン管理は、この情報を守るための仕組みです。

多くの入門記事はバージョン管理を「チーム開発に必要なもの」と説明します。それも正しいのですが、順番としては逆です。バージョン管理はまず、一人で書いている自分を過去の自分から守るために使います。共有はその後の話です。

もう少し具体的に考えてみます。あなたが Web ページを作っていて、CSS のレイアウトを調整しているとします。うまくいかないので display の値を変え、position を変え、余白の数値をあちこち触ります。30 分後、画面はさらに崩れています。ここで「調整を始める前の状態に戻りたい」と思っても、変更した箇所が 8 か所に散らばっていて、どれを元の値に戻せばいいのか分かりません。エディタの取り消し履歴も、途中でファイルを切り替えたせいで途切れています。

バージョン管理があれば、この状況は「作業前の記録に戻す」という 1 つの操作で終わります。8 か所を思い出す必要はありません。記録した時点の状態が丸ごと保存されているからです。

手作業のコピーが破綻するまでの流れ

バージョン管理を知らない状態で、多くの人はフォルダをコピーして日付や連番を付けます。

プレーンテキスト

myapp/ myapp_backup/ myapp_backup2/ myapp_20260210/ myapp_20260210_ok/ myapp_final/ myapp_final_fix/

この方法は最初の数日は機能します。破綻するのは次の 3 つが重なったときです。

1 つめは、どれが動く版なのか分からなくなることです。myapp_finalmyapp_final_fix のどちらが新しいか、名前からは判断できません。更新日時を見ても、後から少しだけ開いて保存してしまうと逆転します。「たしか金曜に動く版を作った」という記憶だけが頼りになり、その記憶は 1 週間で消えます。

2 つめは、何を変えたのか思い出せないことです。フォルダを 2 つ並べても、中の 40 ファイルのうちどこが違うのかは目視では分かりません。差分を見る道具が無いからです。ファイルサイズが同じでも中身が違うことはあり、更新日時はコピー操作で書き換わります。

3 つめは、部分的に戻せないことです。「ログイン機能の変更だけ取り消して、その後に直したバグ修正は残したい」という要求は、フォルダ丸ごとのコピーでは絶対に叶いません。フォルダは時点でしか区切れず、意味では区切れないからです。

そして最後に、コピーは容量を食います。node_modules のような依存ライブラリのフォルダを含めて 10 回コピーすれば、それだけでディスクが数ギガバイト埋まります。中身の 99 パーセントは同じファイルなのに、全部が別のデータとして保存されるからです。

チームで作業する場合はさらに深刻です。2 人がそれぞれフォルダをコピーして別々に直したあと、その 2 つを 1 つに戻す手段がありません。片方の変更を捨てるか、40 ファイルを 1 つずつ見比べて手で貼り合わせるかの二択になります。実際にこれをやると、必ずどこかで変更が消えます。しかも消えたことに気付くのは数日後です。

手作業のコピーが悪いのではありません。コピーは「時点」しか記録できず、「変更の意味」を記録できないことが限界です。バージョン管理システムは、この意味の単位を扱うために作られています。

Git が記録しているもの

Git は、変更を保存すると「コミット」という記録を 1 つ作ります。ページ冒頭の図解が示している通り、コミットは鎖のようにつながっていき、それぞれが 1 つ前のコミットを指し示します。この鎖がプロジェクトの履歴になります。

ここで初学者がよく誤解する点があります。コミットは「前回からの差分」を持っているのではなく、その時点のプロジェクト全体のスナップショットを持っています。写真を撮るイメージが近いです。1 コミットが 1 枚の写真で、写真には変更していないファイルも含めて全部が写っています。

プレーンテキスト

コミットA ← コミットB ← コミットC 全ファイル 全ファイル 全ファイル の状態 の状態 の状態

こう聞くと容量が心配になりますが、Git は内容が同じファイルを内部で 1 つのデータとして共有するため、実際のディスク使用量はほとんど増えません。100 ファイルのうち 1 ファイルだけ変えてコミットしても、増えるのはその 1 ファイル分だけです。使う側は「毎回全体が保存されている」と思って構いません。

このモデルの利点は、どのコミットにも単独で戻れることです。差分の積み上げではないので、途中の記録を順番に適用し直す必要がありません。10 個前のコミットの状態を、そのまま丸ごと取り出せます。

なお、画面に表示するときの Git は差分の形で見せてくれます。保存の仕組みはスナップショット、表示は差分。この 2 つは別の話なので、混同しないようにします。

もう 1 つ押さえておきたいのは、コミットの対象がプロジェクト全体であってファイル 1 つではないことです。ある機能を作るときは、HTML と CSS と JavaScript の 3 ファイルを同時に直すことが普通です。この 3 つの変更は意味の上では 1 つの作業なので、Git では 1 つのコミットにまとめます。そうしておけば、その機能を取り消したいときに 3 ファイル分がまとめて戻ります。

冒頭で触れたクラウドストレージの履歴機能との差はここです。あちらはファイル 1 つずつの履歴なので、3 ファイルを別々に戻す操作が必要になり、しかも「どの版とどの版が対になっていたか」が記録されていません。

コミットが持っている情報

1 つのコミットには、ファイルの状態のほかに次の情報が付きます。

項目内容
コミットID9f2c4a1 のような英数字。この記録を一意に指す名前
作者誰が記録したか。名前とメールアドレス
日時いつ記録したか
メッセージ何をしたのかを書いた 1 行以上の説明
親コミット1 つ前のコミットのID

重要なのはメッセージです。「ログインボタンが押せないバグを修正」と書いてあれば、3 か月後の自分がその 1 行を頼りに目的のコミットを探せます。コードは何をしているかを教えてくれますが、なぜそうしたかは教えてくれません。メッセージはその「なぜ」を残す場所です。

コミットIDは長い英数字ですが、実際には先頭 7 文字ほどで指定できます。人間が覚える必要はなく、後で学ぶ git log が一覧で見せてくれます。第 2 章以降で「あのコミットに戻したい」という操作をするときは、この短いIDを使います。

親コミットの情報があることで、Git は履歴を後ろ向きに辿れます。今いる場所から親を辿っていけば、必ず最初のコミットに到達します。最初のコミットだけは親を持ちません。

作者の情報は、次のレッスンで設定する名前とメールアドレスがそのまま入ります。設定していないと Git は警告を出し、環境によってはコミット自体を拒否します。だから最初にやるべき作業がインストールと初期設定になります。

日時は 2 種類が記録されます。コミットを作った日時と、その変更を書いた日時です。普通に使う分には同じ値になるので、当面は 1 つと考えて構いません。第 2 章で履歴を書き換える操作を学ぶと、この 2 つがずれる場面が出てきます。

コミットIDは連番ではありません。中身から計算される固定長の値なので、同じ内容から常に同じIDが出ます。これにより、ファイルが 1 バイトでも書き換わっていれば必ず検知できます。バックアップとしての信頼性はここから来ています。

バージョン管理があると変わる 3 つのこと

実験が怖くなくなる

新しい書き方を試したいが、今動いている部分を壊したくない。バージョン管理が無いとこの葛藤で手が止まります。コミットしてあれば、どれだけ壊しても 1 コマンドで直前の状態に戻せます。

これは学習の速度に直結します。戻る手段があるかどうかが、そのまま試せる回数になるからです。「壊したら終わり」の状態では、人はコードを触らなくなります。後の章で学ぶブランチを使えば、本体を一切触らずに実験用の作業場所を作ることもできます。

変更の理由が残る

半年前に書いた不自然なコードを見つけたとき、履歴を辿ればそれを書いたコミットとメッセージに行き着きます。「この API が特定の順番でしか動かないため」と書いてあれば、そのコードを消してはいけないと分かります。

逆にメッセージが「修正」「更新」だけだと何も分かりません。だからこのコースでは、第 2 章でコミットメッセージの書き方を 1 レッスン使って扱います。バージョン管理は、コードには書けない背景を保管する場所になります。

複数人の変更が合流できる

同じファイルを 2 人が別々に直したとき、Git は両方の変更を突き合わせて 1 つにまとめようとします。違う行を触っているなら自動でまとまり、同じ行を触っていたときだけ人に判断を求めます。

この仕組みが無いと、チーム開発は「今このファイルは私が触っています」という声かけで運用することになります。人数が 3 人を超えた時点で回らなくなります。

さらに、誰がいつその行を書いたかを行単位で調べる機能もあります。原因不明の挙動に出会ったとき、その行を書いた人とコミットメッセージにすぐ辿り着けます。相手を責めるためではなく、当時の意図を聞くためです。これは一人開発でも役に立ちます。過去の自分は他人だからです。

分散型という考え方

Git は分散型バージョン管理システムに分類されます。分散型とは、開発者全員が履歴の完全な複製を自分のパソコンに持つ方式のことです。

これに対して、履歴をサーバー 1 か所だけに置く方式を集中型と呼びます。集中型ではサーバーに接続できないと履歴が見られず、コミットもできません。

観点集中型分散型(Git)
履歴の置き場所サーバーのみ全員の手元とサーバー
オフラインでのコミットできないできる
履歴の閲覧通信が必要手元で完結
コミットの速度通信分だけ遅い一瞬で終わる
サーバー障害時作業が止まる手元で継続できる

速度の差も見逃せません。集中型では履歴を 1 件見るたびにサーバーへ問い合わせが飛びます。Git は全部が手元にあるので、1 万件の履歴を検索しても一瞬で終わります。日常的に履歴を見る習慣が付くかどうかは、この体感速度でほぼ決まります。

分散型の実感が湧くのは、飛行機の中やネットワークの無い環境で作業したときです。Git なら普段どおりコミットを積み、後でつながったときにまとめて共有できます。

このコースでは第 1 章から第 3 章まで、ネットワークをまったく使わずに手元の Git だけで進めます。GitHub との連携は第 4 章で扱います。順番を逆にすると、手元で起きていることと通信で起きていることが混ざって理解しづらくなるためです。「push したのに反映されない」という定番の混乱は、ほぼすべてこの区別が付いていないことから起きます。

ありがちな失敗と、その先にあるもの

学習を始めたばかりの人が最もよくやる失敗は、コミットを溜め込むことです。1 日作業してから 1 回だけコミットする、あるいは「機能が完成してからコミットしよう」と考えて 3 日分をまとめてしまう。

これをやると、せっかくの履歴が「3 日前」か「今」の 2 択になり、細かく戻すことができません。動かなくなったときに戻れる場所が遠すぎて、結局手作業でコピーしていた頃と変わらなくなります。3 日分の変更が 1 つのコミットに入っていると、メッセージも「いろいろ修正」としか書けません。

溜め込む理由はたいてい「まだ完成していないから」です。しかし Git のコミットは完成品を出す操作ではありません。作業の途中経過に印を付けているだけです。

対策は単純です。「ここまでは動いている」と思った瞬間にコミットします。関数を 1 つ書き終えた、表示の崩れを直した、この程度の粒度で構いません。コミットは後からまとめることもできるので、細かすぎて困ることはほとんどありません。

逆の失敗もあります。何も変わっていないのにとりあえずコミットする、あるいは 1 行の空白を消しただけで 1 コミットにする。これをやると履歴が意味のない記録で埋まり、後から目的のコミットを探せなくなります。目安は「このコミットのメッセージを 1 行で書けるか」です。書けないほど大きいなら分割し、書くことが無いほど小さいなら次の変更とまとめます。

もう 1 つの失敗は、コミットを「完成の宣言」だと思ってしまうことです。コミットは手元の記録にすぎず、他人には一切見えません。第 4 章で扱う push をするまでは、どれだけ雑にコミットしても誰にも影響しません。気軽に積んでいい記録だと考えてください。

3 つめは、Git を GUI ツールの操作だけで覚えようとすることです。ボタンの名前と Git の用語が一致していないツールが多く、何が起きているかを掴めないまま操作を丸暗記することになります。エラーが出たときに検索しても、記事に載っているのはコマンドの説明なので対応付けられません。このコースではコマンドで進めます。仕組みが分かった後で GUI に移るのは自由ですが、順番は逆にしないほうが結局早く済みます。

次のレッスンでは Git を実際にインストールし、自分の名前とメールアドレスを設定します。この設定を先にやっておかないと、最初のコミットで警告が出て手が止まります。

この章のポイント
  • バージョン管理は共有のための道具である前に、動いていた状態に戻るための道具
  • Git のコミットは差分ではなく、その時点のプロジェクト全体のスナップショット
  • コミットにはメッセージが付き、コードに書けない「なぜ」を残せる
  • 分散型なので、履歴は全員の手元に完全な形で存在しオフラインでも作業できる