【Git】特定のコミットまで戻す方法|reset・switch・revertレンジの使い分けと安全な巻き戻し完全ガイド

【Git】特定のコミットまで戻す方法 Git

「数日前の状態まで戻したい」「このコミットから先を全部なかったことにしたい」「過去のある時点を一時的に試したい」——Gitで特定のコミットまで戻す操作は、ユーザーの意図によって最適な方法が分かれます。git reset --hardを打てばよいわけではなく、「戻った状態を履歴に残すか」「リモートも巻き戻すか」「戻ってから作業を続けるか」で選ぶコマンドは変わります。

この記事では「特定コミットに戻る」の意図を4つに分類し、それぞれの実行手順を整理します。ハッシュの特定、3種のresetモード、検証用に戻るswitch --detach、履歴を残して戻すrevertレンジ、そして誤って戻しすぎた場合のreflog救済まで、実務で迷わない形で解説します。

この記事で学べること

  • 「戻る」の4つの意図(完全復元/一時検証/履歴として残す/リモートも同期)の見分け方
  • 戻り先のコミットをSHA・相対参照・タグ・reflogで特定する方法
  • git reset--soft--mixed--hardの使い分け
  • 一時的に戻って試すgit switch --detachと派生ブランチ運用
  • 履歴を残して戻すgit revert <range>の使い方
  • 個人ブランチでpush --force-with-leaseを安全に使う手順
  • 戻しすぎたときにgit reflogで救済する復旧フロー
スポンサーリンク

まず意図を見極める:「戻る」の4パターン

何のために戻したいかで使うコマンドが変わります。意図をはっきりさせてから操作を始めましょう。

意図 適切なコマンド 履歴
① 不要なコミットを全部消して、その時点を本線にする git reset --hard <SHA> 書き換え
② 戻しはするが変更内容はステージや作業ツリーに残す git reset --soft/--mixed <SHA> 書き換え
一時的に戻って確認したい(ブランチは触らない) git switch --detach <SHA> 変更なし
④ 共有ブランチで戻した事実を履歴に残す git revert <SHA>..HEAD 打ち消しコミット追加

基本方針:push済み・共有ブランチは④のrevert個人ブランチや未pushの作業は①②のresetが原則です。「戻してみたいだけ」なら③のswitch --detachで覗いてから判断しましょう。「戻る」の意図が決まれば、残りの操作は機械的に進められます。

戻り先コミットを特定する

戻る先の参照にはSHA以外にもタグ・ブランチ・相対参照・reflog参照が使えます。覚えておくと事故が減ります。

戻り先を調べるコマンド
# 直近10件のコミットをSHA付きで確認
git log --oneline -10

# 分岐を含めて俯瞰
git log --oneline --graph --all -15

# リリースタグを戻り先にする
git tag -l "v*"
git log -1 v1.2.0    # v1.2.0 が指すコミットを確認

# 「5コミット前」のように相対指定
git log -1 HEAD~5

# 過去の操作履歴から目的のコミットを探す
git reflog

# 特定のファイルが関わるコミットだけ絞る
git log --oneline -- src/app.js

戻り先として指定できるもの

  • コミットSHAa1b2c3d(先頭7文字で一意ならOK)
  • タグv1.2.0release-2024q1
  • ブランチmainorigin/main(リモート追跡)
  • 相対参照HEAD~3(3つ前)・HEAD^(親)
  • reflog参照HEAD@{2}(2つ前のHEAD位置)・"HEAD@{yesterday}"(昨日時点)

戻り先をタグで管理しておくと、リリース時点への復帰がシンプルになります。タグ運用の詳細はタグ(tag)の使い方を参照してください。

意図①:完全に戻して本線にする(reset –hard)

最も一般的な「戻る」です。個人ブランチで「このコミット以降は全部なかったことにしたい」場合、git reset --hard <SHA>を使います。ブランチの先頭(HEAD)が指定SHAに移動し、それ以降のコミット・ステージ・作業ツリーの変更はすべて失われます。

reset –hard で戻す
# 戻り先のSHAを確認
git log --oneline -10

# そのコミットの状態に完全に戻す
git reset --hard a1b2c3d

# 相対参照で「5コミット前に戻す」
git reset --hard HEAD~5

# タグ時点に戻す(リリース状態に戻したいとき)
git reset --hard v1.2.0

# リモート追跡ブランチに合わせて戻す
git fetch origin
git reset --hard origin/main

