LoginSignup
8
6

More than 5 years have passed since last update.

元に戻すgitコマンド その5(過去の特定のコミットまで戻りたいと思った時 編)

Posted at

はじめに

Webエンジニアになって2年目。そろそろ、addcopmmitpushpull、あと、ブランチ切ること以外も覚えたいなと思い、Gitでずっと気になってた各種「元に戻す操作」を勉強がてら記事を書こうと思いました。

Gitの操作をしていると、失敗して元に戻したい時は結構多いんじゃないかなーと思います。戻したい場面はいくつかあると思います。

今回は、過去の特定のコミットまで戻りたいと思った時の対処について書いていこうと思います。

過去のコミット時点でのファイル内容を取得

practice.txt追加分1を記入し、コミットし、追加分2を記入してはコミットするを5回繰り返しました。

practice.txt
練習用のテキストファイル

追加分1
追加分2
追加分3
追加分4
追加分5

ついでにリモートにpushして、ログを見て見ました。

$ git log --oneline

136f0da (HEAD -> master, origin/master) add code 5
c372ce4 add code 4
e1caddd add code 3
f146df3 add code 2
d88d5ff add code

追加分1のコードだけを残したいと思います。git show コミットIDで確認して見ましょう。

$ git show d88d5ff
commit d88d5ff1283aa86432ab3d45436be149f25e25be
Author: name
Date:   Fri Jun 1 04:04:46 2018 +0000

    add code

diff --git a/practice.txt b/practice.txt
index ff49fb7..7d2e919 100644
--- a/practice.txt
+++ b/practice.txt
@@ -1 +1,3 @@
-練習用のテキストファイル
\ No newline at end of file
+練習用のテキストファイル
+
+追加分1

追加分2~5を記入する前の状態に戻りたい時は、git reset コミットIDで戻れます。

$ git reset d88d5ff
Unstaged changes after reset:
M       practice.txt

下の図を見てください。

上の図は、コミットまでに必要な2段階手順の模式図1です。

この図を見ながら、説明します。ワーキングディレクトリとステージングエリアの状態と、コミット履歴を確認します。

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   practice.txt

no changes added to commit (use "git add" and/or "git commit -a")

$ git log --oneline
d88d5ff (HEAD -> master) add code
...

ファイルの中身は、変わってません。

practice.txt
練習用のテキストファイル

追加分1
追加分2
追加分3
追加分4
追加分5

つまり、先ほどの$ git reset d88d5ffを実行したことによって、コミットログは消えましたが、ワーキングディレクトリには、最新コミットまでの内容が入っていました。

practice.txt
$ git diff
diff --git a/practice.txt b/practice.txt
index 7d2e919..d9c1c95 100644
--- a/practice.txt
+++ b/practice.txt
@@ -1,3 +1,7 @@
 練習用のテキストファイル

 追加分1
+追加分2
+追加分3
+追加分4
+追加分5
\ No newline at end of file

git diffしたら、追加分1はコミットしてある状態とわかりました。

一度、コミットしてlogを見て見ましょう。

$ git add practice.txt
$ git commit -m "add code2~5"

$ git log --oneline
c2d276a (HEAD -> master) add code2~5
d88d5ff add code

ファイルの中身はまったく同じなのですが、最新のコミットIDが、136f0daだったのに、c2d276aに変わってしまいました。

なぜかというと、コミットIDがファイルの変更だけでなく、コミット時刻やAuthorなど、様々なパラメータの情報から計算された値だからです。そのため、コミット時刻やAuthorなどが違う今回のコミットと、前回のコミットは、全く違うコミットとして扱われます。

そこで以下のことに注意してください。

注意すること

gitで元に戻す操作は、これまでのコミットの履歴を書き換えることになります。すでに取り込まれたコミットを書き換えるということは

  • コミットメッセージ
  • コミットした人
  • リビジョン番号(コミットID)
  • 変更日時
  • 変更ファイル

などが書き換わります。ずっと一人開発のプトジェクトであれば、そこまで大きな問題にはならないと思いますが、チーム開発の場合は大きな問題となり得る行為なので、仮にやる際はメンバー合意の上、十分注意して行ってください。

