Readit News logoReadit News
game_the0ry · a month ago
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.

nothrabannosir · a month ago
Debugging from git history is a separate question from merge vs rebase. Debugging from history can be done with non-rebased merges, with rebased merges, and with squashed commits, without any noticeable difference. Pass `--first-parent` to git-log and git-bisect in the first two cases and it's virtually identical.

My preference for rebasing comes from delivering stacked PRs: when you're working on a chain of individually reviewable changes, every commit is a clean, atomic, deliverable patch. git-format-patch works well with this model. GitHub is a pain to use this way but you can do it with some extra scripts and setting a custom "base" branch.

The reason in that scenario to prefer rebasing over "merging in master" is that every merge from master into the head of your stack is a stake in the ground: you can't push changes to parent commits anymore. But the whole point of stacked diffs is that I want to be able to identify different issues while I work, which belong to different changes. I want to clean things up as I go, without bothering reviewers with irrelevant changes. "Oh this README could use a rewrite; let me fix that and push it all the way up the chain into its own little commit," or "Actually now that I'm here, let me update dependencies and ensure we're on latest before I apply my changes". IME, an ideal PR is 90% refactors and "prefactors" which don't change semantics, all the way up to "implemented functionality behind a feature flag", and 10% actual changes which change the semantics. Having an editable history that you can "keep bringing with you" is indispensible.

Debugging history isn't really related. Other than that this workflow allows you to create a history of very small, easily testable, easily reviewable, easily revertible commits, which makes debugging easier. But that's a downstream effect.

game_the0ry · a month ago
> Debugging from git history is a separate question from merge vs rebase.

But the main benefit proponents or rebase say its for keeping the history clean which also makes it easier to pinpoint and offending commit.

Personally, a clean commit history was never something that made my job easier.

> Other than that this workflow allows you to create a history of very small, easily testable, easily reviewable, easily revertible commits, which makes debugging easier. But that's a downstream effect.

I would agree that it is important for commits to go from working state to working state as you are working on a task, but this is an argument for atomic commits, not about commit history.

DenisM · a month ago
If you have not already, try Graphite. You will be delighted as it serves that exact purpose.
rpcope1 · a month ago
I have worked on several codebases where it was enforced that the commit be rebased off of whatever the main branch was, all units of work squashed to a single commit, and only "working" code be checked into the main branch. This gives you a really good linear history, and when you're disciplined about writing good final commit messages and tagging them to a ticket, it means bisecting to find challenging bugs later becomes tractable, as each commit nominally should work and should be ready to deploy for testing. I've personally solved a number of challenging regressions this way.
cryptonector · a month ago
I think one should be allowed to push commits that don't build or pass tests provided a) they are marked as such (so you can skip them sooner when bisecting) and b) the HEAD commit after each push does build and pass tests.
drysine · a month ago
>each commit nominally should work

Except it can be the result of 10 squashed commits.

skipkey · a month ago
I can give you an example of when I am glad I rebased. There have been many times I have been working on a feature that was going to take some time to finish. In that case my general workflow is to rebase against main every day or two. It lets me keep track of changes and handle conflicts early and makes the eventual merge much simpler. As for debugging I’ve never personally had to do this, but I imagine git bisect would probably work better with rebased, squashed commits.
hnben · a month ago
> I can give you an example of when I am glad I rebased

I think the question was about situations where you were glad to rebase, when you could have merged instead

bluGill · a month ago
I used hg (mercurial) before git. Every time I see someone make an argument like yours I think "only because git's merge/branch model is bad and so you need hacks to make it acceptable".

Git won, which is why I've been using it for more than 10 years, but that doesn't mean it was ever best, it was just most popular and so the rest of the eco system makes it worth it accepting the flaws (code review tools and CI system both have much better git support - these are two critical things that if you use anything else will work against you).

