ブランチと HEAD — 付箋が2段で指している

ブランチを「ファイル一式のコピー」だと思っていると、git branch を打っても何もコピーされないこと、コミットすると片方のブランチだけが進むこと、git branch -D で何が消えるのかが、どれも腑に落ちません。この回では、ブランチをコミットに貼った付箋、HEAD を今乗っているブランチを指す付箋として見直し、ブランチを作る・見る・剥がすコマンドを1つずつ確かめます。

画面の見方
  • 上のターミナルは、実際に打ったときと同じ出力です(Git は英語で答えます)
  • 下の中で起きていることは、コマンドの結果、Git の中の何が変わったかを図にしたものです。丸がコミット、黒い札がブランチ、ピンクの札が HEAD です
  • 右の説明をスクロールして読み進めると、左のターミナルでコマンドが1つずつ打たれます。→ キーでも1つずつ進みます(← で1つ戻ります。スマホでは「次へ」ボタン)。最後まで進んでから → を押すと次の区間へ移ります。黄色く光っているのが、そのコマンドで変わった場所です

1. ブランチはコミットに貼った付箋 — git branch

ブランチの中身は、コミット ID が1つ書いてあるだけです。ファイルは入っていません。だから、ブランチを作ってもファイルはコピーされず、作業ツリーも変わりません。今いるコミットに、名前つきの付箋が1枚増えるだけです。

コマンド何をするか
git branchブランチの一覧を出す。* が今乗っているブランチ
git branch --show-current今乗っているブランチの名前だけを出す
git branch 名前今いるコミットに付箋を貼る(乗り換えはしない)
git branch 名前 コミットID指定したコミットに付箋を貼る
git branch -a手元のブランチに加えて、GitHub 側のブランチの写しも並べる

ブランチは付箋 — git branch で作る・見る

  1. コミットが3つ並んでいます。黒い札の main が ブランチ、ピンクの札が HEAD です。どちらも、何かを指している 付箋 です。

  2. git branch だけを打つと、ブランチの一覧が出ます。* が付いているのが、HEAD が乗っているブランチです。

    $ git branch
    * main
  3. --show-current は、今乗っているブランチの名前だけを1行で出します。Claude Code は作業を始める前に、これでどのブランチにいるかを確かめます。

    $ git branch --show-current
    main
  4. 後ろに名前を書くと、今いるコミットに付箋を1枚貼る だけです。ファイルは1つもコピーされず、作業ツリーも変わりません。

    $ git branch feature
  5. 一覧に feature が増えました。ただし * は main に付いたままです。git branch 名前 は付箋を作るだけで、乗り換えはしません。

    $ git branch
      feature
    * main
  6. (HEAD -> main, feature) は「このコミットに main と feature の2枚が貼ってあり、HEAD は main に乗っている」と読みます。同じコミットに、付箋は何枚でも貼れます。

    $ git log --oneline -1
    e5c0a62 (HEAD -> main, feature) パンを追加
  7. 名前の後ろにコミット ID を書けば、過去のコミットにも付箋を貼れます。

    $ git branch old 8b41d07
  8. --all を付けると、すべての付箋から辿れるコミットが出ます。付箋が3枚に増えても、コミットは3つのままです。

    $ git log --oneline --all
    e5c0a62 (HEAD -> main, feature) パンを追加
    8b41d07 (old) 卵を追加
    3f2a9c1 牛乳を追加
  9. -a は、GitHub 側のブランチの写しも一緒に並べるオプションです。このリポジトリはまだ GitHub とつないでいないので、手元の一覧と同じです。

    $ git branch -a
      feature
    * main
      old

同じコミットに、付箋は何枚でも貼れます。git log の (HEAD -> main, feature) は、そのコミットに貼ってある付箋の一覧です。Claude Code はよく git log --oneline -3 で直近の歴史を確かめますが、括弧の中を読めば、どのブランチがどこを指しているかが分かります。

