I am helping with integration of a long-running project back into the mainline. One of the feature branches that wants to come back is itself an octopus merge of (not a single octopus merge, but several merges over time) many other "mini-feature" branches.
Each of these mini-feature branches has, interspersed amongst its unique content, a common set of commits that got cherry-picked into each respective mini-feature branch. I want to clean up all these common commits before merging back into the mainline.
What might be relevant is that the feature branch's current state is "correct", i.e., the way the code looks is what it's supposed to look like.
Options I've tried:
- straight-up
rebase -i
: led to a lot of conflict, likely from cherry-picks having been applied to multiple mini-feature branches and been resolved rebase -i -p
: this results in exactly the same branch structure with common commits duplicated
Under normal circumstances, I would simply wave the white flag of surrender and do a squash-merge. However, what I need to do is replace these common commits with a new and improved set of common commits--which suggests that a rebase is the most appropriate way to proceed.
What would be ideal is to use rebase -i
with a conflict-resolution strategy that tries to resolve the conflict by selecting the conflict choice that makes the result look most like the end goal (the current octopus feature branch). But I don't know how to tell git to do that.
My questions:
- Is there some way I can tell git to resolve conflicts in the way described above?
- Is there a better, cleaner way to achieve my goal?
- Am I resigned to reconstructing these mini-feature branches by hand using rebase?
I'm going to assume you understand a lot about merging (in terms of finding—or in some cases, creating—a common merge base, diffing the end-points against the merge base, and combining the diffs) and jump straight into these answers:
No. In some cases, there is an easy answer: just use the final version. But this only works when the final version is the right version. The somewhat vague text description you gave suggests to me that you want, at this point, to pick an intermediate result that will, on a future merge with whatever its merge-base(s) and branch-tips will be, present minimal amounts of conflict.
This is (obviously) a difficult problem, perhaps even NP-hard or NP-complete (proof by vigorous hand waving :-) ). There are too many degrees of freedom in the interior nodes.
Maybe. To find out, draw what sort of graph you have now, and what sort of graph you would like to have in the end. (That is, draw the commit structure, with its branches and merges, and some approximation to the set of commits that will be in each.) Label the commits with approximate content and use that to envision the merge steps. Rearrange the intermediate structures according to what you want to keep and what you're willing to discard, to get whatever it is you value.
Rather than rebase (which copies commits and then moves branch labels), consider constructing the new structure using
git cherry-pick
(which copies commits—there's no branch label motion, except for the current branch being advanced in the usual way). This way you are only adding structures along side the existing ones. You can build them up until they are complete (adding merges usinggit merge
whenever you like), and then when all is/are complete, then you declare your "flag day" and make everyone switch from the old commits (named by the existing branch names) to the new commits (everyone who has the old commits must obtain the new ones and move their branch names to point to the new ones).