はじめに

「コードを編集したけど元に戻したい」「直前のコミットを取り消したい」「共有済みのコミットを安全に打ち消したい」。Gitを使った開発では、このような変更取り消しの場面に頻繁に遭遇します。

しかし、Gitには変更取り消しのためのコマンドが複数存在し、使い分けに悩む方も多いのではないでしょうか。git restoregit resetgit revertはそれぞれ異なる目的と影響範囲を持ち、状況に応じた適切な選択が求められます。

本記事では、Gitの変更取り消しコマンドを体系的に解説します。この記事を読み終えると、以下のことができるようになります。

  • git restoreで作業ツリーやステージングエリアの変更を取り消せる
  • git resetの3つのモード(soft/mixed/hard)を理解し、コミットを取り消せる
  • git revertで共有済みコミットを安全に打ち消せる
  • 状況に応じて最適な変更取り消し方法を選択できる

実行環境と前提条件

本記事の内容は、以下の環境で動作確認を行っています。

項目 要件
Git 2.40以上
OS Windows 10/11、macOS 12以上、Ubuntu 22.04以上
ターミナル コマンドプロンプト、PowerShell、Terminal.app、bash等
エディタ VS Code推奨

前提条件として、以下の知識があることを想定しています。

  • コマンドライン操作の基礎知識(cdls/dirmkdir等)
  • テキストエディタの基本操作
  • Gitの基本コマンド(git initgit addgit commitgit status)の理解

Git変更取り消しコマンドの概要

Gitで変更を取り消すには、主に以下の3つのコマンドを使用します。

コマンド 主な用途 影響範囲 履歴の変更
git restore 作業ツリー・ステージングエリアの変更取り消し ファイル単位 なし
git reset コミットの取り消し、HEADの移動 リポジトリ全体 あり
git revert コミットの打ち消し(新規コミット作成) 指定コミット なし(追加のみ)

これらのコマンドを正しく使い分けることで、安全かつ効率的に変更を取り消すことができます。

使い分けの基本方針

どのコマンドを使うべきかは、以下の判断基準で決定します。

状況 推奨コマンド
ファイルの編集を破棄したい(コミット前) git restore
ステージングを取り消したい git restore --staged
直前のコミットをやり直したい(ローカルのみ) git reset
複数のコミットを取り消したい(ローカルのみ) git reset
共有済みのコミットを取り消したい git revert
特定のコミットの変更のみを打ち消したい git revert

git restore - 作業ツリーとステージングエリアの変更取り消し

git restoreは、Git 2.23で導入された比較的新しいコマンドです。作業ツリー(ワーキングディレクトリ)やステージングエリア(インデックス)のファイルを、特定の状態に復元するために使用します。

作業ツリーの変更を取り消す

ファイルを編集したものの、その変更を破棄して直前のコミット状態に戻したい場合に使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 特定のファイルの変更を取り消す
git restore <ファイル名>

# 例:app.jsの変更を取り消す
git restore app.js

# カレントディレクトリのすべてのファイルを取り消す
git restore .

# すべての変更を取り消す(リポジトリルートから)
git restore :/

実行例を見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 現在の状態を確認
$ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   app.js
        modified:   index.html

# app.jsの変更を取り消す
$ git restore app.js

# 結果を確認
$ git status
On branch main
Changes not staged for commit:
        modified:   index.html

app.jsの変更が取り消され、index.htmlの変更のみが残っています。

ステージングエリアの変更を取り消す

git addでステージングしたファイルをアンステージ(ステージング取り消し)する場合は、--stagedオプションを使用します。

1
2
3
4
5
6
7
8
# 特定のファイルをアンステージする
git restore --staged <ファイル名>

# 例:app.jsをアンステージする
git restore --staged app.js

# すべてのファイルをアンステージする
git restore --staged .

実行例を見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 現在の状態を確認
$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   app.js
        new file:   utils.js

# app.jsをアンステージする
$ git restore --staged app.js

# 結果を確認
$ git status
On branch main
Changes to be committed:
        new file:   utils.js
Changes not staged for commit:
        modified:   app.js

app.jsがステージングエリアから作業ツリーに戻りました。ファイルの内容自体は変更されていません。

作業ツリーとステージングエリアを同時に取り消す

--staged--worktreeを組み合わせることで、両方を同時に取り消すことができます。

1
2
3
4
5
# ステージングと作業ツリーの両方を取り消す
git restore --staged --worktree <ファイル名>

# 短縮形
git restore -SW <ファイル名>

特定のコミットからファイルを復元する

--sourceオプションを使用すると、特定のコミットからファイルを復元できます。

1
2
3
4
5
6
7
8
# 直前のコミットからファイルを復元
git restore --source=HEAD~1 <ファイル名>

