44

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:

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?

UPDATE:

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-ff ensures 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 _1234 and 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.

Ethan Brown
  • 26,892
  • 4
  • 80
  • 92
  • 4
    I think this is too much. If your feature branch is that long-lived, you should be doing merges. Otherwise you end up with all of your past commits from master up to the head of your feature branch having not had tests run on them. Your history also ends up being very not representative of what actually happened. – Andrew Marshall Mar 08 '12 at 01:11
  • 2
    Well, this *Use a git wrapper to encourage workflow adhesion* is the cornerstone. You basicly don't use git anymore. You use your wrapper. – Alexander Gorshenev Jul 29 '14 at 11:50
  • 1
    Hi, your step 1 to 3 exactly match my thoughts, but to make things clear, do you still use these 3 steps at the moment? I'm afraid of misunderstand any of your new edits. – Marson Mao Oct 08 '14 at 10:43
  • 2
    Hi, Marson, rarely do we ever do 1-3 anymore. Our rebase craze is over. We've learned that a dogmatic pursuit of a linear commit history is wrong-headed. We still prefer `git pull --rebase`, and rebase to correct workflow issues, but most of the time we're merging branches. – Ethan Brown Oct 08 '14 at 16:17

1 Answers1

27

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.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • 1
    Is it correct that in this case, whichever conflicts are resolved when merging master into the feature branch have to be resolved once again when rebasing? (On the contrary, I see how this is a very convenient workflow if you don't expect merge conflicts) – rethab Jun 01 '16 at 15:23
  • 1
    @rethab: if you turn on rerere (check out `git help rerere`) it may be able to resolve them automatically, depending on whether it can correctly identify the conflict as one it's resolved in the past. – Lily Ballard Jun 02 '16 at 18:30
  • Why do u say : to remove the intermediate "unnecessary" merge. The merge from master may be needed for using some new APIs etc. I think the word unnecessary should be removed from the answer. – cryptickey May 24 '21 at 08:32