以前gitで一度行った変更をなかったことにする方法4つを紹介しましたが、
日常的に git を使用していると他にも様々な
「なかったことにしたい」「元に戻したい」
という状況に遭遇します。
そのひとつひとつについて対処方法を紹介していきます。
詳細は gitで一度行った変更をなかったことにする方法4つ を参照してください。
$ 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
)すれば済みます。
詳細は gitで一度行った変更をなかったことにする方法4つ を参照してください。
詳細は gitで一度行った変更をなかったことにする方法4つ を参照してください。
詳細は gitで一度行った変更をなかったことにする方法4つ を参照してください。
git の管理下にないファイルはgit clean
でまとめて削除することができます。
しかし「git の管理下にない」=「後から元に戻すことはできない」なのでgit clean
は -f
を指定しない限りファイルを削除しません。-n
では削除対象のファイル名を表示するだけです。
実際の削除は慎重に確認をしたうえで行いましょう。
新しいファイルを git add
したのであればこれでも同じ効果。
ただし、このコマンドの意味するところは「git リポジトリから $file
を削除する」なので注意しましょう。
個々の変更箇所について元に戻すかそのままにしておくか選択することができます。
ただし git rebase
直後に git commit
や git merge
などのコマンドを実行していない時に限ります。
それも既にやってしまったという場合:git reflog
で各種操作が行われた時点のコミットが一覧できます。
適切なコミット(例えば abadcafe
) を探してgit reset --hard abadcafe
とすれば元に戻せます。
問題4や問題13と同様です。
実行後は適宜 git add
と git commit
をしましょう。
リリースブランチの更新作業が終わった後、
元のブランチで git stash pop
すれば中断した作業を再開することができます。
例えば以下のような状況だったとします:
$ 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
などとやっても、変更履歴を辿れば結局はパスワードを参照できてしまいます。
どちらのコマンドもかなりの荒業なので、周囲の状況をよく確認したうえで使いましょう。
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 していた人達に大混乱を招くため、
必ず周囲の状況を確認して周知を徹底したうえで行いましょう。
(まあ、元のパスワードが恥ずかしいものだった場合は、やはり git filter-branch
したくなりますが……)
(2011-09-02T18:06:54+09:00追加)
例えば以下のような状況だったとします:
$ git checkout master
$ git merge feature-x
Auto-merging SOMEFILE
CONFLICT (content): Merge conflict in SOMEFILE
Automatic merge failed; fix conflicts and then commit the result.
$ git diff | wc -l 12000
$ git diff | grep '<<<<<<<' | wc -l 8000
マージに際してコンフリクトがあった場合、
作業ディレクトリにあるファイルはコンフリクト状況を示す<<<<<<<
やら >>>>>>>
やらのマーカーが埋め込まれた状態になります。
コンフリクトした箇所が10箇所くらいなら手作業でどうにかするところですが、
上記のように8000箇所くらい
(あるいはコンフリクトしたファイルが誠に遺憾ながら政治的な理由によりプレインテキストでない場合)
になるとさすがにそうも言ってられません。
手っ取り早く修正するにはファイルの内容をどちらか片方のブランチの最新版の内容で置き換えるのが一番なのですが、
どうすればよいでしょうか。
git checkout
を使います。通常、このコマンドはブランチの切り替えに使いますが、
ファイル名を指定すると作業ディレクトリ中の対応するファイルの内容を
特定のブランチでのファイルの内容で置き換えることができます。
例えば作業ディレクトリにある SOMEFILE
ファイルの内容をmaster
ブランチの内容で置き換えるには以下のコマンドでできます:
$ git checkout master -- SOMEFILE
実際にはブランチだけでなく任意のコミットを指定することもできるので、
以下のようにして1前のコミットでの状態を取得することもできます:
$ git checkout HEAD~1 -- SOMEFILE
実際にコンフリクトが発生した場合は
「どのブランチをどのブランチにマージしようとしていたか」
という情報が内部的に記録されているので、
直接ブランチ名を指定する代わりに以下のコマンドを使うこともできます:
$ git checkout --ours -- SOMEFILE
--ours
はマージ結果を取り込むブランチ(上記の例の場合 master
)を表します。
現在のブランチのものから復元するため「ours」という訳です。
$ git checkout --theirs -- SOMEFILE
--theirs
はマージしようとしたブランチ(上記の例の場合 feature-x
)を表します。
現在のブランチとは別のブランチのものから復元するため「theirs」という訳です。
(2012-07-02T14:33:15+09:00追加)
大抵の場合、一度は bar
を git checkout
しているはずです。
ならば git reflog HEAD
で「 bar
から別のブランチに git checkout
した」ログが見つかるはずです。
そのログから $n の値を決めて git branch bar HEAD@{$n}
を実行すれば復元できます。
一度も checkout
してなかった場合は reflog にも記録されていないので調べようがありませんが、
この場合はそもそも bar
で有益な作業をしていた訳ではないので、復元できなくとも問題ないはずです。
(2013-11-18T18:57:45+09:00追加)
本来なら
git checkout -b bar master
...
git push origin bar
とすべきところを
git checkout -b foo master
...
git push origin foo
とやってしまいました。
ローカルのブランチは
git branch -m foo bar
でリネームできるので、これで
git push origin bar
とすれば本来のブランチ名で push はできます。
が、既に push してしまった foo についてはどうすれば無かったことにできるでしょうか。
(2015-05-14T18:49:58+09:00追加)
問題18の亜種ですね。