# 特定のコミットハッシュからファイルを復元
git restore --source=abc1234 <ファイル名>

# 特定のブランチからファイルを復元
git restore --source=feature-branch <ファイル名>

実行例を見てみましょう。

1
2
3
4
5
6
7
8
# 2つ前のコミットからconfig.jsonを復元
$ git restore --source=HEAD~2 config.json

# 結果を確認
$ git status
On branch main
Changes not staged for commit:
        modified:   config.json

復元されたファイルは作業ツリーに配置され、未ステージの変更として扱われます。

git restoreの注意点

git restoreを使用する際は、以下の点に注意してください。

注意点 説明
変更の消失 作業ツリーの変更を取り消すと、その変更は完全に失われます
復元不可 git restoreで取り消した変更は、通常の方法では復元できません
未追跡ファイル 未追跡ファイル(新規作成ファイル)には影響しません

重要な変更がある場合は、取り消す前にgit stashで一時保存することを検討してください。

git reset - コミットの取り消しとHEADの移動

git resetは、現在のブランチのHEADを指定したコミットに移動させるコマンドです。コミットの取り消しや履歴の巻き戻しに使用します。

git resetの3つのモード

git resetには3つの主要なモードがあり、それぞれ影響範囲が異なります。

モード HEAD ステージングエリア 作業ツリー 用途
--soft 移動 維持 維持 コミットのやり直し
--mixed(デフォルト) 移動 リセット 維持 ステージングのやり直し
--hard 移動 リセット リセット 変更の完全な破棄

git reset –soft(コミットのやり直し)

--softモードは、HEADのみを移動させ、ステージングエリアと作業ツリーはそのまま維持します。コミットをやり直したい場合に最適です。

1
2
3
4
5
6
7
8
# 直前のコミットを取り消す(変更はステージング済みのまま)
git reset --soft HEAD~1

# 3つ前のコミットまで戻る
git reset --soft HEAD~3

# 特定のコミットまで戻る
git reset --soft <コミットハッシュ>

実行例を見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 現在のログを確認
$ git log --oneline -3
abc1234 (HEAD -> main) 認証機能を追加
def5678 ログイン画面を実装
ghi9012 初期コミット

# 直前のコミットを取り消す
$ git reset --soft HEAD~1

# ログを確認(コミットが消えている)
$ git log --oneline -2
def5678 (HEAD -> main) ログイン画面を実装
ghi9012 初期コミット

# ステータスを確認(変更はステージング済み)
$ git status
On branch main
Changes to be committed:
        new file:   auth.js
        modified:   app.js

変更内容はステージングエリアに残っているため、修正してから再度コミットできます。

git reset –mixed(デフォルト)

--mixedモードは、HEADとステージングエリアをリセットし、作業ツリーはそのまま維持します。オプションを省略した場合はこのモードが適用されます。

1
2
3
4
5
# 直前のコミットを取り消す(変更は未ステージ状態に)
git reset HEAD~1

# 明示的にmixedモードを指定
git reset --mixed HEAD~1

実行例を見てみましょう。

1
2
3
4
5
6
7
8
9
# 直前のコミットを取り消す
$ git reset HEAD~1

# ステータスを確認(変更は未ステージ状態)
$ git status
On branch main
Changes not staged for commit:
        new file:   auth.js
        modified:   app.js

変更内容は作業ツリーに残っていますが、ステージングは解除されています。

git reset –hard(変更の完全な破棄)

--hardモードは、HEAD、ステージングエリア、作業ツリーのすべてをリセットします。すべての変更が失われるため、最も注意が必要なモードです。

1
2
3
4
5
6
7
8
# 直前のコミットと変更をすべて破棄
git reset --hard HEAD~1

# 特定のコミットまで完全に戻る
git reset --hard <コミットハッシュ>

# HEADの状態に完全にリセット(すべての変更を破棄)
git reset --hard HEAD

実行例を見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 現在の状態
$ git status
On branch main
Changes not staged for commit:
        modified:   app.js

$ git log --oneline -2
abc1234 (HEAD -> main) 認証機能を追加
def5678 ログイン画面を実装

# 直前のコミットと変更をすべて破棄
$ git reset --hard HEAD~1

# 結果を確認
$ git log --oneline -2
def5678 (HEAD -> main) ログイン画面を実装
ghi9012 初期コミット

$ git status
On branch main
nothing to commit, working tree clean

コミットも作業ツリーの変更もすべて失われました。

git resetの実践的なユースケース

直前のコミットメッセージを修正したい

1
2
3
4
5
# コミットを取り消し(変更はステージング済みのまま)
git reset --soft HEAD~1

