Gitで元に戻す:revert・reset・restoreの違いと使い分け

公開 更新 書いた人 大林 諒

Gitの「戻す」には似た名前のコマンドがいくつもあり、間違えると作業が消えます。AIが直した内容を取り消したいときも、調べるほど reset や --force のような強そうな言葉が出てきて手が止まります。この記事では、料金表の修正を例に、状況ごとにどのコマンドを使うかを、実際に動かした表示つきで順に確かめます。読み終わると、今の状況で安全に戻す方法を自分で選べて、消えない操作と消える操作を区別できます。

この記事の内容
  1. 先に答えると
  2. 状況別の早見表
  3. 先に3つの置き場所を知っておく
  4. コミット前に戻す:restore
  5. addを取り消す:restore –staged
  6. 直前のコミットを直す:commit –amend
  7. 本題:revertとresetの違い
  8. –hard と –force が危ない理由
  9. reflogで戻せる範囲
  10. ファイル1つだけ昔の版に戻す
  11. Gitで戻るのは手元のファイルだけ
  12. できないことと、その代わり
  13. よく詰まるところ
  14. 次に読む

先に答えると

判断の分かれ目は「pushしたかどうか」です。pushはGitHubに送る操作で、送ったあとは他の人の手元にも同じ履歴が届いている可能性があります。送る前なら履歴を巻き戻してかまいません。送ったあとは、打ち消す変更を新しく足します。

  • コミット前の変更を捨てるのは git restore。捨てた変更は戻せません
  • pushしていないコミットを直すのは git commit --amend と git reset
  • pushしたコミットを取り消すのは git revert。履歴が残るので安全です
  • 間違って消したコミットや枝は git reflog で探して取り戻せます
  • Gitで戻るのはGitが管理しているファイルだけで、公開中のサイトやデータベースは別に反映が要ります

状況別の早見表

戻したいこと使うコマンド消えるものpush後
保存(コミット)前の変更を捨てるgit restore menu.htmlその変更。戻せない関係なし
git add を取り消すgit restore --staged menu.htmlなし関係なし
直前のコミットを直すgit commit --amendなし。作り直しになる使わない
前のコミットを打ち消すgit revert コミットなし。履歴に足す使える
手元のコミットを取り消して変更は残すgit reset --soft HEAD~1なし使わない
手元のコミットを変更ごと捨てるgit reset --hard HEAD~1コミット前の変更すべて使わない
特定のファイルだけ昔に戻すgit restore --source コミット menu.htmlそのファイルのコミット前の変更使える
消した枝を取り戻すgit reflog のあと git switch -cなし手元のみ

HEAD は今いる場所のコミット、HEAD~1 はその1つ前です。例の料金表 menu.html は、次の履歴から始めます。

$ git log --oneline
908a31b 料金表を修正
06b31a7 問い合わせの電話番号を修正
e36e910 最初の版

先に3つの置き場所を知っておく

Gitの変更には手元の中だけで3つの段階があります。作業中のファイル、git add で選んだ「次のコミットに入れるもの」の置き場(ステージ)、コミットした履歴です。それぞれのコマンドは、どの段階を戻すかが違います。

戻すコマンドは、どの場所を戻すかが違う
戻すコマンドは、どの場所を戻すかが違う

コミット前に戻す:restore

和食の基本の料金を、手元で間違えて9,999円にした場面です。git restore は、作業中の変更を捨てて直前の状態に戻します。

$ git diff
diff --git a/menu.html b/menu.html
index f9030d7..a62f4e1 100644
--- a/menu.html
+++ b/menu.html
@@ -1,6 +1,6 @@
 <h1>料金表</h1>
 <table>
-<tr><td>和食の基本</td><td>6,200円</td></tr>
+<tr><td>和食の基本</td><td>9,999円</td></tr>
 <tr><td>家庭の洋食</td><td>6,500円</td></tr>
 </table>
 <p>お問い合わせ: 03-0000-1111</p>

$ git restore menu.html

$ grep -n 和食 menu.html
3:<tr><td>和食の基本</td><td>6,200円</td></tr>

確認の表示は出ません。一度捨てると戻せないので、git diff で中身を見てから実行します。

古い記事では git checkout -- menu.html と書かれていることがあります。今でも動き、同じ結果になります。

addを取り消す:restore –staged

git add . を実行したら、入れてはいけない .env(APIキーなどの設定ファイル)まで選ばれた場面です。--staged をつけると選択だけが外れ、ファイルは消えません。

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   .env
	modified:   menu.html

$ git restore --staged .env

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   menu.html

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	.env

