T-CREATOR

Git rev-spec チートシート:^/~/A..B/A...B を完全図解

Git rev-spec チートシート:^/~/A..B/A...B を完全図解

Git のコミット履歴を自由自在に操作したいと思ったことはありませんか。複雑なブランチ構造の中で、特定のコミットやコミット範囲を正確に指定できれば、マージやリベース、ログの確認がぐっと効率的になります。

しかし、^~.....といった記号を見ても、「どれがどういう意味だっけ?」と混乱してしまう方も多いのではないでしょうか。本記事では、Git の rev-spec(リビジョン指定)記法を徹底的に図解し、実務ですぐに使えるチートシートとしてまとめました。

rev-spec 早見表

各記法の特徴を一覧で確認できます。

#記法意味用途例結果
1HEAD^HEAD の第 1 親コミットgit show HEAD^1 つ前のコミットを表示
2HEAD^2HEAD の第 2 親コミット(マージ時)git show HEAD^2マージ元ブランチの最新コミット
3HEAD~HEAD の第 1 親コミットgit show HEAD~1 つ前のコミットを表示
4HEAD~3HEAD から 3 世代前のコミットgit log HEAD~3..HEAD直近 3 コミットのログ
5A..BA より後で B に到達可能なコミット群(A を除く)git log main..featurefeature ブランチ独自のコミット
6A...BA と B の共通祖先以降の対称差分git log --left-right main...feature両ブランチの差分コミット

背景

Git のコミット履歴はグラフ構造

Git はコミット履歴を 有向非巡回グラフ(DAG) として管理しています。各コミットは親コミットへの参照を持ち、ブランチやマージによって複雑なツリー構造を形成します。

このグラフ構造を効率的に操作するために、Git では rev-spec(リビジョン指定) という記法が用意されています。

図の意図:コミット履歴がどのようなグラフ構造になっているかを示します。

mermaidgitGraph
  commit id: "A"
  commit id: "B"
  branch feature
  commit id: "D"
  commit id: "E"
  checkout main
  commit id: "C"
  merge feature id: "F"
  commit id: "G"

この図では、main ブランチと feature ブランチが分岐し、最終的にマージされている様子がわかります。コミット F はマージコミットであり、複数の親を持つ特殊なコミットです。

rev-spec が必要な場面

実務では以下のような場面で rev-spec を活用します。

  • 特定のコミット範囲のログ確認:「このブランチにしかないコミット」を調べる
  • リベースやチェリーピック:特定の親コミットを基準に操作する
  • マージコミットの調査:どのブランチからマージされたかを確認する
  • 差分の抽出:2 つのブランチ間の変更内容を比較する

これらを正確に行うには、^~..... の使い分けが不可欠です。

課題

記法の違いがわかりにくい

Git を使い始めたばかりの開発者にとって、以下のような疑問が生じます。

  • HEAD^HEAD~ は何が違うのか?
  • A..BA...B のドット 2 つと 3 つの違いは?
  • マージコミットの場合、どの親を指定すればいいのか?

公式ドキュメントを読んでも、抽象的な説明が多く、実際のコミット構造と結びつけて理解するのが難しいという課題があります。

誤った指定によるトラブル

rev-spec の理解が不十分なまま操作すると、以下のようなトラブルが発生します。

#誤った操作例発生する問題
1git rebase HEAD^ を意図せず実行想定外のコミットを基準にリベースされる
2A..BA...B を混同抽出したいコミット範囲が異なる
3マージコミットで ^2 を指定し忘れマージ元ブランチの履歴を参照できない

こうしたミスを防ぐには、各記法の 動作原理を図で理解 することが重要です。

解決策

^~ の使い分け

^(キャレット)と~(チルダ)は、いずれも親コミットを指定する記法ですが、意味と用途が異なります

^(キャレット):親の「選択」

^どの親コミットを選ぶか を指定します。

typescript// 基本構文
HEAD ^ // 第1親コミット(HEAD^ と HEAD^1 は同じ)
  HEAD ^
  2; // 第2親コミット(マージコミットの場合)