要注意:--hard未コミットの変更も全部消えます。編集中のファイル・ステージ済みの変更・未追跡ではない変更はすべて失われ、reflogには残りません。作業途中のファイルがあるなら先にgit stashで退避するか、新ブランチを切ってから実行してください。

実行前のチェック

安全に戻すための事前確認
# 現状の変更を確認
git status

# 重要な変更が残っていれば退避
git stash push -m "before reset"

# 念のため現在地を別ブランチで保全
git branch backup/before-reset

# リモートと比べて自分がどれだけ先行しているか確認
git log --oneline origin/main..HEAD

意図②:戻しつつ変更は残す(reset –soft / –mixed)

「コミットは消したいけど、変更内容は手元に残して再構成したい」というケースは--soft--mixedを使います。

モード HEAD移動 ステージ 作業ツリー 主な用途
--soft 保持 保持 複数コミットを1つに統合
--mixed(既定) リセット 保持 addをやり直す
--hard リセット リセット 完全破棄

複数コミットを1つに統合して再コミット(–soft)

5コミットを1つにまとめる
# 5コミット前まで戻すが、変更はステージに全部残る
git reset --soft HEAD~5

# ステージの内容をまとめて1コミットに
git commit -m "feat: 認証機能を追加(旧5コミットを統合)"

add をやり直しながら戻す(–mixed)

変更は残してaddからやり直す
# 3コミット前まで戻す。ステージは空、変更は作業ツリーに残る
git reset HEAD~3   # --mixed はデフォルトなので省略可能

# 必要なファイルだけaddして再コミット
git add src/auth.py
git commit -m "refactor: auth周りだけまとめ直し"

ポイント:--soft--mixed変更は残るため、完全に作業を失う心配がありません。「コミットの粒度を整える」「addからやり直す」「別ブランチで再作業する」などの中間段階として活用できます。各モードの詳細はrevertとresetの違いと使い分けを参照。

意図③:一時的に戻って試す(switch –detach)

「過去の状態でビルドが通るか確認したい」「バグがどこから入ったか調べたい」——ブランチは触りたくないけど中身だけ覗きたい場合はgit switch --detachが最適です。detached HEADという一時状態に入り、作業終了後は元のブランチへ戻るだけで履歴は無傷のまま残ります。

一時的に戻って確認する
# 過去のSHAへdetached HEADで移動
git switch --detach a1b2c3d

# 確認作業(ビルド・テスト実行など)
npm install && npm test

# 元のブランチに戻る(履歴は書き換えられていない)
git switch main

戻った状態で作業したくなったら派生ブランチ化

detached HEADから派生ブランチに昇格
# detachedで見ているコミットを新ブランチの起点にする
git switch -c experiment/from-old a1b2c3d

# これで普通に作業&コミットでき、後からPRで取り込めば本線に反映

注意:detached HEAD状態でコミットを積んだ後、ブランチ化せずに別ブランチへ切り替えるとそのコミットは迷子になります。慌てずにgit switch -c rescue/xxxで現在位置をブランチ化してから移動しましょう。詳しくはdetached HEAD状態から元の作業ブランチに戻る方法を参照。

バグ発生点の特定にはgit bisectを使うと効率的です。bisectでバグを仕込んだコミットを特定する方法を参照してください。

意図④:戻した事実を履歴に残す(revert レンジ)

main/developなど共有ブランチでは、resetで履歴を書き換えると他メンバーのローカルが壊れます。代わりにgit revert打ち消しコミットを追加して「戻った状態」を表現します。複数コミット分をまとめて戻すときはレンジ指定が便利です。

共有ブランチを安全に戻す
# 戻したい範囲を確認(A から HEAD までを打ち消す)
git log --oneline a1b2c3d..HEAD

# a1b2c3d の次以降のコミットを全部打ち消す
git revert --no-commit a1b2c3d..HEAD

# まとめて1コミットにする
git commit -m "revert: a1b2c3d 以降の変更を一括で戻す"

# pushして反映
git push origin <branch>

個別にrevertコミットを作りたい場合

各コミットごとに打ち消しコミット
# レンジ指定(新しい順に個別のrevertコミットが作成される)
git revert a1b2c3d..HEAD

# 途中でコンフリクトしたら
git status          # 衝突ファイル確認
# 手動で修正
git add <conflict_file>
git revert --continue

# 中止したい場合
git revert --abort

