Git運用 レッスン6

履歴の修復

reset・revert・reflogなどを使って誤った変更を取り消し、Gitの履歴を安全に修復する方法を学ぶ

git resetの3つのモード

git resetは、 ブランチの指すコミットを別のコミットに移動させるコマンドです。 モードによって、作業ディレクトリとステージ(インデックス)への影響が変わります。

--soft

コミットだけ取り消し、変更内容はステージ済みの状態として残す

--mixed(デフォルト)

コミットとステージを取り消し、変更内容は作業ディレクトリに残す

--hard

コミット・ステージ・作業ディレクトリの変更内容をすべて破棄する

# 直前のコミットを取り消すが、変更内容はステージ済みで残しておきたい
git reset --soft HEAD~1

# 直前のコミットを取り消し、変更をすべて完全に破棄する(注意して使う)
git reset --hard HEAD~1

ポイント

--hardは変更内容を完全に破棄するため、 未保存の作業を失うリスクがあります。実行前に本当に破棄してよい内容か確認しましょう。

git revert: 公開済みの履歴を安全に取り消す

git resetは 履歴そのものを書き換えるため、すでに他の人がpullした共有済みの履歴に対して使うと、 自分と相手の履歴が食い違ってしまいます。

公開済みの変更を打ち消したい場合は、git revertを使います。 revertは過去のコミットを削除するのではなく、その変更内容を打ち消す新しいコミットを追加します。

# 問題のあったコミットを打ち消す新しいコミットを作成する
git revert a1b2c3d

# 履歴には元のコミットも残ったまま、打ち消しのコミットが追加される
# a1b2c3d  feat: 割引ロジックを変更
# f9e8d7c  Revert "feat: 割引ロジックを変更"

git reflogで失ったコミットを復旧する

誤ってgit reset --hardを 実行してしまったり、ブランチを削除してしまった場合でも、git reflogを使えば 復旧できることがあります。reflogは、HEADやブランチ参照がどう移動してきたかの履歴を記録しています。

# HEADの移動履歴を確認する
git reflog

# 出力例:
# f9e8d7c HEAD@{0}: reset: moving to HEAD~1
# a1b2c3d HEAD@{1}: commit: feat: 割引ロジックを変更

# 失われたコミット a1b2c3d を指すブランチを作って復旧する
git branch recovered-work a1b2c3d

ポイント

reflogに記録が残っている限り、コミット自体が完全に消えることは基本的にありません。 誤操作に気づいたら、慌てず新しい変更を加える前にreflogを確認しましょう。

ただしreflogには限界があります。reflogはローカルのリポジトリだけに記録され、pushやcloneでは共有されません。 また記録には期限があり、到達可能なコミットは既定で90日、到達不能になったコミットは既定で30日で ガベージコレクションの対象になります。さらに、一度もコミットしていない変更(未コミットの作業内容やステージだけの変更)は そもそもreflogに記録されないため、reflogでは救えません。

誤ってpushしてしまった場合の対処

共有ブランチに対してforce-pushで履歴を上書きすると、 他の人が同じブランチをベースに進めていた作業が失われる恐れがあります。 どうしても必要な場合は、通常の--forceより 安全な--force-with-leaseを使います。

# リモートの状態が自分の把握しているものと違う場合はpushを拒否してくれる
git push --force-with-lease origin feature/my-branch

--force-with-leaseは、 自分が最後に取得したリモートの状態から誰かが変更を加えていた場合にpushを中断するため、 他の人の作業を気づかずに上書きしてしまう事故を防げます。 それでもmain・developなど共有度の高いブランチへのforce-pushは避けるべきです。

ポイント

  • • resetは--soft/--mixed/--hardでステージ・作業ディレクトリへの影響が異なる
  • • 公開済みの履歴を安全に取り消すには、新しい打ち消しコミットを追加するrevertを使う
  • • reflogはHEADの移動履歴を記録しており、誤操作からの復旧に使える
  • • 共有ブランチへのforce-pushは避け、必要な場合は--force-with-leaseを使う

確認クイズ

1 / 3

すでに他の人がpull済みの公開済みコミットを安全に取り消す方法はどれか?