ブランチと 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 で作る・見る
コミットが3つ並んでいます。黒い札の
mainが ブランチ、ピンクの札が HEAD です。どちらも、何かを指している 付箋 です。git branchだけを打つと、ブランチの一覧が出ます。*が付いているのが、HEAD が乗っているブランチです。$ git branch * main--show-currentは、今乗っているブランチの名前だけを1行で出します。Claude Code は作業を始める前に、これでどのブランチにいるかを確かめます。$ git branch --show-current main後ろに名前を書くと、今いるコミットに付箋を1枚貼る だけです。ファイルは1つもコピーされず、作業ツリーも変わりません。
$ git branch feature一覧に
featureが増えました。ただし*はmainに付いたままです。git branch 名前は付箋を作るだけで、乗り換えはしません。$ git branch feature * main(HEAD -> main, feature)は「このコミットにmainとfeatureの2枚が貼ってあり、HEAD はmainに乗っている」と読みます。同じコミットに、付箋は何枚でも貼れます。$ git log --oneline -1 e5c0a62 (HEAD -> main, feature) パンを追加名前の後ろにコミット ID を書けば、過去のコミットにも付箋を貼れます。
$ git branch old 8b41d07--allを付けると、すべての付箋から辿れるコミットが出ます。付箋が3枚に増えても、コミットは3つのままです。$ git log --oneline --all e5c0a62 (HEAD -> main, feature) パンを追加 8b41d07 (old) 卵を追加 3f2a9c1 牛乳を追加-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部で扱います。
2. HEAD は「今乗っているブランチ」を指す付箋
HEAD もまた付箋です。ただし、HEAD が指しているのはコミットではなくブランチです。HEAD → ブランチ → コミット と2段で指している、と覚えてください。git status の1行目に出る On branch main は、「HEAD は main に乗っている」という意味です。
この2段の仕組みのおかげで、コミットしたときに動くものが決まります。新しいコミットができると、HEAD が乗っているブランチだけが、そのコミットへ貼り替わります。HEAD はそのブランチを指したままなので、一緒に進みます。同じコミットに貼ってあった他の付箋は、動きません。
コミットで進むのは、HEAD が乗っているブランチだけ
mainとfeatureが同じコミットに貼ってあります。git statusの1行目On branch mainは「HEAD はmainに乗っている」という意味です。$ git status On branch main nothing to commit, working tree cleanファイルに1行足して、コミットしてみます。
$ echo "バターを買う" >> todo.txt新しいコミットができ、HEAD が乗っている
mainだけ がそこへ進みました。featureは元のコミットに残っています。$ git commit -am "バターを追加" [main fbb4973] バターを追加 1 file changed, 1 insertion(+)HEAD は
mainを指したままなので、mainに連れられて一緒に進みました。これが HEAD → ブランチ → コミット の2段で指す仕組みです。$ git log --oneline --all fbb4973 (HEAD -> main) バターを追加 e5c0a62 (feature) パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加今度は
featureに乗り換えます。HEAD の付箋がfeatureに貼り替わり、作業ツリーは「パンを追加」の中身に戻ります。乗り換えの詳しい動きは、1-4の回で扱います。$ git switch feature Switched to branch 'feature'今乗っているのは
featureです。$ git branch --show-current featureここでも1行足してコミットします。
$ echo "ジャムを買う" >> todo.txt今度は
featureだけが進み、mainは動きません。2枚の付箋が別々のコミットを指し、歴史が二股に分かれ ました。$ git commit -am "ジャムを追加" [feature bee85e4] ジャムを追加 1 file changed, 1 insertion(+)どちらのコミットも消えていません。付箋が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 — ブランチを作って乗る
git branch ideaで付箋を作ります。$ git branch idea乗っているのは
mainのままです。このままコミットすると、進むのはmainです。$ git branch --show-current maingit switch -c 名前は、付箋を作って、そのまま乗り換える コマンドです。HEAD の札がmemoに移りました。$ git switch -c memo Switched to a new branch 'memo'*がmemoに付いています。3枚の付箋は、まだ同じコミットに貼ってあります。$ git branch idea main * memo新しいファイル
notes.txtを作って、コミットします。$ echo "牛乳は低脂肪" > notes.txtステージに写しました。
$ git add notes.txt進んだのは HEAD が乗っている
memoだけです。mainとideaは元のコミットに残っています。$ git commit -m "メモを追加" [memo ad1a0b0] メモを追加 1 file changed, 1 insertion(+) create mode 100644 notes.txt作業用のブランチを
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 — 付箋を剥がす
eggの付箋は「卵を追加」に貼ってあります。このコミットはmainの歴史の途中にあるので、mainからも辿れます。$ git log --oneline --all 71d9b3e (HEAD -> main) バターを追加 e5c0a62 パンを追加 8b41d07 (egg) 卵を追加 3f2a9c1 牛乳を追加git branch -d 名前で付箋を剥がします。(was 8b41d07)は、剥がす前に貼ってあったコミットです。$ git branch -d egg Deleted branch egg (was 8b41d07).剥がしたのは付箋だけで、コミットは4つとも残っています。「卵を追加」は
mainから辿れるので、何も失っていません。$ git log --oneline 71d9b3e (HEAD -> main) バターを追加 e5c0a62 パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加今乗っているブランチは剥がせません。
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 で剥がすと
試し用のブランチ
tryを作って乗ります。$ git switch -c try Switched to a new branch 'try'1行足して、
tryの上でコミットします。$ echo "試しに書く" >> todo.txttryだけが1つ先へ進みました。「試し」のコミットは、今のところtryの付箋からしか辿れません。$ git commit -am "試し" [try 5f877b0] 試し 1 file changed, 1 insertion(+)mainに戻ります。$ git switch main Switched to branch 'main'-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'-D(大文字)は、確認せずに剥がします。グラフで薄くなったのが、どの付箋からも辿れなくなった 「試し」のコミットです。$ git branch -D try Deleted branch try (was 5f877b0).--allを付けても「試し」は出てきません。コミット自体はまだ残っていますが、ブランチから辿る方法ではもう見つかりません。$ git log --oneline --all e5c0a62 (HEAD -> main) パンを追加 8b41d07 卵を追加 3f2a9c1 牛乳を追加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は名前が違うだけの、ふつうのブランチ