「pushしたら別ブランチ向けのコミットまで入ってしまった」「他人のfeatureブランチの変更が自分のPRに混入している」——共同開発では避けられない事故です。原因はさまざまですが、公開済みの履歴を安全に整えるには手順を間違えないことが重要。焦ってforce pushすると他メンバーの作業まで巻き添えになります。
# 自分のfeature/Aに、誤って feature/B のコミットが混ざった例 * 9f8e7d6 feat(A): 目的の変更 * abc1234 feat(B): 他ブランチのコミット ← 混入 * def5678 feat(B): 他ブランチのコミット ← 混入 * 1a2b3c4 feat(A): 目的の変更
この記事では、push後に別ブランチの変更が混ざった場合の診断方法と5つの対処パターンを整理します。原則としてgit revertやgit restoreで公開履歴を壊さずに戻し、どうしても必要な場合のみ--force-with-leaseで最小限に書き換えます。再発防止のためのbranch protection/pull戦略まで一貫してまとめました。
この記事で学べること
- 混入コミットの種類(単発/merge/range/ファイル部分)を見極める方法
git log --left-right --cherryによる影響範囲の特定- 5パターンの対処:誤push/誤merge/rebase混入/ファイル混入/広範混入
revert(履歴保持)とreset --hard + force-with-lease(履歴書き換え)の使い分け- マージコミットの打ち消し(
revert -m 1) - safety branchとreflogによる退路確保
- branch protection/PR運用/pull戦略による再発防止
混入が起きる5つの典型パターン
ポイント:判断基準は「影響規模」と「履歴保持の必要性」。少数commitならrevertで履歴を残す、広範混入や履歴整理が必要なら新ブランチでまとめてから--force-with-lease、という使い分けが鉄則です。
STEP 0:混入コミットを正確に特定する
# 最新化 git fetch --prune # 左右比較で混入を可視化 git log --oneline --decorate --graph --left-right --cherry-mark origin/main...HEAD # < :origin/main 側固有 # > :HEAD 側固有(混入候補) # = :両方にある(重複) # どのブランチ由来かも確認 git log --oneline --all --source -30 # 特定コミットがどのブランチで生まれたか git branch --contains <SHA> # 直前の操作履歴(rebase/cherry-pick/mergeの痕跡) git reflog -30 # ファイル単位で見たい場合 git log --follow -p -- path/to/file
診断でまず確認すべきこと
- 混入は単発か範囲かマージか
- 混入元のブランチ名(
branch --containsで特定) - 混入がpush済みで他メンバーがpullした可能性
- 影響を受けるPR/CI/タグ/リリースの有無
STEP 1:作業前の保険(safety branch)
履歴操作の前に必ず退避ブランチを切ります。万一の誤操作でもすぐ戻せるようにしておきましょう。
# 現在の先端を安全ブランチとして固定 git switch -c safety/backup-$(date +%Y%m%d-%H%M) # 元ブランチに戻る git switch <元のブランチ> # HEADの移動履歴を確認 git reflog
ポイント:safety branchを切る癖を付けておけば、操作ミスで履歴が壊れてもgit reset --hard safety/backup-xxxで一発復帰できます。「どうせ元に戻せる」安心感が、複雑な履歴操作でのミスを減らします。
ケース①:別ブランチへ誤pushした
「ブランチ切替を忘れてmainで作業+push」パターン。最も安全な対処は混入コミットを正しいブランチに救出→誤push先でrevert。公開履歴を壊さないので他メンバーへの影響を最小化できます。
# 1. 正しいブランチへ変更を救出(cherry-pick) git switch -c feature/rescue git cherry-pick <hash1> <hash2> git push -u origin feature/rescue # 2. 誤push先で打ち消し(履歴を残す) git switch main git pull origin main git revert <hash1> <hash2> git push origin main # 3. 正しいブランチからPR作成
誰もpullしていない直後なら reset + force-with-lease も選択肢
# 混入直前の正しいSHAを確認 git log --oneline origin/main -5 # そのSHAに巻き戻す git switch main git reset --hard <base_SHA> # 安全な強制push(他人の間接pushを保護) git push --force-with-lease origin main
警告:main/developなど保護ブランチへのforce pushは禁止のことが多く、他メンバーが既にpullしていた場合は作業を破壊します。チーム合意+pullしてないことの確認がない限り避け、前述の「救出+revert」を選ぶのが安全。詳細はpushを取り消す方法も参照。
作業ブランチ選択ミス全般は間違えて別ブランチで作業したときの復旧方法で詳しく解説しています。
ケース②:誤merge(マージコミット一発で丸ごと混入)
「git merge feature/Bをmainに対して実行してしまった」など、マージコミット1件で相手ブランチの全変更が入った場合。マージコミット用のrevert -m 1で安全に打ち消せます。
# マージコミットのSHAを確認 git log --merges --oneline -5 # 例: 9f8e7d6 Merge branch 'feature/B' into main # 親番号を確認(通常は親1=main側が基準) git show --no-patch --pretty=raw 9f8e7d6 # -m 1 で main側を基準に打ち消し git revert -m 1 9f8e7d6 # conflict解消 git add . git revert --continue git push origin main
-m の番号の意味
- -m 1:親1(通常main側)を基準 → main状態に戻す(通常これ)
- -m 2:親2(取り込んだ側)を基準 → featureに寄せる(稀)
- 親順の確認は
git show --pretty=raw <マージSHA>のparent行で
マージコミット全般の取り消しはmergeコミットを取り消して履歴を元に戻す方法とマージの取り消し方法も参照してください。
# revertで打ち消したfeatureを後で再取り込みしたい場合 # 単にmergeしても取り込まれない(既にrevertされた履歴があるため) # 対処法①:revertコミット自体をさらにrevert git revert <revert_commit_SHA> # 対処法②:feature側に新コミットを足してから再マージ
ケース③:rebase/cherry-pickで一部コミットだけ混入
「mainへrebaseした際に意図しないコミットが拾われた」「cherry-pickの範囲指定を間違えた」のように、一部コミットだけ混入している場合はその対象を狙い撃ちでrevertします。
# 混入コミットをリスト化 git log --oneline --cherry-mark --left-right origin/main...HEAD # まとめて打ち消すブランチを作る git switch -c fix/revert-mixed # --no-commit でまとめて戻し、1件のcommitにする git revert --no-commit <hashA> <hashB> <hashC> git commit -m "revert: 誤って混入した3コミットを打ち消し" # PRを作って合意後マージ git push -u origin fix/revert-mixed
注意:rebase/cherry-pick由来の混入は同内容で別SHAになっていることが多いため、単純にgit resetでは両方のSHAを消せません。revertで元ブランチ側の対象SHAを選ぶか、複数のrevertが必要になる場合があります。詳細はコミット履歴が二重化する原因と修正方法を参照。
ケース④:ファイル単位で混入(コミット全体を戻すのは過剰)
「コミットの中で数ファイルだけ不要」「意図しない設定ファイルが変更された」のようにファイル単位の混入なら、対象ファイルだけを親コミットの状態に戻すのがきれいです。
# 混入コミット X の親(X^)からファイルを取り出す git restore --source <X^> -- path/to/fileA path/to/fileB # 旧コマンド git checkout <X^> -- path/to/fileA path/to/fileB # 確認 git status git diff --staged # commit&push git add . git commit -m "revert: 混入したファイルを親コミットの状態に戻す" git push
ポイント:この方法はgit revert <X>より差分が小さく、レビューしやすいのが利点。ただし「混入コミットの一部だけ打ち消す」扱いなので、コミットメッセージで何を戻したか明記して後で追跡可能にしましょう。
ファイル復元全般は消したファイルを戻す方法も参考になります。
ケース⑤:広範に混入(PR/タグ/複数メンバーに波及)
「PRレビュー中に大量の混入commitが発覚」「リリースタグ付近まで波及」のように影響が広範な場合は、整理用ブランチで一括整理してからチーム合意のうえで--force-with-leaseで上書き、という流れが現実的。
# 影響範囲を把握 git log --oneline --cherry-mark --left-right origin/main...HEAD # 整理用ブランチで作業 git switch -c fix/history # 対話的rebaseで混入コミットをdrop git rebase --rebase-merges -i origin/main # エディタで不要commitを drop / squash / reword # レビュー用にpush git push -u origin fix/history # PRで変更内容を提示、合意後にmainへ反映 # (主要メンバーに影響説明+再同期手順を周知) git switch main git fetch origin git reset --hard fix/history git push --force-with-lease origin main
警告:共有mainへのforce pushは他メンバー全員の再同期が必要になります。事前通知+再clone手順の案内+CI/webhook/タグへの影響確認を徹底。保護ブランチ設定でforce pushが禁止されているケースも多く、どうしても必要な場合のみ管理者経由で一時解除→実施→再有効化の運用が無難です。
再発防止:運用と設定で事故を減らす
運用ルールのベストプラクティス
- Branch protection rulesでmain/developへの直接pushを禁止
- PRベースのマージ:レビュー+CIパスを必須にする
- PR mergeはSquash統一で履歴をクリーンに
- commit前に
git branch --show-currentで作業ブランチ確認 - pullは
--rebaseを既定にして不要マージコミットを抑止 - 長命ブランチは定期的にmainをrebaseして衝突リスク低減
# pullをrebaseに git config --global pull.rebase true # FF以外のpullを止めて慎重にする派 git config --global pull.ff only # 参照掃除 git config --global fetch.prune true # 初回push時の自動upstream(Git 2.37+) git config --global push.autoSetupRemote true # 強制pushはforce-with-leaseをデフォルトに git config --global alias.fp "push --force-with-lease" # 現在のブランチを派手に表示(prompt設定) # → PS1にgitブランチを含めるなど
実践シナリオ
シナリオ① mainに自分のfeature commitを誤push
# 救出ブランチ git switch -c feature/rescue main git cherry-pick <feature_SHA1> <feature_SHA2> git push -u origin feature/rescue # mainで打ち消し git switch main git pull origin main git revert <feature_SHA1> <feature_SHA2> git push origin main
シナリオ② 誤merge直後
git revert -m 1 <マージSHA> git push # 再度featureを取り込む時はrevert of revertで
シナリオ③ 1ファイルだけ混入
git restore --source <混入SHA>^ -- path/to/file git add path/to/file git commit -m "revert: fileを混入前の状態に戻す" git push
シナリオ④ PR大量にcommit混入
git switch -c fix/history git rebase --rebase-merges -i origin/main # 不要commit drop → push → PR git push -u origin fix/history
やってはいけない落とし穴
共有ブランチでreset –hard+force push
mainの混入を消すためにreset --hard+force pushすると、他メンバーがpullしていた場合にローカルを破壊します。共有ブランチはrevertで打ち消しコミット追加が原則。forceは個人ブランチ限定で--force-with-leaseを。
safety branchを切らずに直接rebase
対話的rebaseでdrop操作を誤ると重要なcommitを失います。git branch safety/backupで退避しておけば、いつでもreset --hard safety/backupで復帰可能。時間コストは1秒、得られる安心感は絶大です。
revert of revertを知らずに再マージで困る
一度revertした機能ブランチを後で再取り込みしたいとき、単純にmergeすると既に取り込まれた扱いで差分が反映されません。「revertをさらにrevert」または「feature側に新commit追加」で対処。
ファイル単位で戻すべきところをコミット単位でrevert
「ファイル2つだけ混入」なのにgit revert <commit>でコミット全体を打ち消すと、必要な変更まで戻ってしまいます。影響を受けるファイルが明確ならgit restore --sourceで限定的に戻すほうがレビューも楽で事故が減ります。
チーム通知を怠ってforce push
たとえ--force-with-leaseでも、共有ブランチを書き換えたら他メンバーへの周知は必須。再同期コマンド(fetch+reset --hard)をSlackなどで具体的に共有しましょう。
よくある質問
revert。git revert -m 1 <マージSHA>で親1(取り込み先)を基準に打ち消し。親2を基準にしたい特殊ケースは-m 2です。git revert --no-commit <SHA1> <SHA2> <SHA3>でまとめて適用し、最後にgit commitで1コミットにまとめるとレビューしやすい形になります。--forceは無条件上書き、--force-with-leaseは「ローカルのorigin/xxx認識と一致する時だけ上書き」する安全装置。他人がpushしていた場合は拒否されるため、作業喪失事故を防げます。関連記事
- 【Git】pushを取り消す方法 — push後の取り消し全般
- 【Git】間違えて別ブランチで作業したときの復旧方法 — 作業ブランチ取り違えの救済
- 【Git】mergeコミットを取り消して履歴を元に戻す方法 — マージ打ち消しの詳細
- 【Git】マージの取り消し方法 — merge前への復帰
- 【Git】コミットの取り消し方 — reset/revert/amendの使い分け
- 【Git】pull後にマージコミットが大量発生する原因と履歴整理方法 — 不要マージの対処
- 【Git】コミット履歴が二重化する原因と修正方法 — 二重化パターン
- 【Git】よく使うgitコマンドまとめ — 日常コマンドの早見表
まとめ
- 混入は5パターン:誤push/誤merge/rebase・cherry-pick混入/ファイル単位/広範
- まず
git log --cherry-mark --left-rightで影響範囲を正確に特定 - safety branchを切ってから履歴操作を開始(reflog併用)
- 公開履歴はrevertで打ち消し(
revert -m 1はマージ用) - ファイル単位なら
git restore --source <X^> -- file - どうしても履歴書き換えが必要なら合意+
--force-with-lease - 予防:branch protection/PRベース/
pull.rebase=true/commit前ブランチ確認
pushした後に別ブランチの変更が混入するのは、初級者〜中堅でも起きる事故です。対処の肝は「公開履歴を壊さないで戻す」こと。revertとgit restore --sourceを使い分ければ、多くの場合は履歴を残しながら修正可能です。どうしても整理が必要な場合はsafety branch+--force-with-lease+チーム通知をセットで。予防はbranch protection/PR運用/pull.rebase設定で確実に強化できます。