Claude Code が作業の前に打つ git branch --show-current は、「いまどのブランチの上にいるか」の確認です。出力が1行だけなので、プログラムから読みやすいのです。-a で出てくる GitHub 側の写し(remotes/origin/main のような行)は、第2部で扱います。

HEAD もまた付箋です。ただし、HEAD が指しているのはコミットではなくブランチです。HEAD → ブランチ → コミット と2段で指している、と覚えてください。git status の1行目に出る On branch main は、「HEAD は main に乗っている」という意味です。

この2段の仕組みのおかげで、コミットしたときに動くものが決まります。新しいコミットができると、HEAD が乗っているブランチだけが、そのコミットへ貼り替わります。HEAD はそのブランチを指したままなので、一緒に進みます。同じコミットに貼ってあった他の付箋は、動きません。

コミットで進むのは、HEAD が乗っているブランチだけ

  1. main と feature が同じコミットに貼ってあります。git status の1行目 On branch main は「HEAD は main に乗っている」という意味です。

    $ git status
    On branch main
    nothing to commit, working tree clean
  2. ファイルに1行足して、コミットしてみます。

    $ echo "バターを買う" >> todo.txt
  3. 新しいコミットができ、HEAD が乗っている main だけ がそこへ進みました。feature は元のコミットに残っています。

    $ git commit -am "バターを追加"
    [main fbb4973] バターを追加
     1 file changed, 1 insertion(+)
  4. HEAD は main を指したままなので、main に連れられて一緒に進みました。これが HEAD → ブランチ → コミット の2段で指す仕組みです。

    $ git log --oneline --all
    fbb4973 (HEAD -> main) バターを追加
    e5c0a62 (feature) パンを追加
    8b41d07 卵を追加
    3f2a9c1 牛乳を追加
  5. 今度は feature に乗り換えます。HEAD の付箋が feature に貼り替わり、作業ツリーは「パンを追加」の中身に戻ります。乗り換えの詳しい動きは、1-4の回で扱います。

    $ git switch feature
    Switched to branch 'feature'
  6. 今乗っているのは feature です。

    $ git branch --show-current
    feature
  7. ここでも1行足してコミットします。

    $ echo "ジャムを買う" >> todo.txt
  8. 今度は feature だけが進み、main は動きません。2枚の付箋が別々のコミットを指し、歴史が二股に分かれ ました。

    $ git commit -am "ジャムを追加"
    [feature bee85e4] ジャムを追加
     1 file changed, 1 insertion(+)
  9. どちらのコミットも消えていません。付箋が2枚あるので、両方の先端から歴史を辿れます。

    $ git log --oneline --all
    bee85e4 (HEAD -> feature) ジャムを追加
    fbb4973 (main) バターを追加
    e5c0a62 パンを追加
    8b41d07 卵を追加
    3f2a9c1 牛乳を追加

途中で打った git switch feature は、HEAD を別のブランチに乗り換えるコマンドです。乗り換えると作業ツリーの中身も入れ替わります。書きかけの変更があるときにどうなるかを含め、乗り換えの詳しい動きは 1-4「git checkout が分かるまで」で扱います。

3. git switch -c — 作って乗る

git branch 名前 は付箋を作るだけで、HEAD は元のブランチに乗ったままです。そのままコミットすると、進むのは元のブランチです。作業用のブランチを作ったら、たいていはすぐ乗り換えたいので、作ると乗り換えるを1回で済ませる書き方があります。

コマンド付箋を作るHEAD を乗り換える
git branch memoするしない
git switch -c memoするする
git checkout -b memoするする(switch -c の古い書き方)

