git revertとgit resetの違い
間違ったコミットを修正するコマンドに、git revertとgit resetがあります。それぞれの違いは以下のようになっています。
git revertは指定したコミットの内容を打ち消して、新しいコミットを作成します。
指定したコミット自体はコミット履歴から消さず、そのまま残せることがポイントです。
一方、git resetは指定したコミットを削除します。最新のコミットから指定したコミットより上のコミット履歴がごっそりなくなります。
ポイントをまとめると以下のようになります。
コマンド | コミット履歴 | 内容 |
---|---|---|
git revert | 消えない | 指定したコミットを打ち消した新しいコミットを作成する。 |
git reset | 消える | 指定したコミットまで戻る。 |
git revertとgit resetの処理の違いのイメージ
git revertとgit resetの処理の違いをイメージにすると以下のようになります。
git revert
例えば、以下のようにコミット履歴「A~D」があり、最新のコミット「D」にmainブランチがあるとします。
コミット「D」を取り消したいなというときgit revertを使うと次のようになります。
#コミット履歴(A〜D)
A---B---C---D
main
#コミットDの作業を打ち消す
$ git revert D
A---B---C---D---F
main
新しいコミット「F」がつくられ、コミットが1つ前に進むのが、git revertです。
git reset
同じく、以下のようにコミット履歴「A~D」があり、最新のコミット「D」にmainブランチがあるとします。
過去のコミット「D」を取り消したいなというときgit resetを使うと次のようになります。
#コミット履歴(A〜D)
A---B---C---D
main
#コミットDの作業を打ち消す
$ git reset C
A---B---C
main
指定したコミット「C」まで戻り、取り消したかったコミット「D」がコミット履歴から消去されるのがgit resetです。
以下でそれぞれの実際の使い方を実例を交えて解説しています。
git revertの使い方
コマンド
git revertは指定したコミットの内容を打ち消して、新しいコミットを作成する処理です。
git revert <打ち消したいコミット>
実行後にエディタが立ち上がるので、コミットメッセージを入力します。
なお、revertとは「元に戻す」という意味です。
git revertの処理内容のイメージ
例えば以下のようなA~Bのコミット履歴があるときに、コミットBの内容を打ち消したいとします。
#コミット履歴(A〜D)
A---B---C---D
master
その場合「git revert B」を実行します。
#コミットBの作業を打ち消す
$ git revert B
A---B---C---D---D'
master
すると、コミットBの編集内容を削除した、状態の新しいコミットD’が生成されます。
コミット履歴の中でコミットBがなくなっていないのがポイントです。
実例
例えば以下のようなコミットログがあるとします。
$ git log --oneline
0701d9d (HEAD -> test, second/aa, origin/aa, aa) [U]docker-compose add webpack port 3035
a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
6adca49 [A]destroyメソッド & Modal追加
この中で、3つ目のコミット「6adca49」の内容を取り消したい場合は以下のようにします。
$ git revert 6adca49
すると、エディタが起動し次のような内容が表示されます。
Revert "[A]destroyメソッド & Modal追加"
This reverts commit 6adca498b4dbdc4df8c18fe15a713507b80222d6.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch test
# Your branch is up to date with 'origin/aa'.
#
# Changes to be committed:
# modified: app/controllers/api/pj1/clients_controller.rb
# deleted: app/javascript/components/Modal.vue
# modified: app/javascript/components/clients/Clients.vue
# modified: config/routes.rb
#
VScodeの場合はVScode内で新しいファイルが起動します。
もしvimが起動した場合の使い方は以下をご参考ください。
【vi(vim)とは何か?】vimテキストエディタを使う方法とコマンド一覧
一番上に表示されている「Revert “[A]destroyメソッド & Modal追加”」の部分が、git revert後に作成したコミットに付与されるコミットメッセージです。
必要に応じてこの内容を編集してください。
なお、#の部分はコメントアウトなので無視して問題ありません。
今回はコミットメッセージを「Revertで”[A]destroyメソッド & Modal追加”を打ち消しました。」として保存して閉じます。
すると以下のように実施した内容が表示されます。
$ git revert 6adca49
Removing app/javascript/components/Modal.vue
[test f5351fe] Revertで"[A]destroyメソッド & Modal追加"を打ち消しました。
4 files changed, 4 insertions(+), 154 deletions(-)
delete mode 100644 app/javascript/components/Modal.vue
コミット履歴を確認します。
$ git log --oneline
f5351fe (HEAD -> test) Revertで"[A]destroyメソッド & Modal追加"を打ち消しました
。
0701d9d (second/aa, origin/aa, aa) [U]docker-compose add webpack port 3035
a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
6adca49 [A]destroyメソッド & Modal追加
指定した「6adca49」を打ち消したコミットとして新たに「f5351fe」が最新のコミットになっていることがわかります。
このコミットの内容を見てみましょう。
実行内容の詳細はgit show <コミット番号>
で確認できます。
$ git show f5351fe
commit f5351fe4e4634f66c15e1d819222659d05967348 (HEAD -> test)
Author: author <example@gmail.com>
Date: Thu Jan 27 18:55:10 2022 +0900
Revertで"[A]destroyメソッド & Modal追加"を打ち消しました。
This reverts commit 6adca498b4dbdc4df8c18fe15a713507b80222d6.
diff --git a/app/controllers/api/pj1/clients_controller.rb b/app/controllers/api/pj1/clients_controller.rb
index 78ac0a1..9b60278 100644
--- a/app/controllers/api/pj1/clients_controller.rb
+++ b/app/controllers/api/pj1/clients_controller.rb
@@ -1,5 +1,5 @@
class Api::Pj1::ClientsController < ApiController
- before_action :set_client, only: [:show, :update, :destroy]
+ before_action :set_client, only: [:show, :update]
#例外処理
rescue_from Exception, with: :render_status_500
このように、指定したコミットで行っていた内容が全て削除されていることがわかります。
エディタを立ち上げない方法
わざわざエディタを起動したくない場合は、「–no-edit」オプションを使うことでデフォルトで設定されるコミットメッセージの冒頭に「Revert」がついたコミットメッセージを採用することができます。
git revert --no-edit
実例
例えば以下のようなコミットログがあるとします。
$ git log --oneline
0701d9d (HEAD -> test, second/aa, origin/aa, aa) [U]docker-compose add webpack port 3035
a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
6adca49 [A]destroyメソッド & Modal追加
この中で、3つ目のコミット「6adca49」の内容を取り消し、かつエディタを起動しない場合は以下のようにします。
$ git revert 6adca49 --no-edit
Removing app/javascript/components/Modal.vue
[test 2d9d637] Revert "[A]destroyメソッド & Modal追加"
Date: Thu Jan 27 19:05:07 2022 +0900
4 files changed, 4 insertions(+), 154 deletions(-)
delete mode 100644 app/javascript/components/Modal.vue
コミット履歴を確認します。
$ git log --oneline
2d9d637 (HEAD -> test) Revert "[A]destroyメソッド & Modal追加"
0701d9d (second/aa, origin/aa, aa) [U]docker-compose add webpack port 3035
a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
6adca49 [A]destroyメソッド & Modal追加
指定した「6adca49」を打ち消したコミットとして新たに「2d9d637」が最新のコミットになっていることがわかります。
コミットメッセージもデフォルトの「Revert “[A]destroyメソッド & Modal追加”」となっています。
実行内容の詳細はgit show <コミット番号>
で確認できます。
$ git show 2d9d637
commit 2d9d6370b4e8b805c12dc3b6d11b6bdf7b3e984b (HEAD -> test)
Author: author <example@gmail.com>
Date: Thu Jan 27 18:55:10 2022 +0900
Revertで"[A]destroyメソッド & Modal追加"を打ち消しました。
This reverts commit 6adca498b4dbdc4df8c18fe15a713507b80222d6.
diff --git a/app/controllers/api/pj1/clients_controller.rb b/app/controllers/api/pj1/clients_controller.rb
index 78ac0a1..9b60278 100644
--- a/app/controllers/api/pj1/clients_controller.rb
+++ b/app/controllers/api/pj1/clients_controller.rb
@@ -1,5 +1,5 @@
class Api::Pj1::ClientsController < ApiController
- before_action :set_client, only: [:show, :update, :destroy]
+ before_action :set_client, only: [:show, :update]
#例外処理
rescue_from Exception, with: :render_status_500
このように、指定したコミットで行っていた内容が全て削除されていることがわかります。
現在編集中のファイルはrevertできない
なお、現在編集していてるファイルがある場合はgit revertできません。
ステージ前(git add前)のファイルがある場合に表示されるエラー
$ git revert 6adca49
error: Your local changes to the following files would be overwritten by merge:
app/controllers/api/pj1/clients_controller.rb
Please commit your changes or stash them before you merge.
Aborting
fatal: revert failed
git add & git commitでコミットするか、git stashで一時的に変更を隠すかのどちらかをする必要があります。
ステージ後(コミット前)のファイルがある場合に表示されるエラー
$ git revert 6adca49
error: your local changes would be overwritten by revert.
hint: commit your changes or stash them to proceed.
fatal: revert failed
git commitでコミットするか、git stashで一時的に変更を隠すかのどちらかをする必要があります。
conflictの発生要因と対処法
現在のコミットと指定したコミットで同じ個所を修正している場合、コンフリクト(conflict)が発生します。
コンフリクトの発生例
$ git revert @@{12} --no-edit
Auto-merging style.css
CONFLICT (content): Merge conflict in style.css
error: could not revert 9390048... add line3
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'
対処法
主な対処法は以下の2つです。
- –abortオプションを使う。
- 手動で修正してコミットする。
–abortオプションを使う
git revert --abort
を実行するとgit revertの処理自体をキャンセルしなかったことにできます。これで、revertコマンドを実行していない状態に戻ります。
手動で修正してコミットする
もう一つはエディタを開いて、手動で内容を修正してコミットする方法です。
#conflictが発生しているファイルを確認する
$ git st
On branch ft2
You are currently reverting commit 2767830.
(fix conflicts and run "git revert --continue")
(use "git revert --skip" to skip this patch)
(use "git revert --abort" to cancel the revert operation)
Unmerged paths:
(use "git restore --staged <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: style.css
#vimエディタで修正する
vi style.css
#add & commit
git add <対象ファイル> && git commit -m "メッセージ"
マージ(merge)したコミットを打ち消す方法
-mオプションを使うとgit mergeしてあるコミットを打ち消すこともできます。
その際は、現在いるブランチかトピックブランチのどちらのコミットを採用するかを、1か2の番号で指定する必要があります。
git revert -m <パターン番号> <マージコミット>
なお、「-m」オプションは「–mainline」のショートオプションです。
パターン番号は1と2の二つです。
- 1: 現在のブランチ
- 2: 派生しているブランチ
実行イメージ
以下のようなコミット履歴がある場合、Hがマージコミットになります。
#コミット履歴(Hがマージコミット)
A---B---C
/ \
D---E---F---G---H
master
この状態でコミットHの作業を打ち消して、新しいコミットを作ることができます。
その際、採用するコミットをメインのブランチとする場合は-mオプションの引数で「1」を指定します。
$git revert -m 1 H
すると、コミット履歴は以下のようになります。
A---B---C
/ \
D---E---F---G---H---H'
master
パターン番号で「1」を指定しているので、新しく生成されたコミット「H’」の内容は「G」と同じになります。
なお、「2」を指定した場合は「C」と同じになります。
マージコミットをrevertする時の注意点
マージコミットをrevertした場合、revertしたブランチのコミット履歴はmerge済みの状態です。このため、git mergeしようとすると、「Already up-to-date.」と表示されます。
$ git merge topic
Already up-to-date.
git resetの使い方
git resetは現在のブランチの最新のコミットから、指定したコミットの上までをごっそり削除する超強力なコマンドです。
すなわち、指定したコミットまでコミット履歴を遡るということです。
主な使い方は2つあります。
指定したコミットまで戻る
指定したコミットまで戻る場合は引数でコミット番号のみを指定します。
git reset <コミット>
コミットの指定方法には、ハッシュ値のコミット番号やHEAD, @, タグ、HEAD@{n}や@@{n}が使えます。
HEADの省略形である、@ を使うことが一般的です。
また、変更したファイルを残すか、全て削除するかをオプションで指定する必要があります(後述)。
指定したファイルのみ指定したコミットの状態に戻す
指定したファイルのみ指定したコミットの状態に戻す場合は、引数でコミット番号とファイルパスの相対パスを指定します。
git reset <コミット> <ファイルパス>
実例
例えば、「app/controllers/api/pj1/clients_controller.rb」というファイルを現時点より3つ前のコミットの状態に戻したい場合は以下のようにします。
$ git reset @^^^ app/controllers/api/pj1/clients_controller.rb
Unstaged changes after reset:
M app/controllers/api/pj1/clients_controller.rb
git resetの注意点
git resetの注意点は大きく3つあります。
オプションにより変更中のファイルを残すかどうかが変わる
git resetには「–soft」「–mixed」「–hard」の3つのオプションがあり、どれを選択したかで、コミットを遡ったときに、現在のブランチの作業内容を残すかどうかが変わります。(後述)
コミットログから消えてなくなる
1つ目は、指定したコミットまで戻るため、それより新しいコミット履歴がごっそりなくなります。git logで参照しても表示されなくなります。
ただしGitから完全に消えるわけではなく、Gitコマンドの操作履歴にはどんな処理をしたかの記録が残っているので、復活させることはできます。(復活方法については後述しています)
複数人の共同開発レポジトリでは使わない
2つ目はGithubのリモートレポジトリなどで複数人による開発をしている場合には基本的に使ってはいけないということです。
コミット履歴は過去のコミットに全て紐づいています。このため、過去のコミット履歴が変わると、その後のコミットのコミット番号も変わります。
git resetでコミットを戻してリモートレポジトリにプッシュしてしまうと、他の人たちのコミット履歴とズレてエラーが発生します。
もちろんgit resetを使ってはいけないというわけではありません。git resetは非常に便利なコマンドなので、自分のローカルのみの作業やリモートレポジトリより先に進んでいるコミットで使用する分には問題ありません。
git resetの重要なオプション
git resetには3つの重要なオプションがあります。「–soft」「–mixed」「–hard」です。
それぞれで何をどこまで消すのかが変わってきます。まとめると以下のようになっています。
オプション | ステージ前ファイル (git add前) | インデックスされたファイル (git add後、commit前) | 未追跡 (untracked) |
---|---|---|---|
–soft | 残る | 残る | 残る |
–mixed(デフォルト) | 残る | 削除 | 残る |
–hard | 削除 | 削除 | 残る |
インデックスファイルやワークツリーなどすべてを消去し、真っさらな過去に戻るりたい場合は「–hard」を使用します。
現在の作業中のファイルを修正したい場合は残したいファイルに応じて「–mixed」か「–soft」を使います。
オプション無しでgit resetを実行した場合は「–mixed」オプションを付けた処理と同じになります。
なお、いずれのオプションにおいても未追跡(untracked)のファイルは残ります。
–softオプション
–softオプションはgit resetを実行する時点でのはファイルの変更内容を全て残すコマンドです。
現在作業中の内容が残り、git resetしたことで生じた差分も残ります。
git reset --soft <コミット>
git reset --soft <コミット> <ファイルパス>
git reset –softの実例
例えば、git statusが以下のように、git addしたファイル「docker-compose.yml」とgit add前のファイル「clients_controller.rb」、未追跡のファイル「test.html.erb」の3つがあるとします。
$ git status
On branch test
Your branch is ahead of 'origin/aa' by 1 commit.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: docker-compose.yml
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/controllers/api/pj1/clients_controller.rb
Untracked files:
(use "git add <file>..." to include in what will be committed)
app/views/layouts/test.html.erb
コミットの履歴は以下のようになっています。
$ git log --oneline
c9c53c7 (HEAD -> test) comment out
0701d9d (second/aa, origin/aa, aa) [U]docker-compose add webpack port 3035
a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
6adca49 [A]destroyメソッド & Modal追加
61d3b4b [A]ClientEdit.vue
00093d1 [F]ClientNewからClientForm.vueを切り出し
この状態で、3つ前のコミット「6adca49 [A]destroyメソッド & Modal追加」までgit reset –softを使って戻ります。
3つ前のコミットを指定するには「@^^^」を使います。(もしくは、@~~~ やHEAD^^^など)
$ git reset --soft @^^^
実行しても何も表示されません。git logで履歴を確認します。
$ git log --oneline
6adca49 (HEAD -> test) [A]destroyメソッド & Modal追加
61d3b4b [A]ClientEdit.vue
00093d1 [F]ClientNewからClientForm.vueを切り出し
すると、「c9c53c7」「0701d9d」「a90d4ef」の3つのコミットが削除され、現在のブランチの最新のコミットを指すHEAD->が、指定したコミット「6adca49」になっているのがわかります。
この状態でgit statusを確認すると以下のようになります。
$ git st
On branch test
Your branch is behind 'origin/aa' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: app/controllers/api/pj1/clients_controller.rb
modified: config/initializers/content_security_policy.rb
modified: docker-compose.yml
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/controllers/api/pj1/clients_controller.rb
Untracked files:
(use "git add <file>..." to include in what will be committed)
app/views/layouts/test.html.erb
もともとあった、git addしたファイル「docker-compose.yml」とgit add前のファイル「clients_controller.rb」、未追跡のファイル「test.html.erb」はそのままです。
新たに、git add後のファイルに「content_security_policy.rb」と「clients_controller.rb」が追加されていることがわかります。
つまり、コミットを戻しはしたけど、ファイルの変更内容はgit reset –softを実行した当時の状態のままで、かつ、コミットを戻ったことで生まれたファイルの変更をステージのファイルとして保持しています。
–hardオプション
–hardオプションはgit resetを実行する時点でのはファイルの変更内容を全て削除して、完全に指定したコミットの状態に戻るコマンドです。(未追跡ファイルは残ります)
git reset --hard <コミット>
git reset --hard <コミット> <ファイルパス>
git reset –hardの実例
例えば、git statusが以下のように、git addしたファイル「docker-compose.yml」とgit add前のファイル「clients_controller.rb」、未追跡のファイル「test.html.erb」の3つがあるとします。
$ git status
On branch test
Your branch is ahead of 'origin/aa' by 1 commit.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: docker-compose.yml
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/controllers/api/pj1/clients_controller.rb
Untracked files:
(use "git add <file>..." to include in what will be committed)
app/views/layouts/test.html.erb
コミットの履歴は以下のようになっています。
$ git log --oneline
c9c53c7 (HEAD -> test) comment out
0701d9d (second/aa, origin/aa, aa) [U]docker-compose add webpack port 3035
a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
6adca49 [A]destroyメソッド & Modal追加
61d3b4b [A]ClientEdit.vue
00093d1 [F]ClientNewからClientForm.vueを切り出し
この状態で、3つ前のコミット「6adca49 [A]destroyメソッド & Modal追加」までgit reset –hardを使って戻ります。
3つ前のコミットを指定するには「@^^^」を使います。(もしくは、@~~~ やHEAD^^^など)
$ git reset --hard @^^^
HEAD is now at 6adca49 [A]destroyメソッド & Modal追加
git resetが実行され、最新のコミットを指すHEADが移動しました。git logで履歴を確認します。
$ git log --oneline
6adca49 (HEAD -> test) [A]destroyメソッド & Modal追加
61d3b4b [A]ClientEdit.vue
00093d1 [F]ClientNewからClientForm.vueを切り出し
すると、「c9c53c7」「0701d9d」「a90d4ef」の3つのコミットが削除され、現在のブランチの最新のコミットを指すHEAD->が、指定したコミット「6adca49」になっているのがわかります。
この状態でgit statusを確認すると以下のようになります。
$ git status
On branch test
Your branch is behind 'origin/aa' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
Untracked files:
(use "git add <file>..." to include in what will be committed)
app/views/layouts/test.html.erb
nothing added to commit but untracked files present (use "git add" to track)
もともとあったgit addしたファイル「docker-compose.yml」とgit add前のファイル「clients_controller.rb」がなくなっていることがわかります。
なおGit未追跡のファイル「test.html.erb」は–hardをしてもそのまま残ります。
オプション無し(–mixedオプション)
git resetのオプションが無い場合は–mixedオプションと同じ処理になります。
git resetを実行する時点での最新のコミットとリセット後のファイルを比較して差分が生じたファイルをgit add前(ステージ前)として残します。(未追跡ファイルは残ります)
git reset <コミット>
git reset <コミット> <ファイルパス>
git reset (–mixed)の実例
例えば、git statusが以下のように、git addしたファイル「docker-compose.yml」とgit add前のファイル「clients_controller.rb」、未追跡のファイル「test.html.erb」の3つがあるとします。
$ git status
On branch test
Your branch is ahead of 'origin/aa' by 1 commit.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: docker-compose.yml
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/controllers/api/pj1/clients_controller.rb
Untracked files:
(use "git add <file>..." to include in what will be committed)
app/views/layouts/test.html.erb
コミットの履歴は以下のようになっています。
$ git log --oneline
c9c53c7 (HEAD -> test) comment out
0701d9d (second/aa, origin/aa, aa) [U]docker-compose add webpack port 3035
a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
6adca49 [A]destroyメソッド & Modal追加
61d3b4b [A]ClientEdit.vue
00093d1 [F]ClientNewからClientForm.vueを切り出し
この状態で、3つ前のコミット「6adca49 [A]destroyメソッド & Modal追加」までオプション無しのgit resetを使って戻ります。
3つ前のコミットを指定するには「@^^^」を使います。(もしくは、@~~~ やHEAD^^^など)
$ git reset @^^^
Unstaged changes after reset:
M config/initializers/content_security_policy.rb
git resetが実行され、「Unstaged changes after reset」ステージ前のファイルが生成されたことがわかります。git logで履歴を確認します。
$ git log --oneline
6adca49 (HEAD -> test) [A]destroyメソッド & Modal追加
61d3b4b [A]ClientEdit.vue
00093d1 [F]ClientNewからClientForm.vueを切り出し
すると、「c9c53c7」「0701d9d」「a90d4ef」の3つのコミットが削除され、現在のブランチの最新のコミットを指すHEAD->が、指定したコミット「6adca49」になっているのがわかります。
この状態でgit statusを確認すると以下のようになります。
$ git status
On branch test
Your branch is behind 'origin/aa' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
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: config/initializers/content_security_policy.rb
Untracked files:
(use "git add <file>..." to include in what will be committed)
app/views/layouts/test.html.erb
no changes added to commit (use "git add" and/or "git commit -a")
もともとあったgit addしたファイル「docker-compose.yml」とgit add前のファイル「clients_controller.rb」がなくなっていることがわかります。
代わりに、git add前のファイルに「content_security_policy.rb」が新たに追加されています。
なおGit未追跡のファイル「test.html.erb」は–hardをしてもそのまま残ります。
削除したコミットを復活させる方法
削除したコミットを復活させるコマンド
削除したコミットを復活させるには、git reflogというコマンドと、git resetを合わせて使います。
git reflogとは全てのブランチのGitコマンドの履歴を表示するコマンドです。
各コマンドにはコマンド履歴がHEAD@{n}として割り振られています。
間違ったgit resetを実行したときのHEAD@{n}を指定して、git resetを実行すると、コミットがgit resetを実行する前の状態に戻ります。
#コマンド履歴の一覧を確認
$ git reflog
#コミットを戻す
$ git reset <コマンド履歴を指定>
なお、HEAD@{n}以外にも@@{n}で指定することができます。
注意点:コミット番号とHEAD@{n}は違う
コミット履歴の番号はHEAD@{n}とは異なるものです。nの数値が違っても、コミット番号が同じ場合もあります。
129ba79 (image_tag) HEAD@{4}: checkout: moving from fix to image_tag
129ba79 (image_tag) HEAD@{5}: commit: [A]home.index ホバーの画像切り替え
checkoutしてブランチを移動しただけなど、指しているコミット履歴の内容は同じなので、git resetするときに、git reflogで表示されるコミット番号でも指定できます。
実例
例えば以下のような状態のコミット履歴があるとします。
$ git log --oneline
c9c53c7 (HEAD -> test) comment out
0701d9d (second/aa, origin/aa, aa) [U]docker-compose add webpack port 3035
a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
6adca49 [A]destroyメソッド & Modal追加
61d3b4b [A]ClientEdit.vue
00093d1 [F]ClientNewからClientForm.vueを切り出し
git statusは以下のようになっています。
$ git status
On branch test
Your branch is ahead of 'origin/aa' by 1 commit.
(use "git push" to publish your local commits)
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/controllers/api/pj1/clients_controller.rb
Untracked files:
(use "git add <file>..." to include in what will be committed)
app/views/layouts/test.html.erb
no changes added to commit (use "git add" and/or "git commit -a")
この時に、git reset --soft @^^^
を実行して、以下のようなログとgit statusの状態になったとします。
$ git log --oneline
6adca49 (HEAD -> test) [A]destroyメソッド & Modal追加
61d3b4b [A]ClientEdit.vue
00093d1 [F]ClientNewからClientForm.vueを切り出し
$ git status
On branch test
Your branch is behind 'origin/aa' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: app/controllers/api/pj1/clients_controller.rb
modified: config/initializers/content_security_policy.rb
modified: docker-compose.yml
Untracked files:
(use "git add <file>..." to include in what will be committed)
app/views/layouts/test.html.erb
このgit resetを取り消して元の状態に戻るには、まずgit reflogでgit reset直前のコミット番号を探します。
$ git reflog
6adca49 (HEAD -> test) HEAD@{0}: reset: moving to @^^^
c9c53c7 HEAD@{1}: reset: moving to @^
9e8f510 HEAD@{2}: revert: Revert "[A]destroyメソッド & Modal追加"
c9c53c7 HEAD@{3}: reset: moving to @^
6b26abb HEAD@{4}: revert: Revert "[A]destroyメソッド & Modal追加"
c9c53c7 HEAD@{5}: commit: comment out
0701d9d (second/aa, origin/aa, aa) HEAD@{6}: reset: moving to @^
2d9d637 HEAD@{7}: revert: Revert "[A]destroyメソッド & Modal追加"
0701d9d (second/aa, origin/aa, aa) HEAD@{10}: rebase (finish): returning to refs/heads/test
すると一番上に「6adca49 (HEAD -> test) HEAD@{0}: reset: moving to @^^^」でreset: moving to @^^^
を実行したという記録があるのがわかります。
この直前に戻りたいのでその下のコミット「c9c53c7」もしくは「HEAD@{1}」を指定して、git resetを実行します。
そのコミット以降に編集したコミット前のファイルの変更内容も残したい場合は「–soft」オプションをつけます。
$ git reset HEAD@{1}
Unstaged changes after reset:
M app/controllers/api/pj1/clients_controller.rb
以上でgit resetの取り消しが完了です。
git logを確認すると消したはずのコミットが復活していることがわかります。
$ git log --oneline
c9c53c7 (HEAD -> test) comment out
0701d9d (second/aa, origin/aa, aa) [U]docker-compose add webpack port 3035
a90d4ef [U]content-security-policy(CSP) enable webpack-dev-server
6adca49 [A]destroyメソッド & Modal追加
61d3b4b [A]ClientEdit.vue
00093d1 [F]ClientNewからClientForm.vueを切り出し
git statusを確認すると以下、間違ったgit resetを実行する前の状態に戻っています。
$ git status
On branch test
Your branch is ahead of 'origin/aa' by 1 commit.
(use "git push" to publish your local commits)
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/controllers/api/pj1/clients_controller.rb
Untracked files:
(use "git add <file>..." to include in what will be committed)
app/views/layouts/test.html.erb
no changes added to commit (use "git add" and/or "git commit -a")
以上で復活完了です。