gaoshan · a month ago
FWIW I have used git bisect with merged commits and it works just as well (unless the commit is enormous... nothing like settling on a sprawling 100 file change commit as the culprit... good argument for discrete commits, but then it wouldn't matter if it were rebased or merged)
BeetleB · a month ago
I do the same except with merge. I don't see how rebase makes it any better.
any1 · a month ago
> Fundamentally, I do not debug off git history.

Are you saying that you've never used git bisect? If that's the case, I think you're missing out.

zbentley · a month ago
Bisect is one of those things where if you're on a certain kind of project, it's really useful, and if you're not on that kind of project you never need it.

If the contributor count is high enough (or you're otherwise in a role for which "contribution" is primarily adjusting others' code), or the behaviors that get reported in bugs are specific and testable, then bisect is invaluable.

If you're in a project where buggy behavior wasn't introduced so much as grew (e.g. the behavior evolved A -> B -> C -> D -> E over time and a bug is reported due to undesirable interactions between released/valuable features in A, C, and E), then bisecting to find "when did this start" won't tell you that much useful. If you often have to write bespoke test scripts to run in bisect (e.g. because "test for presence of bug" is a process that involves restarting/orchestrating lots of services and/or debugging by interacting with a GUI), then you have to balance the time spent writing those with the time it'd take for you to figure out the causal commit by hand. If you're in a project where you're personally familiar with roughly what was released when, or where the release process/community is well-connected, it's often better to promote practices like "ask in Slack/the mailing list whether anyone has made changes to ___ recently, whoever pipes up will help you debug" rather than "everyone should be really good at bisect". Those aren't mutually exclusive, but they both do take work to install in a community and thus have an opportunity cost.

This and many other perennial discussions about Git (including TFA) have a common cause: people assume that criticisms/recommendations for how to use Git as a release coordinator/member of a disconnected team of volunteers apply to people who use Git who are members of small, tightly-coupled teams of collaborators (e.g. working on closed-source software).

yawaramin · a month ago
From what I can tell the vast majority of developers don't use git bisect and never will.
PaulDavisThe1st · a month ago
Git bisect is a wonder, especially combined with its ability to potentially do the success/fail testing on its own (with the help of some command you provide).

It is a tragedy that more people don't know about it.

gwking · a month ago
This may be outdated because git’s defaults have improved a lot over the years. When I first used git on a team was in 2011. As I recall, there were various commands like git log -p that would show nothing for a merge commit. So without extra knowledge of the git flags you would not find what you were looking for if it was in a side path of the merge history. This caused a lot of confusion at times. We switched to a rebase approach because linear history is easier for people to use.

To answer your question directly, if somewhat glibly, I’m glad I rebased every time I go looking for something in the history because I don’t have to think about the history as a graph. It’s easier.

More to your point, there are times when blame on a line does not show the culprit. If you move code, or do anything else to that line, then you have to keep searching. Sometimes it’s easier to look at the entire patch history of a file. If there is a way to repeatedly/recursively blame on a line, that’s cool and I’d love to know about it.

I now manage two junior engineers and I insist that they squash and rebase their work. I’ve seen what happens if they don’t. The merges get tangled and crazy, they include stuff from other branches they didn’t mean to, etc. the squash/rebase flow has been a way to make them responsible for what they put into the history, in a way that is simple enough that they got up to speed and own it.

languid-photic · a month ago
I manage a maintained fork and periodically rebase our changes on top of upstream.

In this case, rebasing is nice because our changes stay in a contiguous block at the top (vs merging which would interleave them), so it's easy for me and others to see exactly where our fork diverges.

friedbeef · a month ago
Doesn’t that mean you have to fix all the merge conflicts introduced by your commits on every rebase though?
__MatrixMan__ · a month ago
If you haven't used git bisect to find a regression, you should try it.

You can write a test (outside of source control) and run `git bisect good` on a good commit and `git bisect bad` on bad one and it'll do a binary search (it's up to you to rerun your test each time and tell git whether that's a good or a bad commit). Rather quickly, it'll point you to the commit that caused the regression.

If you rebase, that commit will be part of a block of commits all from the same author, all correlated with the same feature (and likely in the same PR). Now you know who you need to talk to about it. If you merge, you can still start with the author of the commit that git bisect found, but it's more likely to be interleaved with nearby commits in such a way that when it was under development, it had a different predecessor. That's a recipe for bugs that get found later than they otherwise would've.

If you're not using git history to debug, you're probably not really aware of which problems would've turned out differently if the history was handled differently. If you do, you'll catch cases where one author or another would've caught the bug before merging to main, had they bothered to rebase, but instead the bug was only visible after both authors thought they were done.

PaulDavisThe1st · a month ago
> it's up to you to rerun your test each time and tell git whether that's a good or a bad commit

not true. You can use

   git bisect run script-command arguments
where script-command is a ... script that will test the result of the build.

d_watt · a month ago
Once we had a slowdown in our application that went unadressed for a couple of months. Using git bisect to binary search across a bunch of different commits and run a perf test, every commit being a "good" historical commit allowed that to be much easier, and I found the offending commit fast.
game_the0ry · a month ago
Ok, I see. This is a use case I did not think about. Worthy of a blog post, I think.

Besides testing for a perf slow down, any other use cases for git bisect + rebase?

nicoburns · a month ago
The main benefit I've found is when there is work happening concurrently in multiple feature branches at once (e.g. by different people). Rebase-merging greatly simplifies dealing with merge conflicts as you only have a simple diff against a single branch to deal with. The more work you have in progress at once the more important this becomes.
OptionOfT · a month ago
In fact, for searching how a file got to the state it is I prefer that when PRs are merged, they are merged and not rebased. I want the commit shas to be the same.

Rebasing on main loses provenance.

If you want a clean history doing it in the PR, before merging it. That way the PR is the single unit of work.

yawaramin · a month ago
Merging a PR with rebase doesn't lose provenance. You can just keep all the commits in the PR branch. But even if you squash the branch into a single commit and merge (which these tools automate and many people do), it still doesn't lose provenance. The provenance is the PR itself. The PR is connected to a work item in the ticketing system. The git history preserves all the relevant info.
game_the0ry · a month ago
> That way the PR is the single unit of work.

Well if I have a diff of the PR with just the changes, then the PR is already a "unit of work," regardless of merge or rebase, right?

simonw · a month ago
Do you ever use git bisect?

I like to keep a linear history mainly so I don't have to think very hard about tools like that.

tcoff91 · a month ago
--first-parent with bisect really helps when the history is messy.
recursive · a month ago
I've used git bisect on a repo whose commit graph is at least 20-wide at some points. In the two cases I used it, it identified the individual commit. I didn't think very hard about it. It was the first time I used bisect. Maybe I got lucky.
mathis-l · a month ago
I’ve worked on a code base that was about 15 years old and had gone through many team changes. It was a tricky domain with lots of complicated business logic. When making changes to the code, the commit history was often the only way to figure out if certain behavior was intended and why it was implemented this way. Documentation about how the product should behave often lacked the level of detail. I was certainly always thankful when a dev that was long gone from the team had written commit messages that communicated the intent behind a change.
jefurii · a month ago
I use `git rebase` all the time, along with `git add -p`, `git diff` and other tools. It helps me to maintain logical commit history.

- Reshuffle commits into a more logical order. - Edit commit subjects if I notice a mistake. - Squash (merge) commits. Often, for whatever reason pieces of a fix end up in separate commits and it's useful to collect and merge them.

I'd like to make every commit perfect the first time but I haven't managed to do that yet. git-rebase really helps me clean things up before pushing or merging a branch.

just6979 · a month ago
I'd be willing to bet most devs do something like this, or wish they could be doing it, but don't know about rebase or are scared of it. However, that might be because they're only thinking about rebase as OP's article uses it: only as an alternative to merge for get changes from another branch.

Interactive rebasing to write local history on your working branch is incredibly useful, but also doesn't have anything to do with the "rebase vs merge" conundrum, and as long as you're not pushing to a shared branch, it doesn't have much to do with "erasing other's history".*

If you can look at a working branch (with more than a trivial addition or fix) and not feel the need to do a interactive rebase (once you know how) before making a PR, then you're either a magical 100x unicorn dev that makes every commit the perfect commit, or you cheated and made a new branch and cherry-pick-squashed your way to a clean history.

xorcist · a month ago
What is the argument here? Does it still hold if you s/rebase/comments/g ?

Of course a readable code history aids in debugging. Just as comments and indentation do. None of these are technically necessary, but still a good idea.

Of course running the rebase command doesn't guarantee a readable commit history, but it's hard to craft commits without it. Each and every commit on linux-kernel has been rebased probably a dozen times.

tsimionescu · a month ago
I have often been happy to have a clean linear history when asking myself things like "does build X.Y.Z include this buggy change I found in commit abcdefg?". With a history full of merges, where a commit from 1st of January might be merged only on the 20th of July, this gets MUCH harder to answer.

This is especially true if you have multiple repos and builds from each one, such that you can't just checkout the commit for build X.Y.Z and easily check if the code contains that or not (you'd have to track through dependency builds, checkout those other dependencies, possibly repeat for multiple levels). If the date of a commit always reflects the date it made it into the common branch, a quick git log can tell you the basic info a lot of the time.