直前のコミットを直す:commit –amend

コミットの題名を間違えた、ファイルを入れ忘れたというときに使います。pushする前に限ります。

$ git log --oneline -2
9df5a2a 洋食のなまえをなおす
908a31b 料金表を修正

$ git commit --amend -m "家庭の洋食の名前を修正"
[main 6c42cca] 家庭の洋食の名前を修正
 Date: Sat Nov 7 09:33:10 2026 +0900
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git add style.css

$ git commit --amend --no-edit
[main 411a411] 家庭の洋食の名前を修正
 Date: Sat Nov 7 09:33:10 2026 +0900
 2 files changed, 2 insertions(+), 1 deletion(-)
 create mode 100644 style.css

番号が 9df5a2a から 6c42cca、411a411 と変わりました。amendは元のコミットを書き換えるのではなく、新しいコミットを作って差し替えています。pushのあとに使えない理由はここにあり、送った履歴と手元の履歴が食い違います。

本題:revertとresetの違い

一番間違えやすいところです。2つとも「コミットを取り消す」と説明されますが、していることは逆方向です。

revertreset
していること打ち消すコミットを足す枝の先端を前に動かす
履歴全部残る消えたように見える
pushしたあとそのままpushできる拒否される
向いている場面共有済みのコミットまだ手元だけのコミット

revert:打ち消しを足す

洋食の料金を7,000円にしてpushしたあと、間違いに気づいた場面です。git revert は逆向きの変更をコミットにして足します。実行すると既定のメッセージの編集画面が開くので、そのまま保存して閉じます。

$ git log --oneline -3
9f963e8 洋食の料金を7,000円に修正
411a411 家庭の洋食の名前を修正
908a31b 料金表を修正

$ git revert HEAD
[main a617bd7] Revert "洋食の料金を7,000円に修正"
 Date: Sat Nov 7 09:33:10 2026 +0900
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git log --oneline -3
a617bd7 Revert "洋食の料金を7,000円に修正"
9f963e8 洋食の料金を7,000円に修正
411a411 家庭の洋食の名前を修正

$ git push
To ../website.git
   9f963e8..a617bd7  main -> main

間違えたコミットも、打ち消したコミットも残ります。To の行の ../website.git は、この記事で動かすために手元に作った練習用の送り先です。

GitHubのプルリクエスト(Pull Request、PR。枝を本筋に入れる確認依頼)でマージしたものなら、画面の下のほうにある「Revert」を押すと、打ち消しのプルリクエストが新しく作られます。それをマージすれば同じことになります。

マージ済みのプルリクエストの Revert ボタン
マージ済みのプルリクエストの Revert ボタン

reset:枝の先端を動かす

git reset は、枝の先端を前のコミットに動かします。3種類あり、違いは「捨てたコミットの変更をどこに残すか」です。

種類履歴ステージ作業中のファイル使う場面
--soft戻る残る残るコミットだけやり直す
--mixed戻る空になる残る選び直してコミットする。指定なしのときもこれ
--hard戻る空になる消える変更ごと捨てる

「夏の教室を追加」をコミットした状態から、それぞれ動かします。--soft は変更がステージに残ります。

$ git log --oneline -2
19bef1e 夏の教室を追加
4f04141 和食の料金を6,300円に修正

$ git reset --soft HEAD~1

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   menu.html

コミットし直してから --mixed を試すと、変更はステージから外れて作業中のファイルに残ります。

$ git reset --mixed HEAD~1
Unstaged changes after reset:
M	menu.html

--hard は変更も消えます。先に枝を作っておきます。

$ git branch backup-before-reset

$ git reset --hard HEAD~1
HEAD is now at 4f04141 和食の料金を6,300円に修正

$ grep -c 夏の教室 menu.html
0

$ git log --oneline -1 backup-before-reset
19bef1e 夏の教室を追加
reset の3種類で、残るものと消えるもの
reset の3種類で、残るものと消えるもの

pushしたあとにresetすると

pushしたコミットを手元でresetしてからpushすると、拒否されます。

$ git reset --hard HEAD~1
HEAD is now at 4f04141 和食の料金を6,300円に修正

$ git push
To ../website.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to '../website.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Gitは「手元の履歴のほうが古い」と判断しています。ここで --force をつけて押し通す方法が検索で出てきますが、次の節のとおり危険です。pushしたものを取り消すならrevertを使います。

–hard と –force が危ない理由

git reset --hard は、コミットしていない変更を確認なしで消します。git push --force は、送り先の履歴を手元の内容で上書きします。外注エンジニアBが先にpushしたコミットがあっても消えます。

