Gitコマンド

git revert

1つ以上の過去のコミットが行った変更を打ち消すような変更を生成し、新しい1つのコミットを作成します。

git revert [--[no-]edit] [-n] [-m parent-number] [-s] [-S[]] …​
git revert (--continue | --skip | --abort | --quit)

git revert の概要

1つ以上の過去のコミットが行った変更内容を打ち消すような新たな変更を生成し、新しい1つのコミットを作成します。通常、間違って行った変更を元通りにするために利用します。

もし間違ったコミットを完全に履歴から消去したいのであれば、git revert ではなく、代わりに git reset --hard <commit>を使ってブランチの状態を強制的に書き換えることができます。

ただし、もしその間違った変更がすでに共有されたリモートのブランチに push されているのであれば、このgit revert で歴史を先に進めながら、変更を打ち消す事が推奨されます。リモートの履歴の強制的な変更は、チームメンバーに影響を与えてしまうからです。

使用例

直前のコミットを打ち消します。

git revert HEAD

過去2つ前のコミットだけを打ち消すコミットを作ります。

git revert HEAD~2

「..」でコミットをつなげれば、範囲を指定することもできます。前者のmaster~という指定により、master~5の一つ古いコミット(masterの先頭からかぞえて3番目のコミット)までが指定されることに注意して下さい。チルダはHEADやブランチ名などの参照(refs)の移動における世代を遡る指定方法です。

git revert master~5..master~2

merge コミットの取り消し

git revert -m <親番号>

git revert -m <親番号>とすれば、もし revert で打ち消す対象がマージコミットであれば、マージの際に統合された2つの歴史のうち、どちらを「正(mainline)」とすればよいかが分からない限り、実行することができません。この「-m」オプションにより、親の番号を指定することで、「正」とする歴史に対して行われた変更を打ち消す事ができるようになります。

親番号は git show <merge commit> とすれば、確認できます。

git show 05deeb4

// ff71f80 が「1」、1be9949 が「2」
commit 05deeb4967965d32bee9b810f879de4d0391ec76
Merge: ff71f80 1be9949
Author: Alex Johanson <alexj89@gmail.com>
Date:   Sat Jul 1 19:42:06 2017 +0900

    Merge branch 'master' into prod

マージコミットを打ち消すと、打ち消されたブランチに含まれていた変更が、その後のマージにの際にも取り込みが発生しない、という事になります。これは注意が必要です。

// マージコミット「M」で取り込んだ BRANCH_B の変更を「M'」で打ち消したならば、
// その後の、マージコミット「M2」では、「C」と「D」の変更のみが取り込まれる 
BRANCH_A ---O---O---M---o--M'--M2---
                  /           /
BRANCH_B ---A---B---C---D----O

 

下記は、マージコミットのrevertに関する、リーナス自身(Gitを創始者)の言及です(revert-a-faulty-merge.txt より)

Reverting a regular commit just effectively undoes what that commit did, and is fairly straightforward. But reverting a merge commit also undoes the _data_ that the commit changed, but it does absolutely nothing to the effects on _history_ that the merge had.

So the merge will still exist, and it will still be seen as joining the two branches together, and future merges will see that merge as the last shared state – and the revert that reverted the merge brought in will not affect that at all.

[抄訳]
通常のコミットの revert は、そのコミットが行った事を効率的に取り消しでき、かなり直感的だ。ただ、マージコミットの revert においては、たしかにコミットが変更した「データ」は打ち消すが、マージが与えた「履歴」への影響に対しては、一切何も行わない。

つまりマージ自体は残る。2つのブランチは一度統合したものとみなされ続け、それ移行にマージを行う際も、そのマージコミットが最後の共有地点であり続ける。revert コマンドが及ぼした「打ち消し」は、この事に何の影響も与えない。

git revert によるマージコミットの打ち消しは、この事を念頭において実行する必要があるでしょう。

複数のコミットの取り消し

【方法1】git revert -n <commit>

この-nオプションでは、コミットを作成せず、ステージングするだけで留めてくれます。このオプションを付けて複数のコミットを指定すれば、複数のコミットの取り消し変更を一度にステージングに上げることができます。

但し、コミットメッセージに自動的にリバート対象のSHA1が残りませんので、コミットメッセージをうまく自分で調整しないと何をしているコミットなのか分かりにくくなる可能性があります。

【方法2】コミット範囲の指定

こちらは上述の範囲指定のを使って複数のコミットを指定する方法です。「..」を使って「コミットA..コミットB」の時系列で指定します。AがBよりも過去になります。また。リバーとされるのはAの直後のコミットとなり、A自体は含みません。

git revert <commit A>..<commit B>

この方法であれば、自動的に連続してリバートコミットが作成されるので、コミットメッセージにSHA1を含めることができます。

git revert によるコミットメッセージ編集画面
revert コミット作成時は、自動的にコミットメッセージが生成される。

関連するコマンド

git reset

打ち消すコミットを作るのでなく、コミットやブランチの歴史を書き換えてしまうコマンドです。共有されたレポジトリで公開済みのコミットを変更する時は注意が必要です。通常そのような場合、git revert の利用が推奨されます。

その他のオプション

git revertで利用可能な主なオプション

オプション 詳細
-n
--no-commit
コミットを自動で作成せず、作業ツリーとインデックスの更新に留めます。ファイルを確認したり、調整したりしたい場合に使います。
-m {parent-number}
--mainline {parent-number}
マージコミットを打ち消します。parent-numberには、親の番号を指定し、統合された歴史のうち、どちらに戻すのかを決めます。

Git公式ドキュメント

Git公式ドキュメント「git revert」へのリンクです。