Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mercurial - backout an old merge

Tags:

mercurial

I have a branch that looks like this:

A->B->C->D->...->Z
     ^
1->2-^

where C is a merge from 2 and its ancestors.

I realize now that I should not have merged. I could go back to B and graft D...Z but that's a lot of work. Can I backout JUST C?

When I try to hg backout --merge C I get abort: cannot backout a merge changeset.

These changes have been pushed to the central repo and I'm not looking to modify history or anything, I just want the inverse of 2 and it's ancestors back to the common descendant with B.

like image 863
George Mauer Avatar asked Aug 08 '13 22:08

George Mauer


People also ask

How do I revert a changeset in Mercurial?

Revert changes already committed To backout a specific changeset use hg backout -r CHANGESET . This will prompt you directly with a request for the commit message to use in the backout. To revert a file to a specific changeset, use hg revert -r CHANGESET FILENAME . This will revert the file without committing it.

What is backout in mercurial?

Backout works by applying a changeset that's the opposite of the changeset to be backed out. That new changeset is committed to the repository, and eventually merged.


Video Answer


5 Answers

Merge is public and there are public commits on this merge

Assume we have this published history of commits (top is the newest):

                  revZ
                   |
                  ...  
                   |
                  revD
                   |
                  revC     <- unwated merge commit (rev2 to revB)
                   |   \    
wanted branch ->  revB  rev2    <- unwanted branch
                   |     |

Instead, we would like to have top state as if we had this history:

                  revZ'
                   |
                  ...  
                   |
                  revD'
                   |       
wanted branch ->  revB  rev2    <- unwanted branch
                   |     |

How to achive this (step by step)

  1. Callapse the history after merge commit (revD - revZ) into one commit

    $ hg update -r revC                # Update to merge commit
    $ hg revert --all -r revZ          # revert to the newest commit
    $ hg commit -m "collapsed commits" # Create new commit (revTmp1)
    
                       revZ
                        |  
                       ...
                        |  
               revTmp1 revD
                     \ /
                     revC
                      |  \    
                    revB  rev2
                      |    |
    
  2. Copy changes after merge (revTmp1) to wanted branch (revB)

    $ hg update -r revB    # Update to the last "wanted" commit before merge
    $ hg graft -r revTmp1  # Copy changes from revTmp1 (create revTmp2 commit)
    
                       revZ
                        |  
                       ...
                        |  
               revTmp1 revD
                     \ /
            revTmp2  revC
                  \  / \    
                 revB  rev2
                   |     |
    
  3. Create a "backout" commit

    $ hg update -r revZ                   # Update to the top commit
    $ hg revert --all -r revTmp2          # Copy state revTmp2
    $ hg commit -m "reverted revC merge"  # Create revZ' commit
    
                       revZ'
                        |
                       revZ
                        |  
                       ...
                        |  
               revTmp1 revD
                     \ /
            revTmp2  revC
                  \  / \    
                  revB  rev2
                   |     |
    
  4. Clean the temporary commits

    $ hg strip revTmp1 revTmp2
    
                      revZ'          <- reverted revC merge
                       |  
                      revZ
                       |  
                      revD
                       |
                      revC     <- unwated merge commit (rev2 to revB)
                       |   \    
    wanted branch ->  revB  rev2    <- unwanted branch
                       |     |
    
like image 170
Marcin Raczyński Avatar answered Sep 25 '22 18:09

Marcin Raczyński


A little bit late, but I had the same situation a while ago. This worked for me smoothly:

hg update -C -r "revision-C"
hg revert --all -r "revision-B"
hg commit -m 'UNDO blah blah whatever the merge did'
hg update -C -r "revision-Z-or-whatever-the-current-head-is"
hg merge -r "the-new-revision-created-in-step-3"

Basically, this is exactly what backout does.

DISCLAIMER: Please note however that if you later in the future want to merge the 1->2 branch again, you will most probably run into some issues. And "some issues" is a somewhat euphemestic term here. In fact, even BIG PROBLEMS may appear in such a case. This scenario is dangerous mainly because the problems may arise much later and totally unexpected. It is highly recommended to abandon the "1" branch completely to avoid these risks. (Also see the comments below.)

For details, see Backout wiki page.

like image 41
Marwin Avatar answered Sep 23 '22 18:09

Marwin


Use backout tool, but be aware of what you are doing:

  1. Update to the merge change set (C)
  2. Right click on that change set -> click Backout
    • After you do backout on branch, you don't need to commit it immediately. Better check before, if there (among changes that arose after chosing Backout option) aren't some changes from other branches that you don't want to back out. If so, uncheck them before commit.
  3. Click Commit
    • If you would merge branch 1-2 (or "numbered branch") into branch A-Z now (or later), you will lose all changes from "numbered branch" before changeset 2 (including) - that's what warning in @Marvin's answer is about.
    • To avoid that, you need to propagate this backout into "numbered branch" and redo backed out changes (later steps).
  4. If branch A-Z is not direct ancesstor of "numbered branch", then find first descendant branch between A-Z and 1-2 and update workbench to its tip.
  5. Right click on the latest revision in A-Z (= backout) -> click Merge with local
    • If there are more branches between A-Z and 1-2, repeat steps 4. and 5.
    • Don't merge anything into branch 1-2 yet.
    • Note: Always merge any branch only into the one from which it origins! Otherwise you risk losing some changes after future merges.
  6. Update to the tip of "numbered branch".
  7. Find all files (on filesystem) that were changed by backout and copy them to some temp directory.
  8. Right click on the latest revision of numbered branch's ancesstor -> click Merge with local
  9. Copy files from step #7 back to filesystem.
  10. Check changes in worbench and uncheck all changes, that are not from branch 1-2 (for example changes from revisions D - Z).
    • Unfortunately this has to be checked manualy. But it's the only safe way how to correct both branches.
  11. Click commit

Tip: To be really sure what files were affected by any merge, right click on that revision and click Diff to parent

This is basically scenario that we used today, when we find out that one branch (which was still in development) was accidentally merged into default (instead of another one, which had the same color in revisions graph :). (Both branches had pushed changes after this merge.) It might seems time-consuming, but still better than backout merge only and find numerous unexpected errors days (or weeks) later. (Own experience.)

like image 31
juice Avatar answered Sep 21 '22 18:09

juice


You could use the thg backout tool.

  1. Update to the merge change set (C)
  2. thg backout
  3. Pick which parent you want to backout (whatever the revision for (2) is) - Note you actually pick on the dialog the parent whos changes you want to keep not backout.
  4. Click Next
  5. Click Commit

This will create a new head which you will need to merge with Z or rebase on to Z.

like image 28
Tom Avatar answered Sep 24 '22 18:09

Tom


You can rebase D to Z onto B. The documentation for rebase even discusses some similar situations. This should be doable in one command.

like image 29
Livius Avatar answered Sep 23 '22 18:09

Livius