54

I have two branches, default and branch1. By mistake one person in our team merged branch1 with default. The content in branch1 is not yet ready to merge with default (it contains a major rework of the build & deploy environment).

We did an experiment with 'hg backout', backing out the merge (not sure this is the right way to do it). Then the changes from branch1 gets deleted on default, which is fine - but we can not remerge with branch1.

How should we solve this issue?

cdleary
  • 69,512
  • 53
  • 163
  • 191
Peter Moberg
  • 3,098
  • 4
  • 29
  • 35
  • Have the merge been pushed and is now public to a lot of people, or have you contained the damage to a controllable list of repositories? – Lasse V. Karlsen Nov 11 '10 at 08:21
  • I hate to say this, but you can't unless nobody has pulled. – Tim Post Nov 11 '10 at 08:32
  • Unfortunately the merge was pushed to the public repository and a few people pulled it before we had any change to revert it. – Peter Moberg Nov 11 '10 at 10:01
  • 3
    The idea that you can't seems insane to me. You can literally copy the files at one position to the other position & commit as new changes.Question is, is that the only way to do it. – Josh Sutterfield Nov 20 '15 at 22:09

6 Answers6

91

There are many scenarios here where you might want to do this, I'll make each scenario a headline, so that you can find the scenario that fits your case. Note that I'm still learning Mercurial, and I'd like pointers if something I say is wrong, using the wrong terminology, could be done better, etc.

No further changes, merge not shared (no pushes/pulls)

The programmer has merged, but not done anything else, nor has (s)he shared the changes with anyone, in any way

In this case, simply discard the local clone, and get a fresh clone from a safe repository.

Local changes on top of merge, not shared

The programmer has merged, and continued working based on that merge. The changesets that followed the merge should be kept, but the merge itself should be removed. The changes (merge + following changesets) have not been shared with anyone

In this case I would do one of four:

  1. Try to use the REBASE extension, this will move the changesets from one location to another. If the changesets are based on code-changes that were introduced with the merge, some manual work must be done to reconcile the differences.
  2. Try to use the MQ extension to pull the changesets that are to be kept into a patch-queue, then push them back in a different location. This will, however, have the same problem as the REBASE extension in terms of changes based on the merge
  3. Try to use the TRANSPLANT extension to "copy" the changes from one location to another. Still, same problem exists as with the first two.
  4. Do the work again, probably with the help of a diffing tool to take changes done in the changesets I want to discard, and re-do them in the correct location.

To get rid of the merge changeset + all the following changesets, there's a couple of options:

  1. Use the strip command in the MQ extension

    hg strip <hash of merge changeset>
    
  2. Clone and pull, and specify the hash of the changesets leading up to, but not including the merge. In essence, create a new clone by pulling from the damaged clone into a new one, and avoid pulling in the merge you don't want.

    hg clone damaged -r <hash of first parent> .
    hg pull damaged -r <hash of second parent>
    

Merge pushed to others, control over clones

The programmer has pushed to master repository, or to someone else, or someone pulled from the programmers repository. However, you (as in the group of developers) have control over all the repositories, as in, you can contact and talk to everyone before more work is done

In this case, I would see if step 1 or 2 could be done, but it might have to be done in a lot of places, so this might involve a lot of work.

If nobody has done work based on the merge changeset, I would use step 1 or 2 to clean up, then push to the master repository, and ask everyone to get a fresh clone from the master repository.

Merge pushed, you don't have control over clones

The programmer pushed the mergeset, and you don't know who will have the merge changeset. In other words, if you succeed in eradicating it from your repositories, a stray push from someone who still has it will bring it back.

Ignore the merge changeset and work in the two branches as though it never happened. This will leave a dangling head. You can then later, when you've merged the two branches, do a null-merge for this head to get rid of it.

  M         <-- this is the one you want to disregard
 / \
*   *
|   |
*   *
|   |

Simply continue working in the two branches:

|   |
*   *
| M |       <-- this is the one you want to disregard
|/ \|
*   *
|   |
*   *
|   |

Then later you merge the two, the real merge you want:

  m
 / \
*   *
|   |
*   *
| M |       <-- this is the one you want to disregard
|/ \|
*   *
|   |
*   *
|   |

You can then do a null-merge to get rid of the dangling head. Unfortunately I don't know how to do that except through TortoiseHg. It has a checkbox where I can discard the changes from one of the branches.