git reset その他のオプション

git resetには、以下のようなオプションがあります。

オプション 結果
--soft 戻した分のコミット内容をインデックスに反映する
--mixed オプション無しと同じ動作で、戻した分のコミット内容だけワーキングディレクトリに反映する
--hard ワーキングディレクトリもコミット履歴も完全削除

試してみようgit reset --hard

このコマンドは、ワーキングディレクトリやコミット履歴まで消去してしまうとても恐ろしいgitコマンドです。

$ git reset --hard コミットID

これまでのを例に出すと、追加分1までのコードもコミット履歴もなくなりました。

practice.txt
練習用のテキストファイル

追加分1

ちなみに、このままでは、リモートレポジトリにpushできません。リモートレポジトリは、ローカルのgit resetは行われていないので、削除された分を反映しべきかどうかがわからないいわゆる、Non Fast Forward 問題でpushできないとなります。

通常ならgit fetchしてgit mergeしてコンフリクト消せば問題なくpushできるとは思いますが、今のローカルの状態を強制的に反映させることもできます。それが、フォースプッシュです。

git push origin master -f

これで、ローカルで消えたコミット履歴までリモートに反映させてしまいました。このファイル上書き感は、FTP育ちのWEB制作な自分にとっては煩わしくなくて好きなのですが、危険性も考えた上で行ってください。少なくとも、チーム開発においては、勝手には絶対やらない方がいいと思います。

git resetを元に戻す場合

間違って、リセットしちゃった場合も考えて、それの戻し方も説明していきます。git reflogというものを使うと、reset --hardとかも含めた復元をしてくれます。

$ git feflog

d88d5ff (HEAD -> master, origin/master) HEAD@{0}: reset: moving to d88d5ff
c349449 HEAD@{1}: pull origin master: Merge made by the 'recursive' strategy.
7a2ed7d HEAD@{2}: commit: add code2~5
d88d5ff (HEAD -> master, origin/master) HEAD@{3}: reset: moving to d88d5ff
c2d276a HEAD@{4}: commit: add code2~5
d88d5ff (HEAD -> master, origin/master) HEAD@{5}: reset: moving to d88d5ff
136f0da HEAD@{6}: commit: add code 5
c372ce4 HEAD@{7}: commit: add code 4
e1caddd HEAD@{8}: commit: add code 3
f146df3 HEAD@{9}: commit: add code 2
d88d5ff (HEAD -> master, origin/master) HEAD@{10}: commit: add code

ためしに、最初にgit resetをした136f0daまで戻ってみましょう。そして、logを見て見ましょう。リモートはd88d5ffで止まっていることがわかります。

$ git reset --hard 136f0da
HEAD is now at 136f0da add code 5

$ git log --oneline
136f0da (HEAD -> master) add code 5
c372ce4 add code 4
e1caddd add code 3
f146df3 add code 2
d88d5ff (origin/master) add code

コミットログもファイルも元に戻っていることがわかります。

practice.txt
練習用のテキストファイル

追加分1
追加分2
追加分3
追加分4
追加分5

なので、間違えてgit resetをしてしまっても、焦らず、git reflogしてgit reset --hardして戻りましょう。
でも基本的には、間違えてgit resetしないことが望ましいですね。

ファイル内容は戻して、コミット履歴を消さない方法(git revert

git resetはコミット履歴を消してしまうため、チーム開発では使わない方がいいと思います。

この場合には、git revert コミットIDが有効なコマンドになります。

revertコマンドを実行すると、エディタが開き、revertコミットのコミットメッセージを編集することができます。

ワーキングディレクトリ内のコードの内容は指定したIDまでの内容に戻しましたが、コミット履歴までは消さないのでチーム開発には有効だと思います。

図解は、

このslide shareの13ページ目が参考になりました。

以上です。

親記事(まとめ記事)

この記事の親記事(まとめ記事?)がありまして、そこでは、gitを使ったさまざまな元に戻す方法の記事をまとめています。ぜひ、見てください。

参考

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6