ポイント:レンジ指定A..Bは「Aは含まず、B以下を含む」という半開区間です。Aは戻り先として残すことになります。A自体も打ち消したい場合はA^..Bを使います。打ち消しコミット群を1つにまとめたいなら--no-commitで一括適用→手動commitが便利です。

revertの詳細・マージコミットの取り消しはコミットの取り消し方mergeコミットを取り消して履歴を元に戻す方法を参照してください。

ローカルで戻した状態をリモートにも反映する

ローカルをresetで過去に戻しただけではリモートは変わりません。「リモートも同じ状態に戻したい」場合、安全性に応じて2ルートから選びます。

個人ブランチ:–force-with-lease で同期

個人ブランチの強制push(安全版)
# 最新リモート情報を取得
git fetch origin

# --force ではなく --force-with-lease を使う
git push --force-with-lease origin feature/my-branch

# stale エラーが出たらfetchし直して再実行

–force-with-lease の安全機構

  • リモートが自分のローカルorigin/xxxと一致していればpush成功
  • 他人が間にpushしていた場合は拒否され、上書き事故を防ぐ
  • --forceは無条件上書きで危険、基本は避ける
  • エイリアス登録推奨:git config --global alias.forcepush "push --force-with-lease"

共有ブランチ:reset + push はしない、revertをpush

禁止事項:main・develop・master等の共有ブランチにgit reset --hardgit push --forceは厳禁です。他メンバーのローカル環境が破壊されます。共有ブランチで戻すときは必ず「意図④ revertレンジ」+通常のpushで行ってください。

push関連の詳しい扱いはpushを取り消す方法も参考にしてください。

戻しすぎた/戻し間違えたときの救済

--hardで戻したら必要なコミットまで消えた」「違うSHAに戻ってしまった」——push前で時間が経っていなければ、git reflogから救済できる可能性が高いです。Gitは直近90日間、HEADの移動履歴をローカルに保存しています。

reflog で元の位置に戻す
# HEADの移動履歴を一覧表示
git reflog
# 出力例:
#   a1b2c3d HEAD@{0}: reset: moving to HEAD~5
#   f4e5d6a HEAD@{1}: commit: これが消えた大事な作業
#   9876543 HEAD@{2}: commit: その前の作業

# 戻したい位置のSHA(例: f4e5d6a)に再reset
git reset --hard f4e5d6a

# 位置参照(HEAD@{N})でも指定可能
git reset --hard HEAD@{1}

ポイント:reset直後ならgit reflogの先頭付近に「reset: moving to ...」のログが残っているはずです。失敗に気付いたら他の操作をする前にreflogを確認してください。作業を積み重ねるほどreflogが伸び、過去の位置が埋もれていきます。

実践シナリオ

シナリオ① 昨日の状態に戻したい(個人ブランチ)

昨日のコミットまで戻す
# 昨日のコミットをreflogで特定
git reflog --date=relative | head -20

# 時刻指定でも可
git log --oneline --since="1 day ago"

# 安全のため保全ブランチを作ってからreset
git branch backup/today
git reset --hard "HEAD@{yesterday}"   # または明示的なSHA
# 他の時刻指定例: "HEAD@{2.hours.ago}" "HEAD@{2024-01-15}"

シナリオ② リリース状態に戻したい

v1.2.0の状態に戻す
# タグを確認
git tag -l "v*"

# タグ時点に戻す
git reset --hard v1.2.0

# リモートも同期(個人ブランチのみ)
git push --force-with-lease origin <branch>

シナリオ③ mainを特定コミットに戻す(共有ブランチ)

共有mainを安全に戻す
# 最新化
git switch main
git pull origin main

# 戻したい範囲を確認
git log --oneline a1b2c3d..HEAD

# レンジ指定でrevert(打ち消しコミットを1つにまとめる)
git revert --no-commit a1b2c3d..HEAD
git commit -m "revert: a1b2c3d 以降を一括で戻す"

# PR経由でマージするか、レビュー後に直接push
git push origin main

シナリオ④ 戻った状態を新ブランチで試したい

派生ブランチで戻った状態を検証
# mainを壊さず、古いコミットから新ブランチを切る
git switch -c experiment/old-state a1b2c3d

# 自由に試す(作業してもmainは無傷)
# 検証後は不要なら削除
git switch main
git branch -D experiment/old-state

派生ブランチの使い方はGitで過去のコミットから新しいブランチを作り作業をやり直す方法に詳しい手順があります。

やってはいけない落とし穴

