ブランチ(枝)とmergeは、Gitの説明で必ず出てくるのに、必要な場面が分かりにくい機能です。
この記事では、サンプル料理教室の料金表を枝で直して合流させるところまでを、実際に動かした表示つきで読みます。あわせて、GitHub Flow、Git Flow、GitLab Flowの違いも整理します。読み終わると、自分の会社で枝が要るのかどうかを判断でき、外注先の話にも付いていけるようになります。
この記事の内容
先に答えると
ブランチは、本筋を止めずに変更を試すための別ルートです。合流させる操作がmergeで、終わった枝は消します。一人でホームページを直しているだけなら、枝を使わず main だけで問題ありません。
- 枝が要るのは、人と一緒に作業するとき、本番に出す前に確かめたいとき、AIに大きな変更をさせるときの3つです。
- 基本の流れは
git switch -c 枝の名前、コミット、git switch main、git merge 枝の名前、git branch -d 枝の名前です。 - 古い説明の
git checkoutは、枝の切り替えならgit switchと同じ働きです。 - 運用の型は3つ有名ですが、小さな会社はGitHub Flowで足ります。
ブランチとmainの話
mainは、リポジトリ(ファイルと履歴をまとめて入れておく箱)の本筋になる枝です。GitHubで新しく作るリポジトリの既定の枝の名前が main で、clone(手元への複製)をしたときも最初にこの枝が開きます。古いリポジトリでは master という名前のこともあります。
枝が役に立つのは、作業が割り込むときです。代表が夏の教室のページを作っている途中で、受付Aから「月謝の表示が古いので今日中に直してほしい」と連絡が来たとします。作りかけのページをmainで編集していると、月謝の修正だけを先に出すことができません。夏の教室の作業を枝に分けておけば、mainに戻って月謝だけ直せます。作りかけは枝に残ったままです。
一人ならmainだけでよい
代表が一人で料金表を直してGitHubへ送るだけなら、枝は要りません。main にコミットして送れば済みます。覚えることを増やす必要はありません。
枝が要るのは次のときです。
| 場面 | 枝を切る理由 |
|---|---|
| 外注エンジニアBなど人と同じリポジトリを触る | お互いの作りかけが混ざらない。プルリクエスト(Pull Request、PR)で見てもらえる |
| 本番に出す前に確かめたい | mainに入れる前に、枝の上で動きを見られる |
| AIに大きな変更をさせる | 気に入らなければ枝ごと捨てられる。mainは無傷で済む |
3つ目は実際に運用していて効きます。AIに複数のファイルをまとめて書き換えさせるときは、先に枝を切り、変更を枝の上でコミットしておきます。気に入らなければ git switch main の1回で元の状態に戻れます。

