はじめに
GitHub をつかった開発で「あ!修正中のコミットをまちがって main ブランチに push してる...
」となったときのおはなしです。
最近、うっかりやらかしてしまって冷や汗をかきました。
そのときの対処法を備忘録としてまとめます。
まず、落ち着く
あわてて作業すると、ミスがミスを呼びさらに状況が悪化することがままあります。
そうなったら最悪。なので、まずは深呼吸して落ち着きましょう。
あわてたところで時間は戻らないのです。
それから、関係者(チームメンバーや上司など)に状況を共有しておくのがよいでしょう。
Revert で元に戻す
1. 打ち消したいコミットの SHA を確認する
git log --oneline
-
git log --onelineで、コミット履歴を確認します。 - 打ち消したいコミットの SHA ハッシュをメモっておきます。
a1b2c3d うっかりコミットC
e4f5g6h うっかりコミットB
i7j8k9l うっかりコミットA
d0e1f2g 正しいコミットX
2. Revert コマンドでコミットを打ち消す(コミットはまだしない)
すでにリモートに push してしまっているので、履歴を書き換える git reset は使いません。
代わりに git revert コマンドを使って、誤って pushしたコミットを打ち消します。
うっかりコミット A 〜 C を打ち消す場合は、以下のコマンドを実行します。
d0e1f2g は正しいコミットなので、打ち消しません。
# 複数の SHA を指定する場合(古い順に)
git revert --no-commit i7j8k9l e4f5g6h a1b2c3d
# 範囲指定を使う場合
git revert --no-commit d0e1f2g..a1b2c3d
-
git revert --no-commit <打ち消したいコミットの SHA>で、指定したコミットを打ち消します。
NOTE :
- スペースで区切って複数の SHA を指定することができます。
..を使った範囲指定も可能ですが、開始点は含まれない ので要注意です。
(A..Bは「A の直後から B まで」のようにみえるけど、実際には「A からは到達できないが、B からは到達できるコミットの集合」を意味します。ややこしい...)- スペースで区切って複数のコミットを列挙するほうが安全です。急いでいるときこそ、確実な方法を選ぶのがよきです。
- 大量にコミットがあって列挙すんのたいぎぃなという場合だけ、細心の注意をはらって範囲指定を使います。
- まとめて一つの revert コミットにしたい場合は、
--no-commit(ステージングに置くだけでコミットしない)オプションを使用します。--no-commitオプションがない場合、各コミットごとに revert コミットが作成されてしまいログが荒れるので要注意です。
3. 変更をコミットしてリモートリポジトリに push する
git commit -m "Revert: うっかりコミット A to C"
git push origin main
-
git commit -m <コメント>で revert した変更をコミットします。 -
git push origin mainでリモートリポジトリに push します。
これで、誤って push した変更を元に戻すことができました。
ここまでできたら、まずは一安心です。
新しいブランチを作成して、変更を移動する
誤って push した変更を新しい feature ブランチに移動して開発を続けたい場合は、以下の手順で移動できます。
さっきの例のうっかりコミット A 〜 C を新しいブランチに移動します。
a1b2c3d うっかりコミットC
e4f5g6h うっかりコミットB
i7j8k9l うっかりコミットA
d0e1f2g 正しいコミットX
1. 新しくブランチを作成する
git checkout -b feature/ukkari
-
git checkout -b <新しいブランチ名>で新しいブランチを作成して切り替えます。
NOTE :
- この時点の main ブランチは、revert で元に戻した状態になっている前提です。
2. cherry-pick を使って新しいブランチに変更を移動する
git cherry-pick i7j8k9l e4f5g6h a1b2c3d
git push origin feature/ukkari
-
git cherry-pick <誤って push したコミットの SHA>で、誤って push したコミットを新しいブランチに適用します。 -
git push origin <新しいブランチ名>で新しいブランチをリモートリポジトリに push します。
NOTE :
- cherry-pick も
..を使った範囲指定が可能です。が、やはり確実な方法としてスペースで区切って複数の SHA を指定する方法がおすすめです。- もし、cherry-pick 中にコンフリクトが発生した場合は、内容を修正(手動で解決)してから
git cherry-pick --continueで再開します。
これで、誤って push した変更を新しいブランチに移動することができました。
そもそも、うっかり push を防ぐには
そもそも「あ!」ってならないように、なにかしらできることがないか調べてみたところ、GitHubの「ブランチ保護ルール」を設定する方法がありました。
こちらを設定しておくと、main ブランチへの直接 push を防止できるようです。
チームで開発している場合は、特に有効かもしれません。
おわりに
ミスはつきものです。にんげんだもの。
ミスをしたときに、いかに冷静に対処できるかが重要だと考えます。
また、ミスを未然に防ぐ仕組みをつくることも大切です。
今回の記事が、わたしと同じようなうっかりさんの参考になればうれしいです。
参考リンク