VCS を使ったときのメリットは多々ありますが、そのうちの一つは、いつでも修正内容をもとに戻せることだと思います。
- ローカルで修正した内容をもとに戻したい
- 直前の commit の内容を変更したい
- commit を取り消したい
- git reset --hard を取り消したい
- push したコミットを変更したい
- 参考文献
ローカルで修正した内容をもとに戻したい
$ git checkout -- <pathspec>…
あるいは、
$ git restore <pathspec>…
によって、指定したファイル filename
が修正前の状態に戻ります。
git checkout
git checkout
には大きく分けると2つの責務があり、branch の切り替え、あるいは、working tree 下のファイルの復元 です。
何らかの変更を加えたあとでその変更を取り消したい、というときに使うのはこのうちの後者に該当します。
公式的には git checkout [<tree-ish>] [--] <pathspec>…
を使おう、というのが一番通りが良いと思うのですが、多くの場合は git checkout -- <pathspec>...
を使うことになると思います。具体的には
$ git checkout -- fileA.c
というような形でしょうか。 tree-ish
を省略した上記の形式においては、git の index 領域の fileA.c
の内容で working tree 上のファイル内容を復元する(更新する)、という意味になります。
(tree-ish
を明示的に指定すると、指定した tree-ish
に紐づくファイル内容で、working tree 領域および index 領域の内容が更新されます)
git restore
git checkout
に複数の責務があると分かりづらい、という理由で、git の v2.23 から git switch
と git restore
が導入 されました。
名前の通り、「取り消し」に使用するのは git restore
の方です。
git checkout -- fileA.c
と同じことを git restore
で明示的に行おうとすると、
$ git restore --worktree <pathspec>…
になります。--worktree
は、復元する対象領域を worktree にする、という意味です (index 領域に復元したいのであれば、--staged
を付与する必要があります)。
直前の commit の内容を変更したい
直前に行った commit の内容を変更したいというときは、index 領域に変更内容 (ファイルの追加や削除、コンテンツの変更) を登録 (git add
) した上で、
$ git commit --amend
を使います。コミットメッセージは index 領域とは関係ありませんが、上記コマンド実行時に再指定が可能です。コミットメッセージだけ変更したい、ということであれば、git commit --amend -m <new-commit-msg>
というように CLI で完結できます。
commit を取り消したい
コミットをなかったことにしたい場合は、git reset
を使います。
$ git reset [<mode>] [<commit>]
上記形式での git reset
は、current branch の HEAD をcommit
に変更するとともに、その commit で示されるファイルツリーの内容を working tree、および、index 領域に反映したりしなかったりします。
詳細な動作は mode
の指定で行います。ちなみに、mode
指定を省略すると、--mixed
を指定したものとして解釈されます。
例えば、直前の commit だけ取り消して作業を続行したいということであれば、 git reset HEAD~
だったりを実行することになるでしょう。
mode | 振る舞い |
---|---|
--soft |
working tree も index 領域も変更しない。commit したという事実だけが取り消される。つまり、working tree 上の変更、および、Index 領域上の変更は reset によって失われない。 |
--mixed |
index 領域だけ commit のファイルツリーの内容に置き換え、working tree はそのままとなる。つまり、working tree 上の変更は reset によって失われない。 |
--hard |
working tree も index 領域も commit のファイルツリーの内容に置き換えられる。もう全部きれいにしたい!というときに使う。結果として、取り消した commit の内容は(頑張らないと)見えなくなる。 |
--keep |
コミットを無かったことにしたい、という文脈においては --mixed と同じ。正直、存在理由がよくわからない。Index 領域は commit で置き換えられ、working tree の内容はそのままになる。--soft との違いとしては、reset 実行タイミングにおける working tree 上のファイルに変更が存在しており、かつ、当該ファイルの commit と HEAD に差分がある場合、reset が失敗する。詳細は、 git-reset マニュアルの DISCUSSIONS セクション を読んでくれ。 |
--merge |
マニュアル精読したけどさっぱりわからん。助けてくれ。 |
git reset --hard を取り消したい
git reset --hard
によって誤って特定のコミットを削除してしまい、これを復元したい場合。
git の gc によって回収されていない限りは git reflog
によって参照を取得できるので、この参照をもとに操作すれば良いです。
$ git reflog
git reflog
を実行すると、HEAD
の変更ログ (branch の切り替え、reset の実行、commit など) 一覧と識別子(例えば HEAD@{1}
) が取得できます。これを引数にして、git reset --hard HEAD@{1}
を実行すれば、そのときのファイルツリーの内容が復元できます。
push したコミットを変更したい
基本的には、
$ git revert <commit>
をしたあとで git push
を使うことになると思います。git revert
は、対象 commit
を打ち消す新たなコミットを生成するサブコマンドですね。
他にももし master ブランチを対象とすると絶対に怒られる方法としては以下などがあり得るでしょうが絶対に怒られると思います。
$ git reset --hard HEAD~ $ git push --force