

本質問の最後に `revert`で問題に遭遇した例をネットからあげます。

私も過去に何度か `revert`
でヒヤリハットがあってから、`revert`を極力使わないようにしているのですが（もう5年以上使ってないかも）、どういうときに問題が発生するのか（差分が出ない、変更したのに変更されない等）イマイチ把握できていません。なので、仕事場で仲間が
`revert`
を使おうとした場合、あまり説得力がある形で、`revert`の危険性を説明することができません。（危険なので使うのはやめようという意図はありません。問題が出るときもありましたと言いたいのですが、再現方法がわからないので、どういうときに危険なのか言えないのが困りごとです）

下記の例では「差分が出なくなる」とありますが、出るときもあるような気がしており、なかなか説得力がうまれません。

**質問**

  * `revert`すると必ずプルリクエスト時に差分が出なくなるものなのでしょうか？ それとも差分が出るときもあるのでしょうか？
  * プルリクエスト時に差分が出たとしても、マージ後なぜかその差分が反映されていない現象に遭遇したこともあります

問題が起きたとき
`revert`の`revert`をするとよいとありますが、開発が進んでいると`revert`の`revert`時にコンフリクトが起きて辛い思いをしたことがあります（とはいえ、`revert`した2,3日後なので、全員の記憶も残っており、なんとかなってしまうのですが）。

（過去の記憶で書いてしまっているので、今困っているわけではないですし、そもそも問題が起こる場合をうまく再現する方法がわかっていないので回答しづらいかもしれません。申し訳ありません）

遭遇する例としては、まだgitのことをよく知らない人（git 戻す等で検索して git reset, git
revert等がひっかかるが区別がつかない）が、とにかくわからないが（といっても本人はわかってるつもり）`$ git revert なにかしらのコミット`
して、プルリクエストを作ります。そのプルリクエストがマージされたあとに、新規ブランチをつくり、新しい機能を追加し、プルリクエストを作ると差分が出ないときがあるような気がしています。（この場合の`git
revert` はコミットに慣れていないので、当人のコミットミスを戻したい場合が多く、`git revert`が適切でない場合が多い、`git commit
--amend`や後述する`git reset --mixed`が適切であることの方が多い）