共有ブランチで reset –hard + force push する

mainやdevelopなど複数人が触るブランチをgit reset --hardgit push --forceで戻すと、他メンバーのローカルから最新コミットが消え、ビルドや作業が壊れます。共有ブランチは必ずrevertレンジ+通常pushで戻しましょう。GitHubのBranch protection rulesでforce pushを物理的に禁止するのも有効です。

未コミットの作業を残したまま –hard する

編集中のファイルがある状態で--hardすると、未コミットの変更はreflogにも残らず完全に失われます。迷ったらgit stash pushで退避するか、保全用ブランチを切ってから実行してください。

戻りすぎてもreflogを見ずにさらに操作を重ねる

reset失敗に気付いた後、慌ててさらにresetcheckoutを試すと、reflogのエントリが増えて本来戻りたかった位置が埋もれます。失敗に気付いたらまず手を止め、git reflogを確認してから次の一手を打ちましょう。

SHAを短縮しすぎて曖昧エラー

SHAを3〜4文字で指定すると同じ接頭辞のコミットが複数見つかってfatal: ambiguous argumentエラーになることがあります。安全のためgit log --onelineで表示される7〜8文字の短縮形を使いましょう。

force-with-lease を –force と同じ感覚で使う

--forceは他人のpushを知らずに上書きしてしまう危険なコマンドです。--force-with-leaseを既定にし、--forceは最終手段に留めましょう。git別名にforcepush = push --force-with-leaseを登録しておくと安全側に倒せます。

よくある質問

Qreset –hard と revert、どちらで戻すべき?
A個人ブランチかつ未push/保護されていないならreset --hard、共有ブランチ・push済みならrevertレンジが原則です。迷ったら「他人に影響するか?」で判断し、影響するならrevertを選びましょう。
Qreset –hard したあとに「やっぱり戻したい」
Agit reflogで元の位置のSHAを探し、git reset --hard <SHA>で再度reset。reflogの期限は90日なのでお早めに。ただし未コミットだった変更は復元できません。
QHEAD~3 と HEAD^3 は同じ?
A違います。HEAD~3は「3つ前の先祖」、HEAD^3は「3番目の親」を指します。マージコミットでないならHEAD~1HEAD^(1番目の親)ですが、「戻る」用途ではHEAD~Nの方が直感的で誤用しにくいです。
Q特定のコミットまで戻ったうえで、新ブランチとして残したい
Aresetでブランチを書き換える代わりにgit switch -c new-branch <SHA>で新ブランチを作るのが安全です。元のブランチは無傷で、戻った状態だけ別ブランチに切り出せます。
Qpush済みのmainを数コミット戻したい。どうすれば?
Agit revert --no-commit <開始SHA>..HEADでレンジ打ち消しを実行し、1つのコミットにまとめてgit pushします。絶対にpush --forceはしないでください。
Q戻り先のSHAがわからない
Agit log --oneline --graph --allで履歴を俯瞰、git reflogで過去の操作履歴、git log --since="2 days ago"で時刻検索など複数の探し方があります。「壊れる前の動いていた状態」を探したいならgit bisectが有効です。
Qreset –hard しても作業ツリーが元に戻らない
A未追跡ファイルは--hardの対象外です。これらも消したいならgit clean -fdを併用します(ただし対象確認にgit clean -nを必ず先に実行)。

関連記事

まとめ

  • 「戻る」の意図は4パターン:完全復元/変更は残す/一時検証/履歴として残す
  • 戻り先はSHAだけでなくタグ・ブランチ・相対参照・reflog参照で指定可
  • 個人ブランチの完全復元はgit reset --hard、粒度調整は--soft--mixed
  • 一時的に戻して確認するならgit switch --detachが安全
  • 共有ブランチはgit revert <range>で打ち消しコミットを追加
  • リモート同期は個人ブランチなら--force-with-lease、共有ブランチは通常push
  • 戻しすぎたらgit reflogで高確率救済——他の操作をする前に確認

「特定のコミットまで戻す」は一見単純な操作ですが、意図と影響範囲を意識しないと他メンバーの作業を壊す事故につながります。意図→コマンド→実行前チェック→実行→リモート同期の順で進めれば、慌てずに安全なリカバリができます。迷ったらgit reflogがあるので「取り返しのつかない操作」はほとんどありません——ただし共有ブランチへのforce pushは例外なので、保護ブランチ設定とチームのコミュニケーションで予防しましょう。