How do you reverse the effect of a merge on polarised branches without dying of agony?
This problem has been plaguing me for months and I have finally given up.
You have 1 Repository, with 2 Named Branches. A and B.
Changes that occur to A will inevitably occur on B.
Changes that occur directly on B MUST NEVER occur on A.
In such a configuration, merging "B" into "A" produces a dire problem in the repository, as all the changes to B appear in A as if they were made in A.
The only "normal" way to recover from this situation appears to be "backing out" the merge, ie:
hg up -r A hg backout -r BadMergeRev --parent BadMergerevBeforeOnA
Which looks all fine and dandy, until you decide to merge later in the correct direction, and you end up with all sorts of nasty things happening and code that was erased / commented out on specifically branch B suddenly becomes unerased or uncommented.
There has not been a working viable solution to this so far other than "let it do its thing, and then hand fix all the problems" and that to be honest is a bit fubar.
Here is an image clarifying the problem:
[Original image lost]
Files C & E ( or changes C & E ) must appear only on branch b, and not on branch a. Revision A9 here ( branch a, revno 9 ) is the start of the problem.
Revisions A10 and A11 are the "Backout merge" and "merge the backout" phases.
And revision B12 is mercurial, erroneously repeatedly dropping a change that was intended not to be dropped.
This Dilemma has caused much frustration and blue smoke and I would like to put an end to it.
It may be the obvious answer to try prohibiting the reverse merge from occurring, either with hooks or with policies, I have found the ability to muck this up is rather high and the chance of it happening so likely that even with countermeasures, you must still assume that inevitably, it will happen so that you can solve it when it does.
In the model I have used Seperate files. These make the problem sound simple. These merely represent arbitrary changes which could be a separate line.
Also, to add insult to injury, there have been substantial changes on branch A which leaves the standing problem "do the changes in branch A conflict with the changes in branch B which just turned up ( and got backed out ) which looks like a change on branch A instead "
The problem with all these retro-active solutions is as follows:
( I admit, many of the above are a bit daft, but they are outside of my control ).
The only solutions that are viable are the ones that assume that people can and will do everything wrong, and that there is a way to 'undo' this wrongness.
You can manually trigger a rollback with 'hg rollback'. This will undo the last transactional command. If a pull command brought 10 new changesets into the repository on different branches, then ' hg rollback ' will remove them all. Please note: there is no backup when you rollback a transaction!
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.
You need to login to the server and use the hg strip command. If you cannot login to the server, you are out of luck; you can hg backout then push again, but this will leave the bad commits on the server along with the commit that undoes them. Show activity on this post. hg revert -r .
I think I found a solution which permanently fixes the bad merge, and which does not require you to manually check any diffs. The trick is to go back in history and generate commits parallel to the bad merge.
So we have repository with separate branches per maintained version of a single product. Like the situation posed in the question, all changes made on a branch of an earlier version (ie. the bugfixes for in that version) must all eventually be merged to the branches of the later versions.
So specifically, if something is checked in on BRANCH_V8, it must be merged to BRANCH_V9.
Now one of the developers makes following mistake : he merges all changes from BRANCH_V9 into BRANCH_V8 (ie. a merge in the wrong direction). Furthermore, after that bad merge he performs some extra commits before he notices his mistake.
So the situation is as shown in the graphical log below.
o BRANCH_V8 - 13 - important commit right after the bad merge | o BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |\ | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 (ie. last known good state) | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |
We can fix this mistake as follows:
hg update 11
$EDITOR some/file.txt
(this is necessary because Mercurial does not allow empty commits)hg commit -m "generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9"
o BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | o BRANCH_V8 - 13 - important commit right after the bad merge | | | o BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |/| o | BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | | o BRANCH_V9 - 10 - last commit on BRANCH_V9
Merge the newly generated head with the revision in which the bad merge happened, and throw away all changes before committing. Do not simply merge the two heads, because you will then lose the important commit which happened after the merge as well!
hg merge 12
(ignore any conflicts)hg revert -a --no-backup -r 14
hg commit -m "throwing away wrong merge from BRANCH_V9"
The situtation now looks like : o BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9 |\ | o BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | +---o BRANCH_V8 - 13 - important commit right after the bad merge | | o | BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |\| | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |
Ie. there are two heads on BRANCH_V8: one which contains the fix of the bad merge, and the other containing the left-over important commit on BRANCH_V8 which happened right after the merge.
hg merge
hg commit -m "merged two heads used to revert from bad merge"
The situation in the end on BRANCH_V8 is now corrected, and looks like this:
o BRANCH_V8 - 16 - merged two heads used to revert from bad merge |\ | o BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9 | |\ | | o BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | | o | | BRANCH_V8 - 13 - important commit right after the bad merge |/ / o | BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |\| | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |
Now the situation on BRANCH_V8 is correct. The only problem remaining is that the next merge from BRANCH_V8 to BRANCH_V9 will be incorrect, as it will merge in the 'fix' for the bad merge as well, which we do not want on BRANCH_V9. The trick here is to merge from BRANCH_V8 to BRANCH_V9 in separate changes :
In detail:
hg update BRANCH_V9
hg merge 14
hg commit -m "Merging in last good state of BRANCH_V8"
The situation is now : @ BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8 |\ | | o BRANCH_V8 - 16 - merged two heads used to revert from bad merge | | |\ | +---o BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9 | | | | | o | | BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | | | | | o | BRANCH_V8 - 13 - important commit right after the bad merge | | |/ +---o BRANCH_V8 - 12 - wrong merge from BRANCH_V9 | |/ | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |
hg merge 15
hg revert -a --no-backup -r 17
hg commit -m "Merging in bad merge from BRANCH_V8 and its fix and throwing it all away"
Current situation : @ BRANCH_V9 - 18 - Merging in bad merge from BRANCH_V8 and its fix and throwing it all away |\ | o BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8 | |\ +-----o BRANCH_V8 - 16 - merged two heads used to revert from bad merge | | | | o---+ | BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9 | | | | | | o | BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | | | +-----o BRANCH_V8 - 13 - important commit right after the bad merge | | | o---+ BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |/ / | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |
hg merge BRANCH_V8
hg commit -m "merging changes from BRANCH_V8"
In the end the situation looks like this:
@ BRANCH_V9 - 19 - merging changes from BRANCH_V8 |\ | o BRANCH_V9 - 18 - Merging in bad merge from BRANCH_V8 and its fix and throwing it all away | |\ | | o BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8 | | |\ o | | | BRANCH_V8 - 16 - merged two heads used to revert from bad merge |\| | | | o---+ BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9 | | | | | | | o BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | | | o | | | BRANCH_V8 - 13 - important commit right after the bad merge |/ / / o---+ BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |/ / | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |
After all these steps, in which you do not have to check any diff manually, BRANCH_V8 and BRANCH_V9 are correct, and future merges from BRANCH_V8 to BRANCH_V9 will be correct as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With