Reputation: 21
The wrong development branch was merged into a main branch.
The same repository holds the following branches and corresponding content. Initially the respective main and dev branches have the same state.
design-main
design-dev
D-files-v1 (the design itself - can never contain any test files)
test-main
test-dev
D-files-v1 (the design itself - latest from main design branch)
T-files-v1 (files for testing the design in D-files)
Git repo policy restrictions
design-dev
D-files-v2
test-dev
D-files-v1
T-files-v2
design-main
D-files-v2
test-main
D-files-v1
T-files-v2
design-main
D-files-v2
test-main
D-files-v2
T-files-v2
In the step 3 (above), the wrong branch was merged into design-main. Instead of design-main -> test-main, the actual merge was test-dev -> design-main! The state of design-main after the merge is:
design-main
D-files-v2
T-files-v2 [these files are not allowed in this branch]
The most straightforward way to resolve the issue is to move HEAD of design-main back 1 commit, just prior to the merge [git reset --hard HEAD~1]. Then do the design-main -> test-main merge (as was the original intent) This is not allowed due to git policy restrictions on rewriting history, and developer not having privileges to push main branches
Git revert seems to be the ideal solution [git revert -m 1]. Indeed, this does restore the state of design-main to that of the pre-merge state. But (my understanding) it achieves this by adding a commit that undoes what was done in the previous merged. And in doing so, history is preserved.
With design-main restored to its' desired state, we can now proceed with step 3, above, and merge design-main -> test-main. After this merge, test-main is not in the desired state - the D-files are updated but the T-files have been deleted!
test-main
D-files-v2
T-files-v2 <-- these gone after the merge
Two different methods - git reset --hard and git revert - both restored design-main to the pre-merge state. But subsequent merges of fixed design-main -> test-main did not act the same way.
Could someone suggest a combination of dev branch changes and merges that would result in the main branches being in the state described in step 3, above?
It would be helpful to also know why the git revert approach caused subsequent merges to not work as expected.
Upvotes: 1
Views: 66
Reputation: 30297
I think this is what I would do. Suppose that you mean to merge B
into A
.... Like this:
* Merge of B into A (HEAD -> A)
|\
* | Commit in A
* | Commit in A
* | commit in A
| * Commit in B (B)
| * Commit in B
| * Commit in B
but this is what you did instead:
git checkout B
git merge A # the complete opposite
Took care of all conflicts and wrapped it up... So you end up with this:
* Merge of A into B (HEAD -> B)
|\
* | Commit in B
* | Commit in B
* | commit in B
| * Commit in A (A)
| * Commit in A
| * Commit in A
Ok.... No need to panic. The problem is not so much about getting the contents of the branches correctly set.... That can easily be done with a revert.... The real problem is that to git commits from A
are part of B
now and that will influence merges later on because they won't be considered depending on the direction of later merges.
In order to pull this off we need to convince git of a few things:
To pull this off, we will need to do a few tricks.
First, let's create, right after the wrong merge we did, a new commit that will make stuff look the way it did in their latest common ancestor... Let's work on a temporary branch so that it is clearer what we will do:
git checkout -b temp B
git branch bad-merge # will use it later
git restore --worktree --staged --source $( git merge-base HEAD^ HEAD^2 ) -- .
git commit "creating a new commit with common ancestor content to get out of the mess of the wrong merge of A into B"
Now let's trick each branch to believe that they have that common ancestor and that their contents are the way they were before the wrong merge:
git checkout A
git merge --no-ff -s ours -m "Hacking A to believe there's a new common ancestor with B to fix a busted merge" temp
# we use -s ours so that the content of the new commit does not pick up anything from the branch that is being merged
# B is a little trickier because it has already moved from where it was
git checkout B~
# working on detached HEAD, it's all good
git merge --no-ff -s ours -m "Hacking B to believe there's a new common ancestor with A to fix a busted merge" temp
# let's place B over here
git branch -f B
Now.... I think we achieved the impossible... The branches are the way they were before the bad merge, diffs against their common ancestor should give you the same results as before the merge and blaming should take the right path on each branch for analysis.
The cherry on top will be to reuse the previous bad merge to do the merge in the right direction:
git checkout A
git merge -s ours --no-commit B
git restore --worktree --staged --source bad-merge -- .
git commit "Merging B into A"
And we are ready to rock and roll as usual.
If you have been working on top of the bad merge with the easy (but busted) way around with reverts and getting unexpected merge results later on then it is sounding like something to look at more closely so that you can also include that work past the busted merge in the... recovery process.
Upvotes: 1