私自身は、自分のフィーチャーブランチで開発中に revert した場合、プルリクエストが取り込まれる前であれば [git reset
--mixed](https://ja.stackoverflow.com/a/23220/9008)
するなどして、revertをなかったことにするなどして、コミットをまとめてしまう（もしくは整理する）ことがあります。この場合、フィーチャーブランチ開発中の自分の実装ミス、もしくは開発中の仕様変更をrevetするので、コミット履歴としてrevertを残しておく有用性がないためです。

一度プルリクエストが通ったものに不備があったりする場合は
GitHubのrevertボタンを押す等すると、過去に不備があったこと、そしてそれを打ち消したことがわかるので
revertコミットを残しておくことは有用だと思います。歴史の書き換えもすでにみんなが見ている（みんなが起点にしている）のでやってはいけませんし。（しかし、差分が出なくなる,
変更したのに変更がなくなるパターンがわかってないのでなんか怖い。おそらくこの場合は問題は起こらない気がしてるが...
気がしているのではなく根拠を説明したいです）

* * *

ここまで書いて思ったのですが、  
まとめると  
gitに不慣れな人が、単純にコミットメッセージを作り間違えた、必要なファイルを`git add`し忘れていたといった単純な当人のコミットミスを修正するために
`git revert`を使うと後々「差分が出なくなる」「変更したのに変更が反映されない」等の問題がおこる気がしているのですが、当人がもう`git
revert`したくなっているので`git commit
--amend`等の説明をしたいのですが、聞く耳を持っていない場合に強く説得したほうが良い気がするが、強い根拠を示せないということになりそうです。(`git
commit --amend` のほうがキレイですよみたいな言い方にとどまってしまう。そしてもう `git push` してしまっていると `git
push -f` をまだ不慣れな人に教えることになる...）

（蛇足：当人が聞く耳をもたないのではなく、聞く余裕がないほどの納期プレッシャーを当人がかけられていることがある）

## revertで問題が発生している例

[Git マージコミットをrevert後に再度マージしたいけど差分が出ない時には -
チョコパイを240こ食べたエンジニア](https://chooringo.hatenablog.com/entry/2017/03/26/225718)

> Aブランチ⇒メインブランチへのマージ、再度プルリク作成！  
>  あれ、、、差分があるはずなのに出てこない汗

[GitHubでマージ済のプルリクをrevertした後に引き続きrevertされたブランチで作業を続行したい時 -
Qiita](https://qiita.com/bigplants/items/3d1d3ada97d0c86d2548)

> GitHub上でプルリクをマージした後に、まだマージできる状態では無かったと気付きrevertする事ってありませんか？  
>  その後に問題になるのが、revertしたのはいいけど、引き続きそのrevertされたブランチで作業してまたプルリクを発行したい。という状況です。  
>  この時に何も考えずに普通に作業を続行してプルリクを発行した場合、revertされた分の変更が失われてしまいます。  
>  結構でかい事故に繋がる場合があるので、この時の対処方法を書きたいと思います。


CC BY-SA 4.0
2020-08-05T09:50:51.413
69274
2023-11-26T15:07:31.283
2020-08-05T23:37:32.660
9008
9008
question
git github
revertするとプルリクエスト時に差分は必ず出なくなってしまうものですか？出る場合があるとすればその場合の違いはなんですか？


これは revert という操作自体に問題があるというよりかは、revert が何をしているかの理解に問題があるのではないかと思います。私個人としては
revert 自体に問題があるとは思いません（もちろんチームとして合意を取った上で revert は避けるという決定を否定するものではありません）。

まず大前提として、[git revert](https://git-scm.com/docs/git-revert)
は既に積んだコミットを打ち消すようなコミットを **新しく** 積むコマンドです。既に積んだコミットを削除する、歴史改竄を伴うコマンドではありません。

ですから、revert したいコミットの後に、そのコミットで変更した部分を更に変更するコミットが積まれていれば、当然 revert 時点で conflict
が発生します。

また、あるブランチ A をマージ、その後ブランチ A を revert した後にブランチ A を再度マージしようとすれば、ブランチ A の分岐点は
revert した地点より前で既にマージされているコミットたちばかりなので、何も変わりません。revert で消えた変更を取り戻すには、たとえば
revert の revert をすることで新しいコミットを積めば良いです。

以下、小さい動作例を示します。

[code]

    $ git init
    Initialized empty Git repository in /home/nek/tmp/rev/.git/
    $ # 新しく example.txt を作成してコミットします。
    $ echo foo > example.txt
    $ git add example.txt
    $ git commit -m "add example.txt"
    [master (root-commit) d0de588] add example.txt
     1 file changed, 1 insertion(+)
     create mode 100644 example.txt
    $ # 先ほどコミットした example.txt を revert します。
    $ git revert HEAD
    hint: Waiting for your editor to close the file...
    [master e93d91b] Revert "add example.txt"
     1 file changed, 1 deletion(-)
     delete mode 100644 example.txt
    $ git log
    commit e93d91b3617f4d70f04ae8ed100f17d492350c32 (HEAD -> master)
    Author: nekketsuuu <nekketsuuu@users.noreply.github.com>
    Date:   Wed Aug 5 19:20:21 2020 +0900
    
        Revert "add example.txt"
    
        This reverts commit d0de58827a3c518f12c2885a8db97ed2aec5ab94.
    
    commit d0de58827a3c518f12c2885a8db97ed2aec5ab94
    Author: nekketsuuu <nekketsuuu@users.noreply.github.com>
    Date:   Wed Aug 5 19:19:42 2020 +0900
    
        add example.txt
    $ # ファイルが削除されています
    $ ls
    $ # もう一度やってみます。
    $ echo foo > example.txt
    $ git add example.txt
    $ git commit -m "add example.txt, again"
    [master 9124376] add example.txt, again
     1 file changed, 1 insertion(+)
     create mode 100644 example.txt
    $ # 今度は conflict するような変更を挟んでみましょう。
    $ echo bar > example.txt
    $ git add example.txt
    $ git commit -m "improve example.txt"
    [master a9eb03c] improve example.txt
     1 file changed, 1 insertion(+), 1 deletion(-)
    $ # example.txt を作成したコミットを revert しようとすると conflict します。
    $ git revert HEAD~1
    error: could not revert 9124376... add example.txt, again
    hint: after resolving the conflicts, mark the corrected paths
    hint: with 'git add <paths>' or 'git rm <paths>'
    hint: and commit the result with 'git commit'
    nek@G-GEAR-NEK:~/tmp/rev$ git status
    On branch master
    You are currently reverting commit 9124376.
      (fix conflicts and run "git revert --continue")
      (use "git revert --abort" to cancel the revert operation)
    
    Unmerged paths:
      (use "git reset HEAD <file>..." to unstage)
      (use "git add/rm <file>..." as appropriate to mark resolution)
    
            deleted by them: example.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    $ ls
    example.txt
    $ # ここでは削除する方を選択してみます。
    $ git rm example.txt
    example.txt: needs merge
    rm 'example.txt'
    $ git revert --continue
    [master a48ed94] Revert "add example.txt, again"
     1 file changed, 1 deletion(-)
     delete mode 100644 example.txt
    $ git log
    commit a48ed94fd72ba3bd2c6886f173dee46f8de3974e (HEAD -> master)
    Author: nekketsuuu <nekketsuuu@users.noreply.github.com>
    Date:   Wed Aug 5 19:22:48 2020 +0900
    
        Revert "add example.txt, again"
    
        This reverts commit 912437675711610a16f9690244223c047d4276dc.
    
    commit a9eb03c37c08f884eca636d62f5d7e209d83b51d
    Author: nekketsuuu <nekketsuuu@users.noreply.github.com>
    Date:   Wed Aug 5 19:21:48 2020 +0900
    
        improve example.txt
    
    commit 912437675711610a16f9690244223c047d4276dc
    Author: nekketsuuu <nekketsuuu@users.noreply.github.com>
    Date:   Wed Aug 5 19:21:24 2020 +0900
    
        add example.txt, again
    
    (省略)
    $ # 無事削除されました。
    $ ls
    $ # 最後にお試しとして、revert コミットを revert してみます。
    $ git revert HEAD
    [master 037b437] Revert "Revert "add example.txt, again""
     1 file changed, 1 insertion(+)
     create mode 100644 example.txt
    $ git log
    commit 037b4371b6afd63b3197ecbfc8f0f48bbe6c9490 (HEAD -> master)
    Author: nekketsuuu <nekketsuuu@users.noreply.github.com>
    Date:   Wed Aug 5 19:23:10 2020 +0900
    
        Revert "Revert "add example.txt, again""
    
        This reverts commit a48ed94fd72ba3bd2c6886f173dee46f8de3974e.
    
    commit a48ed94fd72ba3bd2c6886f173dee46f8de3974e
    Author: nekketsuuu <nekketsuuu@users.noreply.github.com>
    Date:   Wed Aug 5 19:22:48 2020 +0900
    
        Revert "add example.txt, again"
    
        This reverts commit 912437675711610a16f9690244223c047d4276dc.
    
    commit a9eb03c37c08f884eca636d62f5d7e209d83b51d
    Author: nekketsuuu <nekketsuuu@users.noreply.github.com>
    Date:   Wed Aug 5 19:21:48 2020 +0900
    
        improve example.txt
    
    commit 912437675711610a16f9690244223c047d4276dc
    Author: nekketsuuu <nekketsuuu@users.noreply.github.com>
    Date:   Wed Aug 5 19:21:24 2020 +0900
    
        add example.txt, again
    
    (省略)
    $ # foo ではなくて bar が戻ってきます。
    $ cat example.txt
    bar
    $
    
[/code]


CC BY-SA 4.0
2020-08-05T10:38:18.933
69277
2020-08-05T14:56:00.353
2020-08-05T14:56:00.353
19110
19110
69274
answer


gitの質問は私はラーメン定食に例えるのですが、

最初に以下の注文をラーメン屋の親父にプッシュします。

> ラーメン＋ライス＋餃子

気が変わってチャーハンが食べたくなりました  
なので注文の変更（コミット）を作ってラーメン屋の親父にプッシュします

> ラーメン＋チャーハン＋餃子

でもでもやっぱりやめたとして注文の変更の打ち消し（リバート）をかけるときにやることは  
コミットを打ち消すコミットを作ることになります。  
「チャーハンをライスに変更する」ってことですね。

いまの流れは以下の通りです

> ラーメン＋ライス＋餃子 注文１  
>  ↓  
>  ラーメン＋チャーハン＋餃子 注文２  
>  ↓  
>  ラーメン＋ライス＋餃子 注文３

ここで大事なのは最初の注文１と注文３は違います。  
ソース上は一緒でも歴史も違うし親のコミットも違います。

じゃあここで自分のローカルで以下のコミットを作ってプッシュするとどうなるでしょうか？

> ラーメン＋ライス＋餃子 注文１  
>  ↓  
>  ラーメン＋チャーハン＋餃子 注文２

簡単ですね、ラーメン屋の親父が言うわけです、「その注文はもう受け付けてるよ」＝「差分がないよ」  
当たり前ですね。1を2にするコミットはすでにリポジトリ上に存在するわけですから、なのでやらなければいけないことは注文3を更新して注文4にする作業です。

差分出る場合は何でしょうか？途中で違うコミットが混じってしまったり、そもそも違うコミットになっていると出なくなります。

> ラーメン＋ライス＋餃子 注文１  
>  ↓  
>  ラーメン＋チャーハン＋餃子 注文２  
>  ↓  
>  ラーメン＋ライス＋餃子 注文３

という状態で

> ラーメン＋ライス＋餃子 注文1  
>  ↓  
>  つけ麺＋チャーハン＋餃子 注文4

を作ると  
親父がいうのです。「おいおい注文１は２にしたんだけど注文４ーつのはどういった了見だい？」  
まあコンフリクトですね。


CC BY-SA 4.0
2020-08-06T02:08:15.267
69291
2020-08-06T02:08:15.267
22665
69274
answer


  * A) masterブランチ上で、マージしたfeatureブランチをrevertする
  * B) featureブランチ(自分の作業ブランチ)上で、自分の作業をrevertする

両者では若干事情が変わるかと思います。  
質問文末に付記されているリンク2件は前者ですが、質問本文の状況は後者の話も混じっているように感じました。

* * *

取り敢えず、

>   * revertすると必ずプルリクエスト時に差分が出なくなるものなのでしょうか？ それとも差分が出るときもあるのでしょうか？
>   * プルリクエスト時に差分が出たとしても、マージ後なぜかその差分が反映されていない現象に遭遇したこともあります
>

を考えてみます。これは冒頭の状況A)で発生する事象かと思います。

次のスクリプトで事象が再現できます:

[code]

    #!/bin/bash
    
    mkdir myrepo
    cd myrepo
    
    git init
    git commit --allow-empty -m init
    
    # 1.ブランチfeatureで作業開始(hello.txt作成)
    git checkout -b feature
    git tag start-working
    echo 'Hello!' > hello.txt
    git add hello.txt
    git commit -m 'hello.txtを追加しました'
    
    # 2.ブランチmasterにfeatureをマージしたけれど...
    git checkout master
    git merge --no-ff --no-edit feature
    
    # 3.作業途中だったことに気づいてrevert
    git revert --no-edit -m 1 @
    
    # 4.あらためてfeatureで作業(world.txt作成)
    git checkout feature
    echo 'World!' > world.txt
    git add world.txt
    git commit -m 'world.txtを追加しました'
    git tag end-working
    
    # 5.featureでの作業が完了したのでmasterにマージ
    git checkout master
    git merge --no-ff --no-edit feature
    
    # hello.txt とworld.txtができていてほしい...
    # けれど存在するのはworld.txtだけ
    ls -l
    
[/code]

ここで **5.** のマージを行う直前のmaster(`master^`)と、feature(`end-
working`タグ)を比較してみます([2連ドット](https://git-scm.com/docs/git-
diff#Documentation/git-diff.txt-emgitdiffemltoptionsgtltcommitgtltcommitgt--
ltpathgt82308203)):

[code]

    $ git diff --name-status master^..end-working
    A       hello.txt
    A       world.txt
    
[/code]

featureブランチで作業した内容が確かに差異として表れています。

一方、実際に **5.** でマージ対象になるのは、`master^`と`end-
working`の共通の親からの差分になります([3連ドット](https://git-scm.com/docs/git-
diff#Documentation/git-diff.txt-emgitdiffemltoptionsgtltcommitgtltcommitgt--
ltpathgt82308203)):

[code]

    $ git diff --name-status master^...end-working
    A       world.txt
    
[/code]

`hello.txt`の追加は、既に **2.** のコミットでマージ済みなので、 **5.** のマージには含まれません。

質問に戻ると、

>   * revertすると必ずプルリクエスト時に差分が出なくなるものなのでしょうか？ それとも差分が出るときもあるのでしょうか？
>

差分が出るかどうかはrevertとは無関係です。既にmergeしているかどうかです。  
また、変更したはずのものが最終結果に含まれないのは、変更をrevertしたからです。

>   * プルリクエスト時に差分が出たとしても、マージ後なぜかその差分が反映されていない現象に遭遇したこともあります
>

前述の説明の通り、何(どの時点)との差異を見るかによって検出される差分は当然異なります。

* * *

質問文にあるリンク先の問題

  * [GitHubでマージ済のプルリクをrevertした後に引き続きrevertされたブランチで作業を続行したい時](https://qiita.com/bigplants/items/3d1d3ada97d0c86d2548) \- Qiita

> GitHub上でプルリクをマージした後に、まだマージできる状態では無かったと気付きrevertする事ってありませんか？  
>  その後に問題になるのが、revertしたのはいいけど、引き続きそのrevertされたブランチで作業してまたプルリクを発行したい。という状況です。  
>  この時に何も考えずに普通に作業を続行してプルリクを発行した場合、revertされた分の変更が失われてしまいます。

の対策としては、「GitHubでマージ済のプルリクをrevertした後に引き続きrevertされたブランチで作業を続行」すること自体が事故の元なのでやめるべきです。  
また、問題があるからrevertしたものを、mergeするためにrevertする(問題がある状態に戻す)というのも、結果が正しくとも意味的におかしいです。

プルリクをレビューしてマージしたのであれば、そのfeatureブランチは閉じ、改めて`master`からfeatureブランチを切り直してプルリクを作り直すべきです。  
この場合、`cherry-pick`が利用可能かと思います:

[code]

    # 5.featureでの作業が完了したのでmasterにマージ
    # 5-1. ...するのではなく、新しいfeatureブランチをmasterから作成
    git checkout master
    git checkout -b feature-reborn
    
    # 5-2. featureでの作業を全てcherry-pickで引き継ぐ
    git cherry-pick start-working..end-working
    
    # 6. 新生feature-rebornをmasterにマージ
    git checkout master
    git merge --no-ff --no-edit feature-reborn
    
    
    # hello.txt, world.txtともに存在する
    ls -l
    
    
[/code]

ただ、個人的には、こういった誤merge(レビュー通した上で発生することはレアでしょう)は管理者の権限において(`revert`でなく)`reset
--hard`でmergeを無かったことにしても良いのではないかと考えます。

* * *

以上が冒頭に記載した状況A)についてです。

次に状況B)についてですが、状況A)とは異なりmergeコミットが無い(分岐、統合が発生しない)ので上で説明したような事態は発生しません。

Gitの操作で安全なものと危険なものを分類する際、一般的には

  * 安全な操作: 歴史(commit)を新しく積み上げるもの
  * 危険な操作: 過去の歴史(既存のcommit)を消したり改変したりするもの

というになるかと思います。  
この分類で行くと、`revert`は安全な操作、`commit --amend`は危険な操作だが対象が限定的、`reset`,
`rebase`は危険な操作です。

Gitに不慣れな人が、危険な操作でなく安全な操作を好んで使う、というのは、特に問題ある行動ではないと考えます。


CC BY-SA 4.0
2020-10-25T06:32:03.597
71497
2020-10-25T06:32:03.597
2808
69274
answer