Git強制プッシュ完全ガイド|–forceと–force-with-leaseの違いから安全な使い方まで

git

「git pushがエラーで弾かれる…」「リベース後にpushできない」「間違ったコミットを修正したい」こんな時、git push --forceという”禁断のコマンド”が頭をよぎりますよね。

でも待ってください!強制プッシュは核兵器レベルで危険です。一瞬でチーム全体の作業を破壊し、数時間~数日分のコードを消し飛ばす可能性があります。

しかし、正しく理解して使えば、強制プッシュはGit運用における強力な味方になります。この記事では、強制プッシュの仕組みから、安全な--force-with-leaseの使い方、万が一の復旧方法まで、すべてを徹底解説します。

スポンサーリンク
  1. 強制プッシュとは?なぜ必要なのか
    1. 通常のgit pushが失敗する理由
    2. 強制プッシュの動作
    3. 強制プッシュが必要になる典型的なシーン
  2. git push –force の危険性
    1. 危険度レベル:★★★★★(最高)
    2. 実際の被害例
  3. 安全な強制プッシュ:–force-with-lease
    1. –force-with-lease とは?
    2. –force-with-lease の仕組み
    3. –force と –force-with-lease の比較表
    4. –force-with-lease の注意点
  4. 強制プッシュの実践的な使い方
    1. パターン1:個人ブランチでのリベース
    2. パターン2:プルリクエスト修正後
    3. パターン3:機密情報の削除
    4. パターン4:間違ったコミットの修正
  5. 共有ブランチで強制プッシュする場合の手順
    1. 事前準備チェックリスト
    2. ステップ1:チーム全員に通知
    3. ステップ2:バックアップ作成
    4. ステップ3:強制push実行
    5. ステップ4:チーム全員へ同期手順を案内
  6. 強制プッシュの取り消し・復旧方法
    1. 方法1:reflogで復旧(最も確実)
    2. 方法2:コミットハッシュで復旧
    3. 方法3:他のメンバーから復旧
    4. 方法4:GitHubの保護機能で復元
  7. ブランチ保護設定
    1. GitHub でブランチを保護する
    2. GitLab でブランチを保護する
    3. Bitbucket でブランチを保護する
  8. よくあるトラブルシューティング
    1. トラブル1:「protected branch」エラー
    2. トラブル2:「non-fast-forward」エラー
    3. トラブル3:「Everything up-to-date」
    4. トラブル4:強制push後にチームメンバーがpushできない
    5. トラブル5:間違って本番ブランチに強制pushした
  9. ベストプラクティスとエイリアス設定
    1. 推奨エイリアス
    2. .gitconfig 設定例
    3. チーム開発のルール例
  10. よくある質問(FAQ)
    1. Q1:–force と -f は同じですか?
    2. Q2:強制pushすると履歴は完全に消えますか?
    3. Q3:チーム開発で強制pushは絶対NGですか?
    4. Q4:–force-with-leaseは100%安全ですか?
    5. Q5:間違って強制pushした場合、何分以内なら復旧できますか?
    6. Q6:プルリクエストを修正するとき、毎回強制pushが必要ですか?
    7. Q7:強制pushの権限を特定の人だけに制限できますか?
    8. Q8:一人で開発している場合、–forceと–force-with-leaseどちらを使うべきですか?
    9. Q9:CIが走っている最中に強制pushするとどうなりますか?
    10. Q10:強制pushを完全に禁止する方法はありますか?
  11. まとめ:強制プッシュの鉄則
    1. 使っていい場面
    2. 絶対に使ってはいけない場面
    3. 黄金ルール

強制プッシュとは?なぜ必要なのか

通常のgit pushが失敗する理由

まず、なぜpushがエラーになるのかを理解しましょう。

通常のpush:

$ git push origin main

エラー例:

To https://github.com/user/repo.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to 'https://github.com/user/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.

このエラーの意味:

ローカル:  A ← B ← C ← D
                        ↑ あなたのHEAD