HEAD ^ 3; // 第3親コミット(3方向マージの場合)

マージコミットは複数の親を持つため、^ で何番目の親かを明示的に指定できます。

図の意図:マージコミットにおける ^ の動作を示します。

mermaidflowchart RL
  G["G<br/>(HEAD)"] -->|"^1"| F["F<br/>(マージコミット)"]
  F -->|"^1"| C["C<br/>(main側の親)"]
  F -->|"^2"| E["E<br/>(feature側の親)"]
  C --> B["B"]
  E --> D["D"]
  D --> B
  B --> A["A"]

この図から、F^1 は main 側の親である C を、F^2 は feature 側の親である E を指すことがわかります。

~(チルダ):親の「遡り」

~第 1 親を何世代遡るか を指定します。

typescript// 基本構文
HEAD~   // 第1親コミット(HEAD~ と HEAD~1 は同じ)
HEAD~2  // 第1親の第1親(2世代前)
HEAD~3  // 第1親の第1親の第1親(3世代前)

~ は常に 第 1 親のみを辿る ため、マージコミットでも第 2 親以降は参照できません。

図の意図:~ による世代遡りの動作を示します。

mermaidflowchart RL
  G["G<br/>(HEAD)"] -->|"~1"| F["F"]
  F -->|"~2"| C["C"]
  C -->|"~3"| B["B"]
  B -->|"~4"| A["A"]

  F -.->|"無視される"| E["E"]
  E -.-> D["D"]
  D -.-> B

この図では、HEAD~3 は常に第 1 親を辿って B に到達し、E や D は無視されます。

組み合わせ例

^~ は組み合わせて使えます。

bash# 第2親の2世代前
git show HEAD^2~2

# 第1親の第2親(複雑なマージ構造の場合)
git show HEAD~^2

A..BA...B の使い分け

ドット記法はコミット範囲を指定する際に使います。

A..B(ドット 2 つ):範囲指定

A..B「A より後で B に到達可能なコミット」 を指します(A 自体は含まれません)。

bash# feature ブランチにしかないコミットを表示
git log main..feature

図の意図:A..B で抽出されるコミット範囲を示します。

mermaidflowchart RL
  G["G<br/>(feature)"] -.->|"含まれる"| F["F"]
  F -.->|"含まれる"| E["E"]
  E -.->|"含まれる"| D["D"]
  D --> C["C<br/>(main)"]
  C --> B["B"]
  B --> A["A"]

  style D fill:#90EE90
  style E fill:#90EE90
  style F fill:#90EE90
  style G fill:#90EE90

main..feature は、main には含まれず feature にのみ到達可能なコミット(D、E、F、G)を抽出します。

A...B(ドット 3 つ):対称差分

A...B「A と B の共通祖先以降の、どちらか一方にのみ含まれるコミット」 を指します。

bash# main と feature の両方の差分を表示
git log --left-right main...feature

図の意図:A...B で抽出されるコミットの対称性を示します。

mermaidflowchart RL
  G["G<br/>(feature)"] -.->|"右側"| F["F"]
  F -.->|"右側"| E["E"]
  E -.->|"右側"| D["D"]

  H["H<br/>(main)"] -.->|"左側"| C["C"]

  C --> B["B<br/>(共通祖先)"]
  D --> B
  B --> A["A"]

  style C fill:#FFB6C1
  style D fill:#90EE90
  style E fill:#90EE90
  style F fill:#90EE90
  style G fill:#90EE90
  style H fill:#FFB6C1

main...feature は、共通祖先 B 以降の main 側(C、H)と feature 側(D、E、F、G)の両方を抽出します。--left-right オプションを付けると、どちら側のコミットか明示されます。

実務での活用パターン

以下の表は、よく使う rev-spec パターンをまとめたものです。

#コマンド例用途
1git log HEAD~5..HEAD直近 5 コミットのログ表示
2git diff HEAD^..HEAD最新コミットの差分確認
3git log main..featurefeature ブランチ独自のコミット
4git log --left-right main...feature両ブランチの差分を左右で表示
5git show HEAD^2マージコミットの第 2 親を表示
6git rebase -i HEAD~3直近 3 コミットをインタラクティブリベース

