Any idea what is causing this?
The choice of merge base commit, of course. This choice is not yours: Git makes it automatically, based on the commit graph. That's why this step:
so we created hotfix branch based off of master
was probably the founding error (or "root cause", if you prefer).
You can see which commits Git considers to be the merge base(s) for develop
and master
using:
git merge-base --all develop master
This will print out one or more commit hash IDs.1 If it prints just one hash ID—which is the usual case—then that one hash ID is the hash ID of the merge base commit. If it prints two or more, Git will construct a merge base (for git-merge-recursive
anyway) by merging the listed commits; these situations are tricky.
Remember that git merge
is all about combining changes, yet Git does not store changes at all. Git stores snapshots: each commit holds a full snapshot of every file, as a sort of frozen-in-time archive, saying that this is how the project is at this (snapshot) time. The only way to turn snapshots into changes is to take at least two snapshots and compare them, as if playing a game of Spot the Difference.
Spotting the differences between the two branch tips (master
and develop
) is not useful, because applying those differences to one of the branch tips would just produce the other snapshot. (In which case, why are we even bothering with branches?) So that's not how merge works.
Instead, merge finds a snapshot that is somewhere in the past, back before master
and develop
diverged. By comparing that snapshot with the one at the end of master
, Git can see what has changed in master
. By comparing that same snapshot with the one at the end of develop
, Git can see what has changed in develop
. And then, Git can combine these two sets of changes, so as to keep your changes, but add their changes as well.
This process is simple—well, relatively simple—algorithmically (though a lot of work in practice, which is why we have the computer do it). In some cases it's too simple, and produces the wrong results. Yours would appear to be one of those cases.
If you find the commit where the bug itself is introduced (some point in the past, perhaps the distant past) and make a branch where you fix that bug right after its introduction, you can then merge this branch into both master
and develop
. The changes from this branch's merge base, with either master
or develop
, to this branch's tip will be just the fix. The changes from this merge base to the tip of master
, or the tip of develop
, will be everything else that happened. So merging this branch into both master
and develop
will typically produce the correct result, without introducing other, unwanted changes. But that's often more difficult—sometimes much more difficult—than just making a hotfix branch for master
. So the choice is yours.
You could consider using git cherry-pick
as a way to copy the hotfix to the develop
branch. Cherry-picking has its own hazards—for instance, if the hotfix proves to have its own bug(s), now you need to fix it twice—but it's another tool in the shop. Git is a big shop with a lot of tools. Pick the correct saw, file, hammer, plane, screwdriver, drill, and so on, and you'll get a lot of good things done. Pick the wrong one and ... well...
1It could potentially print none at all, if the histories were unrelated, but in that case you would have seen an error about unrelated histories.