Deleted Comment

douglee650 · a month ago
Isn't it more of a style decision where if your team rebases, and practices clean code discipline, and excellence is a habit rather than an enforcement — the signals visibly emit in `status`.

Most committers don't really understand remotes, much less rebasing.

mrkeen · a month ago
Any time I'm doing anything remotely to do with merging, I use 'git diff' or 'git difftool'.

If I diff against master, I see changes in 300+ files, when I've only changed 5 (because other people have changed 300+ files.)

> Fundamentally, I do not debug off git history.

Neither. The usual argument I hear against rebase is that it destroys history. Since I don't debug off git history, I'm quite happy to destroy it, and get back to diffing my 5-file changes against (current) master.

tunesmith · a month ago
I love rebase locally, especially when I have a few non-urgent branches that sit around for a little while. I hate rebase after pushing. The rule of thumb that has worked for me is "don't rewrite someone else's history". Rewriting origin's history is not so bad, but if there's even a chance that a team member has pulled, or based work off your pushed branch (ugh), rebase is horrible.
dcre · a month ago
This is the post that made jj click for me, and not coincidentally it is about a rebase operation that feels complicated in git but trivial in jj.

https://lottia.net/notes/0013-git-jujutsu-miniature.html

cryptonector · a month ago
Whenever I go spelunking in the history I always find clean, linear history, with small commits (where possible) to be much easier to understand and search than merges.
lucasoshiro · a month ago
> Fundamentally, I do not debug off git history.