枝を切って合流するまでを動かす
ここからの表示は、すべて実際に動かしたものです。GitHubの代わりに、手元のフォルダに練習用のリモート(共有の置き場となるリポジトリ)を作って試しました。そのため送り先がフォルダの形で出ます。本物のGitHubならURLになります。ハッシュ(9033672 のようなコミットの番号)は毎回違う値になります。
枝を作って移る
$ git switch -c fix-menu-price
Switched to a new branch 'fix-menu-price'
$ git branch
* fix-menu-price
main
-c は作って移る指定です。git branch は枝の一覧で、* が今いる枝です。
枝の上でコミットする
menu.html の体験レッスンを3,000円から3,500円に変えて、コミットします。
$ git add menu.html
$ git commit -m "料金表を修正"
[fix-menu-price 9033672] 料金表を修正
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --oneline --graph --all
* 9033672 (HEAD -> fix-menu-price) 料金表を修正
* 84b3ca8 (origin/main, main) ホームページの最初の版を登録
git log --oneline --graph --all が、枝の様子を見るコマンドです。--graph で線の図を付け、--all で全部の枝を出します。カッコの中が枝の札で、上が新しいコミットです。HEAD -> は今いる枝、origin/main はGitHub側の main の控えです。
mainへ戻る
ファイルの中身まで戻ることを確かめます。
$ grep 体験 menu.html
<p>体験レッスン 3,500円</p>
$ git switch main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
$ grep 体験 menu.html
<p>体験レッスン 3,000円</p>
枝を切り替えると、フォルダの中のファイルがその枝の状態に変わります。修正が消えたように見えて驚くところですが、枝に残っています。
枝をmainへ合流させる
$ git merge fix-menu-price
Updating 84b3ca8..9033672
Fast-forward
menu.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
git merge は、指定した枝を今いる枝へ取り込みます。先にmainへ移ってから打つのが決まりです。
終わった枝を消す
$ git branch -d fix-menu-price
Deleted branch fix-menu-price (was 9033672).
$ git log --oneline --graph
* 9033672 (HEAD -> main) 料金表を修正
* 84b3ca8 (origin/main) ホームページの最初の版を登録
枝の札を外しただけなので、コミットはmainに残っています。最後に git push を打てば、GitHubにも届きます。
fast-forwardと3-wayマージ
合流には2通りあります。先ほどの表示の「Fast-forward」は、mainが枝を切ったときのまま動いていなかった場合で、札を先へ進めるだけで終わります。
一方、枝で作業している間にmainも進んでいると、札を動かすだけでは合流できません。割り込みの話を再現します。add-summer-class で「夏の教室を追加」をコミットし、そのあとmainに戻って「月謝を修正」をコミットした状態です。
$ git log --oneline --graph --all
* c5ec49a (HEAD -> main) 月謝を修正
| * 51809aa (add-summer-class) 夏の教室を追加
|/
* 9033672 料金表を修正
* 84b3ca8 (origin/main) ホームページの最初の版を登録
線が枝分かれしています。左の列がmain、右の列が枝です。|/ の下で2本に分かれたと読みます。ここで合流させます。
$ git merge add-summer-class
Merge made by the 'ort' strategy.
index.html | 1 +
1 file changed, 1 insertion(+)
$ git log --oneline --graph
* a5e02a9 (HEAD -> main) Merge branch 'add-summer-class'
|\
| * 51809aa (add-summer-class) 夏の教室を追加
* | c5ec49a 月謝を修正
|/
* 9033672 料金表を修正
* 84b3ca8 (origin/main) ホームページの最初の版を登録
a5e02a9 が新しくできた合流用のコミットで、マージコミットと呼びます。この場合はメッセージを書くエディタが開くので、既定の文言のまま保存して閉じれば進みます。ort はGitの合流の方式の名前で、気にしなくて構いません。
| fast-forward | 3-wayマージ | |
|---|---|---|
| 起きる条件 | mainが枝を切ったときのまま | mainも枝も進んでいる |
| 何をするか | 札を先へ進めるだけ | 2つの枝と共通の元を比べて合わせる |
| 新しいコミット | できない | マージコミットが1つできる |
| 履歴の形 | 1本のまま | 分かれて合流した形が残る |
3-wayの「3」は、2つの枝の先頭と、分かれる前の共通の元の3点を比べるという意味です。同じ行を両方で変えていなければ、Gitが自動で合わせます。