動かすと次のようになります。Bが「夏の教室を追加(Bの作業)」をpushしたあとに、それを知らない代表が強制的に送った場面です。

$ git push --force-with-lease
To ../website.git
 ! [rejected]        main -> main (stale info)
error: failed to push some refs to '../website.git'

$ git push --force
To ../website.git
 + f09079d...4f04141 main -> main (forced update)

$ git log --oneline origin/main -2
4f04141 和食の料金を6,300円に修正
a617bd7 Revert "洋食の料金を7,000円に修正"

送り先からBのコミットが消えました。--force-with-lease は、最後に受け取ったあとで他の人がpushしていれば止める指定で、1回目はこれが効いています。ただし git fetch で受け取ったあとなら通ってしまうので、中身を見ずに実行すれば同じことです。

Bの手元にはコミットが残っていて、次にBがpushすると戻ってきます。

$ git fetch
From ../website
 + f09079d...4f04141 main       -> origin/main  (forced update)

$ git status -sb
## main...origin/main [ahead 2]

$ git push
To ../website.git
   4f04141..f09079d  main -> main

消したはずのコミットが他の人の手元から戻ることがあり、上書きされた人の作業はその人の手元にしか残りません。共有している枝を上書きする怖さはここにあります。やる前にすることは次の4つです。

  1. 枝を作る(git branch backup-before-reset)。これだけで元の場所に戻れます
  2. git status でコミットしていない変更がないか見る
  3. 共有している枝なら、他の人に一言伝える
  4. main には使わない。自分だけの枝に限る

GitHubのブランチ保護を入れていれば、保護した枝へのforce pushは既定で止まります。ただしFreeの Organization の非公開リポジトリでは使えません(2026年11月時点)。

reflogで戻せる範囲

git reflog は、手元で枝の先端やHEADが動いた記録です。git log に出なくなったコミットも、ここには残っています。

$ git reflog -4
4f04141 HEAD@{0}: reset: moving to HEAD~1
19bef1e HEAD@{1}: commit: 夏の教室を追加
4f04141 HEAD@{2}: reset: moving to HEAD~1
19bef1e HEAD@{3}: commit: 夏の教室を追加

$ git reset --hard 19bef1e
HEAD is now at 19bef1e 夏の教室を追加

$ git log --oneline -2
19bef1e 夏の教室を追加
4f04141 和食の料金を6,300円に修正

消した枝も同じです。枝の先端だったコミットを探して、枝を作り直します。

$ git branch -D add-summer-class
Deleted branch add-summer-class (was 5b5a968).

$ git reflog -3
4f04141 HEAD@{0}: checkout: moving from add-summer-class to main
5b5a968 HEAD@{1}: commit: 夏の教室のお知らせを追加
4f04141 HEAD@{2}: checkout: moving from main to add-summer-class

$ git switch -c add-summer-class 5b5a968
Switched to a new branch 'add-summer-class'

戻せる範囲は次のとおりです。

状況戻せるか
コミットしたあとresetやamendで見えなくなった戻せる
枝を消した手元でコミットしていれば戻せる
コミットしていない変更を捨てた戻せない
git add までしていた変更を捨てた運がよければ拾える
別のPCでした操作戻せない。reflogはそのフォルダだけの記録で、cloneしても引き継がれない
記録が古い既定で90日、今の枝から外れたものは30日を過ぎると消える対象になる

コミットしていない変更はreflogに残らないので、git reset --hard のあとは探す先がありません。例外は git add まで済んでいた内容で、次のように残っていることがあります。ファイル名も分からない断片なので、頼りにはしないでください。

$ git fsck --lost-found
dangling blob f9f844eaf1cd2e2719bb34112c83acb7d56bb290

$ git show f9f844e
<h1>サンプル料理教室</h1>
<p>addだけした案内文</p>

作業中のものは、捨てる前にコミットしておくのが一番の保険です。

ファイル1つだけ昔の版に戻す

料金表だけ以前の内容に戻したいときは、まず昔の版を git show コミット:ファイル名 で見ます。何も変わらないので安心して試せます。

$ git log --oneline -- menu.html
4f04141 和食の料金を6,300円に修正
a617bd7 Revert "洋食の料金を7,000円に修正"
9f963e8 洋食の料金を7,000円に修正
411a411 家庭の洋食の名前を修正
908a31b 料金表を修正
06b31a7 問い合わせの電話番号を修正
e36e910 最初の版

