I wish rebase was taught as the default - I blame the older inferior version control software. It’s honestly easier to reason about a rebase than a merge since it’s so linear.
Understanding of local versus origin branch is also missing or mystical to a lot of people and it’s what gives you confidence to mess around and find things out
I've heard people say before that it is easier to reason about a linear history, but I can't a think of a situation where this would let me solve a problem easier. All I can think of is a lot of downsides. Can you give an example where it helps?
If this was the main strategy used even for public/shared branches, then everyone would have to deal with changing, conflicting histories all the time.
Oh, that's why. I barely used any CVS before Git, so I was always puzzled about the "weird" opinions on this topic. I'm still puzzled by the fact that some people seem to reject entirely the idea of rewriting history - even locally before you have pushed/published it anywhere.
Sometimes people look sort of "superstitious" to me about Git. I believe this is caused by learning Git through web front-ends such as Github, GitLab, Gitea etc., that don't tell you the entire truth; desktop GUI clients also let the users only see Git through their own, more-or-less narrow "window".
TBH, sometimes Git can behave in ways you don't expect, like seeing conflicts when you thought there wouldn't be (but up to now never things like choosing the "wrong" version when doing merges, something I did fear when I started using it a ~decade ago).
However one usually finds an explanation after the fact. Something I've learned is that Git is usually right, and forcing it to do things is a good recipe to mess things up badly.
Funnily enough in all my years of using git, this thread is the first time I've encountered merge. It sounds easier I suppose, but I don't really have a problem with rebase and will likely just continue as is
> Warning - Because changing your commit history can make things difficult for everyone else using the repository, it's considered bad practice to rebase commits when you've already pushed to a repository.
Allow me (today) to be that person to propose checking out Jujutsu instead [0]. Not only it has a superpower of atomic commits (reviewers will love you, peers will hate 8 small PRs that are chained together ;-)) but it's also more consistent than git and works perfectly well as a drop-in replacement.
In fact, I've been using Jujutsu for ~2 years as a drop-in and nobody complained (outside of the 8 small PRs chained together). Git is great as a backend, but Jujutsu shines as a frontend.
You don't have to chain 8 PRs together, Github tries really hard to hide this from you but you can in fact review one commit at a time, which means you don't need to have a stack of 8 PRs that cascade into each other.
I've seen that you can read one commit at a time, but never anything for reviewing (or diffing between them if they change) - is there a UI beyond just clicking on the list of commits?
Though I forget if you can even comment in the individual commits in that view. Complex multi-commit PRs have generally been a nightmare on GitHub in my experience.
You can comment on the individual commits' changes, but you can't comment on the commit (e.g. its message) itself. I believe you can do this in Gerrit.
Which means it's probably all I have seen - so you can't approve commits either, or track file-reading progress through commits (I think? i.e. "viewed file X at commit Y, but not at commit Z"), or diff changes to a commit (e.g. by order/message) after a rebase, just "you can see the current commit history". Not sure I'd consider that "can review one commit at a time", just "can read one commit at a time". "Review" means "there is support for this thing as a first class entity" imo, and that ain't what github is offering, nor anything even close to it.
That's true, but acceptance is still all or nothing.
When chaining commits it's possible to (for example) have a function that does THING and then have another PR that have a function that uses first one.
It's somewhat PITA when team has no-dead-code hard rule, but otherwise it's quite manageable and invites rich feedback. Reviewer and feedback can focus on atomic change (in example: function that does THING) and not on a grand picture.
The key thing to point out is that jujutsu is a rebase-based workflow, and no on who uses jujutsu ever worries about rebasing (they may not even be aware of it). It's a good demonstration of a tool that got rebase right, unlike git.
Pre-jujutsu, I never rebased unless my team required it. Now I do it all the time.
Pre-jj, I never had linear history, unless the team required it. Now most of my projects have linear history.
I'm a big fan of rebasing to keep the commit history clean and as a form of self-discipline when coding to make sure I'm grouping the changes atomically.
I will try to give Jujutsu a go based on your recommendation!
The comments sections in git posts vs. jj posts are always hysterical to me.
If there’s a jj post on HN, people come out of the woodworks to say that git is easy and it’s crazy to suggest that anyone finds it difficult or confusing. Also people saying they’ve figured out git is super usable if you only ever use commit, merge, pull.
Then you have git posts where everyone talks about how hard some basic things are, how easy it is to mess up your repo, how frustrating rebase is, etc.
Maintaining linear history is arguably more work. But excessively non-linear history can be so confusing to reason over.
Linear history is like reality: One past and many potential futures. With non-linear history, your past depends on "where you are".
----- M -----+--- P
/
----- D ---+
Say I'm at commit P (for present). I got married at commit M and got a dog at commit D. So I got married first and got a Dog later, right? But if I go back in time to commit D where I got the dog, our marriage is not in my past anymore?! Now my wife is sneezing all the time. Maybe she has a dog allergy. I go back in time to commit D but can't reproduce the issue. Guess the dog can't be the problem.
Until you commit to trunk, you haven't gotten the dog. You're proposing getting a dog. The action of getting the dog happens at merge to trunk. That's when history is created.
> The response is often hesitation or outright fear. I get it. Rebase has a reputation for destroying work, and the warnings you see online don’t help.
The best method for stop being terrified of destructive operations in git when I first learned it, was literally "cp -r $original-repo $new-test-repo && go-to-town". Don't know what will happen when you run `git checkout -- $file` or whatever? Copy the entire directory, run the command, look at what happens, then decide if you want to run that in your "real" repository.
Sound stupid maybe, but if it works, it works. Been using git for something like a decade now, and I'm no longer afraid of destructive git operations :)
This is almost exactly what git does, except it's a million times faster. Every commit is one of those copies, and you can instantly jump to any one of them using git checkout.
If you like this mental model, you'll feel right at home with git. You will love git reflog.
I never understood why rebase is such a staple in the git world. For me "loosing" historical data, like on which branch my work was done is a real issue.
In the same class, for commit to not have on which branch they were created as a metadata is a rel painpoint. It always a mess to find what commit were done for what global feature/bugfix in a global gitflow process...
I'll probably be looking into adding an commit auto suffix message with the current branch in the text, but it will only work for me, not any contributors...
Every commit message starts with the ticket number of whatever issue tracking system you're using. If you're not using issue tracking with a system large enough for multiple devs, you've got a much bigger problem.
I was working on a local branch, periodically rebasing it to master. All was well, my git history was beautiful etc.
Then down the line I realised something was off. Code that should have been there wasn't. In the end I concluded some automatic commit application while rebasing gobbled up my branch changes. Or frankly, I don't even entirely know what happened (this is my best guess), all I know is, suddenly it wasn't there.
No big deal, right? It's VCS. Just go back in time and get a snapshot of what the repo looked like 2 weeks ago. Ah. Except rebase.
I like a clean linear history as much as the next guy, but in the end I concluded that the only real value of a git repo is telling the truth and keeping the full history of WTF really happened.
You could say I was holding it wrong, that if you just follow this one weird old trick doctor hate, rebase is fine. Maybe. But not rebasing and having a few more squiggles in my git history is a small price to pay for the peace of mind that my code change history is really, really all there.
Nowadays, if something leaves me with a chance that I cannot recreate the repo history at any point in time, I don't bother. Squash commits and keeping the branch around forever are OK in my book, for example. And I always commit with --no-ff. If a commit was never on master, it shouldn't show up in it.
Squash merges are a hacky solution to the git bisect problem that was solved correctly by --first-parent 20 years ago. There are fully employed software developers working on important stuff that literally were never alive in a world where squash merges were needed.
Don't erase history. Branch to a feature branch, develop in as many commits as you need, then merge to main, always creating a merge commit. Oftentimes, those commit messages that you're erasing with a squash are the most useful documentation in the entire project.
Github is not Git but I find the Squash and Merge functionality on Github's Pull Request system means I no longer need to worry about rebasing or squashing my commits locally before rebasing.
At work though it is still encouraged to rebase, and I have sometimes forgotten to squash and then had to abort, or just suck it up and resolve conflicts from my many local commits.
That way, I don't care if your branch contains 100 commits or 1 commit. I don't need to worry about commit messages like:
- fix 1
- fix 2
- dfljfdlkfdj
- does it work now?
Do whatever you want with your commits on your feature branch. Just make sure the title of your PR is clean and follows our formatting. Git history is always well formatted and linear.
When things get messy I use Sublime Merge with two tabs, one with the code that's open in VS Code and one with the same project but different branch/commit.
It works well on Linux. I've managed to make it work with Windows + WSL but I don't recommend it.
> I always use VS Code for this step. Its merge conflict UI is the clearest I’ve found: it shows “Accept Current Change,” “Accept Incoming Change,” “Accept Both Changes,” and “Compare Changes” buttons right above each conflict.
I still get confused by vscode’s changing the terms used by Git. «Current» vs «incoming» are not clear, and can be understood to mean two different things.
- Is “current” what is on the branch I am rebasing on? Or is it my code? (It’s my code)
- Is “incoming” the code I’m adding to the repo? Or is it what i am rebasing on to? (Again, the latter is correct)
I find that many tools are trying to make Git easier to understand, but changing the terms is not so helpful. Since different tools seldom change to the same words, it just clutters any attempts to search for coherent information.
I have been contributing code for 10+ years, and I have worked on teams that did rebase and others that did not.
Not once have a ever debugged a problem that benefited from rebase vs merge. Fundamentally, I do not debug off git history. Not once has git history helped debug outside of looking at the blame + offending PR and diff.
Can someone tell me when they were fixing a problem and they were glad that they rebased? Bc I can't.
> the worst case scenario for a rebase gone wrong is that you delete your local clone and start over. That’s it. Your remote fork still exists.
This is absolute nonsense. You commit your work, and make a "backup" branch pointing at the same commit as your branch. The worst case is you reset back to your backup.
In about 12 years of using git (jj user now) I almost never rebased through the CLI, but I found shuffling branches around in a GUI pretty intuitive. I liked GitUp[0], which gave me undo way before jj existed.
The common view that a Git GUI is a crutch is very wrong, even pernicious. To me it is the CLI that is a disruptive mediation, whereas in a GUI you can see and manipulate the DAG directly.
Obligatory jj plug: before jj, I would have agreed with the top comment[1] that rebasing was mostly unnecessary, even though I was doing it in GitUp pretty frequently — I didn't think of it as rebasing because it was so natural. Now that I use jj I see that the cost-benefit analysis around git rebase is dominated by the fact that both rebasing and conflict resolution in git are a pain in the ass, which means the benefit has to be very high to compensate. In jj they cost much less, so the neatness benefit can be quite small and still be worth it. Add on the fact that Claude Code can handle it all for you and the cost is down to zero.
git rebase appears to be a random number generator to me. I've got two PRs in flight, I realize that one branch requires something I did in the other branch. I go merge branch one, then rebase branch two off main. Half the time; success, get on with my day. The other half of the time; anarchy, madness, dogs and cats living together.
I don't understand why I would push to origin my local branch. This will (potentially) require multiple push -f. I prefer to share the work in a state I considere complete.
As an alternative, just create a new branch! `git branch savepoint-pre-rebase`. That's all. This is extremely cheap (just copy a reference to a commit) and you are free to play all you want.
You are a little more paranoid? `git switch -c test-rebase` and work over the new branch.
I avoid rebase like plague (perhaps because of my early experiences with it). I used to get continuous conflicts for the same commits again and again, and the store and replay kinda helped with it but not always. Merge always worked for me (once I resolve conflicts, thats the end of it). Now I always merge main into my feature branch and then merge it back to main when ready. Does it pollute the history? Maybe, but Ive never looked. It does not matter to our team.
Same here. I lead a team and I really don't care about keeping the git history "clean". It is what it is. Squashing commits wouldn't help me at all since the history rarely ever factors into our workflow.
Rebase and other fancy Git things have caused problems in the past so I avoid getting too complex with Git. I'm not a Git engineer, I'm a software engineer.
Merging has always just worked, and I know exactly what to expect. If there's a big hairy branch that I need to merge, and I know there will be conflicts, I create a branch from Main, merge the hairy branch into it, and see what happens. Fix the issues there, and then merge that branch to Main when everything is working. Merge is simple, and I don't have to be master of Git to get things done.
67 comments
[ 3.2 ms ] story [ 82.0 ms ] threadUnderstanding of local versus origin branch is also missing or mystical to a lot of people and it’s what gives you confidence to mess around and find things out
Sometimes people look sort of "superstitious" to me about Git. I believe this is caused by learning Git through web front-ends such as Github, GitLab, Gitea etc., that don't tell you the entire truth; desktop GUI clients also let the users only see Git through their own, more-or-less narrow "window".
TBH, sometimes Git can behave in ways you don't expect, like seeing conflicts when you thought there wouldn't be (but up to now never things like choosing the "wrong" version when doing merges, something I did fear when I started using it a ~decade ago).
However one usually finds an explanation after the fact. Something I've learned is that Git is usually right, and forcing it to do things is a good recipe to mess things up badly.
https://docs.github.com/en/get-started/using-git/about-git-r...
> Warning - Because changing your commit history can make things difficult for everyone else using the repository, it's considered bad practice to rebase commits when you've already pushed to a repository.
A similar warning is in Atlassian docs.
In fact, I've been using Jujutsu for ~2 years as a drop-in and nobody complained (outside of the 8 small PRs chained together). Git is great as a backend, but Jujutsu shines as a frontend.
[0]: https://www.jj-vcs.dev/latest/
`jj` is the only tool that make me use `rebase` personally. Before, I see as the punishment given by my team wishes :)
My peers are my reviewers...
Though I forget if you can even comment in the individual commits in that view. Complex multi-commit PRs have generally been a nightmare on GitHub in my experience.
When chaining commits it's possible to (for example) have a function that does THING and then have another PR that have a function that uses first one.
It's somewhat PITA when team has no-dead-code hard rule, but otherwise it's quite manageable and invites rich feedback. Reviewer and feedback can focus on atomic change (in example: function that does THING) and not on a grand picture.
But I also often use method of logical PRs and even written about it: https://xlii.space/eng/pr_trick/
Pre-jujutsu, I never rebased unless my team required it. Now I do it all the time.
Pre-jj, I never had linear history, unless the team required it. Now most of my projects have linear history.
A better UI makes a huge difference.
I will try to give Jujutsu a go based on your recommendation!
If there’s a jj post on HN, people come out of the woodworks to say that git is easy and it’s crazy to suggest that anyone finds it difficult or confusing. Also people saying they’ve figured out git is super usable if you only ever use commit, merge, pull.
Then you have git posts where everyone talks about how hard some basic things are, how easy it is to mess up your repo, how frustrating rebase is, etc.
It’s fun to watch.
Linear history is like reality: One past and many potential futures. With non-linear history, your past depends on "where you are".
Say I'm at commit P (for present). I got married at commit M and got a dog at commit D. So I got married first and got a Dog later, right? But if I go back in time to commit D where I got the dog, our marriage is not in my past anymore?! Now my wife is sneezing all the time. Maybe she has a dog allergy. I go back in time to commit D but can't reproduce the issue. Guess the dog can't be the problem.The best method for stop being terrified of destructive operations in git when I first learned it, was literally "cp -r $original-repo $new-test-repo && go-to-town". Don't know what will happen when you run `git checkout -- $file` or whatever? Copy the entire directory, run the command, look at what happens, then decide if you want to run that in your "real" repository.
Sound stupid maybe, but if it works, it works. Been using git for something like a decade now, and I'm no longer afraid of destructive git operations :)
This is almost exactly what git does, except it's a million times faster. Every commit is one of those copies, and you can instantly jump to any one of them using git checkout.
If you like this mental model, you'll feel right at home with git. You will love git reflog.
In the same class, for commit to not have on which branch they were created as a metadata is a rel painpoint. It always a mess to find what commit were done for what global feature/bugfix in a global gitflow process...
I'll probably be looking into adding an commit auto suffix message with the current branch in the text, but it will only work for me, not any contributors...
I was working on a local branch, periodically rebasing it to master. All was well, my git history was beautiful etc.
Then down the line I realised something was off. Code that should have been there wasn't. In the end I concluded some automatic commit application while rebasing gobbled up my branch changes. Or frankly, I don't even entirely know what happened (this is my best guess), all I know is, suddenly it wasn't there.
No big deal, right? It's VCS. Just go back in time and get a snapshot of what the repo looked like 2 weeks ago. Ah. Except rebase.
I like a clean linear history as much as the next guy, but in the end I concluded that the only real value of a git repo is telling the truth and keeping the full history of WTF really happened.
You could say I was holding it wrong, that if you just follow this one weird old trick doctor hate, rebase is fine. Maybe. But not rebasing and having a few more squiggles in my git history is a small price to pay for the peace of mind that my code change history is really, really all there.
Nowadays, if something leaves me with a chance that I cannot recreate the repo history at any point in time, I don't bother. Squash commits and keeping the branch around forever are OK in my book, for example. And I always commit with --no-ff. If a commit was never on master, it shouldn't show up in it.
Also incremental rebasing with mergify/git-imerge/git-mergify-rebase/etc is really helpful for long-lived branches that aren't merged upstream.
https://github.com/brooksdavis/mergify https://github.com/mhagger/git-imerge https://github.com/CTSRD-CHERI/git-mergify-rebase https://gist.github.com/nicowilliams/ea2fa2b445c2db50d2ee650...
I also love git-absorb for automatic fixups of a commit stack.
https://github.com/tummychow/git-absorb
Don't erase history. Branch to a feature branch, develop in as many commits as you need, then merge to main, always creating a merge commit. Oftentimes, those commit messages that you're erasing with a squash are the most useful documentation in the entire project.
At work though it is still encouraged to rebase, and I have sometimes forgotten to squash and then had to abort, or just suck it up and resolve conflicts from my many local commits.
That way, I don't care if your branch contains 100 commits or 1 commit. I don't need to worry about commit messages like:
- fix 1
- fix 2
- dfljfdlkfdj
- does it work now?
Do whatever you want with your commits on your feature branch. Just make sure the title of your PR is clean and follows our formatting. Git history is always well formatted and linear.
It's the ideal solution.
Wouldn't it be enough to simply back up the branch (eg, git checkout -b current-branch-backup)? Or is there still a way to mess up the backup as well?
I still get confused by vscode’s changing the terms used by Git. «Current» vs «incoming» are not clear, and can be understood to mean two different things.
- Is “current” what is on the branch I am rebasing on? Or is it my code? (It’s my code)
- Is “incoming” the code I’m adding to the repo? Or is it what i am rebasing on to? (Again, the latter is correct)
I find that many tools are trying to make Git easier to understand, but changing the terms is not so helpful. Since different tools seldom change to the same words, it just clutters any attempts to search for coherent information.
Not once have a ever debugged a problem that benefited from rebase vs merge. Fundamentally, I do not debug off git history. Not once has git history helped debug outside of looking at the blame + offending PR and diff.
Can someone tell me when they were fixing a problem and they were glad that they rebased? Bc I can't.
This is absolute nonsense. You commit your work, and make a "backup" branch pointing at the same commit as your branch. The worst case is you reset back to your backup.
The common view that a Git GUI is a crutch is very wrong, even pernicious. To me it is the CLI that is a disruptive mediation, whereas in a GUI you can see and manipulate the DAG directly.
Obligatory jj plug: before jj, I would have agreed with the top comment[1] that rebasing was mostly unnecessary, even though I was doing it in GitUp pretty frequently — I didn't think of it as rebasing because it was so natural. Now that I use jj I see that the cost-benefit analysis around git rebase is dominated by the fact that both rebasing and conflict resolution in git are a pain in the ass, which means the benefit has to be very high to compensate. In jj they cost much less, so the neatness benefit can be quite small and still be worth it. Add on the fact that Claude Code can handle it all for you and the cost is down to zero.
[0]: https://gitup.co/
[1]: https://news.ycombinator.com/item?id=46602056
I remain terrified.
Or just do a merge and move on with your life.
As an alternative, just create a new branch! `git branch savepoint-pre-rebase`. That's all. This is extremely cheap (just copy a reference to a commit) and you are free to play all you want.
You are a little more paranoid? `git switch -c test-rebase` and work over the new branch.
Rebase and other fancy Git things have caused problems in the past so I avoid getting too complex with Git. I'm not a Git engineer, I'm a software engineer.
Merging has always just worked, and I know exactly what to expect. If there's a big hairy branch that I need to merge, and I know there will be conflicts, I create a branch from Main, merge the hairy branch into it, and see what happens. Fix the issues there, and then merge that branch to Main when everything is working. Merge is simple, and I don't have to be master of Git to get things done.