Git: Work on merged branch, which was unmerged afterwards

Viewed 216

I am having the following problem: I worked on a branch A. Branch A was merged into the Master branch, so far so fine.

After some time, the customer decided, to undo the changed, done in Branch A on the Master branch, because it has to be updated.

So the history in the Master branch looks like :

Some stuff
Some other stuff
Some stuff, where the merge of Branch A was reverted
Some other stuff
Some other stuff
Branch A was merged into master
The commit of Branch A (Branch A contains one Commit)

I want to rebase Branch A, so I want something like:

The commit of Branch A (From here I want to continue working)
Some stuff
Some other stuff
Some stuff, where the merge of Branch A was reverted
Some other stuff
Some other stuff
Branch A was merged into master
The commit of Branch A (Branch A contains one Commit)

Note that I don´t want to change the history of my remote master, I need the commit of Branch A to be rebases on the last commits of my master. Then I want to push my updated Branch A to Master again.

Now I want to continue working on Branch A, but first I want to rebase Branch A on the current master branch, to have the newest state.

The problem is now: When I rebase my Branch A on master, git seems to delete the changed I did in Branch A. I think this is because of the merge reverse in the Master branch. So: Git sees, that Branch A was deleted in the master branch, so when I rebase Branch A on master, the deletion will be done too.

I don´t want to copy the files I changed in Branch A one after one on another Branch, which was checkout out of master. I think there is a better solution, any recommendations?

3 Answers

Well, that's a bit tricky. Since the HEAD of your BranchA is already part of master, git considers the code in the master to be the newer one than the BranchA. In this case, it would have been better if you guys had reset the changes in the master branch instead of reverting them with an explicit commit.

All of the following options involve rewriting history, so try them in some temporary branch first.

One option is to change the master branch using rebase --onto such that both merge and revert commit are removed.

git rebase --onto <commit_id_before_merge_commit> <revert_merge_commit_id> master

Second is to change the commit hashes in BranchA such that git won't recognize them the same as in master.

git checkout BranchA
git reset HEAD^n # n is the number of commits that are exclusive to branchA
git add -u # Add all the files. Make sure to add newly created ones.
git commit -m 'some message'
git rebase master # should work now

Thirdly, you can also use interactive rebasing to change the commit ids in the BranchA

git rebase -i <first_exclusive_initial_commit_id_of_branchA>

If you use git checkout branch-a && git rebase -i master, you will get a list of commits in your text editor with the pick command in front of each. You can change this to one of several options, all of which are explained in the given text. For example, you can skip the revert commit and those changes will not be reverted.

Another potential solution is to rebase as you have then git revert the commit which reverted the merge of branch-a.

Since you don't want to touch the history of master, you need to reapply your changes to the top of master, ignoring the fact that they were already applied and reverted. Depending on the exact nature of the reverting commit, lets call it <revert>, I see exactly two ways to accomplish this:

  1. If <revert> only undid your changes, then you can just revert it again to get a commit that reapplies your changes (think --x == x).

  2. If the reverting commit is not pure, I would approach this situation like this:

    • Create a patch sequence for your branch with git format-patch.

    • Apply this patch sequence to master with git am.

    The point is, that git am is agnostic of history: It only looks at the current commit's state and applies the patches to that.

Related