リモート:  A ← B ← C ← E ← F
                            ↑ リモートのHEAD

リモートにはEFという新しいコミットがあるのに、あなたのローカルはDを追加しようとしている。履歴が分岐しているため、Gitは安全のためpushを拒否します。

強制プッシュの動作

強制プッシュは、この安全装置を無視して上書きします。

git push --force origin main

結果:

ローカル:  A ← B ← C ← D
                        ↑ あなたのHEAD

リモート:  A ← B ← C ← D  (E と F は消滅!)
                        ↑ 強制的に上書き

リモートのEF完全に消え去ります。もし他の人がEFを基に作業していたら…悲劇が起こります。

強制プッシュが必要になる典型的なシーン

1. git rebase 後

# リベースで履歴を書き換えた
git rebase -i HEAD~3

# pushしようとするとエラー
git push origin feature/new-design
# → rejected!

# 強制プッシュが必要
git push --force origin feature/new-design

2. コミット履歴の修正後

# 直前のコミットを修正
git commit --amend -m "Fix typo in commit message"

# pushが拒否される
git push origin main
# → rejected!

# 強制プッシュが必要
git push --force origin main

3. コミットの取り消し後

# 間違ったコミットをリセット
git reset --hard HEAD~1

# pushが拒否される
git push origin main
# → rejected!

# 強制プッシュが必要
git push --force origin main

4. リモートの間違いを修正

# 誤ってpushした機密情報を削除
git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch secrets.txt' HEAD

# 強制プッシュして反映
git push --force origin main

git push –force の危険性

危険度レベル:★★★★★(最高)

なぜ危険なのか?

リスク説明影響範囲
コミットの消失リモートのコミットが完全に消えるチーム全員
作業の喪失他の人の作業が上書きされるチーム全員
同期エラー他のメンバーがpull/pushできなくなるチーム全員
マージ地獄修復にコンフリクト解決が必要チーム全員
信頼の喪失チームメンバーからの信頼を失うあなた

実際の被害例

ケース1:チームメンバーAさんの悲劇

【時系列】
10:00 - Aさんが新機能をコミット&push(コミットE, F)
11:00 - あなたがリベースして強制push(E, Fが消滅)
11:30 - Aさんがpushしようとする→エラー
12:00 - Aさんが無理やりpull→自分の変更が消える
12:30 - 「2時間の作業が消えたんですけど…」

ケース2:本番環境への誤爆

【時系列】
14:00 - mainブランチで開発していた(つもりがmainだった)
15:00 - リベースして整理
15:30 - git push --force origin main(本番ブランチに強制push)
15:31 - CIが走って本番デプロイ開始
15:32 - 本番環境が壊れる
15:33 - 「やってしまった…」

ケース3:履歴の完全消失

【被害】
- 3ヶ月分のコミット履歴が消える
- 重要な設計判断の記録が失われる
- 誰が何をしたか分からなくなる
- git blameが使えなくなる
- チーム全体で復旧に丸1日

安全な強制プッシュ:–force-with-lease

–force-with-lease とは?

--force-with-leaseは、安全装置付きの強制プッシュです。

通常の –force:

git push --force origin main
# → リモートの状態を無視して強制上書き

安全な –force-with-lease:

git push --force-with-lease origin main
# → リモートが想定通りなら上書き、
#   そうでなければエラーで停止

–force-with-lease の仕組み

シナリオ1:安全な場合(成功)

【状況】
- あなたが最後にfetchした時点:リモートはコミットC
- 現在のリモート:コミットC(変化なし)
- あなたのローカル:コミットDを追加

【実行】
$ git push --force-with-lease origin main
✅ 成功! リモートは D に更新される

シナリオ2:危険な場合(失敗)

【状況】
- あなたが最後にfetchした時点:リモートはコミットC
- 現在のリモート:コミットE(誰かがpushした!)
- あなたのローカル:コミットDを追加

【実行】
$ git push --force-with-lease origin main
❌ エラー!
error: failed to push some refs to 'origin'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.

–force と –force-with-lease の比較表

項目--force--force-with-lease
安全性❌ 最低✅ 高い
他人の変更無視して上書きチェックしてから上書き
使用場面一人開発のみチーム開発でも可
推奨度❌ 非推奨✅ 推奨
エイリアス-fなし

–force-with-lease の注意点

⚠️ 罠:fetch直後は意味がない

# これは危険!
git fetch origin main
git push --force-with-lease origin main

なぜ?

  • fetchで最新状態を取得
  • --force-with-leaseは「最後にfetchした時点」をチェック
  • つまり、fetchした直後なら常に成功してしまう

正しい使い方:

# fetch せずに確認
git push --force-with-lease origin main

# エラーが出たら内容を確認
git fetch origin main
git log origin/main..HEAD  # 何が違うか確認
git log HEAD..origin/main  # リモートの新しいコミット確認

# 問題なければ再度実行
git push --force-with-lease origin main

強制プッシュの実践的な使い方

パターン1:個人ブランチでのリベース

# 自分専用のfeatureブランチ
git checkout feature/my-work

# コミット履歴を整理
git rebase -i HEAD~5

# 競合があれば解決
git rebase --continue

# 安全に強制push
git push --force-with-lease origin feature/my-work

ポイント:

  • ✅ 自分だけが使うブランチなら安全
  • --force-with-leaseで念のため確認
  • ✅ チームに事前通知不要

パターン2:プルリクエスト修正後

# プルリク用ブランチで作業
git checkout feature/new-api

# レビュー指摘でコミットを修正
git rebase -i origin/main

# または直前のコミットを修正
git commit --amend

# 強制pushで反映
git push --force-with-lease origin feature/new-api

ポイント:

  • ✅ プルリク中のブランチは自分専用
  • ✅ GitHub/GitLabのプルリクは自動更新される
  • ⚠️ レビュワーに通知(コミットハッシュが変わる)

パターン3:機密情報の削除

# 機密ファイルをコミット履歴から完全削除
git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch config/secrets.yml' \
  --prune-empty --tag-name-filter cat -- --all

# 強制pushで反映
git push --force --all origin
git push --force --tags origin

ポイント:

  • ⚠️ チーム全員に事前通知必須
  • ⚠️ 全員が再cloneまたはリセット必要
  • ⚠️ 作業が止まる時間を作る

パターン4:間違ったコミットの修正

# 間違ったコミットをリセット
git reset --hard HEAD~3

# 正しい内容で再コミット
git add .
git commit -m "Correct implementation"

# 強制push
git push --force-with-lease origin feature/fix-bug

ポイント:

  • ✅ 自分専用ブランチなら問題なし
  • ⚠️ 共有ブランチなら絶対NG

共有ブランチで強制プッシュする場合の手順

どうしてもmaindevelopに強制pushしなければならない場合の手順です。

事前準備チェックリスト

□ 本当に強制pushが必要か?(revertで対応できないか?)
□ チーム全員に連絡したか?
□ 作業中のメンバーがいないか確認したか?
□ バックアップを取ったか?
□ 影響範囲を把握したか?
□ リカバリー手順を準備したか?
□ 上司・チームリーダーの承認を得たか?

ステップ1:チーム全員に通知

【緊急】mainブランチへ強制push予定

実施日時:2025年12月12日 14:00
対象ブランチ:main
理由:機密情報の削除
影響:コミット abc123 以降の履歴が変更されます

【全員にお願い】
1. 13:50までに作業中の変更をpush
2. 14:00-14:30は作業を一時停止
3. 14:30以降に下記コマンドで同期

# 同期コマンド
git checkout main
git fetch origin
git reset --hard origin/main

ステップ2:バックアップ作成

# ブランチを別名で保存
git branch backup-main-20251212 main
git push origin backup-main-20251212

# またはローカルにバンドル保存
git bundle create main-backup-20251212.bundle main

ステップ3:強制push実行

