初級ではGitを「セーブポイントを何度でも作れて、いつでも戻れる仕組み」と捉えました。中級では、その正体であるコミット・ブランチ・マージ・リモートを見ていきます。
概要 — まず全体をつかむ
詳細 — 1段階ずつ追う
これは何をする係?
1回の記録=コミットは、その時点のスナップショット(※1 作業場所を丸ごと写した1枚)です。差分だけでなく「その瞬間の全体像」を指し示し、各コミットは1つ前の親コミットを覚えています。
だから履歴はスナップショットの連なりになります。連なりをたどれば必ず過去の完全な状態に戻れる——これが「安全」の正体です。1点でも独立して残っているので、途中が壊れても後ろの記録は無傷です。
登場人物メモ:
- ※1 スナップショット — その時点のファイル全体を写した記録の1枚
- ※2 HEAD — 「いま自分が見ている/作業している位置」を指す目印
- ※3 リモート — GitHub などの共有先。手元の履歴を送受信する相手
この部分をもっと深く(上級)
Gitの中身は、実は4種類のオブジェクトを中身のハッシュ値で結んだ小さなデータベースです。ここが分かると、あらゆる操作の理由が一気に見えてきます。
- blob(※1)— ファイルの中身そのもの。ファイル名は持たない
- tree(※2)— ディレクトリに相当。どのファイル名がどのblobかを並べた一覧
- commit(※3)— ある瞬間のtree(=全体像)と、親コミット・作者・メッセージを指す
- tag — 特定のコミットに付ける不変の名札(リリース地点など)
肝はコンテンツアドレッシング——それぞれのオブジェクトを、中身から計算したハッシュ値(SHA)そのものを名前にして保存することです。中身が1バイトでも違えば名前が変わり、同じ中身なら世界中で1つに共有されます。
アニメーション『Gitの履歴グラフ(分岐とマージ)』を開く
だから「コミットはスナップショット」でも容量は膨れません。変わっていないファイルは同じblobを使い回すからです。そして各コミットは親コミットのハッシュを含むので、履歴の1点を書き換えると以降のハッシュがすべて連鎖的に変わり、改ざんは即座にばれます。
登場人物メモ:
- ※1 blob — ファイルの中身を保存したオブジェクト。名前は中身のハッシュ
- ※2 tree — ファイル名とblobの対応表。ディレクトリ構造を表す
- ※3 commit — treeと親コミットを指す、履歴の1節目
やさしく言うと(初級)
Gitは、ファイルの変更の履歴を記録する道具(※1 バージョン管理)です。ゲームのセーブポイントを何度でも作れて、いつでも好きな地点に戻れるイメージ。
記録した1つ1つの地点は※2 リポジトリという「作業場所ぜんぶの記録箱」にたまっていきます。上書きで前が消えるのではなく、過去の状態がそのまま残るのが肝です。
登場人物メモ:
- ※1 バージョン管理 — 変更の履歴を記録し、前の状態に戻せるようにする仕組み
- ※2 リポジトリ — ファイルと、その全履歴を保管する箱
履歴を枝分かれさせる — ブランチとマージ
- ブランチ — 履歴を枝分かれさせる。main(本流)を壊さず、
featureなどの枝で新機能を試せる - マージ — 枝を本流に統合する。枝の変更を取り込んで1つに合流させる
下の図は、mainの連なりから途中で feature が分かれ、最後にmainへマージ(合流)する流れです。
アニメーション『Gitの履歴グラフ(分岐とマージ)』を開く
枝を切るのが軽いのは、コミットが独立したスナップショットで、ブランチはその1点を指す名札にすぎないから。だから「試してダメなら枝ごと捨てる」が気軽にできます。
この部分をもっと深く(上級)
枝を統合する方法は2つあり、履歴の形が根本的に違います。
- fast-forward — 分岐後に本流が1歩も進んでいなければ、マージは名札を前へ動かすだけ。新しいコミットは作られない
- 3-wayマージ — 両方が進んでいるときは、共通の祖先と両先端の3点を比べて自動統合し、親を2つ持つマージコミットを作る。同じ箇所を別々に変えていればコンフリクトとして人に委ねる
- リベース — 自分のコミット群を、別の土台の上へ1つずつ作り直す。履歴は一直線になり読みやすいが、作り直された各コミットは別ハッシュの別物になる
ここに鉄則があります。共有済みの履歴はリベースしない。他人が既に持つコミットを別物に置き換えると、相手の履歴と食い違い、pushで衝突や履歴の重複を招くからです。
アニメーション『マージ vs リベース(履歴の形)』を開く
仕事の流れ
- 変更をステージに載せる(記録する範囲を選ぶ)
- コミットする(スナップショットを1枚作る)
- 手元の履歴をリモートへ push(共有先へ送る)
- 他の人の変更をpull(共有先から取り込む)
- 枝ができたらマージで本流へ統合する
下の図は、手元での記録(add→commit)から共有先とのやり取り(push→pull)までを1手ずつ追ったものです。
アニメーション『Gitの流れ(add→commit→push)』を開く
- 作業ツリー — いま編集している手元のファイル群。ここでの変更はまだ記録されていない
- ステージ — 次のコミットに含める変更を並べる控えの場(index)
- ローカルリポジトリ — 手元にある履歴の本体。コミット(スナップショット)が積まれていく
- リモート — GitHub などの共有先。仲間と履歴をやり取りする相手
やさしく言うと(初級)
- ファイルを編集する(いつも通り書く)
- きりのいいところで記録する(=コミット。「ここまでできた」の目印)
- さらに編集して、また記録する…を繰り返す
- まずくなったら、前の記録に戻す
こうして「セーブ→編集→セーブ」を積み重ねると、作業のすべての節目が残ります。
みんなで使う — リモートとコンフリクト
- リモート — GitHub 等の共有先。push で送り、pull で受け取る。手元にも全履歴があるので、共有先が落ちても作業は続く
- プルリクエスト — 「この枝をmainに入れたい」というレビュー依頼の形(サービス側の機能)
- コンフリクト — 同じ箇所を別々に変えた枝を統合すると衝突する。Gitはどちらを採るか決められないので、人が見て解消する
このコミット履歴の安定性が、変更のたびに自動でテスト・デプロイする自動化(CI/CD など)の土台になります。「いつでも戻せる記録」があるからこそ、機械に任せて素早く回せます。
この部分をもっと深く(上級)
- reflog — HEADやブランチが過去に指していた位置の記録。リベースやリセットで「コミットを見失った」ときも、reflogをたどれば元のハッシュが見つかり復旧できる。オブジェクト自体は消さずに残っているのが効く
- packfile — 大量の緩いオブジェクトを、差分(デルタ)でまとめて圧縮した1ファイル。似たオブジェクトの共通部分を省き、転送も保管も小さくする。だから巨大な履歴でもクローンが現実的な速さで済む
- 分散モデル — 各クローンが全履歴を丸ごと持つ。中央サーバは「みんなが待ち合わせる場所」にすぎず、落ちても手元で作業・コミットは続けられる。pushとfetchは、この独立した履歴どうしを参照の差分だけ同期する操作
土台のしくみはプログラムが動くまで、履歴を自動化に載せる話はCI/CDへ続きます。
やさしく言うと(初級)
- 消えない — 記録した地点はずっと残る(うっかり上書きで泣かない)
- 戻れる — おかしくなっても、動いていた地点にすぐ戻せる
- たどれる — 誰がいつ何を変えたか、履歴で分かる
- 試せる — 本番を壊さず、枝分かれさせて実験できる
1人でも「戻れる安心」が大きく、複数人ならなおさら効いてきます。
⚠️ うまくいかないとき
- コンフリクト解消のミス — 衝突を雑に消すと、必要な変更まで捨ててしまう
- 巨大なコミット — まとめすぎると、どの変更が原因か切り分けられない
- push し忘れ/pull し忘れ — 手元と共有先がずれ、あとで衝突が増える
- 秘密情報のコミット — パスワード等を記録すると履歴に残り続ける(消しても過去に残る)
土台のしくみが気になったらプログラムが動くまでや、実行環境を箱で運ぶDockerもあわせてどうぞ。
この部分をもっと深く(上級)
- 共有済み履歴のリベース — 他人が持つコミットを書き換え、履歴が二重化・衝突する
- detached HEADでの作業放置 — ブランチに紐づけず移動すると、コミットが参照から外れて見失う(reflogで拾える)
- 巨大バイナリのコミット — blobは差分圧縮が効きにくく、履歴が肥大化して二度と軽くならない
- 秘密情報のコミット — 一度コミットするとオブジェクトとして履歴に残り続け、後から消しても過去のハッシュには残る
やさしく言うと(初級)
- 記録し忘れ — コミットしていない変更は履歴に残らず、戻れない
- 記録が大きすぎる — 一度にまとめて記録すると、どこで壊れたか分かりにくい(こまめに)
- 同じ所を同時に変更 — 複数人が同じ行を別々に直すと、あとで衝突が起きる(中級で扱う「コンフリクト」)
しくみの続き(コミット・ブランチ・マージ)は中級で。翻訳の話とセットで読むならプログラムが動くまでもどうぞ。
理解度チェック
そのまま解けます(成績は保存されません)。無料アカウントを作ると、学習の記録と進捗の山登りが始まります。
問1. Gitの「ブランチ」の説明として正しいのは?
問2. コンフリクト(衝突)が起きるのはどんなとき?
問3. Gitの1回のコミットが記録するものとして、本文の説明に最も近いのは?
問4. Gitで枝を切る(ブランチ作成)のが軽いのはなぜ?
問5. Gitで「リモート(共有先)が落ちても手元の作業は続けられる」のはなぜ?
問6. 「この枝をmainに入れたい」というレビュー依頼の形を何と呼ぶ(GitHub等のサービス側の機能)?
問7. 「いま自分が見ている・作業している位置」を指す目印を、英字4文字で何と呼ぶか答えてください。
問8. 枝の変更を本流に取り込んで1つに合流させる操作を何と呼ぶか、カタカナまたは英語で答えてください。