Edited at

GitHubでマージ済のプルリクをrevertした後に引き続きrevertされたブランチで作業を続行したい時

More than 3 years have passed since last update.

GitHub上でプルリクをマージした後に、まだマージできる状態では無かったと気付きrevertする事ってありませんか?

その後に問題になるのが、revertしたのはいいけど、引き続きそのrevertされたブランチで作業してまたプルリクを発行したい。という状況です。

この時に何も考えずに普通に作業を続行してプルリクを発行した場合、revertされた分の変更が失われてしまいます。

結構でかい事故に繋がる場合があるので、この時の対処方法を書きたいと思います。


前提

以下の操作を行っている。


  1. プルリクを発行

  2. プルリクをマージ

  3. マージ済のコミットのrevertプルリクを発行

  4. revertプルリクをマージ

ここまでの操作はGitHub上ですんなり行える。


再度revertされたブランチのプルリクを発行してみる

普通の感覚だと、また差分が復活していると思うのでプルリクが発行できそうな気分になると思います。

しかし、実際は「差分ないからプルリクできないよ」と言われる。

この状態で、引き続きこのdevelopブランチで作業を続行し、プルリクを発行しても、差分にはrevertされる前に更新した変更が反映されない。

ではどうするか?


revertのrevertをする方法

一度、revertした後に再びマージする場合は revert を revert しなさい。とLinusさんが言っています。

http://stackoverflow.com/a/6217372/683157

revertのrevertをする??否定の否定?よくわからん!!

という事で操作方法を説明します。

この場合は、一度、最新のmaster(revertされた状態)を取り込んでから再度そのrevertのコミットをrevertするという流れになります。

まず現在の状態をlogすると、

*   078468e (origin/master, master) Merge pull request #2 from bigplants/revert-1-develop

|\
| * 0f2ceea Revert "second commit."
|/
* fb717a2 Merge pull request #1 from bigplants/develop
|\
| * 8e7a601 (HEAD, origin/develop, develop) second commit.
|/
* 340f1ad first line
* f3f6327 first commit

こんな感じになってるとします。

そこで一旦、最新のorigin/masterをマージします。

1. git checkout develop

2. git fetch

3. git merge origin/master

でこうなりました。

*   078468e (HEAD, origin/master, master, develop) Merge pull request #2 from bigplants/revert-1-develop

|\
| * 0f2ceea Revert "second commit."
|/
* fb717a2 Merge pull request #1 from bigplants/develop
|\
| * 8e7a601 (origin/develop) second commit.
|/
* 340f1ad first line
* f3f6327 first commit

この後にrevertのマージコミットをrevertします。この場合は、hash 078468eがrevert対象です。

☁  revert_merged_commit_test [develop] git revert 078468e

error: Commit 078468e32c96bc0afcbf0a28d94c84c0c4a6cf5e is a merge but no -m option was given.
fatal: revert failed
☁ revert_merged_commit_test [develop]

怒られました。。。 -mオプションつけてね。との事です。

これは、revertするにしてもどの状態に戻すの?的な事を聞かれています。

git show 078468eで確認してみます。

commit 078468e32c96bc0afcbf0a28d94c84c0c4a6cf5e

Merge: fb717a2 0f2ceea
Author: Daiki Hirakata <daiki@bigplants.net>
Date: Tue Oct 27 01:40:39 2015 +0900

Merge pull request #2 from bigplants/revert-1-develop

Revert "second commit."

(END)

ここで確認するポイントは、2行めの、

Merge: fb717a2 0f2ceea

です。

この1番めのhashはベースのほうです。

-mオプションはmainlineの略で左から1,2と数えます。

この場合、-m 1fb717a2を指し、-m 20f2ceeaを指します。

なので、今回はベースのほうに戻したいので、-m 1を指定します。

☁  revert_merged_commit_test [develop] git revert 078468e -m 1

[develop d89fb5a] Revert "Merge pull request #2 from bigplants/revert-1-develop"
1 file changed, 1 insertion(+)
☁ revert_merged_commit_test [develop]

ここでgit diff masterを行うと見事に以前の変更が復活している事が分かります。

このあとpushすれば、通常通りプルリクを発行できます。

最終的なlog treeは、

* d89fb5a (HEAD, origin/develop, develop) Revert "Merge pull request #2 from bigplants/revert-1-develop"

* 078468e (origin/master, master) Merge pull request #2 from bigplants/revert-1-develop
|\
| * 0f2ceea Revert "second commit."
|/
* fb717a2 Merge pull request #1 from bigplants/develop
|\
| * 8e7a601 second commit.
|/
* 340f1ad first line
* f3f6327 first commit
(END)

面倒くさいと思いました??

もっと簡単な方法もあります。


masterブランチでresetして強制pushする方法

実はこれが一番楽です。

この操作はGitHub上でRevertしていない前提ですので注意してください。

間違ってプルリクをマージした直後にやることは、


  1. git checkout master

  2. git pull

  3. git reset --hard HEAD^

  4. git push -f

これでokです。

じゃあ、なんでこれを先に説明しないんだ!って思いますよね?

この操作の場合、過去の歴史を書き換える事になるのであまり良い方法ではありません。

commit log上から消えてしまうんです。

たしかに消したい過去の一つや二つ人生にはあると思います。

でも他の人が別ブランチに最新の変更を取り込み済の場合は迷惑をかけます。泥沼にはめる事にもなる場合もあるので、気をつけましょう。

しかも、GitHubは最近の更新で、ブランチを保護する機能を追加しました。

ここで保護されたブランチは、強制pushを認めない事になります。

その場合はgit push -fが使えません。

git push -fは時に便利でもありますが、トラブルの元になるので、壊れてほしくないmasterブランチなどには保護をかけておくことをおすすめします。

以上です。