I'm really sorry. Using bisect and log -S saved hours of code debugging

e40 · a month ago
This "debate" is just insane.

Any workflow that has a review process uses rebase. FULL STOP.

If you don't have your code reviewed and you push code to a shared repo, fine, don't use rebase if you don't want to.

MrDarcy · a month ago
Two very useful use cases for rebase: 1) rewrite history to organize the change list clearly. 2) stacked pull requests of arbitrary depth.

You’ve never run a bisect to identify which commit introduced a specific behavior?

This is when I’ve found it most useful. Having commits merged instead of squashed narrows down and highlights the root problem.

It’s a rare enough situation I don’t push for merge commits over squashed rebases because it’s not worth it, but when I have had to bisect and the commits are merged instead of squashed it is very very useful.

Those commit authors are who I noted as clear thinkers and have tracked over my career to great benefit.

xlii · a month ago
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.

[0]: https://www.jj-vcs.dev/latest/

jon-wood · a month ago
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.
unshavedyak · a month ago
Yup, that's what my team does. It works wonderfully, and it fits well with Github's "large PR" mindset imo. It could be a bit better in the Github UI, but so can most things. I vastly prefer it to individually reviewing tons of PRs.

The funny thing about this debate for me is that i find it comes down to the committer. If the committer makes small commits in a stacked PR, where each commit is a logical unit of work, part of the "story" being told about the overall change, then i don't personally find it's that useful to stack them. The committer did the hard part, they wrote the story of changes in a logical, easy to parse manner.

If the story is a mess, where the commits are huge or out of logical order, etc - then it doesn't matter much in my view.. the PR(s) sucks either way.

I find stacked PRs to be a workflow solution to what to me is a UI problem.

Groxx · a month ago
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.

xlii · a month ago
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.

But I also often use method of logical PRs and even written about it: https://xlii.space/eng/pr_trick/

Liskni_si · a month ago
You do if you find yourself in a team where PRs are squash-merged. :-(
yawaramin · a month ago
> reviewers will love you, peers will hate 8 small PRs that are chained together

My peers are my reviewers...

Zambyte · a month ago
Also been using Jujutsu for about 2 years. I feel like I have learned so much about how git actually works by simply not using git.
BeetleB · a month ago
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.

A better UI makes a huge difference.

kps · a month ago
And `jj undo`, so nothing is terrifying.
mamcx · a month ago
And if a major problem, `jj op log` + `jj op log restore` fix it. This is the major super power of jj: Before I need to nuke the git repo on bad rebases (not chance in hell I can find how undo the massive 20+ steps bad rebase)
Aperocky · a month ago
well git reflog is that, annoying, yes, but we have LLM so I don't actually need to remember how all the command syntax exactly like back in 2019.
grokys · a month ago
Does it support submodules yet? That was the thing that stopped me using it last time I checked.
tcoff91 · a month ago
Submodules are cursed. I feel bad for you that you have to work in a repo that uses them.
steveklabnik · a month ago
Not natively, but you can still use the regular git commands to update them, and it works.
nickkell · a month ago
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!

stouset · a month ago
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.

It’s fun to watch.

_flux · a month ago
I think I'd love to use Jujutsu, but I enjoy Magit (for Emacs) too much to entertain the thought of switching :/.

Besides, Magit rebasing is also pretty sweet.

skulk · a month ago
I used to think like this, but then I realized: jj-mode.el exists[0] and you can still use magic since it's still a git repo underneath. Seriously, don't let this hold you back.

[0]: https://github.com/bolivier/jj-mode.el

turboponyy · a month ago
Samesies - need a Majjit before I can consider trying it out.
mamcx · a month ago
+1

`jj` is the only tool that make me use `rebase` personally. Before, I see as the punishment given by my team wishes :)

