2017年07月19日

git 変更をリセットする

コミット内容を訂正する操作(git commit --amend)、コミットの取り消し(git revert)は、コミット自体を無かったことにはできない。
これに対して、"reset" はコミットを無かったことにできる。
"reset" は前の状態に戻す操作。

"reset" がわかりにくいので、図を書いて整理してみた。

まず、"HEAD"、"ステージングエリア"、"作業ツリー" の関係について整理しておく。
  • 作業ツリー:最新のファイルの状態
  • ステージングエリア:コミットするためのファイルの状態
  • HEAD:最新コミットの状態


git_reset01.png
"ステージングエリア"、"作業ツリー" ともに "HEAD" に一致している。


コミット直後の reset


git_reset02.png
  • ファイルを変更したら、作業ツリーが変更
  • "git add" したら、ステージングエリアが変更
  • "git commit" したら、HEAD が変更(1つ進む)



"git reset" では、戻したいコミット位置を指定する。

今 "HEAD"はコミット(Z) 。これから1つ前のコミット(Y)に戻る場合(HEAD^)
git_reset03.png


すべて1つ前に戻す


作業ツリーも、ステージングエリアもすべて1つ前に戻す。すべてクリアされるので、ある意味、簡潔でわかりやすい。
$ git log --oneline
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset --hard HEAD^

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

ステータスを確認してみても、作業ツリーに何も変更がない。




git add を取り消す


"git add" を取り消す。作業ツリーはそのまま。
最新コミットを無かったことにし、かつ、ステージングも無かったことにする。
$ git log --oneline
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset HEAD^

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)

modified: index.html

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

ステータスを確認すると、作業ツリーには変更があるが、ステージングはされていない。





git commit を取り消す


"git commit" を取り消す。作業ツリー、ステージングエリアはそのまま。
単に最新コミット操作を無かったことにする。
$ git log --oneline
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset --soft HEAD^

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)

modified: index.html

ステータスを確認すると、ステージングがされた状態。





最新のコミット位置が "HEAD" なのに、"HEAD" に戻すとはどういうことか?

"HEAD" 位置ではなく、作業ツリー、ステージングエリアが先に進んでいる状態を想定。
コミット後に変更したものを "reset" する操作。
考え方は、コミット直後の "reset" と同じ。
git_reset04.png




git_reset05.png
"resret --soft" は、もともと "HEAD" に居るので、"HEAD" へ戻す操作をしても何も変わらない。



git_reset06.png
"resret --mixed" は、ステージングエリアを戻すので、"git add" を取り消すことになる。



git_reset07.png
"resret --hard" は、作業ツリーも戻す。すべて消え、最新コミット直後に戻る。





昔の状態に reset


コミットIDを指定して、以前のコミット状態に "reset" する。
git_reset08.png





すべて戻す


git_reset09.png
$ git log --oneline
fee81ff master 追加 5
67c1282 master 追加 4
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset --hard 1f391e3

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

ステータスを確認してみても、作業ツリーに何も変更がない。





git add を取り消す


git_reset10.png
$ git log --oneline
fee81ff master 追加 5
67c1282 master 追加 4
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset 1f391e3

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)

modified: index.html

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

ステータスを確認すると、作業ツリーには変更があるが、ステージングはされていない。
作業ツリーのファイルの中身は、コミット(E):fee81ff のまま。
これを再度ステージし、コミットしてみると、
$ git log --oneline
3d15bcd master "reset"後の再コミット:
1f391e3 master 追加 2
6edb23d master 追加 1

となり、ある意味 コミット(C):509957a ~ コミット(D):fee81ff までを "squash" した状態での再コミットと同じになる。





git commit を取り消す


git_reset11.png
$ git log --oneline
fee81ff master 追加 5
67c1282 master 追加 4
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset --soft 1f391e3

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)

modified: index.html

ステータスを確認すると、ステージングがされた状態。
これも再度コミットすると、コミット(C):509957a ~ コミット(D):fee81ff までを "squash" した状態での再コミットと同じになる。





reset を無かったことにする


上記のように、近々のコミットを無かったことにして、昔の状態に "reset" するということはあまりないと思うが、この昔の状態に戻す "reset" の使い方としては、昔の動作を確認するために、一時的に環境をその当時の状態にし、動作確認するときに使える。

このような場合、動作確認が終われば、再度、元のコミットに戻す必要がある。
$ git reset --hard 1f391e3
$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset --hard ORIG_HEAD

$ git log --oneline
fee81ff master 追加 5
67c1282 master 追加 4
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1


"git reset --hard ORIG_HEAD" を使えば、直前の "reset" を無かったことにできる。
間違って "git reset" してしまった場合も使えるので、落ち着いて対応する。

ただし、コミットした状態に戻すだけなので、作業ツリーや、ステージングエリアで変更していたものは戻らないので注意。

また "git reset --hard ORIG_HEAD" は前回位置に戻すだけなので、さらに "git reset" を重ねてしまうと、前回位置が上書き更新され、最初の位置に戻せなくなるので注意。





まとめ


図を書いて整理したおかげで、動作が理解できた。
"reset" で、誤ってステージしてしまった・コミットしてしまった、という場合に修正できる。これを知らないとコミット履歴が汚くなる。"git reset" は便利だが、指定オプションを間違うと、目の前の作業内容を失うこともあるので、作業途中のファイルがあれば、"git stash" で退避しておくと、失敗しても救済できる。




gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
git リモートでの操作
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください