できない.dev

git commit --amend した後にプッシュできない

amend はコミット SHA を作り直すため、push 済みブランチで amend するとリモートと履歴が分岐し non-fast-forward で拒否される。
自分専用ブランチなら `--force-with-lease`、共有ブランチなら追加コミットで修正するのが原則。

#git#amend#push#rewrite#force-with-lease

公開:

要約

git commit --amendコミット SHA を作り直す 操作なので、push 済みのコミットを amend すると履歴がリモートと分岐し、通常の push は non-fast-forward で拒否される。
自分専用ブランチなら --force-with-lease、共有ブランチなら amend ではなく追加コミットで修正、が安全な分岐点になる。

よくある原因

  1. amend で SHA 変化: メッセージ / ファイル変更が同じに見えても、内部的には新しい SHA を持つ別コミットになる。
  2. fast-forward 不成立: リモートの旧 SHA は新 SHA の祖先ではないため、Git は安全に取り込めず push を蹴る。
  3. オプション無し push: ただの git push では rewrite を反映できず、non-fast-forward で拒否される。
  4. 共有ブランチで amend: 他のメンバーが既に旧 SHA を取り込んでいると、後で衝突や重複コミットを生む原因になる。
  5. --force 素打ち: 他者の新規 push を上書きする事故になりやすい。
    共有ブランチで使うと履歴破壊。

解決策

1. 自分専用ブランチでは --force-with-lease

git commit --amend
git push --force-with-lease origin feature/my-branch

git-push 公式ドキュメント のとおり、--force-with-lease はリモートが想定通りの SHA の時だけ上書きするので、知らないうちに他者の push を踏みつぶす事故を防げる。

2. 共有ブランチでは追加コミットで修正

# amend せずに普通の修正コミットを足す
git add fix.txt
git commit -m "fix: typo in previous commit"
git push origin main

main など共有ブランチでは履歴書き換えを避け、修正コミットを追加する。git rebase -i を使った履歴整理は、自分のブランチが PR にマージされる前に限定する。

3. 誤って amend した場合の復旧

git reflog
# 例:
# a1b2c3d HEAD@{1}: commit (amend): ...
# 0e9f8e7 HEAD@{2}: commit: ...   ← amend 前の SHA
git reset --hard 0e9f8e7

git-commit 公式ドキュメント でも amend は新しいコミットを作る挙動と明記されており、元の SHA はガベージコレクション前なら reflog から復元できる。

4. --force は単独で使わない

# NG: 他者の push まで上書きする
git push --force origin main
 
# OK: リモートが想定通りの時だけ上書き
git push --force-with-lease origin main

共有リポジトリで素の --force を使うと履歴破壊の事故が起きる。
alias などで --force-with-lease を既定にしておくと安全。

この記事は役立ちましたか?