vlovich123 · a month ago
How do you handle publishing the stack?
wocram · a month ago
It depends on what you're publishing to, but works with most other tools by using a bookmark for each publish target.
dcre · a month ago
There’s tooling like https://github.com/LucioFranco/jj-spr for managing stacks of PRs, but for stacks of 2 or 3 it’s not too bad to do it manually.
coffeebeqn · a month ago
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

Akranazon · a month ago
The end result of a git rebase is arguably superior. However, I don't do it, because the process of running git rebase is a complete hassle. git merge is one-shot, whereas git rebase replays commits one-by-one.

Replaying commits one-by-one is like a history quiz. It forces me to remember what was going on a week ago when I did commit #23 out of 45. I'm grateful that git stores that history for me when I need it, but I don't want it to force me to interact with the history. I've long since expelled it from my brain, so that I can focus on the current state of the codebase. "5 commits ago, did you mean to do that, or can we take this other change?" I don't care, I don't want to think about it.

Of course, this issue can be reduced by the "squash first, then rebase" approach. Or judicious use of "git commit --amend --no-edit" to reduce the number of commits in my branch, therefore making the rebase less of a hassle. That's fine. But what if I didn't do that? I don't want my tools to judge me for my workflow. A user-friendly tool should non-judgmentally accommodate whatever convenient workflow I adopted in the past.

Git says, "oops, you screwed up by creating 50 lazy commits, now you need to put in 20 minutes figuring out how to cleverly combine them into 3 commits, before you can pull from main!" then I'm going to respond, "screw you, I will do the next-best easier alternative". I don't have time for the judgement.

nicoburns · a month ago
> "oops, you screwed up by creating 50 lazy commits, now you need to put in 20 minutes figuring out how to cleverly combine them into 3 commits, before you can pull from main!"

You can also just squash them into 1, which will always work with no effort.

DHRicoF · a month ago
Then is not rebase your problem, but all your other practices. Long lived feature branches with lot's of unorganized commits with low cohesion.

Sometimes it's ok to work like this, but you asking git not being judgamental is like saying your roomba should accomodate to you didin't asking you to empty it's dust bag.

theryan · a month ago
While it is a bit of a pain, it can be made a lot easier with the --keep-base option. This article is a great example https://adamj.eu/tech/2022/03/25/how-to-squash-and-rebase-a-... of how to make rebasing with merge conflicts significantly easier. Like you said though, it's not super user-friendly but at least there are options out there.
Groxx · 25 days ago
>Replaying commits one-by-one is like a history quiz. It forces me to remember what was going on a week ago when I did commit #23 out of 45.

While I agree this is a rather severe downside of rebase... if you structure your commits into isolated goals, this can actually be a very good thing. Which is (unsurprisingly) what many rebasers recommend doing - make your history describe your changes as the story you want to tell, not how you actually got there.

You don't have to remember commit #23 out of 45 if your commit is "renamed X to Y and updated callers" - it's in the commit message. And your conflict set now only contains things that you have to rename, not all renames and reorders and value changes everything else that might happen to be nearby. Rebase conflicts can sometimes be significantly smaller and clearer than merge conflicts, though you have to deal with multiple instead of just one.

teaearlgraycold · a month ago
This seems crazy to me as a self-admitted addict of “git commit --amend --no-edit && git push --force-with-lease”.

I don’t think the tool is judgmental. It’s finicky. It requires more from its user than most tools do. Including bending over to make your workflow compliant with its needs.

just6979 · a month ago
A merge can have you doing a history quiz as well. Conflicts can occur in merges just as easily as rebases. Trouble with trying to resolve conflicts after a big merge is that now you have to keep the entire history in your head, because you don't have the context of which commit the change happened in. With rebase you'd be right there in the flow of commits when resolving conflicts.

