How to undo a merge on a branch where there have been subsequent commits?

Let's say I have a develop branch where multiple people branch off of to work on either bugfixes or features.

Let's say now I have a branch called featureA that I worked on while some other person worked on featureB and someone else on bugfixA.

I accidentally merged my featureA branch into develop. featureB and bugfixA are also merged into develop but do not need to be unmerged.

Now we carved a branch off of develop with all those merges and called it releaseA. So now I need to unmerge my featureA from releaseAand put a PR for that.

How do I go about this?

Here's what I'm thinking:

  1. git log which will show the commit id for the featureA merge
  2. git reset --merge <commit id for featureA
  3. git commit 'merge revert'
  4. git push to releaseA

Now I haven't tried this because I'm unsure what happens to the other merges if I revert my commit, because featureB and bugfixA merges happened in develop after my merge and don't need to be removed from the releaseA. Only featureA needs to be removed from releaseA and I need to submit another PR for that unmerge.

Will that affect the other merges/commits?

Is this the right track or am I missing something?

Example of git log --oneline --graph

*   f092f2f0f (HEAD -> release/releaseA, origin/release/releaseA, origin/develop, origin/HEAD, develop) Merge branch 'feature/featureB' into 'develop'
|\  
| * f023f02f9 (origin/feature/featureB) commit mesage feat A
* |   f2902f921 Merge branch 'feature/featureA' into 'develop'
|\ \  
| * | 3e2011910 (origin/feature/featureA, feature/featureA) feat: commit feat B
* | |   0131f1921 Merge branch 'bugfix/bugfixA' into 'develop'
|\ \ \  
| |/ /  
|/| |   
| * | f29190198 some commit message
|/ /  
* |   013e9112f Merge branch 'bugfix/bugfixB' into 'develop'
|\ \  
| * | 37f78f300 another rcommit message
* | |   29aef3119 Merge branch 'bugfix/featureC' into 'develop'
|\ \ \  
| |/ /  
|/| |   
| * | a819cd3d6 (origin/bugfix/featureC, bugfix/featureC) update: feat C commit msg
| * | 031119101 fix: some fix
| * | c1ec250d7 fix: some other fix
* | |   771ee7a10 Merge branch 'bugfix/bugfixC' into 'develop'

I think it's important to mention that I ran that command on releaseA branch. releaseA branch is basically carved off develop and I need to unmerge featureA from releaseA but keep it in develop.


Solution 1:

Since you created a new branch, releaseA, for which you want to rewrite history, and you are not messing with the shared develop branch, this should be easy.

Simply use git rebase -i to rewrite the history for releaseA:

  1. The following command will let you rewrite history starting from the parent of the commit you want to remove (f2902f921, the merge commit for featureA):

    git rebase -i --rebase-merges f2902f921^ releaseA
    

    The --rebase-merges switch will replay merge commits as merge commits instead of as simple linear commits. If you don't need this behavior, exclude this switch. See git help rebase for more info.

  2. In the interactive rebase commit list, remove the line for the merge commit, f2902f921. Or replace the word pick with skip. Either way that commit will not be included when the rebase commit list is replayed.

  3. Save and close the rebase commit list. git rebase will now replay the commits in the order given in this list. You will have to deal with any conflicts as commits are replayed.

  4. Check the results. Once you are satisfied, force push the rewritten history for releaseA:

    git push -f releaseA
    

    ⚠️ Warning

    Review the dangers of rewriting history per my link above. That said, given what you want to achieve, there is no way around this.

git rebase -i is one of the most powerful and useful commands in git. Learn it!