Question

I work with a feature branch model where the branches get merged into master when they are ready. Before a larger test (specific to my field of work, involving a lot of heavy hardware), several feature branches (individually tested using simulations) are merged into one "test" branch.

Before the actual test, the "test" branch is again tested using simulations. If, during this test, issues arise due to the way the individual branches behave together (or other bugs arise that weren't seen before), I'd like to be able to make a fix on the individual feature branch but keeping the "test" branch merged.

At the moment, I usually delete the test branch (the merge commit), make the fixes on the respective feature branch and then merge all the relevant branches into the "test" branch again. This is a bit tedious. Of course, I could make some small scripts etc. But in my situation, I have multiple repositories with "test" branches all with different feature branches merged together and writing scripts for all those ... I'd rather look for a better solution.

My question: Is there a way to have some sort of "dynamic" merge, so that, when I add a commit in a merged feature branch, the "test" branch merge is updated with that commit just as if it had been merged with that commit already on the feature branch.

I hope the idea is clear. If not, please let me know so that I can clarify.

Was it helpful?

Solution

Git doesn't have particularly good support for this. The conceptual problem is that each Git commit represents a project state, not a diff. Merging two branches means combining the differences between the two branches' project states. Typically this is easy because Git applies non-conflicting changes from either branch. But when you add a commit to a feature branch and then merge it again, that will be roughly as difficult as the first merge and you will potentially have to solve many of the same conflicts again. Although git-rerere can be locally enabled to reuse a recorded merge conflict resolution from a previous merge:

Another application of rerere is where you merge a bunch of evolving topic branches together into a testable head occasionally, as the Git project itself often does. If the tests fail, you can rewind the merges and re-do them without the topic branch that made the tests fail without having to re-resolve the conflicts again.

To enable rerere functionality, you simply have to run this config setting:

$ git config --global rerere.enabled true

Git Book / Pro Git 2nd ed. by Scott Chacon, Ben Straub.

Consider why you need the feature branches and what the test branch represents. If the test branch will move on to become something like a release candidate, there is limited value in committing additional changes to the feature branches (and then having to merge again). Instead, consider committing the change as a hot fix directly on the test branch. If the test branch is not the basis for a release, note that you are testing a different system than you are releasing, especially as merge conflicts might be resolved differently.

You can also cherry-pick or rebase the fix from the feature branch to the test branch, or vice versa. This duplicates the changes in the commit on top of another history. There might be conflicts, but typically more limited than a full merge.

Neither merging nor rebasing can auto-update a merge commit. It is in principle possible to amend the merge commit or to squash two merge commits, but this is comparatively tricky to do. The easiest approach would be to first reach and commit the project state that you want to have after the merge, then record a new history that produces this state: reset the test branch to before that merge, start an octopus merge of all branches but --no-commit the merge, then use git-checkout to restore the working tree tho the project state that you want, and commit the result as the merge.

The best way to avoid merge conflicts in Git is to avoid long-running branches. I understand this is not always possible, but integrating features early (and possibly protecting them with feature toggles) will cause any conflicts to be raised and solved early, and only once. Similarly, an architecture where you can add new features without modifying lots of existing code will avoid conflicts. In short: shifting complexity from the branching model to the code itself.

Licensed under: CC-BY-SA with attribution
scroll top