Deleted Comment

CJefferson · a month ago
I don't mind rebasing a single commit, but I hate it when people rebase a list of commits, because that makes commits which never existed before, have probably never been tested, and generally never will be.

I've had failures while git bisecting, hitting commits that clearly never compiled, because I'm probably the first person to ever check them out.

Marsymars · a month ago
Sometimes it feels like the least-bad alternative.

e.g. I'm currently working on a substantial framework upgrade to a project - I've pulled every dependency/blocker out that could be done on its own and made separate PRs for them, but I'm still left with a number of logically independent commits that by their nature will not compile on their own. I could squash e.g. "Update core framework", "Fix for new syntax rules" and "Update to async methods without locking", but I don't know that reviewers and future code readers are better served by that.

jillesvangurp · a month ago
Rebase your local history, merge collaborative work. It helps to just relabel rebase as "rewrite history". That makes it more clear that it's generally not acceptable to force push your rewritten history upstream. I've seen people trying to force push their changes and overwrite the remote history. If you need to force push, you probably messed up. Maybe OK on your own pull request branches assuming nobody else is working on them. But otherwise a bad idea.

I tend to rebase my unpushed local changes on top of upstream changes. That's why rebase exists. So you can rewrite your changes on top of upstream changes and keep life simple for consumers of your changes when they get merged. It's a courtesy to them. When merging upstream changes gets complicated (lots of conflicts), falling back to merging gives you more flexibility to fix things.

The resulting pull requests might get a bit ugly if you merge a lot. One solution is squash merging when you finally merge your pull request. This has as the downside that you lose a lot of history and context. The other solution is to just accept that not all change is linear and that there's nothing wrong with merging. I tend to bias to that.

If your changes are substantial, conflict resolution caused by your changes tends to be a lot easier for others if they get lots of small commits, a few of which may conflict, rather than one enormous one that has lots of conflicts. That's a good reason to avoid squash merges. Interactive rebasing is something I find too tedious to bother with usually. But some people really like those. But that can be a good middle ground.

It's not that one is better than the other. It's really about how you collaborate with others. These tools exist because in large OSS projects, like Linux, where they have to deal with a lot of contributions, they want to give contributors the tools they need to provide very clean, easy to merge contributions. That includes things like rewriting history for clarity and ensuring the history is nice and linear.

cousin_it · a month ago
Maybe I'm old, but I still think a repository should be a repository: sitting on a server somewhere, receiving clean commits with well written messages, running CI. And a local copy should be a local copy: sitting on my machine, allowing me to make changes willy-nilly, and then clean them up for review and commit. That's just a different set of operations. There's no reason a local copy should have the exact same implementation as a repository, git made a wrong turn in this, let's just admit it.
jayd16 · a month ago
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.
tjpnz · a month ago
I've had recent interns who've struggled with rebase and they've never known anything but Git. Never understood why that was given they seem ok with basic commits and branching. I would agree that rebase is easier to reason about than merging yet I'm still needing to give what feels like a class on it.
recursive · a month ago
The fact that people have a harder time understanding rebase is evidence that rebase is harder to reason about. Whether you update your understanding based on that evidence is up to you. If I have to pick between merge and rebase, I would generally pick merge. It seems to cause less conflicts with long-lived branches. Commits maintain their identity so each one has to be conflict-resolved at most once.

However, even better for me (and my team) is squash on PR resolve.

eeperson · a month ago
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?
bodge5000 · a month ago
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
echelon · a month ago
git rebase squash as a single commit on a single main branch is the one true way.

I know a lot of people want to maintain the history of each PR, but you won't need it in your VCS.

You should always be able to roll back main to a real state. Having incremental commits between two working stages creates more confusion during incidents.

If you need to consult the work history of transient commits, that can live in your code review software with all the other metadata (such as review comments and diagrams/figures) that never make it into source control.

_flux · a month ago
Merging merge requests as merge commits (rather than fast-forwarding them) gives the same granularity in the main branch, while preserving the option to have bisect dive inside the original MR to actually find the change that made the interesting change in behavior.
jameshush · a month ago
This is one of the few hills I will die on. After working on a team that used Phabricator for a few years and going back to GitHub when I joined a new company, it really does make life so much nicer to just rebase -> squash -> commit a single PR to `main`
fc417fc802 · a month ago
> You should always be able to roll back main to a real state.