With TortoiseHg, I would update to the merge I want to keep (the topmost, lowercase m), then select and right-click on the dangling merge head below, and then check the "Discard all changes from merge target (other) revision": discard changes from target

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • 7
    Your HG fu is strong .. and you have ascii diagrams in addition to non-freehand circles (hence the +1), but the diagrams and circles had little to do with that :) – Tim Post Nov 11 '10 at 09:06
  • 4
    As mentionned in [answer #439790 of question #7476481](http://stackoverflow.com/a/7476481/439790), the way to discard changes during is: `hg merge 4 --tool internal:local` – zigarn Feb 21 '14 at 10:18
  • Most thorough answer I've seen on SO in a long time. :) – Matt Lachman Jun 03 '15 at 18:59
  • One more scenario that lead me to this question - you've run `hg merge`, but realised your mistake before you actually commit. In this case from `hg merge --help` `hg update --clean .` does what you want – user3805082 Oct 10 '16 at 00:15
  • 2
    [This question](http://stackoverflow.com/q/4197744/113632) explains how to replicate TortoiseHg's behavior on the command line. – dimo414 Jan 12 '17 at 05:11
5

We did an experiment with 'hg backout', backing out the merge (not sure this is the right way to do it). Then the changes from branch1 gets deleted on default, which is fine - but we can not remerge with branch1.

I use backout for merge cancel. You can not remerge, but you able to "backout backout merge", i.e. when you want remerge, you make 'hg backout' on "Backed out merge changeset ..." commit and then merge branch again.

Example:

  7     M       remerge
  6   /   \
  5   *   |     hg backout 3 (backout backout)
  4   |   *     fix error 
  3   *   |     hg backout 2
  2   M   |     fail merge
    /     \
  1 *     *
    |     |
  • This looks prommising. Exactly what I need! I will test it. – bugs_ Mar 10 '17 at 10:14
  • Doesn't work: "abort: cannot backout a merge changeset" – fret Jul 05 '19 at 05:59
  • Yes, it does not work anymore, backout of a merge commit was disabled in latest mercurial versions, I have no idea why – Konstantin Vdovkin Jul 07 '19 at 15:52
  • @KonstantinVdovkin Thanks, this worked for me - 'Backout of a backout merge'! Regarding your previous comment, I did the first merge backout using the resolution from here: https://patchwork.mercurial-scm.org/patch/10699/ – Vadim Tofan Dec 09 '19 at 16:59
1

Thanks to everyone for the great input! Since we were kind of in a rush to solve the problem, and our group is relatively new to Mercurial, we used a very pragmatic solution.

On our repository server we created a new repository, then we cloned the old repository up until the revision right before the merge. Then pushed the new clone to the server and sent out the new link to everyone. Luckily we are a quite small development team.

Perhaps not the most cosher way to solve the problem, but it worked :)

Peter Moberg
  • 3,098
  • 4
  • 29
  • 35
0

I had this exact issue. A coworker accidentally merged my branch into the default branch while it was still incomplete. Initially I just backed out that merge which seemed to work fine until I wanted to merge my branch into default for keeps. Files that I needed were being marked for deletion upon merging.

The solution was to go all the way back to my original back out that fixed my coworker's mistake and back out that back out. This stopped the files from being marked as deleted and let me successfully merge my branch into default.

Justin
  • 1,428
  • 1
  • 13
  • 29
0

You cannot really backout a merge nicely. IMO, the best way to handle this would be just to abandon the merge and continue the strand of changesets from before the merge, leaving a dangling head (which can be stripped). If other changes have happened since the merge, they can be rebased onto the new "good" head.

Ringding
  • 2,856
  • 17
  • 10
0

This answer assumes that you have already pushed

This would result in (at least one) unresolved head, depending on what you just forgot. More depending on who just pushed from what branch.

I love HG and use it avidly, but their idea of a branch can drive someone batty when coupled with a history that is (by design) intentionally immutable.

I usually clone a backup (locally) of the repo prior to doing a branch merge, for just this reason. I always check before pulling.

Eric Raymond is working on something that is more or less DVCS agnostic that can (hopefully) help in situations just like the oops you described, but I don't think he's going to have HG support fully implemented for another week or two. Still, it might be worth watching.

But, only useful if nobody has pulled the 'ooopsie' tip.

Tim Post
  • 33,371
  • 15
  • 110
  • 174