git push –force and how to deal with it

間違ったgitコマンドがあなたのプロジェクトのレポに大混乱をもたらした状況に遭遇したことがありますか? 人は間違いを犯すものであり、時にはその間違いがあなたのチームの時間を何時間も費やしてしまうこともあります。 このチュートリアルでは、不運な git push --force からすばやく回復する方法を紹介します。

過信しすぎないようにしましょう。 遅かれ早かれ、これは起こることです。 同じ git リポジトリで複数のリモートで作業している間に、最終的に git push --forcemaster (または、決していじってはいけない別の重要なブランチ) に入ってしまいます。

これは、たとえば、アプリケーションを構築して展開するために別々の git リモートを使っている Deis や Heroku で展開しているときに起こるかもしれません。 長い一日の仕事の後、いつもの git push --force deis master ではなく git push --force を実行することは信じられないほど簡単です。 瞬く間に、あなたのチームメイトは最新の仕事をすべて失ってしまいました。

しかし、ある優秀なガイドが教えてくれたように、Don’t Panic!

最善のケースは、同じコードで作業している他の誰かが、あなたがそれを壊す直前に master の最新バージョンをプルしたことです。

幸運にも、その人のローカル リポジトリにはコミットの完全な履歴があり、あなたの間違いは新しいコードで上書きされ、何も失われることはないでしょう。 しかし、それほど幸運でない場合はどうでしょうか。 では、続きを読んでください!

ケース 1: 間違いの前に master にプッシュした最後の人だった

良い知らせです!

ケース 1: 間違いの前に master にプッシュした最後の人だった。 あなたの目の前に、ミスを元に戻すのに必要なものがすべて揃っています。 ただ、端末を閉じたり、消去したりしないでください。 まず、チームのチャットに入り、あなたの罪を告白してください。

シェルの git push --force コマンドの出力で、次のような行を探します:

 + deadbeef...f00f00ba master -> master (forced update)

最初の記号群 (コミットの SHA プレフィックスみたいなもの) が解決のカギとなります。 deadbeef はダメージを与える直前の master への最後の良いコミットです。

ですから、必要なのは… このコミットを master ブランチに強制的に押し戻すことです (毒をもって毒を制す!)。 あなたは今日を救いました。 6272>

ケース 2: 自分が失敗する前に、誰かが master を変更していた場合

つまり、あなたが git push --force する直前に、誰かが大量の pull request を閉じ、master はあなたのローカルコピーとは全く違うものになってしまったのです。 ローカルに最近のコミットがないため、git push --force sha1:master を実行することはできません (git fetch を実行しても、それらはもうどのブランチにも属していないため、取得することはできません)。

GitHub が到達不可能なコミットを即座に削除しないことは、私たちにとって大きなメリットとなります。 もちろん、それらを取得することはできませんが、回避策があります。

これは、リポジトリを「監視」している場合にのみ動作することに注意してください。 フィードを開き、次のようなものを探します。

an hour agoUsername pushed to master at org/repo - deadbeef Implement foo - deadf00d Fix bar

これでできました。

  • URL https://github.com/org/repo/tree/deadbeef を作成し、deadbeef は破損したブランチへの直近の良いコミットのハッシュです。
  • GitHub のウェブ UI でブランチ/タグ切り替えを開きます。

GitHub branch/tag switcher

GitHub branch/tag switcher

  • 新しい一時ブランチの名前を作成する (例.g., master-before-force-push);
  • [ブランチの作成] をクリックします。

これで、不足しているコミットをすべて取得できます:

$ git fetchFrom github.com:org/repo * master-before-force-push -> origin/master-before-force-push

これで、問題は前の例で説明したものに減りました。

$ git push --force origin origin/master-before-force-push:master

まだ自分の作業が master にある必要があるなら、その上にリベースすればよい:

$ git rebase origin/master

How to avoid such disaster future

  1. GitHubとGitLabには「保護ブランチ」という機能があります。” master, develop, stable などの重要なブランチを保護ブランチとしてマークすると、誰もそこに強制的にプッシュすることができなくなります。

    詳細は GitHub のドキュメントをご覧ください。

  2. --force オプションの代わりに、--force-with-lease を使ってください。 これは、あなたが作業している間に誰かが同じブランチにプッシュした (そして何も変更をプルしていない) 場合、プッシュ操作を停止します。

    アトラシアンのこの記事を参照してください。

  3. 誤って破壊的な行動をしないように、git push --force を使用するコマンドのエイリアスを作成する:

    # ~/.gitconfig deploy = "!git push --force deis \"$(git rev-parse --abbrev-ref HEAD):master\""

    エイリアスについては ProGit の章をお読みください。 git checkout -b experiments はあなたの親友です。

Pro Tip: git push には多くのオプションがあります。 --force--all は特によく合います。 このコンボは、数ヶ月間活動しなかった後、プロジェクトに復帰する際に使用できます。 特別に冒険したい気分の時は試してみてください!

Leave a Reply