Git

【git revert】複数コミットをまとめてrevertする【使い方】

Gitを使って開発をしているとまとめて複数コミットをrevertしたくなることがあると思います。

そのコミット数が数十、数百だったりするとと一つ一つrevertしていては日が暮れてしまうし、何より面倒ですよね。
それにたとえ数コミットしかなくて数秒で終わる作業だったとしても、その数秒の積み重ねは大きな差につながります。

そこで、本記事では複数コミットをまとめて一括でrevertする方法について解説していきます。

この記事で分かること

ポイント

  • 基本的なrevertの使い方
  • 指定した範囲内にあるコミットを複数まとめてrevertする方法
  • revertコミットをまとめてログを綺麗にする方法

単体のコミットを対象とする基本的なrevertの使い方

本題に入る前に、単体のコミットを対象とする基本的なrevertの方法についても確認しておきましょう。

特定のコミット単体をrevertしたい場合、コミットの指定方法には

  • HEADで指定する
  • コミットハッシュで指定する

の二通りがあると思います。

それぞれ、以下のコミットログを例として簡単に説明していきます。

$ git log --oneline
680cded (HEAD -> main) commit3
755ffb0 commit2
404c4dc commit1
0f36277 initial commit

HEADで指定する

HEAD指定する方法は、現在のコミットを起点としていくつ前のコミットなのかで指定してあげます。

revert対象指定方法
現在のコミットHEAD
1つ前のコミットHEAD^
n個前のコミットHEAD~n
HEADによるコミットの指定方法

今回の例で言うと

$ git revert HEAD   // commit3をrevert
$ git revert HEAD^  // commit2をrevert
$ git revert HEAD~2 // commit1をrevert

のような感じです。

コミットハッシュで指定する

コミットハッシュで指定する方法は、単純にrevertしたいコミットのコミットハッシュを指定してやります。

$ git revert 680cded // commit3をrevert
$ git revert 755ffb0 // commit2をrevert
$ git revert 404c4dc // commit1をrevert

のような感じです。

ここまではrevertの基本的な使い方です。
これらを元に、指定した範囲内の複数のコミットをまとめてrevertする方法について見ていきましょう。

複数コミットをまとめてrevertする方法

複数コミットをまとめてrevertするにはrevertしたいコミットの範囲をHEADもしくはコミットハッシュで指定します。

$ git revert {始点のHEAD}...{終点のHEAD}
$ git revert {始点のコミットハッシュ}...{終点のコミットハッシュ}

前述の例にcommit4を追加した下記のようなコミットログでcommit3~commit1までをrevertする方法を例にそれぞれ具体的に解説していきます。

$ git log --oneline
9cd33ed (HEAD -> main) commit4
680cded commit3
755ffb0 commit2
404c4dc commit1
0f36277 initial commit

HEADで指定する

まずはHEADで指定する方法からです。

今回のrevertしたいコミットの始点・終点をそれぞれHEAD表記で書くと下記表の様になります。

revert対象コミットHEAD表記
始点(commit3)HEAD^
終点(initial commit)HEAD~4
始点・終点のHEAD表記

ここで注意していただきたいのが終点に指定するコミットは実際にrevertしたいコミットの1つ先のものにする必要があります。

$ git log --oneline
9cd33ed (HEAD -> main) commit4
680cded commit3 // ここから
755ffb0 commit2
404c4dc commit1 // ここまでrevertしたい
0f36277 initial commit // 実際にコマンドで指定する終点はココ

実際のコマンドは次のようになります。

$ git revert HEAD^...HEAD~4 // commit3~commit1をrevertする

これを実行すると下記のようなrevertコミットのコミットメッセージを入力する画面がcommit3~commit1の各コミットに対して順々に出てきます。
それぞれ必要に応じてメッセージを編集してコミットしていきましょう。

Revert "commit3"

This reverts commit 680cded2cd322309d56a511d1e9c0ece10065d74.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch main
# Your branch and 'origin/main' have diverged,
# and have 5 and 1 different commits each, respectively.
#   (use "git pull" to merge the remote branch into yours)
#
# Revert currently in progress.
#
# Changes to be committed:
#       deleted:    test3.txt
#

全てコミットして、再度コミットログを確認するとcommit3~commit1までのrevertコミットが作成されていることが確認できます。

$ git log --oneline        
b06821e (HEAD -> main) Revert "commit1"
7ee3634 Revert "commit2"
3aba83b Revert "commit3"
9cd33ed commit4
680cded commit3
755ffb0 commit2
404c4dc commit1
0f36277 initial commit

以上が複数コミットをHEAD指定でrevertする方法です。

コミットハッシュで指定する

コミットハッシュで指定する方法も基本的な考え方をHEADのときと同じです!

始点にはcommit3のコミットハッシュ(680cded)、終点にはcommit1の次のコミットinitial commit(0f36277)を指定します。

$ git revert 680cded...0f36277

先程と同じようにrevertコミットのコミットメッセージをそれぞれ入力して保存して、ログを確認すると同様にrevertできていることが確認できます。

$ git log --oneline        
b06821e (HEAD -> main) Revert "commit1"
7ee3634 Revert "commit2"
3aba83b Revert "commit3"
9cd33ed commit4
680cded commit3
755ffb0 commit2
404c4dc commit1
0f36277 initial commit

revertコミットのコミットメッセージの入力を省略する