具体例

例 1:マージコミットの親を調べる

マージ後、どのブランチからマージされたかを確認したい場合があります。

bash# 最新コミットがマージコミットか確認
git log -1 --pretty=%P HEAD

このコマンドは、HEAD の親コミットのハッシュを表示します。親が 2 つあればマージコミットです。

bash# 第1親(main側)のログを表示
git log HEAD^1

# 第2親(feature側)のログを表示
git log HEAD^2

図の意図:マージコミットの親を特定するフローを示します。

mermaidflowchart TD
  start["マージコミットの確認"] --> check["git log -1 --pretty=%P HEAD"]
  check --> judge{親が2つ以上?}
  judge -->|Yes| merge["マージコミットである"]
  judge -->|No| normal["通常のコミット"]
  merge --> show1["git log HEAD^1<br/>(第1親の履歴)"]
  merge --> show2["git log HEAD^2<br/>(第2親の履歴)"]

例 2:ブランチ間の差分を抽出

feature ブランチを main にマージする前に、変更内容を確認します。

bash# feature にしかないコミットを表示
git log main..feature

出力例:

plaintextcommit f8a3b2c
Author: Yuki <yuki@example.com>
Date:   Mon Jan 15 10:30:00 2025 +0900

    Add new feature

commit d4e6f1a
Author: Yuki <yuki@example.com>
Date:   Mon Jan 15 09:00:00 2025 +0900

    Fix typo

main には含まれず、feature にのみ存在するコミットが表示されます。

例 3:対称差分でレビュー

両方のブランチの差分を同時に確認し、レビューを効率化します。

bash# 両ブランチの差分を左右表示
git log --left-right --oneline main...feature

出力例:

plaintext< a1b2c3d Update README
< e4f5g6h Fix bug in parser
> d4e6f1a Fix typo
> f8a3b2c Add new feature

< は main 側、> は feature 側のコミットを示します。

例 4:特定世代のコミットを参照

リリース前に、3 コミット前の状態を確認したい場合があります。

bash# 3コミット前の状態をチェックアウト
git checkout HEAD~3

この操作で、現在から 3 世代前のコミット状態に移動できます。元に戻すには以下を実行します。

bash# 元のブランチに戻る
git checkout main

例 5:複雑な親の組み合わせ

複数のマージが重なった履歴で、特定のコミットを参照します。

bash# 第2親の2世代前を表示
git show HEAD^2~2

図の意図:複雑な親の組み合わせを視覚化します。

mermaidflowchart RL
  HEAD["HEAD"] -->|"^1"| M1["M1<br/>(第1親)"]
  HEAD -->|"^2"| M2["M2<br/>(第2親)"]
  M2 -->|"~1"| F1["F1"]
  F1 -->|"~2"| F2["F2"]
  F2 --> F3["F3"]

  style F2 fill:#FFD700

HEAD^2~2 は、HEAD の第 2 親である M2 から第 1 親を 2 世代遡った F2 を指します。

まとめ

Git の rev-spec 記法は、コミット履歴を正確に操作するための強力なツールです。本記事では、以下の要点を図解とともに解説しました。

まず、^~ の違いについては、^ が親の「選択」、~ が親の「遡り」であることを押さえておきましょう。マージコミットでは ^2 で第 2 親を、~3 で 3 世代前を参照できます。

次に、..... の違いでは、A..B が「A より後で B に到達可能なコミット」、A...B が「共通祖先以降の対称差分」であることを理解することが重要です。ブランチ間の差分確認やレビューで活用できます。

これらの記法を使いこなせば、リベース、チェリーピック、マージの操作がより正確かつ効率的になります。ぜひ実務で試してみてください。

図で理解できる要点:

  • マージコミットは複数の親を持ち、^ で親を選択、~ で世代を遡る
  • A..B は範囲指定、A...B は対称差分を抽出する
  • 組み合わせることで複雑な履歴構造も正確に参照できる

関連リンク