# 新しいメッセージでコミット
git commit -m "新しいコミットメッセージ"

なお、直前のコミットメッセージの修正だけであれば、git commit --amendを使う方法もあります。

複数のコミットを1つにまとめたい

1
2
3
4
5
# 3つのコミットを取り消す
git reset --soft HEAD~3

# まとめて1つのコミットにする
git commit -m "機能Aの実装"

ステージングをやり直したい

1
2
3
4
5
# すべてのステージングを取り消す(作業ツリーは維持)
git reset HEAD

# 特定のファイルのステージングを取り消す
git reset HEAD <ファイル名>

git resetの注意点

注意点 説明
履歴の改変 git resetは履歴を改変するため、プッシュ済みのコミットには使用しないでください
強制プッシュが必要 リモートにプッシュ済みの場合、git push --forceが必要になります
チームへの影響 共有ブランチで使用すると、他のメンバーに影響を与えます
--hardの危険性 変更が完全に失われるため、実行前に必ず確認してください

git revert - 安全なコミットの打ち消し

git revertは、指定したコミットの変更を打ち消す新しいコミットを作成するコマンドです。履歴を改変せずに変更を取り消すため、共有リポジトリでも安全に使用できます。

基本的な使い方

1
2
3
4
5
6
7
8
# 直前のコミットを打ち消す
git revert HEAD

# 特定のコミットを打ち消す
git revert <コミットハッシュ>

# 2つ前のコミットを打ち消す
git revert HEAD~2

実行例を見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 現在のログを確認
$ git log --oneline -3
abc1234 (HEAD -> main) バグのあるコードを追加
def5678 ログイン機能を実装
ghi9012 初期コミット

# 直前のコミットを打ち消す
$ git revert HEAD
[main jkl3456] Revert "バグのあるコードを追加"
 1 file changed, 10 deletions(-)

# ログを確認(打ち消しコミットが追加されている)
$ git log --oneline -4
jkl3456 (HEAD -> main) Revert "バグのあるコードを追加"
abc1234 バグのあるコードを追加
def5678 ログイン機能を実装
ghi9012 初期コミット

元のコミット(abc1234)は履歴に残ったまま、その変更を打ち消す新しいコミット(jkl3456)が作成されました。

複数のコミットを打ち消す

複数のコミットを連続して打ち消す場合は、範囲指定や個別指定が可能です。

1
2
3
4
5
6
7
8
9
# 複数のコミットを個別に打ち消す(それぞれ別のコミットとして)
git revert abc1234 def5678 ghi9012

# 範囲を指定して打ち消す(新しい方から古い方へ)
git revert HEAD~3..HEAD

# コミットを作成せずに打ち消し(後でまとめてコミット)
git revert --no-commit HEAD~3..HEAD
git commit -m "直近3つのコミットを打ち消し"

マージコミットを打ち消す

マージコミットを打ち消す場合は、どちらの親を基準にするかを-mオプションで指定する必要があります。

1
2
# マージコミットを打ち消す(親1を基準に)
git revert -m 1 <マージコミットのハッシュ>

-m 1は、マージ先(通常はmainブランチ)を基準にすることを意味します。

git revertのオプション

オプション 説明
--no-commit / -n コミットを作成せず、変更のみ適用する
--no-edit エディタを開かずにデフォルトメッセージでコミット
--edit / -e コミットメッセージを編集する(デフォルト)
-m <親番号> マージコミットの打ち消し時に親を指定する

コンフリクトが発生した場合

git revert実行時にコンフリクトが発生することがあります。

1
2
3
4
5
6
7
$ git revert abc1234
Auto-merging app.js
CONFLICT (content): Merge conflict in app.js
error: could not revert abc1234... 機能Aを追加
hint: After resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

コンフリクト解消の手順は以下のとおりです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 1. コンフリクトしているファイルを確認
git status

# 2. ファイルを編集してコンフリクトを解消

# 3. 解消したファイルをステージング
git add <ファイル名>

# 4. revertを続行
git revert --continue

# または中断する場合
git revert --abort

git revertの実践的なユースケース

本番環境に影響を与えた変更を素早く取り消す

1
2
3
4
5
6
7
8
# 問題のあるコミットを特定
git log --oneline -5

# 該当コミットを打ち消す
git revert abc1234

# リモートにプッシュ
git push origin main

複数の修正をまとめて打ち消す

1
2
3
4
5
6
7
8
# コミットを作成せずに変更のみ適用
git revert --no-commit HEAD~3..HEAD

# 状態を確認
git status

# まとめて1つのコミットにする
git commit -m "リリース1.2.3の変更を打ち消し"

3つのコマンドの使い分け早見表

状況に応じた最適なコマンドを選択するための早見表です。