git switch -c — ブランチを作って乗る

  1. git branch idea で付箋を作ります。

    $ git branch idea
  2. 乗っているのは main のままです。このままコミットすると、進むのは main です。

    $ git branch --show-current
    main
  3. git switch -c 名前 は、付箋を作って、そのまま乗り換える コマンドです。HEAD の札が memo に移りました。

    $ git switch -c memo
    Switched to a new branch 'memo'
  4. * が memo に付いています。3枚の付箋は、まだ同じコミットに貼ってあります。

    $ git branch
      idea
      main
    * memo
  5. 新しいファイル notes.txt を作って、コミットします。

    $ echo "牛乳は低脂肪" > notes.txt
  6. ステージに写しました。

    $ git add notes.txt
  7. 進んだのは HEAD が乗っている memo だけです。main と idea は元のコミットに残っています。

    $ git commit -m "メモを追加"
    [memo ad1a0b0] メモを追加
     1 file changed, 1 insertion(+)
     create mode 100644 notes.txt
  8. 作業用のブランチを switch -c で作ってからコミットすれば、main を動かさずに作業を積めます。

    $ git log --oneline --all
    ad1a0b0 (HEAD -> memo) メモを追加
    e5c0a62 (main, idea) パンを追加
    8b41d07 卵を追加
    3f2a9c1 牛乳を追加

作ったばかりのブランチは、今いるコミットに貼られます。なので乗り換えても、作業ツリーの中身は変わりません。違いが出るのは、次にコミットしたときです。

4. git branch -d — 付箋を剥がす

付箋を剥がすのが git branch -d 名前 です。剥がすのは付箋だけで、コミットは消しません。剥がしたブランチが指していたコミットが、main など別のブランチの歴史に入っていれば、そちらから辿れるので何も失いません。

「マージ済みのブランチ」とは、この「別のブランチの歴史に入っている」状態のことです。マージそのものは第3部で扱いますが、-d が見ているのは今乗っているブランチから、剥がすブランチの先端まで辿れるかどうかだけです。

git branch -d — 付箋を剥がす

  1. egg の付箋は「卵を追加」に貼ってあります。このコミットは main の歴史の途中にあるので、main からも辿れます。

    $ git log --oneline --all
    71d9b3e (HEAD -> main) バターを追加
    e5c0a62 パンを追加
    8b41d07 (egg) 卵を追加
    3f2a9c1 牛乳を追加
  2. git branch -d 名前 で付箋を剥がします。(was 8b41d07) は、剥がす前に貼ってあったコミットです。

    $ git branch -d egg
    Deleted branch egg (was 8b41d07).
  3. 剥がしたのは付箋だけで、コミットは4つとも残っています。「卵を追加」は main から辿れるので、何も失っていません。

    $ git log --oneline
    71d9b3e (HEAD -> main) バターを追加
    e5c0a62 パンを追加
    8b41d07 卵を追加
    3f2a9c1 牛乳を追加
  4. 今乗っているブランチは剥がせません。used by worktree は「この作業フォルダで使っている」という意味です。剥がすと、HEAD の行き先が無くなってしまうからです。

    $ git branch -d main
    error: cannot delete branch 'main' used by worktree at '/Users/you/memo-app'

今乗っているブランチは、-d でも -D でも剥がせません。剥がしたいときは、先に別のブランチに乗り換えます。

5. -d が断るとき、-D で剥がすと

剥がそうとしたブランチに、今乗っているブランチの歴史に入っていないコミットがあると、-d は not fully merged(マージされていない)と言って止まります。剥がすと、そのコミットを辿る道が無くなるからです。

大文字の -D は、この確認をせずに剥がします。剥がすのは付箋だけで、コミットはすぐには消えません。ただ、どこからも辿れなくなります。git log --all にも出てこなくなり、グラフでは薄く表示されます。