$ git show 06b31a7:menu.html
<h1>料金表</h1>
<table>
<tr><td>和食の基本</td><td>6,000円</td></tr>
<tr><td>家庭の洋食</td><td>6,500円</td></tr>
</table>
<p>お問い合わせ: 03-0000-1111</p>

合っていたら、--source でそのファイルだけ持ってきます。他のファイルと履歴はそのままです。

$ git restore --source 06b31a7 menu.html

$ git status --short
 M menu.html

$ git add menu.html

$ git commit -m "料金表を電話番号の修正の時点に戻す"
[main 261d11a] 料金表を電話番号の修正の時点に戻す
 1 file changed, 2 insertions(+), 2 deletions(-)

古い版に上書きしたのではなく、「古い内容にした」という新しいコミットを足す形なので、pushしたあとでも使えます。

Gitで戻るのは手元のファイルだけ

ここを勘違いすると、戻したつもりで何も戻っていないことになります。

対象Gitの操作で戻るか別に必要なこと
手元のファイル戻るなし
GitHubのリポジトリpushすれば戻るrevertしてpush
公開中のホームページ戻らない反映の仕組みを動かす。手で置いているなら置き直す
データベースの予約データ戻らない別に取ったバックアップから戻す
send_mail.py が送ったメール戻らないお詫びと訂正を送る
GitHubに入れたAPIキー戻らないキーを無効にして作り直す

実際に運用していると、自動で反映されるサイトでも、反映が終わるまでは古い表示のままです。戻したあとは公開中の画面を開いて確かめます。

Gitで戻せるのは手元とGitHubまで。公開サーバーとデータベースは別の話
Gitで戻せるのは手元とGitHubまで。公開サーバーとデータベースは別の話

できないことと、その代わり

できないことその代わり
コミットしていない変更を戻すこまめにコミットする。捨てる前に枝を作ってコミットする
revertで履歴から内容を消す履歴に残るので、秘密の情報は別の対応が要る
他の人の手元の履歴を消す消せない。共有した時点で手が届かないと考える
別のPCのreflogを見るそのPCで見る。GitHubのプルリクエストの枝なら「Restore branch」

.env をコミットしてからrevertしても、ファイルが消えるだけで中身は読めます。

$ git revert --no-edit HEAD
[main aa80234] Revert "設定を追加"
 Date: Sat Nov 7 09:33:11 2026 +0900
 1 file changed, 1 deletion(-)
 delete mode 100644 .env

$ git show HEAD~1:.env
API_KEY=xxxxxxxx

よく詰まるところ

症状原因直し方
git restore で did not match any file(s) known to gitまだ一度も git add していない新しいファイルgit clean -n で確かめ、git clean -f で消す。消したファイルは戻せないので、必ず -n の一覧を見てから
git reset --hard のあとも新しいファイルが残るGitが管理していないファイルは対象外同じく git clean
amendのあと ! [rejected] と出るpushしたコミットを作り直した他の人が使っていない枝に限り、「–hard と –force が危ない理由」の4つを確かめてから git push --force-with-lease。main なら git reset --soft origin/main で変更を残して戻し、新しいコミットにする
is a merge but no -m option was given合流のコミットはどちら側に戻すか指定が要るgit revert -m 1 コミット で、合流前の main 側に戻す
revertしたマージをもう一度入れても Already up to date.Gitは取り込み済みと判断する先に出したrevertをrevertしてから入れる
古いコミットのrevertで CONFLICT後のコミットが同じ行か隣の行を変えた直すか git revert --abort でやめる

最後の場合は、次のように出ます。

$ git revert 908a31b
Auto-merging menu.html
CONFLICT (content): Merge conflict in menu.html
error: could not revert 908a31b... 料金表を修正
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git revert --continue".
hint: You can instead skip this commit with "git revert --skip".
hint: To abort and get back to the state before "git revert",
hint: run "git revert --abort".
hint: Disable this message with "git config set advice.mergeConflict false"

$ git revert --abort

AIに戻す作業を頼むときは、実行前に「どのコマンドを、なぜ使うか」を聞きます。--hard か --force が入っていたら、先に枝を作らせてから進めます。

次に読む

add、commit、pushの段階の話は「git add・commit・pushの違い:保存して送るまでの流れ」にあります。revertで出た競合は「コンフリクトの直し方:起きる理由と、解消の手順」で直せます。秘密の情報を入れてしまったときは「APIキーやパスワードをGitHubにpushしてしまったときの対応」を開きます。AIに作業させるときの進め方は「GitHub CopilotとClaude Code:AIでコードを書くときのGitの使い方」に続きます。

あわせて読む