TIM Labs

gitでアレを元に戻す108の方法

| コメント(0) | トラックバック(0) このエントリーをはてなブックマークに追加

以前 gitで一度行った変更をなかったことにする方法4つ を紹介しましたが、 日常的に git を使用していると他にも様々な 「なかったことにしたい」「元に戻したい」 という状況に遭遇します。 そのひとつひとつについて対処方法を紹介していきます。

問題1: ライブラリの新機能を試すためにあれこれ適当なコードを書いてみた。でももう要らない。

解答1: git reset --hard HEAD~{n}

詳細は gitで一度行った変更をなかったことにする方法4つ を参照してください。

別解1: git branch -d

$ git branch experimental
$ git checkout experimental

$ $EDITOR
$ git commit -am 'foo'
$ $EDITOR
$ git commit -am 'bar'
$ $EDITOR
$ git commit -am 'baz'

$ git checkout master
$ git branch -d experimental

そもそも実験的なことをするのであれば、 一度実験用のブランチ(例えば experimental)を作ってそこで作業し、 要らなくなったらそのブランチを削除(git branch -d)すれば済みます。

問題2: トピックブランチをマージしたけど実はまだ不完全だった。マージをやり直したい。

解答2: git reset --hard ORIG_HEAD

詳細は gitで一度行った変更をなかったことにする方法4つ を参照してください。

問題3: リリース後に発覚したバグ。原因は30日前に自分が行ったコミットだった。なかったことにしたい。

解答3: git revert $commit_id

詳細は gitで一度行った変更をなかったことにする方法4つ を参照してください。

問題4: 新しいコミットしようとして間違えてgit commit --amendで書き換えてしまった。元に戻したい。

解答4: git reset HEAD@{1}

詳細は gitで一度行った変更をなかったことにする方法4つ を参照してください。

問題5: 色々作業していたら作業ディレクトリの内容が混沌としてきた。一度綺麗な状態にしたい。

回答5: git checkout HEAD -- .

別解5: git reset --hard HEAD

問題6: 作業ディレクトリにゴミファイルが溜まってきた。一度綺麗な状態にしたい。

回答6: git clean -n

git の管理下にないファイルは git clean でまとめて削除することができます。

しかし「git の管理下にない」=「後から元に戻すことはできない」なので git clean-f を指定しない限りファイルを削除しません。 -n では削除対象のファイル名を表示するだけです。 実際の削除は慎重に確認をしたうえで行いましょう。

問題7: 新しいファイルを git add した。しかしまだそのタイミングではなかった。元に戻したい。

回答7: git reset HEAD -- $file

別解7: git rm --cached $file

新しいファイルを git add したのであればこれでも同じ効果。 ただし、このコマンドの意味するところは「git リポジトリから $file を削除する」なので注意しましょう。

問題8: git add -p $file した。でも git add しなかったことにしたい。

回答8: git reset HEAD -- $file

問題9: rm $file をした。でもまだその時期ではなかった。元に戻したい。

回答9: git checkout HEAD -- $file

問題10: ファイルをあちこち編集した。でも最後にコミットした状態に戻したい。

回答10: git checkout HEAD -- $file

問題11: ファイルの10箇所くらいを変更した。でも特定のものだけ最後にコミットした状態に戻したい。

回答11: git checkout -p HEAD -- .

個々の変更箇所について元に戻すかそのままにしておくか選択することができます。

問題12: 調子に乗って git rebase -i をしていたら途中で混乱してきた。rebase 開始前の状態に戻したい。

回答12: git rebase --abort

問題13: git rebase が完了してしまった。でも rebase 実施前の状態に戻したい。

回答13: git rebase --hard ORIG_HEAD

ただし git rebase 直後に git commitgit merge などのコマンドを実行していない時に限ります。

別解13: git reflog と git reset --hard

それも既にやってしまったという場合: git reflog で各種操作が行われた時点のコミットが一覧できます。 適切なコミット(例えば abadcafe) を探して git reset --hard abadcafe とすれば元に戻せます。

問題14: git reset --hard HEAD~4 とするつもりが HEAD~5 で実行してしまった。元に戻したい。

回答14: git reset --hard HEAD@{1}

問題4や問題13と同様です。

問題15: 先程コミットした内容は2つに分割すべきだった。コミット前の状態に戻したい。

回答15: git reset HEAD~1

実行後は適宜 git addgit commit をしましょう。

問題16: リリースブランチの更新作業を行おうとしたがまだ作業の途中。一度綺麗な状態にしたいが、後で作業を再開したい。

回答16: git stash save と git stash pop

リリースブランチの更新作業が終わった後、 元のブランチで git stash pop すれば中断した作業を再開することができます。

問題17: かなり昔からパスワードを含んだファイルをコミットしており、公開リポジトリにも push 済みだった。この黒歴史を抹消したい。

例えば以下のような状況だったとします:

$ git checkout master

$ echo 'id=who' >conf
$ echo 'password=secret' >>conf
$ git add conf
$ git commit -m 'Add conf'

$ echo 'width=1024' >>conf
$ git commit -am 'Update conf 1'

$ echo 'height=768' >>conf
$ git commit -am 'Update conf 2'

$ echo 'color=256' >>conf
$ git commit -am 'Update conf 3'

$ git push origin master

「本来 conf には生のパスワードを書いてはいけなかった」という状況です。 このとき、慌てて

$ sed -e '/^password=/d' conf >,conf
$ mv ,conf conf
$ git commit -am 'Remove the secret password'
$ git push origin master

などとやっても、変更履歴を辿れば結局はパスワードを参照できてしまいます。

回答17: git filter-branch と git push -f

どちらのコマンドもかなりの荒業なので、周囲の状況をよく確認したうえで使いましょう。

git filter-branch を使えば指定したコミットを自動で書き換えることが可能です。 今回の例の場合、 conf ファイル中のパスワード行をなかったことにしたいので、 以下のようなコマンドを実行します:

$ git filter-branch \
> --tree-filter 'sed -e "/^password=/d" <conf >,conf; mv ,conf conf' \
> master~4..master

正しくファイルを書き換えることができたか確認し:

$ git log -p master~4..master -- conf

問題なさそうなら公開リポジトリに push した内容を上書きしましょう:

$ git push origin master -f

ただし、このケースで行っていることは、既に公開した内容を自分の都合で上書きしていることに他ならず、 黙って行うと公開リポジトリを既に clone していた人達に大混乱を招くため、 必ず周囲の状況を確認して周知を徹底したうえで行いましょう。

問題18 - 問題108

  • 筆者の経験では108通りもありませんでした。
  • 「元に戻したい」状況があれば適宜追記します。
  • 次回は Mercurial 編です。

トラックバック(0)

トラックバックURL: http://labs.timedia.co.jp/mt/mt-tb.cgi/256

コメントする