# 最終確認
git log origin/main..HEAD  # pushされる内容
git log HEAD..origin/main  # 消える内容

# 実行
git push --force-with-lease origin main

# 確認
git log origin/main

ステップ4:チーム全員へ同期手順を案内

# メンバーが実行するコマンド
git checkout main
git fetch origin

# 作業中の変更がある場合
git stash save "Before force push sync"

# リモートに強制同期
git reset --hard origin/main

# 作業を戻す
git stash pop

強制プッシュの取り消し・復旧方法

方法1:reflogで復旧(最も確実)

リモートの履歴を確認:

git reflog show origin/main

出力例:

abc1234 origin/main@{0}: forced push
def5678 origin/main@{1}: push
789abcd origin/main@{2}: push

復旧:

# 1つ前の状態に戻す
git reset --hard origin/main@{1}

# 強制pushで反映
git push --force origin main

ポイント:

  • reflogは過去90日間保存される
  • @{1}は「1つ前の状態」
  • @{2}なら「2つ前の状態」

方法2:コミットハッシュで復旧

消えたコミットのハッシュを知っている場合:

# 消えたコミットに戻す
git reset --hard abc1234

# 強制pushで反映
git push --force origin main

方法3:他のメンバーから復旧

誰かがまだ古い状態を持っている場合:

# メンバーのローカルからpush
# (メンバー側で実行)
git push --force origin main

方法4:GitHubの保護機能で復元

GitHub:

1. Settings → Branches
2. main ブランチの "Edit" をクリック
3. "View history" から過去の状態を確認
4. 必要に応じて復元

GitLab:

1. Repository → Protected branches
2. Protected branches の履歴から復元

ブランチ保護設定

GitHub でブランチを保護する

1. リポジトリの Settings をクリック
2. Branches → Branch protection rules
3. "Add rule" をクリック
4. Branch name pattern: main (または develop)
5. 以下をチェック:
   ☑ Require a pull request before merging
   ☑ Require approvals (最低1人)
   ☐ Allow force pushes(絶対にチェックしない!)
   ☐ Allow deletions
6. "Create" をクリック

効果:

$ git push --force origin main
remote: error: GH006: Protected branch update failed for refs/heads/main.

強制pushが完全にブロックされます!

GitLab でブランチを保護する

1. Settings → Repository → Protected branches
2. Branch: main を選択
3. Allowed to merge: Maintainers
4. Allowed to push: No one
5. Allowed to force push: OFF (重要!)
6. "Protect" をクリック

Bitbucket でブランチを保護する

1. Repository settings → Branch permissions
2. "Add a branch permission"
3. Branch: main
4. Prevent deletion: ON
5. Prevent rewriting history: ON (これが強制push防止)
6. "Save" をクリック

よくあるトラブルシューティング

トラブル1:「protected branch」エラー

症状:

$ git push --force origin main
remote: error: GH006: Protected branch update failed for refs/heads/main.

原因: ブランチが保護されている

解決方法:

パターンA:保護を一時解除(管理者のみ)

1. GitHub Settings → Branches
2. mainの保護ルールを削除
3. 強制push実行
4. 保護ルールを再設定

パターンB:revertを使う(推奨)

# 強制pushの代わりにrevert
git revert abc1234
git push origin main

トラブル2:「non-fast-forward」エラー

症状:

$ git push --force-with-lease origin main
error: failed to push some refs
hint: Updates were rejected because the remote contains work

原因: 誰かがリモートにpushした

解決方法:

# ステップ1:リモートの変更を確認
git fetch origin main
git log HEAD..origin/main

# ステップ2:内容を確認
git diff HEAD origin/main

# ステップ3A:マージする
git merge origin/main
git push origin main

# ステップ3B:リベースする
git rebase origin/main
git push --force-with-lease origin main

# ステップ3C:本当に上書きする
git push --force origin main  # 慎重に!

トラブル3:「Everything up-to-date」

症状:

$ git push --force origin main
Everything up-to-date

原因: ローカルとリモートが同じ状態

解決方法:

# 現在の状態を確認
git log origin/main..HEAD  # ローカルの新しいコミット
git log HEAD..origin/main  # リモートの新しいコミット

# 両方空なら本当に同じ
# どちらかに出力があれば確認

トラブル4:強制push後にチームメンバーがpushできない

メンバー側の症状:

$ git push origin main
error: failed to push some refs
hint: Updates were rejected

解決手順(メンバー側):

# ステップ1:現在の作業を退避
git stash save "Before sync with forced push"

# ステップ2:リモートの最新を取得
git fetch origin

# ステップ3:強制的にリモートに合わせる
git reset --hard origin/main

# ステップ4:退避した作業を戻す
git stash pop

# ステップ5:コンフリクトがあれば解決
# (手動で解決)

# ステップ6:再度コミット&push
git add .
git commit -m "Resolve conflicts after force push"
git push origin main

トラブル5:間違って本番ブランチに強制pushした

即座に実行:

# ステップ1:reflogで直前の状態を確認
git reflog show origin/main

# ステップ2:1つ前に戻す
git reset --hard origin/main@{1}

# ステップ3:すぐに強制pushで復元
git push --force origin main

# ステップ4:チームに通知
# (Slackなどで即座に報告)

時間が経ってしまった場合:

# バックアップブランチがあれば
git checkout backup-main-20251212
git checkout -b main-restore
git push --force origin main

ベストプラクティスとエイリアス設定

推奨エイリアス

# 安全な強制push
git config --global alias.pushf 'push --force-with-lease'

# 使い方
git pushf origin main

より高度なエイリアス:

# 確認付き強制push
git config --global alias.fpush '!git push --force-with-lease "$@" || (echo "Push failed. Check remote changes with: git fetch && git log HEAD..@{u}" && false) #'

# 使い方
git fpush origin main

.gitconfig 設定例

[alias]
    # 安全な強制push
    pushf = push --force-with-lease

    # 強制pushの影響確認
    push-dry = push --force-with-lease --dry-run

    # リモートとの差分確認
    push-check = !git fetch && git log HEAD..@{u} --oneline

    # 安全装置:常に--force-with-leaseを使う
    push-force = push --force-with-lease

# デフォルトの動作 default = current # 強制pushを警告 followTags = true

チーム開発のルール例

ルール1:個人ブランチのみOK