Well there's your problem. Why are you assuming there are non-working commits in the history with a merge based workflow? If you really need to make an incremental commit at a point where the build is broken you can always squash prior to merge. There's no reason to conflate "non-working commits" and "merge based workflow".

Why go out of the way to obfuscate the pathway the development process took? Depending on the complexity of the task the merge operation itself can introduce its own bugs as incompatible changes to the source get reconciled. It's useful to be able to examine each finished feature in isolation and then again after the merge.

> with all the other metadata (such as review comments and diagrams/figures) that never make it into source control.

I hate that all of that is omitted. It can be invaluable when debugging. More generally I personally think the tools we have are still extremely subpar compared to what they could be.

eeperson · a month ago
> I know a lot of people want to maintain the history of each PR, but you won't need it in your VCS.

I strongly disagree. Losing this discourages swarming on issues and makes bisect worse.

> You should always be able to roll back main to a real state. Having incremental commits between two working stages creates more confusion during incidents.

If you only use merge commits this shouldn't be any more difficult. You just need to make sure you specify that you want to use the first parent when doing reverts.

Izkata · a month ago
> I know a lot of people want to maintain the history of each PR, but you won't need it in your VCS.

Having worked on a maintenance team for years, this is just wrong. You don't know what someone will or won't need in the future. Those individual commits have had extra context that have been a massive help for me all sorts of times.

I'm fine with manually squashing individual "fix typo"-style commits, but just squashing the entire branch removes too much.

hnarn · a month ago
I completely agree. It also forces better commit messages, because "maintaining the history of each PR" is forced into prose written by the person responsible for the code instead of hand-waving it away into "just check the commits" -- no thanks.
astrobe_ · a month ago
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.

ratchetclank · a month ago
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...

orwin · a month ago
Ideally you only rebase your own commit on your own feature branch, just before merging. Having a clean commit history before merging make the main branch/trunk more readable.

Also (and especially) it make it way easier to revert a single feature if all the relevant commits to that feature are already grouped.

For your issue about not knowing which branch the commits are from: that why I love merge commits and tree representation (I personally use 'tig', but git log also have a tree representation and GUI tools always have it too).

chungy · a month ago
Sounds like you'd be a fan of Fossil (https://fossil-scm.org). See for instance: https://fossil-scm.org/home/doc/trunk/www/fossil-v-git.wiki#...
smartmic · a month ago
Let me expand on this with a link to the article "Rebase Considered Harmful" [0].

I also prefer Fossil to Git whenever possible, especially for small or personal projects.

[0] https://fossil-scm.org/home/doc/trunk/www/rebaseharm.md

hhjinks · a month ago
Which branch your work was done on is noise, not signal. There is absolutely zero signal lost by rebasing, and it prunes a lot of noise. If your branch somehow carries information, that information should be in your commit message.
ratchetclank · a month ago
I disagree, without this info, I can't easily tell if any commit is part of a feature or is a simple hotfix. I need to rely on the commiter to include the info in the commit message, which is almost always not the case.
fatbird · a month ago
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.
codesnik · a month ago
it's just "gitflow" is unnecessary complex (for most applications). with rebase you can work more or less as with "patches" and a single master, like many projects did in 90x, just much more comfortably and securely.
throwaway7783 · a month ago
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.
leptons · a month ago
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.

literallyroy · a month ago
I think the callout to squash first will be helpful (if your lots of commits aren’t good info themselves)
throwaway7783 · a month ago
Perhaps. But you can see the DX of rebase is abysmal compared to merge. squash, rerere, force push, remember to push to remote before rebase, more coordination if multiple people are working on feature branch etc.

I still prefer merge. Its simple and gets out of my way as long as I dont care about purity of history

thibaut_barrere · a month ago
PSA: I’m not terrified of rebase, yet it’s good to know this:

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.

ongy · a month ago
I think a large part of this is about how a branch is expected to be used.

Branches that people are expected to track (i.e. pull from or merge into their regularly) should never rebase/force-push.

Branches that are short-lived or only exist to represent some state can do so quite often.

xlii · a month ago
Also branches that are write-only by a single person by consensus. E.g. "personal" PR branches that are not supposed to be modified by anyone but owner.
thibaut_barrere · a month ago
It is this, plus more:

- the web tooling must react properly to this (as GH does mostly)

- comments done at the commit level are complicated to track

- and given the reach of tools like GH, people shooting their own foot with this is (even experienced ones) most likely generate a decent level of support for these tools teams