git checkout と git switch の関係
古い記事やAIの回答には git checkout がよく出てきます。もともと git checkout は、枝の切り替えとファイルを戻す操作の両方を担っていました。役割が2つあって分かりにくいので、Git 2.23で git switch と git restore に分けられました。
| やること | 新しい書き方 | 古い書き方 |
|---|---|---|
| 枝を作って移る | git switch -c fix-menu-price | git checkout -b fix-menu-price |
| 枝を移る | git switch main | git checkout main |
| 編集を捨てて戻す | git restore menu.html | git checkout -- menu.html |
3行目は、コミットしていない編集を捨てる操作です。捨てた編集はGitから取り戻せません。
git checkout もなくなったわけではなく、今も動きます。実際に打つと、表示は同じです。
$ git checkout -b add-summer-menu
Switched to a new branch 'add-summer-menu'
$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
自分で打つときは、git switch のほうが誤解がありません。
GitHub Flow・Git Flow・GitLab Flowの違い
枝の使い方の決まりごとには、有名な型が3つあります。
| GitHub Flow | Git Flow | GitLab Flow | |
|---|---|---|---|
| 残し続ける枝 | main だけ | main(原案の名前は master)と develop | main と、必要に応じて本番用や検証用の枝 |
| 作業の枝 | 短い枝を1本ずつ | 機能・リリース準備・緊急修正で別々の枝 | 機能ごとの枝 |
| 合流先 | プルリクエストで main へ | ふだんは develop、リリース時に main へ | main へ入れ、検証用を経て本番用へ |
| 向いている場面 | 更新をすぐ出すサイトやツール | 版番号を付けて出す製品、複数の版を保守する製品 | 本番の前に検証環境を挟みたいとき |
小さな会社はGitHub Flowで足ります。公式の手順は次の6つです。
- 枝を作る。
- 変更してコミットし、pushする。
- プルリクエストを作って見てもらう。
- 指摘があれば追加でコミットする。
- 承認されたら
mainへmergeする。 - 枝を消す。
Git Flowは2010年に提案された型で、提案した本人が2020年に、継続して更新を出すソフトならもっと単純な型を勧める、という注記を書き足しています。その例に挙げているのがGitHub Flowです。枝が5種類あるGit Flowを、サイト1つと予約メールのスクリプトだけの会社が持つ理由はありません。「開発は develop で」と外注先に言われたら、Git Flowかそれに近い運用だと分かれば十分です。
GitLab Flowは、GitLabが公式に説明している型で、Git Flowより単純な代案という位置づけです。GitHubを使っていても、検証環境を別に持ちたいときは考え方を借りられます。

枝の名前の付け方
GitHubの公式は、短くて内容の分かる名前を勧めています。何をしている枝かが一覧で読めれば十分です。実際に運用していると、先頭に fix-(直す)や add-(足す)を付けて、英小文字とハイフンで書く形が迷いません。
| 悪い例 | 良い例 | 理由 |
|---|---|---|
test | fix-menu-price | 何の枝か分かる |
daihyo-1107 | add-summer-class | 人と日付はコミットに記録されている |
fix menu price | fix-menu-price | 空白は使えない |
終わった枝の消し方
合流の終わった枝を残すと、どれが作業中か分からなくなります。GitHub Flowの手順にも、最後に枝を消すとあります。枝は手元とGitHubの両方にあるので、それぞれ消します。
$ git push -u origin fix-menu-price
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 11 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 375 bytes | 375.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
To ../remote/website.git
* [new branch] fix-menu-price -> fix-menu-price
branch 'fix-menu-price' set up to track 'origin/fix-menu-price'.
$ git branch -d fix-menu-price
Deleted branch fix-menu-price (was 495aae7).
$ git push origin --delete fix-menu-price
To ../remote/website.git
- [deleted] fix-menu-price
上の3つの間で、mainへの合流とpushは済ませています。GitHubの画面なら、マージ済みのプルリクエストの下のほうにある「Delete branch」でも消せます。消した枝は、同じ画面の「Restore branch」で戻せます。
Bが消した枝の控えが手元に残っているときは、git fetch --prune で片づきます。
$ git fetch --prune
From ../remote/website
- [deleted] (none) -> origin/fix-phone-number