複数コミットをまとめてrevertする方法については分かったかと思いますが、コミット数が多いとrevertコミットのコミットメッセージの画面が毎回出てくるのが煩わしい場合もあると思います。
そんなときは--no-editオプションを使うと楽になります!

使い方は先程のコマンドにそのまま--no-editをつけてやるだけです。

$ git revert HEAD^...HEAD~4 --no-edit

これを実行するとコミットメッセージの入力画面が表示されること無く最後まで一気にrevertを実行してくれます!

$ git revert HEAD^...HEAD~4 --no-edit
[main 154e05f] Revert "commit3"
 Date: Thu Jul 6 21:28:23 2023 +0900
 1 file changed, 1 deletion(-)
 delete mode 100644 test3.txt
[main d66ae7e] Revert "commit2"
 Date: Thu Jul 6 21:28:23 2023 +0900
 1 file changed, 1 deletion(-)
 delete mode 100644 test2.txt
[main 3b827ad] Revert "commit1"
 Date: Thu Jul 6 21:28:23 2023 +0900
 1 file changed, 1 deletion(-)
 delete mode 100644 test1.txt

rebaseでrevertコミットをまとめる

ここまでで複数のコミットをまとめてrevertするという目的は達成されていますが、コミット数が多いとrevertコミットが大量にできてしまってログが見づらくなってしまします。
なので、それらのコミットをまとめてログをきれいにする方法も合わせて解説していきます。

これはrevertコミットに限った話ではないですが、コミットログを編集したいときはrebaseコマンドを使います

rebaseコマンドはログを完全に書き換えてしまうものなのでGitに慣れないうちは使わない方が賢明です。
一応rebaseで書き換えたログもrebase操作自体を取り消せば戻すことも可能ではありますが、共用のブランチ等では基本的にしないようにしましょう。

今回は直近の3つのコミットがrevertコミットになっているのでこれらをまとめて行きます。

$ git log --oneline        
b06821e (HEAD -> main) Revert "commit1"
7ee3634 Revert "commit2"
3aba83b Revert "commit3"
9cd33ed commit4
680cded commit3
755ffb0 commit2
404c4dc commit1
0f36277 initial commit

rebaseコマンドでログを編集したいときは下記の様に-iオプションを使います。

$ git rebase -i {終点のコミット}

revertのときと同じように終点のコミットは実際に編集したいコミットの次のコミットを指定する必要があります。

今回の例だと指定するコミットはcommit4になるので、HEAD表記ならHEAD~3、コミットハッシュなら9cd33edとなります。
どちらでも結果は同じなのでHEAD表記を例にすると以下のような形です。

git rebase -i HEAD~3

このコマンドを実行すると次のような画面が出てきます。

pick 154e05f Revert "commit3"
pick d66ae7e Revert "commit2"
pick 3b827ad Revert "commit1"

# Rebase 9cd33ed..3b827ad onto 9cd33ed (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.

デフォルトでは各コミットの横にpickと書かれていますが、これは特に操作はしないという意味です。(まとめ先のコミットのコミットハッシュは変わりますが)

このpickをsquash(もしくはs)に書き換えるとそのコミットを1つ上のコミットにまとめることができます

pick 154e05f Revert "commit3"
squash d66ae7e Revert "commit2"
s 3b827ad Revert "commit1"

# Rebase 9cd33ed..3b827ad onto 9cd33ed (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.

各コミットをまとめたコミットのコミットメッセージの編集画面が出てくるので、必要に応じて編集して保存してやれると

# This is a combination of 3 commits.
# This is the 1st commit message:

Revert "commit3~commit1"
    
This reverts commit 680cded2cd322309d56a511d1e9c0ece10065d74~404c4dc6b90bab69512d475cdba29b1dab7c2d4d.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Thu Jul 6 21:28:23 2023 +0900
#
# interactive rebase in progress; onto 9cd33ed
# Last commands done (3 commands done):
#    squash d66ae7e Revert "commit2"
#    squash 3b827ad Revert "commit1"
# No commands remaining.
# You are currently rebasing branch 'main' on '9cd33ed'.
#
# Changes to be committed:
#       deleted:    test1.txt
#       deleted:    test2.txt
#       deleted:    test3.txt
#

rebaseが完了します。

$ git rebase -i HEAD~3               
[detached HEAD f594e2d] Revert "commit3"
 Date: Thu Jul 6 21:28:23 2023 +0900
 3 files changed, 3 deletions(-)
 delete mode 100644 test1.txt
 delete mode 100644 test2.txt
 delete mode 100644 test3.txt
Successfully rebased and updated refs/heads/main.

ログを確認してみると、commit3~commit1のrevertコミットが1つにまとまっているのが確認できます。

$ git log --oneline
98d2fed (HEAD -> main) Revert "commit3~commit1"
9cd33ed commit4
680cded commit3
755ffb0 commit2
404c4dc commit1
0f36277 initial commit

まとめ

今回は複数コミットをまとめてrevertする方法について解説してきました。

まとめると、

  • git revert {始点のコミット}…{終点のコミット}で指定した範囲のコミットをまとめてrevertできる
  • 終点コミットは実際にrevertするコミットの1つ先のものを指定する
  • 指定方法はHEADとコミットハッシュの二通りがある

といった感じでした!

最後までお読みいただきありがとうございました。

-Git