mr_mitm · a month ago
Is there a reason why that recommendation cannot be changed to "don't ever force push unless you are certain no one else has fetched this branch"?
z3dd · a month ago
Well that's a distinction which makes sense in theory but is not realistic in practice for most projects with multiple contributors.
eichin · a month ago
"fetched this branch" needs to include "started reviewing the PR", and probably other cases; it does mean switching modes for devs who usually rebase privately.

Deleted Comment

Deleted Comment

embedding-shape · a month ago
> 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 :)

psychoslave · a month ago
One step further which is in-scope-of-the-tool spirit will be git clone locally your repository.

And still one step further, just create a new branch to deal with the rebase/merge.

Yes there are may UX pain points in using git, but it also has the great benefits of extremely cheap and fast branching to experiment.

embedding-shape · a month ago
Yeah, works for normal "lets try out what happens when I do this" but it can get messy, depending on what you're trying out. That's why I always recommended beginners to literally "cp -r" the entire directory instead, together with the git repository, so they can feel freer to completely experiment and not be afraid of loosing anything.

I guess it's actually more of a mental "divider" than anything, it tends to relax people more when they can literally see that their old stuff is still there, and I think git branches can "scare" people in that way.

Granted, this is about people very new to git, not people who understands what is/isn't destructive, and just because a file isn't on disk doesn't mean git doesn't know exactly what it is.

afiori · a month ago
in my experience some of the trickiest situations are around gitignore file updates, crlf conversion, case [in]sentivity, etc. where clones and branches are less useful as a testing ground.
codesnik · a month ago
whoa. well, if it really works for you. The thing is, git has practically zero "destructive" commands, you almost always (unless you called garbage collector aggressively) return to the previous state of anything committed to it. `git reflog` is a good starting point.

I think i've seen someone coded user-friendlier `git undo` front for it.

embedding-shape · a month ago
I expanded on it more here: https://news.ycombinator.com/item?id=46601600

TDLR is: people feel safer when they can see that their original work is safe, while just making a new branch and playing around there is safe in 99% of the cases, people are more willing to experiment when you isolate what they want to keep.

g-b-r · a month ago
Except that with this article's advice (delete the repository and clone it again) the reflog and the unreachable commits get lost all the time

Dead Comment

bob1029 · a month ago
The fastest way to eliminate fear is to practice. I had the team go through it one day. They didn't get a choice. I locked us on a screen share until everyone was comfortable with how rebasing works. The call lasted maybe 90 minutes. You just have to decide one day that you (or the team) will master this shit, spend a few hours doing it, and move on.

Rebase is a super power but there are a few ground rules to follow that can make it go a lot better. Doing things across many smaller commits can make rebase less painful downstream. One of the most important things is to learn that sometimes a rebase is not really feasible. This isn't a sign that your tools are lacking. This is a sign that you've perhaps deviated so far that you need to reevaluate your organization of labor.

thunderbong · a month ago
One of the many things I like about fossil is the 'undo' command [0].

Also, since you can choose to keep the fossil repo in a separate directory, that's an additional space saver.

[0] https://www3.fossil-scm.org/home/help/undo

WolfeReader · a month ago
Fossil is great.
xorcist · a month ago
> cp -r $original-repo $new-test-repo

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.

z3dd · a month ago
why copy anything at all? you just need to preserve the head and that's it. As simple as `git branch bkp` and that's all.
recursive · a month ago
In order for that to work you need some level of confidence that rebase doesn't mess with your branch. Rebase has a reputation for "rewriting history".
tomaytotomato · a month ago
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.

freeplay · a month ago
Couldn't agree more. Squash merges to main ONLY.

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.

cdmckay · a month ago
This

Rebase only makes sense if you making huge PRs where you need to break it down into smaller commits to have them make sense.

If you keep your PRs small, squashing it works well enough, and is far less work and more consistent in teams.

Expecting your team to carefully group their commits and have good commit messages for each is a lot of unnecessary extra work.

ivanjermakov · a month ago
Squash is not Github specific and is part of git:

    git merge --squash

quacker · a month ago
Right, but they are referring to configuration on a GitHub repository that can make squash merge automatic for all pull request merges.

e.g. When clicking the big green "Merge pull request" button, it will automatically squash and merge the PR branch in.

So then I don't need to remind or wait for contributors to do a squash merge before merging in their changes. (Or worse, forget to squash merge and then I need to fix up main).