チーム開発に参加して間もない頃、先輩から「PRを出す前に git rebase で履歴を整理して」と言われ、意味がわからないまま実行したことがあります。結果、コンフリクトが連鎖的に発生してパニックに。最終的に git rebase --abort で元に戻せたものの、「rebaseは怖い」という印象だけが残りました。
しかし実務を重ねるうちに、rebaseは正しく理解すれば非常に強力なツールだと実感するようになりました。履歴が一直線に整理され、PRのレビューがしやすくなり、チーム全体の開発効率が上がります。
この記事では、当時の自分のように「rebaseがよくわからない」「mergeとの違いが曖昧」「危険って聞くけど使っていいの?」と悩んでいる方に向けて、仕組みから実務での安全な運用方法まで、図解付きで徹底解説します。
git rebaseとは#
git rebaseとは、コミット履歴を別のコミットの上に付け替える(乗せ直す)操作です。
「rebase」という名前は「re(再び)+ base(土台)」、つまりコミットの土台を変更するという意味です。具体的には、自分が作業していたブランチの「分岐元」を、最新のmainブランチの先頭に移動させます。
これにより、あたかも「最新のmainから作業を開始した」かのような、綺麗な一直線の履歴を作ることができます。
図解:rebaseで履歴がどう変わるか#
rebase前の状態#
featureブランチをmainのコミットBから分岐して作業している状態です。その間にmainにはコミットCが追加されています。
main: A --- B --- C
\
feature: D --- EfeatureブランチはコミットBを土台(base)としています。
git rebase main 実行後#
main: A --- B --- C
\
feature: D' --- E'featureブランチの土台がBからCに変わり、DとEは新しいコミット(D’とE’)として作り直されます。内容は同じですが、コミットIDは別物になります。
ここが最重要ポイント: rebaseは既存のコミットを「移動」するのではなく、新しいコミットとして再作成します。そのため元のコミットID(ハッシュ値)が変わります。これが「履歴を書き換える」と言われる理由です。
mergeとの違い#
rebaseとmergeはどちらも「別ブランチの変更を取り込む」操作ですが、履歴の残り方が根本的に異なります。
mergeの場合#
git checkout feature
git merge mainmain: A --- B --- C --------
\ \
feature: D --- E --- M(マージコミット)mergeは両方の履歴を保持したまま、新しい「マージコミット(M)」を作成して統合します。履歴は分岐したまま残るため、「いつ、どのブランチから取り込んだか」が記録されます。
rebaseの場合#
git checkout feature
git rebase mainmain: A --- B --- C --- D' --- E'rebaseは履歴を一直線に書き換えます。マージコミットは作成されません。結果として「最初からmainの最新で作業していたかのような」綺麗な履歴になります。
比較表#
| 項目 | merge | rebase |
|---|---|---|
| 履歴の形 | 分岐が残る(ダイヤモンド型) | 一直線になる |
| マージコミット | 作成される | 作成されない |
| 履歴の書き換え | なし(安全) | あり(注意が必要) |
| git logの見やすさ | 複雑になりやすい | シンプルで読みやすい |
| 元の履歴の追跡 | 可能(分岐元がわかる) | 不可(一直線になるため) |
| 初心者の安全性 | ◎(事故が起きにくい) | △(理解が必要) |
rebaseのメリット#
1. 履歴が一直線で読みやすい#
git log --oneline で履歴を確認した際、mergeを多用したリポジトリでは分岐と合流が入り乱れて非常に読みにくくなります。rebaseを使えば、すべてのコミットが一直線に並ぶため、「いつ、何が変更されたか」を時系列で追いやすくなります。
2. 不要なマージコミットが発生しない#
mainの変更をfeatureブランチに取り込むたびにmergeすると、Merge branch 'main' into feature というマージコミットが大量に発生します。rebaseならこれが一切発生しません。
3. PRのレビューがしやすくなる#
GitHubでPull Requestを出す際、コミット履歴が整理されていると、レビュアーが「このPRで何をしたか」を把握しやすくなります。特にinteractive rebase(後述)で関連コミットをまとめておくと、レビュー効率が大幅に向上します。
4. コンフリクトを早期に検知できる#
定期的に git rebase main を実行してmainの最新変更を取り込んでおくと、大きなコンフリクトが発生する前に小さな衝突を解決できます。PRを出す直前に大量のコンフリクトと格闘する事態を避けられます。
rebaseのデメリットと危険性#
1. コミットIDが変わる(履歴の書き換え)#
rebaseは既存のコミットを新しいコミットとして再作成するため、コミットIDが変わります。これは「同じ変更内容だが、Gitから見ると別物のコミット」ということを意味します。
2. 共有ブランチで使うと事故が起きる#
これが「rebaseは危険」と言われる最大の理由です。 他のメンバーがすでにpull済みのブランチに対してrebaseを行い、force pushすると、他のメンバーのローカル履歴と食い違いが発生し、最悪の場合コミットが消失します。
# ❌ 絶対にやってはいけない例
git checkout main
git rebase some-branch
git push --force # チーム全員の履歴が壊れる3. コンフリクト解決が連鎖する場合がある#
mergeでは1回のコンフリクト解決で済むことが、rebaseではコミットごとに解決が必要になる場合があります。10個のコミットをrebaseすると、最悪10回のコンフリクト解決が発生します。
筆者の体験: 初めてrebaseを使った時、3つのコミットで同じファイルを修正していたため、コンフリクトが3回連続で発生しました。「さっき直したのにまた同じコンフリクト?」と混乱し、最終的に
git rebase --abortで撤退しました。後にinteractive rebaseでコミットをまとめてからrebaseすることで、コンフリクトが1回で済むことを学びました。
安全にrebaseを使うためのルール#
✅ rebaseしてOKな場面#
| 場面 | 理由 |
|---|---|
| 自分だけのfeatureブランチ | 他の誰にも影響しない |
| まだpushしていないローカルコミット | リモートとの不整合が発生しない |
| PR提出前のコミット整理 | レビュー品質の向上につながる |
❌ rebaseしてはいけない場面#
| 場面 | 理由 |
|---|---|
| main / develop ブランチ | チーム全員の履歴が壊れる |
| 他のメンバーと共有しているブランチ | pull済みの履歴と食い違う |
| すでにマージ済みのコミット | 過去の履歴が破壊される |
覚え方: 「自分しか触っていないブランチならrebase OK。誰かが触った可能性があるブランチはrebase NG」
基本的な使い方#
mainの最新変更をfeatureブランチに取り込む#
最も頻繁に使うパターンです。
# 1. mainの最新を取得
git fetch origin
# 2. featureブランチに移動
git checkout feature
# 3. origin/mainを土台にしてrebase
git rebase origin/main内部的には以下の処理が行われています。
- featureブランチのコミット(D, E)を一旦退避する
- featureブランチのHEADをorigin/mainの先頭(C)に移動する
- 退避したコミットを1つずつCの上に再適用する(D→D’, E→E’)
コンフリクトが発生した場合の対処法#
rebase中にコンフリクトが発生すると、処理が一時停止します。慌てずに以下の手順で対処しましょう。
手順1:状態を確認する#
git statusboth modified: と表示されるファイルがコンフリクトの発生箇所です。
手順2:コンフリクトを解消する#
エディタでコンフリクトマーカー(<<<<<<<, =======, >>>>>>>)を確認し、正しいコードに修正します。
手順3:修正をステージングしてrebaseを続行する#
git add .
git rebase --continue手順4(やり直したい場合):rebaseを中止する#
git rebase --abortこれでrebase実行前の状態に完全に戻せます。何か問題が起きたら、まずこのコマンドで撤退しましょう。
rebase後のpush:–force-with-leaseを使う#
rebase後は通常の git push が拒否されます。これはリモートの履歴とローカルの履歴が食い違っているためです。
❌ 危険な方法#
git push --force--force はリモートの状態を問答無用で上書きします。もし他のメンバーがこのブランチにpushしていた場合、その変更が消失します。
✅ 安全な方法#
git push --force-with-lease--force-with-lease は、「自分が最後にfetchした時点からリモートに新しい変更が追加されていない場合にのみ」force pushを許可します。他のメンバーの変更を誤って上書きするリスクを大幅に減らせます。
実務では
--force-with-leaseを必ず使いましょう。--forceはチーム開発では禁止としているチームも多いです。
git pull –rebase:日常的に使える便利技#
git pull は内部的に git fetch + git merge を実行しますが、--rebase オプションを付けると git fetch + git rebase に変わります。
# 通常のpull(mergeコミットが発生する)
git pull origin main
# rebase付きpull(履歴が一直線になる)
git pull --rebase origin mainこれにより、mainの最新変更を取り込む際に不要なマージコミットが発生しなくなります。
デフォルト設定にする#
毎回 --rebase を付けるのが面倒な場合は、グローバル設定で変更できます。
git config --global pull.rebase trueこの設定以降、git pull が自動的に git pull --rebase として動作します。
interactive rebase:PR前のコミット整理術#
実務で最も活用する場面は、PR提出前のコミット整理です。開発中は「fix typo」「WIP」「テスト修正」といった細かいコミットが大量にできますが、これをそのままPRに含めるとレビュアーの負担になります。
実行方法#
# 直近3つのコミットを対象にinteractive rebaseを開始
git rebase -i HEAD~3エディタが開く#
pick abc1234 ログイン機能の実装
pick def5678 typo修正
pick ghi9012 テストコード追加主なコマンド#
| コマンド | 動作 |
|---|---|
pick | コミットをそのまま使う |
squash(または s) | 前のコミットに統合する |
reword(または r) | コミットメッセージを変更する |
drop(または d) | コミットを削除する |
edit(または e) | コミットの内容を修正する |
実例:3つのコミットを1つにまとめる#
pick abc1234 ログイン機能の実装
squash def5678 typo修正
squash ghi9012 テストコード追加これで3つのコミットが1つにまとまり、コミットメッセージを編集する画面が表示されます。最終的に「ログイン機能の実装」という1つの綺麗なコミットだけが残ります。
実務のコツ: PR前に
git rebase -iでコミットを整理する習慣をつけると、レビュアーから「履歴が綺麗で読みやすい」と好評を得られます。筆者のチームでは「PRのコミットは論理的な単位でまとめる」というルールを設けています。
実務でのおすすめ運用フロー#
筆者のチームで実際に運用しているGitフローを紹介します。
# 1. featureブランチを作成
git checkout -b feature/login-form
# 2. 開発作業(複数コミット)
git commit -m "ログインフォームのUI実装"
git commit -m "バリデーション追加"
git commit -m "typo修正"
# 3. PR前にmainの最新を取り込む
git fetch origin
git rebase origin/main
# 4. コミットを整理する
git rebase -i HEAD~3 # squashで1〜2コミットにまとめる
# 5. force pushしてPRを作成
git push --force-with-lease origin feature/login-formこのフローにより、PRには論理的な単位でまとまった綺麗なコミットだけが含まれ、レビューがスムーズに進みます。
よくあるミスと対処法#
ミス1:rebase中にパニックになる#
対処: まず git status で現在の状態を確認。どうしようもなくなったら git rebase --abort で安全に撤退できます。
ミス2:mainブランチをrebaseしてしまう#
対処: すぐに git reflog でrebase前のコミットIDを見つけ、git reset --hard <コミットID> で復元できます。その後チームに報告しましょう。
ミス3:–forceでforce pushしてしまう#
対処: 他のメンバーに影響がないか確認。影響がある場合は、該当メンバーに git fetch → git reset --hard origin/<branch> を依頼してください。今後は必ず --force-with-lease を使いましょう。
まとめ#
| ポイント | 内容 |
|---|---|
| rebaseとは | コミットの土台を付け替えて履歴を一直線にする操作 |
| mergeとの違い | mergeは履歴を保持、rebaseは履歴を書き換える |
| 安全なルール | 自分だけのブランチでのみ使用。共有ブランチでは絶対にNG |
| push方法 | --force-with-lease を必ず使う |
| interactive rebase | PR前のコミット整理に活用する |
| 困ったら | git rebase --abort で安全に撤退 |
rebaseは「危険なコマンド」ではなく、**「ルールを守れば非常に強力なツール」**です。「自分だけのブランチでのみ使う」「force pushは--force-with-lease」という2つのルールを守るだけで、安全に履歴を整理できるようになります。
よくある質問(FAQ)#
Q. rebaseとmerge、結局どっちを使えばいい?#
featureブランチでmainの最新を取り込む場合はrebase、featureブランチをmainに統合する場合はmerge(またはsquash merge) が一般的な使い分けです。多くのチームがこの方針を採用しています。
Q. rebaseしたらコンフリクトが何回も出るのはなぜ?#
rebaseはコミットを1つずつ再適用するため、同じファイルを複数のコミットで変更していると、各コミットでコンフリクトが発生します。事前にinteractive rebaseでコミットをまとめておくと、コンフリクト回数を減らせます。
Q. rebaseした後にgit pullしたらおかしくなりました#
rebase後のブランチに対して通常の git pull(= fetch + merge)を実行すると、リモートの古い履歴とローカルの新しい履歴がmergeされ、コミットが重複します。rebase後は git push --force-with-lease でリモートを更新してください。
Q. git reflogで元に戻せますか?#
はい。git reflog にはすべての操作履歴が残っているため、rebase前のコミットIDを見つけて git reset --hard <コミットID> で復元できます。reflogはGitの「タイムマシン」のような機能です。