できないことと、その代わり
| できないこと | 代わりにどうするか |
|---|---|
| 1つのフォルダで2つの枝の中身を同時に開く | git switch で切り替える。並べて見たいなら別のフォルダにもう1つcloneする |
| 作っただけの枝をGitHubに出す | git push -u origin 枝の名前 で送る |
| 枝を消して、合流済みの変更を取り消す | 枝を消してもコミットは残る。「Gitで元に戻す:revert・reset・restoreの違いと使い分け」へ |
| 同じ行の変更を自動で合流する | 人がどちらかを選ぶ。「コンフリクトの直し方:起きる理由と、解消の手順」へ |
よく詰まるところ
not fully merged で枝が消えない
$ git branch -d try-new-design
error: the branch 'try-new-design' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D try-new-design'
hint: Disable this message with "git config set advice.forceDeleteBranch false"
まだ合流していないコミットがある枝です。Gitが消すのを止めてくれています。合流が先なら git merge を、本当に捨てるなら大文字の git branch -D try-new-design を打ちます。-D で消すと、その枝にしかないコミットは見えなくなります。
used by worktree で枝が消えない
$ git branch -d try-new-design
error: cannot delete branch 'try-new-design' used by worktree at '/private/tmp/…/gitlab-11/website'
今いる枝は消せません。途中の長いフォルダ名は縮めました。git switch main で移ってから消します。
枝の名前でエラーになる
$ git switch -c "fix menu price"
fatal: 'fix menu price' is not a valid branch name
hint: See `man git check-ref-format`
hint: Disable this message with "git config set advice.refSyntax false"
$ git switch fix-menu-price
fatal: invalid reference: fix-menu-price
$ git switch -c fix-menu-price
fatal: a branch named 'fix-menu-price' already exists
別々の場面の表示を並べました。1つ目は空白が入っていて使えない名前、2つ目はまだない枝へ移ろうとした(-c の付け忘れか、打ち間違い)、3つ目は同じ名前の枝がすでにあるという表示です。3つ目は -c を外して移ります。
枝を移ろうとすると overwritten と出る
$ git switch fix-trial-price
error: Your local changes to the following files would be overwritten by checkout:
menu.html
Please commit your changes or stash them before you switch branches.
Aborting
未コミットの編集が、移り先の枝の内容とぶつかる場合に止まります。編集は消えていません。先にコミットするか、git stash でよけてから移ります。ぶつからないときは、編集を持ったまま移れます。別の枝に持ち込んでしまう原因になるので、移る前に git status を見る癖をつけると安全です。
mainに直接コミットしてしまった
枝を切るつもりだったのに、mainにコミットしてしまった場合です。pushしていなければ、枝に移し替えられます。
$ git branch fix-menu-title
$ git reset --hard origin/main
HEAD is now at a5e02a9 Merge branch 'add-summer-class'
$ git log --oneline --graph --all -4
* 1181a88 (fix-menu-title) 料金表の見出しを修正
* a5e02a9 (HEAD -> main, origin/main) Merge branch 'add-summer-class'
|\
| * 51809aa 夏の教室を追加
* | c5ec49a 月謝を修正
|/
先に枝の札を今の位置に付け、mainの札をGitHub側の位置まで戻しています。git reset --hard は未コミットの編集を捨てる操作なので、git status が空なのを確かめてから打ちます。pushしたあとは、この手順を使いません。
mergeで CONFLICT と出た
$ git merge fix-tool-a
Auto-merging menu.html
CONFLICT (content): Merge conflict in menu.html
Automatic merge failed; fix conflicts and then commit the result.
両方の枝で同じ行を変えています。壊れたわけではなく、やめるなら git merge --abort で合流の前に戻ります。
次に読む
枝の変更をGitHubの上で見てもらう流れは「プルリクエストとは:作り方・レビュー・マージの流れ」、合流でぶつかったときは「コンフリクトの直し方:起きる理由と、解消の手順」にあります。
コミットとpushから確かめたいときは「git add・commit・pushの違い:保存して送るまでの流れ」、他の人の枝を受け取るときは「git clone・pull・fetchの違い:受け取る操作の使い分け」へ。
運用の決めごとは「Git FlowとGitHub Flowの違い:小さなチームはどちらで回すか」、枝を切り替えるコマンドの違いは「git switchとcheckoutの違い:枝を切り替える操作と、detached HEAD」にあります。