はじめに
Webエンジニアになって2年目。そろそろ、add
、copmmit
、push
、pull
、あと、ブランチ切ること以外も覚えたいなと思い、Gitでずっと気になってた各種**「元に戻す操作」**を勉強がてら記事を書こうと思いました。
Gitの操作をしていると、失敗して元に戻したい時は結構多いんじゃないかなーと思います。戻したい場面はいくつかあると思います。
今回は、過去の特定のコミットまで戻りたいと思った時の対処について書いていこうと思います。
過去のコミット時点でのファイル内容を取得
practice.txt
に追加分1
を記入し、コミットし、追加分2
を記入してはコミットするを5回繰り返しました。
練習用のテキストファイル
追加分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
...
ファイルの中身は、変わってません。
練習用のテキストファイル
追加分1
追加分2
追加分3
追加分4
追加分5
つまり、先ほどの$ git reset d88d5ff
を実行したことによって、コミットログは消えましたが、ワーキングディレクトリには、最新コミットまでの内容が入っていました。
$ 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
までのコードもコミット履歴もなくなりました。
練習用のテキストファイル
追加分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
コミットログもファイルも元に戻っていることがわかります。
練習用のテキストファイル
追加分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を使ったさまざまな元に戻す方法の記事をまとめています。ぜひ、見てください。