git reset と revert、どっち?ブランチの切り方と戻し方を実行例つきで整理

「あっ、コミットを間違えた」「変更を取り消したい」……でも、git reset と git revert のどちらを使えばいいのか分からない。そんなときに読んでほしい記事です。

この記事では、ブランチの切り方と、状況別の「戻し方」の使い分けを、実際に動かした出力つきで、一緒に整理していきましょう。実際に実行した出力をそのまま載せているので、安心して真似してみてください。所要時間は、読むだけなら約10分、練習用リポジトリで試すなら+15分が目安です。

この記事は、この1週間続けてきた連載の最終回です。これまでの記事は、記事一覧から読み返せます。

💡
この記事でわかること
  • 「戻したい」ときに最初に確認する3つの質問
  • ブランチを切って、安全に試す手順
  • restore・revert・reset の違いと使い分け
  • 実行前に立ち止まりたい、危険なコマンド

まず結論:3つの質問で戻し方が決まる

質問 状況 使うコマンド
1 まだコミットしていない変更を捨てたい git restore
2 コミットしたが、まだ自分の手元だけ(push前) git reset
3 すでにpushして、他の人も見られる git revert

戻し方の選び方:未コミットなら restore、履歴を残すなら revert、自分だけのブランチで巤き戻すなら reset(pushしたコミットへの reset は避ける)

覚え方は、「履歴を書き換えてよいのは、自分しか知らないときだけ」です。共有済みの履歴を書き換えると、他の人の手元とずれて混乱の元になります。共有済みのときは、「取り消した」という新しいコミットを足す revert を使います。


準備:練習用リポジトリを作る

本番のリポジトリで試す前に、練習用で動きを確認しましょう。練習用なら、失敗しても何も困りません。次のコマンドで準備できます。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
mkdir my-project && cd my-project
git init -b main
echo '# my-project' > README.md
echo 'print("Hello")' > app.py
git add .
git commit -m "初回コミット"

この記事のコマンド出力は、Git 2.47.3 で実際に実行した結果です。出力は英語で表示していますが、日本語の環境では日本語で表示されることがあります。また、本記事で使う git switch と git restore は Git 2.23 以降のコマンドです。


ブランチを切る:本流を汚さずに試す

ブランチは、作業用の枝です。木の枝をイメージしてみてください。本流(main)から枝を分けておけば、失敗しても main には影響しません。新機能や「ちょっと試したい変更」は、先にブランチを切るのが基本です。

  • 1
  • 2
$ git switch -c feature/greeting
Switched to a new branch 'feature/greeting'

switch -c の -c は create(作成)の意味で、作成と切り替えを同時にやってくれます。次は、ブランチの一覧と切り替えを試してみましょう。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
$ git branch -v
* feature/greeting 3f15869 初回コミット
  main             3f15869 初回コミット

$ git switch main
Switched to branch 'main'

* が付いているのが、今いるブランチです。git switch - と書くと、直前のブランチに戻れます。

ブランチを切って、git branch -v で一覧を表示した画面

ブランチ名は、feature/greeting(機能追加)、fix/login-error(不具合修正)のように、種類/内容の形にしておくと一覧で分かりやすくなります。


戻し方①:restore(コミット前の変更を戻す)

ファイルを編集したけれど、やっぱり元に戻したい。そんなときの手段です。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
$ git diff
diff --git a/app.py b/app.py
index 2f9a147..f6ee80e 100644
--- a/app.py
+++ b/app.py
@@ -1 +1,2 @@
 print("Hello")
+print("こんにちは")

$ git restore app.py

$ git status
On branch feature/greeting
nothing to commit, working tree clean

git diff で変更内容を確認してから、git restore app.py で戻しています。

⚠️
restore は元に戻せません

コミットしていない変更は、Gitの履歴に残っていません。git restore で捨てたら、取り戻せません。実行前に、必ず git diff で中身を確認しましょう。

git add したファイルを「add前の状態」に戻したいときは、--staged を付けます。こちらは変更自体は消えません。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
$ git status -s
M  app.py

$ git restore --staged app.py

$ git status -s
 M app.py

git status -s の左の列がステージ済み、右の列が未ステージを表します。M が M に変わり、ステージだけが外れたことが分かります。


戻し方②:revert(取り消しの履歴を足す)

push済みのコミットを取り消すときは、revert を使います。履歴は消さず、「そのコミットを打ち消す新しいコミット」を足します。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
$ git log --oneline
ae1ff8f さようならを追加
65068e2 あいさつを日本語で追加
3f15869 初回コミット

$ git revert --no-edit HEAD
[feature/greeting e4be890] Revert "さようならを追加"
 1 file changed, 1 deletion(-)

$ git log --oneline
e4be890 Revert "さようならを追加"
ae1ff8f さようならを追加
65068e2 あいさつを日本語で追加
3f15869 初回コミット

--no-edit は、メッセージを自動で付ける指定です。さようならを追加 のコミットは残ったまま、その内容を打ち消す Revert コミットが追加されました。履歴が増えるだけなので、他の人の手元とずれる心配がありません。

git revert のあと、git log --oneline に Revert コミットが追加された画面

戻し方③:reset(履歴を巻き戻す)

reset は、ブランチの位置を過去のコミットまで戻すコマンドです。戻した後の変更をどこに残すかで3種類あります。ここがいちばん混乱しやすいところなので、表と一緒に見ていきましょう。

