In our company, we generally have a pretty simple structure of git branching.
master
reflects production.develop
reflects work-in-progress before daily release.- Anything else is usually a branch off
develop
.
Due to its simplicity we rarely have to deal with anything too complex, just the odd rebase and conflict when two developers are working on separate branches touching similar code. Any such work is generally solved with something as simple as.
git fetch
git rebase origin/develop my_branch
# fix conflicts
git push origin my_branch -f
However, in less common circumstances we sometimes have a third layer. Two or more developers may be working
on a longer-term special project that should not go into the daily release. Let’s call this project area52
.
area52
has its own branches, while other developers are still pushing commits into their branches off develop
.
Here is what happens if the feature branch area52
is rebased with conflicts, while other branches are waiting
to be merged into it.
The red circle is the point of our problem. Rebasing the area52
branch had conflicts which needed
to be fixed, meaning that all of its old commits have been changed! Now, the branch a52_b
cannot be
merged because it no longer recognises the new commits that it is merging on to. In github you’ll see
this with a52_b
’s pull request suddenly getting huge numbers of commits before it.
We need to take the commits that we have been doing on a52_b
and apply them on to this new version
of area52
. We have tried three solutions to this problem:
-
Never rebase a master branch on to a feature branch, and instead only ever use
git merge
. This removes the above problem but it’s not very clean — you have to deal with every conflict in a single commit, rather than taking each commit at a time. -
git cherry-pick
each work-in-progress commit on to the new version of the feature branch. This will keep the new rebased commit history, but it’s a bit tedious especially if you have a lot of commits to migrate. -
git rebase --onto
to rebase across branches.
--onto
allows us to specify a new base for our rebasing. For the purposes of this problem, it allows us
to take all commits from a set point and drop them anywhere else, including into a different branch,
even between commits.
All we need to do is find the first commit of our branch, in this case 372533c
.
Then, voila!
git fetch
git rebase --onto origin/develop 372533c~1 my_branch
# fix conflicts
git push origin my_branch -f