views:

12085

answers:

7

I've been using git now for a couple months on a project with one other developer. I have several years of experience with svn, so I guess I bring a lot of baggage to the relationship.

I have heard that git is excellent for branching and merging, and so far, I just don't see it. Sure, branching is dead simple, but when I try to merge, everything goes all to hell. Now, I'm used to that from svn, but it seems to me that I just traded one sub-par versioning system for another.

My partner tells me that my problems stem from my desire to merge willy-nilly, and that I should be using rebase instead of merge in many situations. For example, here's the workflow that he's laid down:

clone the remote repo
git co -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git co master
git merge my_new_feature

Essentially, create a feature branch, ALWAYS rebase from master to the branch, and merge from the branch back to master. Important to note is that the branch always stays local.

Here is the workflow that I started with

clone remote repo
create my_new_feature branch on remote repo
git co -b --track my_new_feature origin/my_new_feature
..work, commit, push to origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, push to origin/my_new_feature
git merge master
..finish my_new_feature, push to origin/my_new_feature
git co master
git merge my_new_feature
delete remote branch
delete local branch

There are 2 essential differences (I think): I use merge always instead of rebasing, and I push my feature branch (and my feature branch commits) to the remote repo.

My reasoning for the remote branch is that I want my worked backed up as I'm working. Our repo is automatically backed up and can be restored if something goes wrong. My laptop is not, or not as thoroughly. Therefore, I hate to have code on my laptop that's not mirrored somewhere else.

My reasoning for the merge instead of rebase is that merge seems to be standard and rebase seems to be an advanced feature. My gut feeling is that what I'm trying to do is not an advanced setup, so rebase should be unnecessary. I've even perused the new Pragmatic Programming book on git, and they cover merge extensively and barely mention rebase.

Anyways, I was following my workflow on a recent branch, and when I tried to merge it back to master, it all went to hell. There were tons of conflicts with things that should have not mattered. The conflicts just made no sense to me. It took me a day to sort everything out, and eventually culminated in a forced push to the remote master, since my local master has all conflicts resolved, but the remote one still wasn't happy.

What is the "correct" workflow for something like this? Git is supposed to make branching and merging super-easy, and I'm just not seeing it.

+2  A: 

With Git there is no “correct” workflow. Use whatever floats your boat. However, if you constantly get conflicts when merging branches maybe you should coordinate your efforts better with your fellow developer(s)? Sounds like the two of you keep editing the same files. Also, watch out for whitespace and subversion keywords (i.e., “$Id$” and others).

Bombe
+29  A: 

"Conflicts" mean "parallel evolutions of a same content". So if it goes "all to hell" during a merge, it means you have massive evolutions on the same set of files.

The reason why a rebase is then better than a merge is that:

  • you rewrite your local commit history with the one of the master (and then reapply your work, resolving any conflict then)
  • the final merge will certainly be a "fast forward" one, because it will have all the commit history of the master, plus only your changes to reapply.

I confirm that the correct workflow in that case (evolutions on common set of files) is rebase first, then merge.

However, that means that, if you push your local branch (for backup reason), that branch should not be pulled (or at least used) by anyone else (since the commit history keep to be rewritten by the successive rebase).

VonC
For a technique that allows rebasing *and* sharing, see http://softwareswirl.blogspot.com/2009/04/truce-in-merge-vs-rebase-war.html
mhagger
+5  A: 

In your situation I think your partner is correct. What's nice about rebasing is that to the outsider your changes look like they all happened in a clean sequence all by themselves. This means

  • your changes are very easy to review
  • you can continue to make nice, small commits and yet you can make sets of those commits public (by merging into master) all at once
  • when you look at the public master branch you'll see different series of commits for different features by different developers but they won't all be intermixed

You can still continue to push your private development branch to the remote repository for the sake of backup but others should not treat that as a "public" branch since you'll be rebasing. BTW, an easy command for doing this is git push --mirror origin .

The article Packaging software using Git does a fairly nice job explaining the trade offs in merging versus rebasing. It's a little different context but the principals are the same -- it basically comes down to whether your branches are public or private and how you plan to integrate them into the mainline.

Pat Notz
+5  A: 

Anyways, I was following my workflow on a recent branch, and when I tried to merge it back to master, it all went to hell. There were tons of conflicts with things that should have not mattered. The conflicts just made no sense to me. It took me a day to sort everything out, and eventually culminated in a forced push to the remote master, since my local master has all conflicts resolved, but the remote one still wasn't happy.

In neither your partner's nor your suggested workflows should you have come across conflicts that didn't make sense. Even if you had, if you are following the suggested workflows then after resolution a 'forced' push should not be required. It suggests that you haven't actually merged the branch to which you were pushing, but have had to push a branch that wasn't a descendent of the remote tip.

I think you need to look carefully at what happened. Could someone else have (deliberately or not) rewound the remote master branch between your creation of the local branch and the point at which you attempted to merge it back into the local branch?

Compared to many other version control systems I've found that using git involves less fighting the tool and allows you to get to work on the problems that are fundamental to your source streams. git doesn't perform magic, so conflicting changes cause conflicts, but it should make it easy to do the write thing by it's tracking of commit parentage.

Charles Bailey
You are implying that the OP has some undiscovered rebase or mistake in his process, right ?
krosenvold
+2  A: 

DO NOT use git push origin --mirror UNDER ALMOST ANY CIRCUMSTANCE.

It does not ask if you're sure you want to do this, and you'd better be sure, because it will erase all of your remote branches that are not on your local box.

http://twitter.com/dysinger/status/1273652486

Scott Brown
+4  A: 

In my workflow, I rebase as much as possible (and I try to do it often. Not letting the discrepancies accumulate drastically reduces the amount and the severity of collisions between branches).

However, even in a mostly rebase-based workflow, there is a place for merges.

Recall that merge actually creates a node that has two parents. Now consider the following situation: I have two independent feature brances A and B, and now want to develop stuff on feature branch C which depends on both A and B, while A and B are getting reviewed.

What I do then, is the following:

  1. Create (and checkout) branch C on top of A.
  2. Merge it with B

Now branch C includes changes from both A and B, and I can continue developing on it. If I do any change to A, then I reconstruct the graph of branches in the following way:

  1. create branch T on the new top of A
  2. merge T with B
  3. rebase C onto T
  4. delete branch T

This way I can actually maintain arbitrary graphs of branches, but doing something more complex than the situation described above is already too complex, given that there is no automatic tool to do the rebasing when the parent changes.

Alex Gontmakher
You could achieve the same with just rebases. The merge is actually not necessary here (except if you don't want to duplicate the commits - but I hardly see that as an argument).
odwl
Indeed I don't want to duplicate the commits. I would like to keep the in-flight structure of my work as clean as possible.But that's a matter of personal taste and is not necessarily right for everybody.
Alex Gontmakher
Wow, what an intriguing solution! Have to look at it again...
UncleCJ
Very nice solution!
orip
+2  A: 

I have one question after reading your explanation: Could it be that you never did a

git checkout master
git pull origin
git checkout my_new_feature

before doing the 'git rebase/merge master' in your feature branch?

Because your master branch won't update automatically from you're friends repository. You have to do that with the 'git pull origin'. I.e. maybe you would always rebase from a never-changing local master branch? And then comes push time you are pushing in a repository which has (local) commits you never saw and thus the push fails.

knweiss