Context
We use several git repositories with the same workflow, involving two branches, and are wondering how best to “synchronize” commits from one to the other.
Put simply, our git repositories contain:
- Long living branches
- Two branches:
- master (branch for ongoing development)
- 1.0 (branch for bugfixes only, to maintain a stable version)
- Both branches are both regularly pushed to a public repo
- Sometimes, ongoing development and bugfixes affect the same lines in the same files, so conflicts occur when merging/rebasing/etc.
We also have some less common conditions:
- Unusual ratio: diffs of bugfixes (on 1.0 branch) are much bigger than those from ongoing development (on master branch).
- Sometimes, commits from the 1.0 branch are cherry-picked to the master branch (“urgent” bugfixes needed both for stable releases and for development)
We could illustrate this as follows (commit 5' is a cherry-pick of commit 5 from the 1.0 branch):
-1--2-3--5'--7-- (master) \ 4--5---6-- (1.0)
Aim
Every so often, we need to make sure all bugfixes on the 1.0 branch are available on the master branch.
When doing this, our needs are:
- The 1.0 branch must not be changed (no commits from master go to 1.0 branch)
- Master needs to remain compatible with origin/master, so that we can push to a remote repo. This basically means avoiding rewriting master’s history (unless there’s a magic way to push this we don’t know about!)
- We don’t want to lose the commit history: we need to be able to see whether a commit from the 1.0 branch has been applied to the master branch.
- We’d rather not have to manually resolve conflicts arising from previous cherry-picks, I think git should be able to work this out by itself (as indicated in the man pages).
- In the future, we’ll reach a similar situation again, and need to resolve it the same way, but don’t want to have to remember what resolutions to use for the commits 4 and 6 that we’ve already sorted out once.
So, an illustration of the situation we’re aiming for is:
-1--2-3--5'--7--4'--6'-- (master) \ 4--5---6-- (1.0)
What we’ve tried
- Using a copy of the 1.0 branch, and rebasing it onto master:
- Seems to work
- But if we do the same operation again in the future, we have to review new commits AND old ones
- Rebasing master onto the 1.0 branch
- Works in a local repo
- But can’t be pushed to remote, as this would be rewriting origin/master
- Merging 1.0 branch into master
- All conflict resolution ends up in ONE single commit, so the history doesn’t show the actual modifications previous commits required
- Ideally, we would need a “git merge –interactive”, similar to “git rebase –interactive”: merge branches but interactively choose which commits to include or not, as we go
The question
It seems to us that this is probably a pretty typical use case for git, or for anyone maintaining and developing software in a public git repo, anyway.
How would you go about this?
Thanks!
There are ups and downs to all the choices, and making an informed decision will rely heavily on reading everything you can possibly find. The biggest problem is that git wasn’t really designed from the ground up with any eye toward maintaining multiple “long term” branches, where you may need to maintain changes on a branch for years. So it’s likely you’ll eventually run into merge issues when code bases between branches have significantly changed.
If you read most of the workflow documents, one of the biggest things your read repeatedly is: “apply patches to the bug fix branches and merge them upward”, never ever the other way around.
Here’s what I came up with as the solution I’ve come up with for our Net-SNMP project. I wrote up a Git WorkFlow [in Net-SNMP] page that you might read, as it contains circles and arrows trying to explain how things work with lots of bug-fix branches.
The downside to merging, though, is that the history becomes very non-linear. Which makes reading “git log”, no matter how many options you try and throw at it, sort of confusing.
One of our developers kindly pointed out that we need to mandate the use of ‘git merge –log’, which at least helps the history a bit.
Good luck!