オプション コミット ステージ ファイルの中身
--soft 取り消す 残る 残る
--mixed(既定) 取り消す 外れる 残る
--hard 取り消す 外れる 消える

git reset の3種類:--soft・--mixed・--hard で、残るものと消えるものが違う(--hard は危険)

練習用に、practice ブランチで「実験1」「実験2」の2コミットを作り、それぞれ試しました。HEAD~1 は「1つ前のコミット」の意味です。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
$ git log --oneline -3
2462be4 実験2を追加
d6a2f3d 実験1を追加
e4be890 Revert "さようならを追加"

$ git reset --soft HEAD~1

$ git status -s
M  app.py

--soft は、コミットだけを取り消し、変更はステージ済みのまま残します。「コミットメッセージを書き直したい」ときに便利です。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
$ git reset HEAD~1
Unstaged changes after reset:
M	app.py

$ git status -s
 M app.py

オプションなしの --mixed は、ステージも外し、ファイルの変更だけを残します。

  • 1
  • 2
$ git reset --hard HEAD~1
HEAD is now at d6a2f3d 実験1を追加

--hard は、ファイルの変更ごと消します。実験2の内容は、作業フォルダからも消えました。本番でやるとヒヤッとします。

間違えて reset --hard したときの救出方法

コミット済みの内容であれば、git reflog(HEADの移動履歴)から取り戻せます。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
$ git reflog -4
d6a2f3d HEAD@{0}: reset: moving to HEAD~1
2462be4 HEAD@{1}: commit: 実験2を追加
...

$ git reset --hard HEAD@{1}
HEAD is now at 2462be4 実験2を追加

HEAD@{1} は「1つ前の位置」です。実験2が復活しました。ただし、これが使えるのはコミット済みの変更だけです。コミットしていない変更は、reflogにも残りません。


実行前に立ち止まりたい危険なコマンド

コマンド 何が起きるか 実行前の確認
git reset --hard 未コミットの変更が消える git status で変更の有無を見る
git restore ファイル名 そのファイルの未コミット変更が消える git diff で中身を確認する
git clean -fd Gitが管理していないファイルが消える 先に git clean -n で確認する
git branch -D マージ前のブランチを強制削除する ブランチ名とコミットを確認する
git push --force 共有ブランチの履歴を上書きする 共有ブランチでは使わない

git clean は、-n(ドライラン)なら実際には消しません。

  • 1
  • 2
$ git clean -n
Would remove memo.txt

Would remove は「消すことになります」という意味で、まだ何も消えていません。


AIに編集を任せる前に、ブランチを切る

CursorなどのAIエージェントにファイルの編集を任せるときも、先にブランチを切って、今の状態をコミットしておくと安心です。Cursorのチェックポイントは、Gitとは別にローカルへ保存される仕組みです。やり直しの土台は、Gitの履歴に残しておきましょう。コミットメッセージの書き方はこちらです。

コミットメッセージを揃えるだけでチーム開発が楽になる話(Conventional Commits)


つまずきやすい点

ブランチを切り替えようとするとエラーになる
  • 1
  • 2
  • 3
  • 4
  • 5
$ git switch feature/greeting
error: Your local changes to the following files would be overwritten by checkout:
	app.py
Please commit your changes or stash them before you switch branches.
Aborting

未コミットの変更が、切り替え先のブランチと衝突するときのエラーです。エラーが出るとドキっとしますが、メッセージをよく読めば、次にやることが書いてあることが多いです。コミットするか、stash(一時退避)します。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
$ git stash push -m 作業中
Saved working directory and index state On main: 作業中

$ git switch feature/greeting
Switched to branch 'feature/greeting'

退避した変更は、元のブランチで git stash pop を実行すると戻ります。

「not fully merged」でブランチを削除できない
  • 1
  • 2
  • 3
$ git branch -d feature/greeting
error: the branch 'feature/greeting' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/greeting'

mainに取り込んでいないコミットがあるという警告です。まず main に git merge で取り込めば、-d で消せます。取り込まずに消す覚悟があるときだけ、-D を使います。

コミットのIDを指定したら「a branch is expected」と出る
  • 1
  • 2
  • 3
$ git switch efc78b9
fatal: a branch is expected, got commit 'efc78b9'
hint: If you want to detach HEAD at the commit, try again with the --detach option.

switch はブランチ用です。過去のコミットを見るだけなら --detach を付けます。この状態(detached HEAD)のまま作業するなら、git switch -c 新しい名前 でブランチを作りましょう。


まとめ

  • ブランチは、試す前に切る。git switch -c 名前 で作成と切り替えが同時にできる
  • 戻し方は3つの質問で選ぶ。コミット前は restore、push前は reset、push後は revert
  • reset --hard と restore は変更が消える。実行前に git status と git diff を見る

まずは練習用リポジトリで、今日の流れを一度なぞってみてくださいね。手を動かしておくと、本番で慌てずに済みます。

この記事は、この1週間の連載の最終回です。最後まで読んでいただき、ありがとうございました。


関連する本・記事

本:『Go言語アンチパターン42問:悪い例→なぜ→直し方で、実務の落とし穴を見抜く』

Gitの操作に慣れてきたら、次は「書いたコードの質」を上げる段階です。この本は、悪い例・理由・直し方の3点セットで、実務で踏みやすい落とし穴を確認できる問題集です。Goを使う開発の方に向いています。

記事・本:


メンバーのSNSアカウントはこちら

◆かいと:IT担当
X(旧Twitter)
Instagram