A pain I have with rebase workflow is that it creates untested commits (because diffs were blindly applied to a new version of the code). If I rebase 100 commits, some of the commits will be subtly broken.
The biggest part of my problem is a total lack of commit discipline but there are times when I'm working on a branch where my commits don't tell a clear story (changed something then changed it back because I decided to do it a different way). That's when I most wish for better ways to tell that story.
I feel like an idiot for not knowing rebase could solve some of this for me. ...will definitely try it next time.
A problem with rebase workflows that I don't see addressed (here or in the replies) is: if I have, say, 20 local commits and am rebasing them on top of some upstream, I have to fix conflicts up to 20 times; in general I will have to stop to fix conflicts at least as many times as I would have to while merging (namely 0 or 1 times).
Moreover, resolution work during a rebase creates? a fake history that does not reflect how the work was actually done, which is antithetical to the spirit of version control, in a sense.
A result of this is the loss of any ability to distinguish between bugs introduced in the original code (pre-rebase) vs. bugs introduced while resolving conflicts (which are arguably more likely in the rebase case since the total amount of conflict-resolving can be greater).
It comes down to Resolution Work is Real Work: your code is different before and after
resolution (possibly in ways you didn't intend!), and rebasing to keep the illusion of a total ordering of commits is a bit of an outdated/misuse of abstractions we now have available that can understand projects' evolution in a more sophisticated way.
I was a dedicated rebaser for many years but have since decided that merging is superior, though we're still at the early stages of having sufficient tooling and awareness to properly leverage the more powerful "merge" abstraction, imho.
I don't like rebases when it is for a bunch of commits, it gives a straight version graph which everyone likes. But when you have conflicts, might have to fix them more than once.
From what I know, rebase creates a patch for each commit and then starts applying them on top of your master (if you are doing it on top of master). So, this has the unintended consequence of causing merge conflicts for multiple commits. With merging you just have resolve conflicts once.
I'm not entirely sure what your problem is but you could try deferring the rebase using !fixup and !squash commits. I prefer this workflow during code reviews because then reviewers can see exactly what's changing.
I tended to use git rebase constantly before pushing, until I realized how toxic it really is.
In the original commit before rebase everything seemed to be working; but someone changed something else in the code you were relying on without creating a direct conflict, and now all your rebased commits crash and burn. What's worse, you discover it much later, when original commits are long gone, and instead of one merge commit being the easy traceable breaking point, you now don't even quite remember which exact commits were rebased and have to check EVERYTHING.
Or, and with merge commit you could've easily just check it right there with simple tests before committing — no such convenient option for rebase.
The thing is, it's a leaky abstraction. Rebase workflow tries to present things simpler then they really are, and you end up paying a price for it.
Rebase is absolutely a part of my everyday workflow, and the rest of my team's workflow as well. (Yesterday I had to show our CTO how to use it, because the rest of the team was getting annoyed by his merge commits.)
Our workflow is:
- locally, commit to local master or a local branch
- occasionally checkout local master (if necessary) and pull using the 'rebase after fetch' option.
- if we had local master commits, fixup any conflicts in our code
- if we were working on a local branch, checkout that branch and rebase it on the new master, fixing any conflicts that arise
- If our work is complete, possibly do a final rebase to reorder and squash local commits, and fast-forward merge to master if we were on a branch. Finally, push the local master to share our work.
Note that we never rebase anything that's been pushed.
Also, if we're worried that a rebase is potentially complex and error prone, we create a new branch at the existing HEAD so that the old commits don't get lost in the reflog. Once the rebase is done, we can delete that branch so the old commits can be garbage collected.
My biggest problem with rebase is I have often found commits in the middle of a rebased sequence which don't build -- which makes sense as after rebasing you end up with commits which have never been tested, and never "existed" before.
For example, if I commit something on a branch, and then later realize I made a typo in that commit, I can make a commit that fixes the typo, then use git rebase -i and make it a fixup commit. Fixup squashes the commit into another one, and discards the message.
Interesting. Can you explain your rebase workflow a bit? I never thought of merge and rebase as overlapping that much, although I only use rebase to combine commits together.
in my experience, rebase works great if the commits are structured and much more painful with lots of overlapping changes, say by continiusly doing _wip_ commits every hour
How do you deal with that?
reply