✅ 許可:feature/your-name/*
✅ 許可:fix/issue-123
❌ 禁止:main, develop, release/*

ルール2:事前通知必須

強制pushする場合:
1. Slackで事前通知(30分前)
2. 理由を明記
3. 影響範囲を説明
4. 同期手順を共有

ルール3:–forceは禁止

✅ 使用OK:git push --force-with-lease
❌ 使用NG:git push --force
❌ 使用NG:git push -f

よくある質問(FAQ)

Q1:–force と -f は同じですか?

A:はい、完全に同じです。

# これらはすべて同じ意味
git push --force origin main
git push -f origin main

ただし、--force-with-leaseには短縮形がありません。

Q2:強制pushすると履歴は完全に消えますか?

A:リモートからは消えますが、復旧可能です。

  • reflog:90日間は保存される
  • 他のメンバーのローカル:まだ残っている
  • GitHub/GitLab:保護機能で復元可能(設定次第)

Q3:チーム開発で強制pushは絶対NGですか?

A:状況によります。

ブランチ強制push
main, master❌ 原則禁止
develop❌ 原則禁止
release/*❌ 禁止
hotfix/*△ 要相談
feature/個人名✅ OK
プルリク用ブランチ✅ OK

Q4:–force-with-leaseは100%安全ですか?

A:いいえ、以下の場合は無効です。

罠1:fetch直後

git fetch origin
git push --force-with-lease origin main
# ↑ 常に成功してしまう

罠2:自動fetchの設定

# .gitconfigに以下がある場合

fetch = +refs/heads/*:refs/remotes/origin/* # バックグラウンドでfetchされて無効化

罠3:他人が同時作業

10:00 - あなたがfetch
10:30 - Aさんがpush
10:31 - あなたが--force-with-lease
       (Aさんの変更を上書き!)

Q5:間違って強制pushした場合、何分以内なら復旧できますか?

A:即座に対処すれば、ほぼ100%復旧可能です。

経過時間復旧の可能性
1分以内99% (reflogで即復旧)
1時間以内95% (メンバーがまだ持っている)
1日以内70% (バックアップ・保護機能)
1週間以内30% (運次第)
1ヶ月以上10% (ほぼ不可能)

重要: 気づいたら即座にチームに連絡してください。

Q6:プルリクエストを修正するとき、毎回強制pushが必要ですか?

A:はい、コミット履歴を書き換える場合は必要です。

# プルリク後にコミットを修正
git commit --amend
# または
git rebase -i HEAD~3

# 強制pushで反映
git push --force-with-lease origin feature/my-pr

これは正常な使い方です。プルリク用ブランチは自分専用なので安全です。

Q7:強制pushの権限を特定の人だけに制限できますか?

A:はい、可能です。

GitHub:

Settings → Branches → Branch protection rules
☑ Restrict who can push to matching branches
→ 特定のユーザー・チームを選択

GitLab:

Settings → Repository → Protected branches
Allowed to force push: Select users/roles

Bitbucket:

Repository settings → Branch permissions
Prevent rewriting history: Exceptions (特定ユーザー)

Q8:一人で開発している場合、–forceと–force-with-leaseどちらを使うべきですか?

A:–force-with-leaseを使いましょう。

理由:

  • 習慣づけが大切(チーム開発に移行した時のため)
  • 自分でも複数デバイスから作業する可能性
  • うっかりミスを防げる

ただし、完全に一人で、ミスしても問題ない場合は--forceでもOKです。

Q9:CIが走っている最中に強制pushするとどうなりますか?

A:CIが失敗するか、古いコードでビルドされます。

ベストプラクティス:

1. CIが完了するまで待つ
2. または、CIを手動でキャンセル
3. 強制push実行
4. CIが再実行される

Q10:強制pushを完全に禁止する方法はありますか?

A:サーバー側フック(pre-receive)で実装できます。

GitHub Actions例:

name: Prevent Force Push

on:
  push:
    branches: [main, develop]

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - name: Check force push
        run: |
          if git log --format=%B -n 1 $GITHUB_SHA | grep -q "force-push"; then
            echo "Force push detected!"
            exit 1
          fi

GitLab CI例:

prevent-force-push:
  script:
    - git reflog show origin/main | grep -q "forced"
    - if [ $? -eq 0 ]; then exit 1; fi
  only:
    - main

まとめ:強制プッシュの鉄則

使っていい場面

自分専用のfeatureブランチ

  • 例:feature/your-name/new-feature
  • 誰にも迷惑をかけない

プルリクエスト用ブランチ

  • コミット履歴の整理
  • レビュー指摘の修正後

実験用・一時ブランチ

  • experiment/*
  • temp/*

絶対に使ってはいけない場面

mainブランチ

  • チーム全員に影響
  • 本番環境に直結

developブランチ

  • 開発全体の基準
  • 全員が使う

他人が作業中のブランチ

  • 相手の作業を破壊
  • 取り返しがつかない

黄金ルール

  1. --forceは使わない、--force-with-leaseを使う
  2. 共有ブランチでは絶対にしない
  3. 事前にチームに連絡する
  4. バックアップを必ず取る
  5. 迷ったらrevertを使う

最重要ポイント:

強制プッシュは最後の手段です。

  • まずgit revertを検討
  • 次にgit mergeを検討
  • それでもダメならgit push --force-with-lease

そして、迷ったら誰かに相談してください。
5分の相談で、数時間の復旧作業を避けられます。

この記事を読んだあなたは、もう安全に強制プッシュを使いこなせるはずです。ただし、使わないで済むなら、それが一番です!

コメント

タイトルとURLをコピーしました