状況 コマンド 具体例
ファイルの編集を破棄したい git restore <ファイル> 間違った編集を取り消す
ステージングを取り消したい git restore --staged <ファイル> git addを取り消す
直前のコミットをやり直したい git reset --soft HEAD~1 コミットメッセージを変更
コミットを取り消して変更も破棄したい git reset --hard HEAD~1 不要なコミットを完全削除
プッシュ済みコミットを取り消したい git revert <コミット> 本番のバグ修正を打ち消し
マージを取り消したい(ローカル) git reset --hard HEAD~1 ローカルでのマージをやり直す
マージを取り消したい(リモート済み) git revert -m 1 <コミット> 共有済みマージを打ち消す

ローカルのみ vs リモート共有済み

最も重要な判断基準は、「変更がリモートリポジトリにプッシュ済みかどうか」です。

状態 推奨コマンド 理由
ローカルのみ git reset 履歴を自由に変更可能
リモート共有済み git revert 履歴を改変せず安全に取り消し可能

リモートにプッシュ済みのコミットに対してgit resetを使用すると、git push --forceが必要になり、他のチームメンバーに影響を与える可能性があります。

実践演習:変更取り消しの練習

実際に手を動かして、各コマンドの動作を確認しましょう。

準備:練習用リポジトリの作成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 練習用ディレクトリを作成
mkdir git-undo-practice
cd git-undo-practice

# Gitリポジトリを初期化
git init

# 最初のファイルを作成
echo "# Git変更取り消し練習" > README.md
git add README.md
git commit -m "初期コミット"

# 追加のファイルを作成
echo "console.log('Hello');" > app.js
git add app.js
git commit -m "app.jsを追加"

echo "body { margin: 0; }" > style.css
git add style.css
git commit -m "style.cssを追加"

演習1:git restoreの練習

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# ファイルを編集
echo "console.log('Modified');" >> app.js

# 変更を確認
git status
git diff app.js

# 変更を取り消す
git restore app.js

# 結果を確認
git status
cat app.js

期待される結果:app.jsの変更が取り消され、元の内容に戻ります。

演習2:git reset –softの練習

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 新しいコミットを作成
echo "function test() {}" >> app.js
git add app.js
git commit -m "テスト関数を追加"

# ログを確認
git log --oneline -3

# コミットを取り消す(変更はステージング済みのまま)
git reset --soft HEAD~1

# 結果を確認
git log --oneline -2
git status

期待される結果:コミットが取り消され、変更はステージングエリアに残っています。

演習3:git revertの練習

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# まずコミットをやり直す
git commit -m "テスト関数を追加(やり直し)"

# ログを確認
git log --oneline -3

# コミットを打ち消す
git revert HEAD

# 結果を確認
git log --oneline -4
cat app.js

期待される結果:新しい打ち消しコミットが作成され、app.jsは元の状態に戻ります。

トラブルシューティング

git reset –hardで消した変更を復元したい

git reflogを使用すると、過去のHEAD位置を確認できます。

1
2
3
4
5
6
7
8
9
# reflogで過去の状態を確認
git reflog

# 出力例
abc1234 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
def5678 HEAD@{1}: commit: 消えた変更

# 復元する
git reset --hard def5678

ただし、reflogの記録は一定期間(デフォルト90日)で削除されるため、早めに対処してください。

git revert中にコンフリクトが発生して中断したい

1
2
# revertを中断して元の状態に戻る
git revert --abort

どのコマンドを使うべきかわからない

以下のフローチャートを参考にしてください。

flowchart TD
    A[変更を取り消したい] --> B{コミット前の変更?}
    B -->|Yes| C[git restore を使用]
    B -->|No| D{ローカルのコミット?}
    D -->|Yes| E[git reset を使用]
    D -->|No| F{リモートにプッシュ済み?}
    F -->|Yes| G[git revert を使用]

まとめ

本記事では、Gitの変更取り消しコマンドであるgit restoregit resetgit revertについて解説しました。

コマンド 用途 履歴への影響 安全性
git restore 作業ツリー・ステージングの取り消し なし 高い
git reset --soft コミットの取り消し(変更維持) あり 中程度
git reset --mixed コミット+ステージングの取り消し あり 中程度
git reset --hard すべての取り消し あり 低い
git revert 打ち消しコミットの作成 なし(追加のみ) 高い

最も重要なポイントは、リモートにプッシュ済みのコミットにはgit revertを使用することです。git resetは履歴を改変するため、ローカルでの作業にのみ使用してください。

これらのコマンドを適切に使い分けることで、開発中のミスを素早く修正し、チーム開発においても安全に変更を取り消すことができるようになります。

参考リンク