-d が断るとき、-D で剥がすと

  1. 試し用のブランチ try を作って乗ります。

    $ git switch -c try
    Switched to a new branch 'try'
  2. 1行足して、try の上でコミットします。

    $ echo "試しに書く" >> todo.txt
  3. try だけが1つ先へ進みました。「試し」のコミットは、今のところ try の付箋からしか辿れません。

    $ git commit -am "試し"
    [try 5f877b0] 試し
     1 file changed, 1 insertion(+)
  4. main に戻ります。

    $ git switch main
    Switched to branch 'main'
  5. -d は断りました。not fully merged は「このブランチには、今いる main の歴史に入っていないコミットがある」という意味です。剥がすとそのコミットを見失うので、-d は止まります。

    $ git branch -d try
    error: the branch 'try' is not fully merged
    hint: If you are sure you want to delete it, run 'git branch -D try'
  6. -D(大文字)は、確認せずに剥がします。グラフで薄くなったのが、どの付箋からも辿れなくなった 「試し」のコミットです。

    $ git branch -D try
    Deleted branch try (was 5f877b0).
  7. --all を付けても「試し」は出てきません。コミット自体はまだ残っていますが、ブランチから辿る方法ではもう見つかりません。

    $ git log --oneline --all
    e5c0a62 (HEAD -> main) パンを追加
    8b41d07 卵を追加
    3f2a9c1 牛乳を追加
  8. HEAD が過去にいた場所の記録(reflog)には、まだ「試し」が残っています。一定期間はここから取り戻せます。取り戻し方は3-4の回で扱います。

    $ git reflog -3
    e5c0a62 (HEAD -> main) HEAD@{0}: checkout: moving from try to main
    5f877b0 HEAD@{1}: commit: 試し
    e5c0a62 HEAD@{2}: checkout: moving from main to try

どこからも辿れなくなったコミットも、HEAD がそこにいたことがあれば reflog(HEAD の移動の記録)に残っています。一定期間はそこから付箋を貼り直して取り戻せますが、いずれ Git が掃除します。reflog から取り戻す手順は 3-4 の回で扱います。

6. main と master — 名前が違うだけ

リポジトリによって、最初のブランチが main だったり master だったりします。違うのは名前だけで、仕組みは他のブランチと同じ付箋です。特別扱いされることもありません。

名前が2通りあるのは、GitHub が2020年に、新しく作るリポジトリの既定のブランチ名を master から main に変えたからです。それ以前に作られたリポジトリや、手元で git init した設定によっては master のままです。どちらなのかは git branch --show-current や git branch で確かめられます。

7. Claude Code の許可画面で見かけたら

この回で見たコマンドを、「何が消えうるか」で並べるとこうなります。ブランチを作る側のコマンドは、どれも付箋を増やすか貼り替えるだけです。

コマンド何が起きるか消えうるもの
git branch / git branch --show-current / git branch -a見るだけなし
git branch 名前 / git branch 名前 コミットID付箋を1枚貼る。HEAD もファイルも動かないなし
git switch -c 名前 / git checkout -b 名前今いるコミットに付箋を貼り、そこへ乗り換える。ファイルは変わらないなし
git branch -d 名前付箋を剥がす。今乗っているブランチから辿れないコミットがあれば止まるなし(コミットは別のブランチから辿れる)
git branch -D 名前確認せずに付箋を剥がすそのブランチにしか無いコミットへの道筋 どこからも辿れなくなる(reflog から一定期間は取り戻せる)

見分け方は、小文字の -d か、大文字の -D かです。-D の許可を求められたら、そのブランチに残しておきたいコミットが無いかを、git log --oneline main..名前 のように確かめてから許可してください。

まとめ

  • ブランチはコミットに貼った付箋。中身はコミット ID だけで、作ってもファイルはコピーされない。同じコミットに何枚でも貼れる
  • HEAD は「今乗っているブランチ」を指す付箋。HEAD → ブランチ → コミットの2段で指している
  • コミットすると、HEAD が乗っているブランチだけが新しいコミットへ進む
  • git branch 名前 は作るだけ。git switch -c 名前 は作って乗り換える
  • git branch -d は、今乗っているブランチから辿れないコミットがあると断る。-D は断らずに剥がし、そのブランチにしか無いコミットはどこからも辿れなくなる
  • main と master は名前が違うだけの、ふつうのブランチ