views:

108

answers:

2

Like most people new to Git, I've had my share of confusion trying to decipher the use cases applicable to git merge and git rebase. I think I've finally decided that, as far as the resulting working copy state, they give you the same thing. Also, they both result in the same conflicts. If this is incorrect, please provide an example to enlighten me.

From my point of view, the main benefit to using rebase instead of merge (if your changes haven't been pushed or pulled) is to keep the history linear. What I really don't understand is the reasoning behind developing git-rerere.

From the manpages, git-rerere is supposed to help you in the case where you're trying to resolve a conflict that you've previously resolved. The example that I'm about to refer to is found at http://www.kernel.org/pub/software/scm/git/docs/git-rerere.html.

If the policy of your project is to not constantly merge changes from the mainline into your topic branch (like the linux kernel), the above example says to create "throw-away" merge commits. Essentially, merge the master into your topic, run tests to make sure everything still works, then do a "git reset --hard HEAD^", essentially throwing the merge commit away. Later on, when you create another "throw-away" merge commit, git-rerere assists you in resolving the conflicts that you've already solved in the frst throw-away merge.

Can someone please explain why instead of going through all the trouble of creating a temporary merge commit, the developer wouldn't instead just rebase his topic onto master? Isn't that the point of git-rebase - get the changes, but avoid the merge? Doesn't this accomplish the same thing, and assuming no one has pulled your topic branch changes, wouldn't this be a much simpler approach? Is the throw-away-merge+git-rerere workflow really just for the case when your changes have been pushed/pulled?

One final question - Linus is quoted as saying "But if I see a lot of 'Merge branch linus' in your logs, I'm not going to pull from you, because your tree has obviously had random crap in it that shouldn't be there..." Would Linus also have a problem with constant rebases?

+2  A: 

Doesn't this accomplish the same thing, and assuming no one has pulled your topic branch changes, wouldn't this be a much simpler approach? Is the throw-away-merge+git-rerere workflow really just for the case when your changes have been pushed/pulled?

You've hit the nail on the head. Rebasing is good for branches you haven't published yet; rerere is useful for branches that you have published (i.e., branches for which rebasing would be inappropriate/annoying for downstream developers).

Would Linus also have a problem with constant rebases?

No, because rebasing doesn't result in merge commits, and, superficially, you can't see that a branch has been rebased, whereas you can easily see if a branch has merged in another branch multiple times.

mipadi
+3  A: 

You're correct that rebasing and merging can produce the same final tree. The history they produce is very different, though, and version control is of course all about history. You've voiced a preference for linear history; that's indeed desirable on a local scale, while merges help to record the larger scale interaction of features, bugfixes, and so on.

The simple answer to your central question (why not just rebase) is that sometimes there's a right place for a branch to start, and if that's the case, you should merge, not rebase.

For example, you could have a bugfix which applies to a maintenance release as well as the current release; you'd want to branch it from the latest commit which is an ancestor of both releases. You can't ever rebase that branch forward along either the master or the maintenance branch, or you'd no longer be able to merge it into the other.

Similarly, all topic branches start somewhere. At some point, you want to preserve that history. You mention one of the clear-cut cases (others have pulled your work), but it could be something far less obvious. Maybe there's some interaction with other features, maybe this feature has subfeatures, and you're trying to keep that entire hierarchy preserved.

So, sure, if the branch is local and there are no reasons to keep its base fixed, it is as you say much simpler to just rebase it and be done. But sometimes that's not the right thing to do.

Your final question is actually about something very different, nothing to do with merge conflicts. Linus said that in the context of merges which didn't need to be done. This is very much an issue of branch and merge philosophy. Junio Hamano wrote an excellent blog post about that very issue. A brief quote which sums up the topic at hand:

when you merge a topic branch 'add-frotz' to your 'master' branch, you are obviously incorporating the new 'frotz' feature, but more importantly, you are stating that it is desirable to have that 'frotz' feature in the 'master' branch

Linus doesn't want to see you merging this odd branch called 'linus' all the time, because clearly you're not merging some specific topic which is desirable to have in your branch. You're repeatedly merging the upstream branch into the topic branch, which is entirely the wrong direction. You don't need all the stuff from master (or linus) to develop your topic. You should finish your topic, then merge it the other way, upstream into master! It's not desirable to have all of the content of master in your topic branch. (And a single developer's master is really a topic branch, as far as an integrator's concerned.)

So, Linus doesn't have a problem with frequent merges; he has a problem with purposeless and counterproductive merges. (And generally, if you make sure your merges are done for a good reason, they won't be frequent - and they'll almost never be downstream merges.) If your rebases are done for a good reason, and they make the history better, then they are a good thing, however frequent.

Jefromi
Great, thanks for the reply. The bugfix comment - branch from the latest commit that's an ancestor of both, was actually very enlightening. It seems like a simple idea, but I was under the impression that one should branch from the tip of a maintentance branch, and then cherry-pick the bug fix over to the master. This approach though seems much cleaner, and does prevent necessarily prevent rebasing.
Josh
@user370562: The principle here is to merge upstream/upwards, and to create your branches in such a way that that's possible. (Incidentally, maintenance branches quite often can be merged straight back into master; the bugfixes you've made on v5 likely still apply to the in-development v6.) Most suggestions of workflows emphasize merging upwards - see `man gitworkflows`, for example. Glad to be of help!
Jefromi