Git Workflows: Rebasing Published/Shared Branches
Our team at work has enthusiastically adopted a rebase workflow, but we might have gotten a little carried away, which is the point of this question: you be the judge.
Using pull –rebase is a no-brainer to me now. However, we also have large feature branches that multiple people work on. Periodically, we want to bring in changes that are happening on master. Conventional wisdom would have us merge since it’s a shared branch. However, in our rebase-obsession, we’ve decided to rebase those branches. Of course that requires everyone’s cooperation. The workflow goes something like this:
- Using git rebase and git merge to set up a good team workflow
- Git squash all commits in branch without conflicting
- Git branch behind remote after rebase
- How do I update my feature branch commits to be on top of the updated master branch?
- How to squash all git commits into one?
- Git Commit during Git Rebase - what really happens?
1) The rebaser coordinates with everyone to make sure they’re all checked-in and pushed on the feature branch, then asks them to do no more work on that branch until they get the all clear.
2) The rebaser rebases the feature branch onto master, deletes the remote feature branch (git push origin :feature), and then pushes the new, rebased feature branch (git push origin feature)
3) The rebaser has everyone fetch, which updates their feature branch, then delete their local feature branch (git branch -D feature), then create a new local feature branch that tracks the remote feature branch. Everyone then gets the all-clear.
This workflow is working, partially because we’re a small group, and the work interruptions are small. However, I worry that we’re learning poor Git habits (rebasing a shared branch), or that the workflow won’t scale well.
On the upside, our repository history is lovely.
What do you think, Git gurus? Are we playing with fire, or rocking a reasonable workflow?
It’s two years since I originally asked the question, and my has our workflow changed since then. We still routinely do
git pull --rebase, so that hasn’t changed. It’s common sense, and it prevents all the unsightly, confusing mini-merges. (We keep mostly in sync, so there’s little cost to
git pull --rebase).
Other than that, however, and the occasional heroic action to correct a mistake, we are over our rebase madness. Merging makes sense most of the time. However, wanton merging is problematic, and does contribute to the confusing, chaotic history I was concerned about two years ago.
Our solution has several components:
The master branch is “pristine”. Topic branches get merged in, and once they do, the topic branch is retired. In other words, merging a topic branch in signifies that that work is ready for production, and is now part of the master branch. Looking at our version history, it’s very clear what’s happening: topic branches getting merged into master, and that’s that.
We use disposable integration branches when necessary. For example, if we have topic branches A, B, and C, and none of them are ready for integration into master, but we need to test them together, we just create a QA branch (usually off of master), and then merge A, B, and C in. At some point, the QA branch is deleted or re-used. The point is that it’s not intended to be permanent in any way, and it doesn’t have the same restrictions as the master branch (you can merge in your topic branches as many times as you want). If the history gets too confusing, you can just delete the QA branch and start fresh (an approach we have found to be very natural).
When merging, always use
git merge --no-ff. This is such a tremendous reversal from our “linear commit history” obsession from two years ago that it deserves comment. Now that we’ve relaxed about a linear commit history, and seen that merges are good and useful, we’ve started to rely on topic branches being actual branches off of master, not just a series of commits that eventually becomes one with master.
git merge --no-ffensures there’s always a merge commit, even when it’s not necessary.
We have a well-understood convention for commit messages and branches and, more importantly, it cross-references our issue-tracking system. Our issue tracking system uses numeric issue numbers, and for any feature (or defect), we have an issue number (1234, for example). If you’re working on that issue, you would create a branch
_1234and start every commit message with
"_1234: doing blah blah". It may seem a little obsessive, but it has really worked well for us, and significantly reduced/eliminated confusion.
Use a git wrapper to encourage workflow adhesion. This is something we’re currently working on, but we’ve realized is entirely necessary to prevent simple and understandable mistakes, like branching off of the wrong thing (we recently had a complete disaster when a developer created a topic branch off of a disposable QA branch: that topic branch was approved to go live, it was merged in…and a bunch of changers that weren’t approved to go live were sucked in). Our git wrapper will require confirmation whenever you do something unusual (like creating a branch off of anything but master, creating a branch not named _NNNN, making a commit that doesn’t start with _NNNN, etc.). Occasionally, we do need to do these things, so the wrapper doesn’t prevent it, but it does keep people from accidentally doing something they shouldn’t.
One Solution collect form web for “Git Workflows: Rebasing Published/Shared Branches”
This sounds like it’s overly complicated and won’t scale well. It’s probably a heck of a lot simpler just to merge
master into your feature branch periodically, and then when it comes time to merge back into master, then you can do the rebase first (to remove the intermediate unnecessary merge) and merge back into master (presumably with
--no-ff to produce a merge commit). This only requires one person deal with the rebase, and they don’t have to do any coordination because nobody else needs to force-update anything (because, presumably, the branch is going to be deleted after it’s merged, rather than kept around in a rewritten state). You may also decide to skip the rebase entirely and just leave the intermediate merges in place, which would more accurately reflect your development workflow and remove the risk of producing commits that don’t actually build.