I’ve used git successfully professionally for almost 10 years and I’m not lying when I say I’ve used rebase about 5 times. We just merge. We squash sometimes. I think it must be because I’ve always worked in smaller orgs but rebase just seems to always be over complicating something simple (a merge). I get that there’s a benefit of a cleaner history but to me the benefit of simplicity merge offers makes it superior
Done right and understood, it is as simple as merging, if conflicts arise no difference to conflicts via merge?
But you get rid of those ugly merges back and forth and unviewable histories.. Not sure why not more people care, I guess it is a little OCD I do that for my stuff.. however the 20-50+ user projects where everyone, often multiple times merges develop or even other branches in.. really unusable history right away, lets not talk about any point later. Git has derailed to a mere backup system where you can jump back, but understanding changes later becomes impossible :(
What people also rarely know: A linear history is without lies, but one can sneak additional changes into a merge commit that was in neither branch quite easy - I hate that!
Most people don't care about most stuff, it's pretty normal.
But here specifically: Most developers don't read code, nor documentation, and essentially no one reads commit messages. VCS is a WOLM - write-only linear memory - to most developers. That's probably also why most people don't care to write any kind of reasonable commit message - the software requires them to input something, so something is input - and also do not care about unreadable (and also unusable) VCS histories. They're never looking at it, it's only written. Hence it's meaningless for them if it is extremely difficult to trace changes or hard to bisect, they don't even attempt such things. There's a reason the history browsers in all the online tools and IDEs effin suck, it's because on average nobody uses history.
I know, I know, this gets across very elitistically, but it's just how most people do their jobs. They get bugged to do a thing, so they sort of do the thing in order to not be bugged any more about it. I'm pretty sure that caring for these things is some kind of OCD like you say, i.e. a mental disorder.
My biggest issue with rebase from a learners perspective is that you get conflicts that simply don’t happen with merge.
I’ve had experiences where I’m trying to rebase a branch and it keeps flagging my most recent changes in the rebase when the intention is that I want all previous changes applied then as a final step show me a conflict if applicable, don’t show me one when it’s not the “final” step.
Admittedly maybe I’m doing it wrong.
I also don’t like how rebasing “main” onto my branch actually seemingly takes my branch and rebases it onto “main”? Maybe that’s a fault in my IDE not entirely sure
I think the difficulty with rebase and merge is much about semantics. When you "rebase" what does that word suggest to you? To me it sounds like "Base Again". So I'm changing the base from which I started the current branch to something else?
Not quite. That would mean that my old base is REPLACED by something else, right?
Instead rebase means take some other branch then put all the changes in the current branch on top of it, keeping everything in the current branch but just adding them on top of something else, right?
It grokked with me when in my IDE (WebStorm) I saw the menu-option allowing me to choose another branch and perform "Check Out and Rebase on top of the current branch". So you don't just "rebase a branch", you combine two branches. And when you combine two things here the order matters, and it is easy to confuse the order.
Similarly if you merge two branches, which one overrides? The left or the right operand? It is easy to get confused. Maybe I am. It is not my fault.
It does not help that git really is checkpoints rather than deltas. The semantic difference is usually ignorable, but it can matter when you're trying to rewrite history. It's coming up with deltas on the fly, and if it gets confused it can get very very lost.
Every rebase really is at least three branches: what you're merging into, where you started, and where you are now. And God help you if somebody rewrote history on any of those.
The fact that all of these have terrible names makes it so much worse.
In my org we require all important data to be listed on a pull request, then enforce squash merge. If you have multiple distinct changes you want reflected in the history then you make multiple PRs.
I use rebase essentially every working day. I use rebase for managing my local tree of patches, regardless of interaction with colleagues. Merge is fine, I guess, but do you never get the order wrong and want to reorder things before you push? Or keep moving around some local debugging patch on the top of your stack, without pushing it to your colleagues?
IMO merge is almost never what you actually want, unless you've been working separately for a long period of time (and generally you should not being doing that because it leads to surprising conflicts / regressions at merge time).
It depends what you mean by separately. Big organisations can have dozens of teams implementing unrelated functionality with asymmetrical overlaps for conflicts. I've never found a situation where rebase was appropriate for this kind of setup.
If the functionality is unrelated, I'm not sure why rebase would be inappropriate. It just wouldn't matter much other than keeping history clean (and rebase would make for cleaner history than merges).
What is the benefit of this to isolated teams? Rebasing a shared main that is under a constant stream of PRs is tedious and time consuming. What is the justification of this in terms of time spent for a company paying for that time? How is that time recouped via the availability of a linear history?
Git merging is completely stupid. It collapses multiple changes into a single commit. The original chain of commits is referenced, but in a useless way that only complicates the git history.
When you merge 17 changes from foo-feature into master, master has only a single commit. You cannot bisect master to determine which of the 17 broke master.
The 17 commits are there, but only in their original form, based on some old commit. Those 17 original commits are not equal to the single merged commit. The single merge could have a bad merge.
If there is going to be a bad merge, it's better to have a bad merge in one of 17 commits being individually rebased, than to have a bad merge in a single merge bomb that conflates 17 commits.
Your little branch of the original 17 commits should be purely a private object; it does not belong in the upstream. It's just a historic accident that your work was originally based on some three-week old random commit that happened to be latest at the time when you started. There is no need for that to be published. Your job is to make sure your code is based on the latest commit and promote it to the branch, as a sequence of individual changes (which all build and pass unit tests, etc).
I've literally not typed "git merge" since 2010, and around that time I learned how not to have Git perpetrate unwanted merges on me by learning never to type "git pull". "git pull" can be configured to be "git pull --rebase", but you will forget. I trained myself to do "git fetch", then "git rebase".
In all my last three jobs, the review tool Gerrit was used. Gerrit is based on cherry picking, which is rebase. Commits are submitted in their original form (not necessarily rebased to the target branch, but indicating which branch they are for). When approved, they are submitted and that means cherry pick. Maybe Gerrit can merge; I've never seen it used that way.
Gerit has rebase right in the UI. You can take a commit and rebase it to its logical parent (latest patch set of an apparent parent with which it had been submitted together), or to the current head of the branch.
FWIW - I've worked with both rebase workflows and merge workflows. If you rebase a long lived branch what breaks is /your/ code not the rest of the codebase in a rebase workflow vs the opposite being true with merges.
It highly depends of culture of specific team. I've seen teams obsessed with "clean" history, rebasing and squashing everything they possibly could. I believe it's a matter of taste, I never understood the argument.
squashing is a form of rebasing, albeit a narrower and simpler case. Indeed I do not understand why rebasing is so popular instead of merging when you're going to squash at the end anyway.
You have to fix conflicts either way so what's the difference? I suppose when merging you only have to fix the cumulative conflicts, while when rebasing you have to incrementally fix the conflicts introduced by every commit which is annoying. I usually squash a branch (rebase on where it diverged) before rebasing to fix this.
It’s worth configuring rerere (reuse recorded resolution): https://git-scm.com/docs/git-rerere for merges and rebases, because repeated rebases or merges of similar code will become substantially easier.
> After rebasing you basically have two versions of the same commit, and that is the root of the problem.
No, you have two commits. Git commits are snapshots, not diffs. Git's data model is simple. The fact that people do not bother trying to understand it is the root of the problem.
Rebase got a lot less annoying now that git has gained the update-refs ability. I'm often working on multiple branches simultaneously, and update-refs lets me easily work on them as a stack.
Here's how i use it: I call my HEAD "bl-dev". Let's suppose I have 5 commits on bl-dev that aren't on main. Each of those commits is a branch with a functional name. If I type "git rebase --update-refs origin/main" all 6 branches get updated, not just bl-dev.
Let's suppose the next thing I do is add something to the bottom of my 5 branches. I dont switch to the branch, I stay on bl-dev and add a commit to bl-dev. Then I type "git rebase --update-refs -i HEAD~7" and move the commit up in the stack to the correct location and again all 6 branches update.
"git rebase --update-refs -i" also gains the ability to change which commit any branch points to.
I don't actually type "--update-refs", it's a gitconfig.