皆さん、こんにちは!
エンジニアアイドル「ハックガールズ」の堤沙也と濱ヶ崎美季です。
私たちと一緒にGitを学ぶこの連載、第5回は、誰もが一度は経験する
「今のGit操作をなかったことにしたい!」
という思いを実現するためのコマンドを紹介します。
元に戻したい!
第4回まででGitの基本的なフローは大まかに理解できたかと思います。
Gitの使い方に慣れてきて、流れるようにコマンドを打っていると、うっかり意図しない変更を進めてしまうことを、誰しも一度は経験するのではないでしょうか。
そんな「今の変更はなかったことにしたい!」「昨日の更新はなかったことにして、一昨日の状態に戻したい!」といった思いを叶える方法が、Gitにはたくさんあります。
さまざまなコマンドのうち、代表的なものを紹介していきます。
今回紹介するコマンドは次の通りです。
- git clean
- git checkout
- git revert
- git reset(--hard / --soft)
作業ディレクトリを元に戻したい!
- 上司:
- 今追加してもらってる機能、表示用の画像を追加してくれないかな?
- あなた:
- それならすぐできるので、対応しますよ! 画像を作業ディレクトリに上げて……
- (1分後)
- 上司:
- やっぱりさっきの追加画像、なくて大丈夫!
- あなた:
- !
このように作業ディレクトリで何らかのファイルを新規作成(追加)した際に、Git管理対象とする前に、それらのファイルを作業ディレクトリから削除するコマンドを紹介します。
git clean
touch sample.txt
git clean -n (削除対象のファイルの確認)
git clean -f sample.txt
これでsample.txtは削除されます。
-fオプションのあとにファイルを引数で指定しなければ、-nオプションで確認したすべてのファイルが削除されます。Git管理下にないファイルの削除なので、復元はできないので慎重に!
図1 git clean -f
illustration by Youko Watanabe
git addを戻したい!
- 上司:
- 今追加してもらってる機能、表示文言だけ修正してくれないかな?
- あなた:
- それならすぐできるので、対応しますよ! sample.txtの文言を修正して git addと……
- (1分後)
- 上司:
- やっぱりさっきの修正なくて大丈夫!
- あなた:
- !
続いては、作業ディレクトリで何らかのファイルに変更を加え(新規作成でも同様)、git addしたあとに、それらの変更を元に戻すコマンドを紹介します。
git checkout
git checkoutは第4回でも紹介しましたが、コマンドの引数として渡す値によって異なる動きをします。
最初は混乱するかもしれませんが、覚えてしまうと大変便利なので、是非抑えておきましょう。
前回は、
git checkout(branch)
でブランチの移動コマンドとして紹介しましたが、ここでの用途は、一度git addしてステージングエリアに上がったファイルを作業ディレクトリに引き戻すために使います。
たとえば、
touch sample.txt
git add sample.txt
git checkout sample.txt
このようにコマンドを実行すると、途中でsample.txtをgit addした事実はなかったことになります。sample.txtがあらかじめ存在するファイルで、その中身を書き換えていた場合、書き換えた事実もなかったことになります。
図2 git checkout
illustration by Youko Watanabe
git commitを戻したい!
- 上司:
- 今追加してもらってる機能、表示文言だけ修正してくれないかな?
- あなた:
- それならすぐできるので、対応しますよ! sample.txtの文言を修正してgit add、これでOKだからgit commitと……
- (5分後)
- 上司:
- やっぱりさっきの修正なくて大丈夫!
- あなた:
- !
続いては、作業ディレクトリで何らかのファイルに変更を加え(新規作成でも同様)、git add、さらにはgit commitまでしたあとに、それらの変更を元に戻すコマンドを紹介します。
git revert
git revert は、commitを打ち消すためのコマンドです。
打ち消すと言っても、revertは「コミットをなかったものとする」のではなく、特定のコミットによって加えられた「変更元に戻すためのを新しいコミットを追加する」ことにより、事実上元に戻すという動きをします。
動作を確認してみましょう。
touch sample.txt
git add sample.txt
git commit sample.txt
git revert(commit hash)
実行後にgit logでcommit履歴を見ると
commit 11baa6c0a5fdxxxxxxxxxxxxxxx
Author: hanako
Date: Tue Mar 31 16:17:13 2014 +0900
revert "READMEを最新に修正"
This reverts commit 1ebd2768edxxxxxxxxxxxxxxx.
commit 1ebd2768edxxxxxxxxxxxxxxx
Author: hanako
Date: Tue Mar 31 16:14:13 2014 +0900
READMEを最新に修正
このように「commit 1ebd2768edxxxxxxxxxxxxxxx を打ち消したcommmit」として、新規commitが追加されているのが確認できると思います。
図3 git revert
illustration by Youko Watanabe
git reset
git resetは、1つ前のgit revertとは違って、「事実上戻す」のではなく「本当に戻す」挙動をします。
revertは「事実」を残したまま進んでいきますが、resetは「事実」自体がなくなるということなので、復元不可能になります(※リモートリポジトリにcommitが残っている時点では復元できます)。
revertより危険な方法ですが、commit履歴が煩雑になる事を避けることができるという意味ではメリットがある方法です。
但し、複数人で共同開発している際、他の人のcommitを許可なくresetし、リモートリポジトリにまでpushしてしまうと取り返しがつかないので扱いには十分注意しましょう。
図4 git reset
illustration by Youko Watanabe
git resetでも、与えるオプションで挙動が違うので注意が必要です。
git reset --soft HEAD^
※HEAD^とは直前のcommitのこと。HEAD^^とすれば、さらに1つ前のcommitを指します。
--softオプションは、作業ディレクトリの内容はそのままで、コミットだけを取り消すときに使います。このコマンドを実行しても作業ディレクトリ・ステージングエリアの状態には一切影響を与えません。その代わり、git logで確認できるcommitの状態が変わります。引数にHEAD^を指定した場合は、直前のcommitがgit logで見ると消えているはずです。
git reset --hard HEAD^
作業ディレクトリの内容を取り消し、コミットも取り消すときに使います。Git管理下にあるすべてに影響を与えます。
touch sample.txt sample2.txt
git add sample.txt
git reset --hard HEAD^
この場合、git addでGit管理下に入れたsample.txtは抹消され、Git管理下にまだ入っていなかったsample2.txtには影響を与えません。それと同時に、--softでの実行時と同じように、git logで確認できるcommitの状態は、直前のcommitが消えた状態になります。