7 ms·
> rebasing creates a cleaner, more understandable history & state of the world without the clutter of merge commits "Cleaner", for some definition of "clean".
by wandernotlost 3y ago
> rebasing creates a cleaner, more understandable history & state of the world without the clutter of merge commits
"Cleaner", for some definition of "clean". In this case, pretty, not accurate.
I just can't understand the draw of rebase based workflows. It seems to be an expression of a preference for aesthetics over accuracy. What is the point of source control, other than to reliably capture what actually happened in history? As soon as you start rewriting that, you compromise the main purpose.
Using merge commits preserves what you actually committed. If you ran tests before you committed, rewriting that commit invalidates that testing. If you need to go back and discover where a problem was introduced or what actually happened, with certainty, in a commit history, rebase undermines that, because it retroactively changes your commits.
It's like a huge portion of the industry is collectively engaging in a lie, so that our commit histories look prettier.
- azornathogron 3y ago> What is the point of source control, other than to reliably capture what actually happened in history? Unless you're committing every keystroke, you're recording a curated history. You choose when to commit, and by choosing you declare some historical states to be worth keeping and the rest to be merely incidental. I think usually history "rewriting" (eg, rebasing) is much more about curation - choosing which aspects of the history you care to record - than it is about presenting a false record.
- lolinder 3y agoExactly. To analogize to history history: OP wants the version control history to look like a collection of primary sources. Here's the president's daily calendar, there's the letter he received on April 24 from a small child in Wisconsin. In this model, it's up to future code historians to piece it all together into a story. When I go back and look at the git history, I would much rather have had someone do the work of compiling the story for me at the time. Commits are your chance to document what you did for future programmers (including future you). If you insist on them faithfully reflecting every change you made over the course of three days, then future you will have to piece that all back together into a coherent story. Why not take the chance to tell the story now, so that future you can skip all the false starts and failed experiments and just see the code that actually made it into main?
- wandernotlost 3y agoThis isn't a novella. We're talking about executable code. What you're suggesting is the equivalent of using an encyclopedia as a legal reference. Merge commits tell the coherent story. Commits reveal the messy history that got you there, which is critical exactly when you need to look at history. If you're not trying to track down the source of a problem and how it was introduced, in a deterministic way, why do you bother keeping source history? Publish pretty changelogs instead.
- lolinder 3y agoCan you give a concrete example of when you've used the messy details of how a change was introduced at a sub-PR level? I'm strongly opposed to squashing, but when have you found that a chronological sequence of commits-as-they-were-committed has been helpful where a sequence of heavily-cleaned-up patches would have obscured useful information? In my experience spelunking through git history, I've only ever been frustrated at the number of different red herrings I've found in a git blame that turned out to be a failed experiment that never got merged in. Concretely: API changes are a big one, where in the history it looks like we may have once accepted something different than we do now, but then it turns out that that change was reverted before ever making it to production. This information being in the log clutters the git blame (the function was actually last changed in 2016, but someone modified it last month only to revert the change before submitting a PR), without providing an ounce of useful information about the history of the production app. As a rule, when debugging problems, I don't care about how your private branches changed over time, I care about how the production code changed over time.
- flir 3y ago> I'm strongly opposed to squashing > As a rule, when debugging problems, I don't care about how your private branches changed over time, I care about how the production code changed over time. Ironically, squashing is probably the best tool you have to deal with developers who won't clean up their PRs. It's a pretty blunt tool though.
- wandernotlost 3y ago
- wandernotlost 3y agoOkay, but rebasing is changing each point in time of that history–that you curated by choosing when to commit–to be something different from what it ever was, retroactively. It's literally creating an entirely new history that nobody has ever actually examined, introducing the possibility that points along that history are inconsistent with what was intended at the point of each commit.
- lolinder 3y ago> creating an entirely new history that nobody has ever actually examined I think the confusion here is that you're assuming that OP's commit history looks like yours, with dozens of commits per PR that no one could possibly examine in detail with each rebase. At least for me, since I'm okay with rewriting history on local branches, I have a very small number of commits that do get examined each time I rebase. I average 3-4 commits per PR. There's usually one that refactors the existing code to lay the foundation for a new feature, maybe one that just moves a few files around (to ensure git recognizes them as moves and not delete/recreate), and 1-2 that introduce the new feature. When I rebase on main, I examine the diff for each commit before pushing to my branch. If something has meaningfully changed, then I adjust the commits appropriately. My commits aren't a history of what actually happened, they're a description of the steps that it takes to add a feature to (or fix a bug in) main. If main changes in a way that introduces a conflict, I want to reevaluate each step that I'd previously laid out.
- ponector 3y agoI see no issues here. If you run tests before commit then you also run them after rebase, same way as after merge. If tests failed - you can force pull your branch from remote and have the same state as before rebase.
- wandernotlost 3y agoYou run tests against each commit in the history that you're rebasing? I doubt it, and I guarantee that nearly nobody using rebase does that.
- ponector 3y agoI agree. But that was what been described. In my experience people are not running tests locally at all. Push to the remote, open pull request and wait for pipeline results. In such situation the result will be the same: you will never know which commit from merge/rebase brakes your pipeline tests.
- foreigner 3y agoThis. It's a dirty lie, that's not what actually happened!
- deleted 3y ago[deleted]
- lolinder 3y agoWhy does it matter what actually happened? Can you give a concrete example of when you care the exact sequence of experiments, false starts, and refinements that a feature went through before making it into a PR?
- flir 3y agoTry it like this, see what you think: Commits serve two needs: saving your work and publishing it. Adopting an "early and often, explain what you did" approach is effective for saving, but when it comes to publication a "refine before release, explain why you did it" strategy is more valuable. The commit history is an artifact of the development process, just like documentation, tickets, or even code. I'm sure you wouldn't complain about people taking the time to write better comments, and a commit message is like a super-comment, because it can apply across multiple files. Honestly, do a maintenance programmer a favour - fix up your commits before publishing them. A linear history makes tools like bisect easier to work with.
- wandernotlost 3y agoI wonder if the difference here is in what your quality threshold for a commit is. I commit when I reach a point of coherence in the code, and ensure that the code passes tests before I commit. Each commit is thus a checkpoint of coherence, where the points in between may be out of order or failing tests. Maybe I just don't consider "saving your work" to be a valid use case for commits. Use an IDE or other local tools for that. Commits are points that are worth saving (or "publishing" if you prefer) beyond your local workspace.
- flir 3y agoKind of. I've been thinking about it since I wrote that, and what I say and what I do are a bit different. I don't rebase when I'm done, I rebase as I go as a constant background hum. So I start by creating a few empty-ish commits that are roughly analogous to the tasks you'd break a ticket down into. Then I create many small WIP commits, but in the commit message I note which task they belong to. So I might have two initial commits in a branch that say "[#123] Refactor foo" and "[#123] Upgrade bar", followed by a bunch of "[WIP] typo fix, merge against foo" and "[WIP] Preparing baz to upgrade bar". Then when I feel I've reached a point of sanity I pull the main branch, rebase my feature branch on top of it, merging my WIP commits as I go. Occasionally I'll even go back and split a WIP commit in half if there's a better logical mapping to the tasks. If I haven't pushed I don't consider it saved, so I wouldn't like to rely on local-only tools in that way. I'd much rather push to a remote repo daily. It's not like anyone's going to see it until I raise a PR. What do you do when you spot a typo ten seconds after you committed something? A separate typo fix commit? I prefer to merge it on to the previous commit. Nobody needs to run "git blame" and see "typo fix" as the last time that line was touched. It's noise.
- kragen 3y agothe point of git is to enable linus or al viro or whoever to review your proposed changes as quickly and efficiently as possible, so they can be confident that what they're merging into their kernel tree is relatively sane, and then to actually do the merge in a reliable way that won't introduce other unintentional changes, and to be able to reproduce their own previous state in that context it makes sense to use rebase to present linus with the cleanest, most comprehensible patch set possible, not your lab notebook of all the experiments you tried and the obvious bugs you had. you don't want to waste linus's time saying 'you have an obvious bug in commit xyz' followed by 'oh, never mind, you fixed that in commit abc' but for my own stuff i prefer merge over rebase because i'm both the producer and the consumer of the feature branch, and rebase seems like more work and more risk
- codeptualize 3y agoThis, I really don't mind merge commits, it's nice to see what happened when. Especially if you run into conflicts and issues caused by bad resolution it's much better to have a clear true history.
- fnordpiglet 3y agoI don’t look at anything other than the merge to a trunk or main as part of the history. It’s not an audit log. I often do check point commits to move local state to a central git as a backup, or commit when I simply want to have a rollback option for something I’m not confident in. I always commit at the end of a day, for instance, and push to a remote, as I don’t trust my laptop or whatever, or worse some cloud dev machine. None of these commits are useful for anyone, not even myself, beyond the immediate utility. I squash intermediate commits between change sets, and try to only reveal atomic change sets on any shared branch. It’s absolutely the history of what has changed, but it is not some sort of journal log of every event in my development workflow the shared branch should absolutely be the evolved history of the source code, but without reflecting the work style of any one developer. It should be a comprehensible history of meaningful changes that can be independent reasoned about and cherry picked or reverted to as necessary. Every other commit is noise to everyone, including yourself, once it leaves your own branch. Since it didn’t even run in production there’s not even a plausible regulatory reason to keep them.
- hgomersall 3y agoWhy not have both? If you can filter by merges, what's the harm in having intermediate positions? There have been various points I actually wanted to have the vim undo log as well. That's what I'd really like - essentially a way of undoing back to time zero, with commits denoting feature complete positions and merges denoting, well, mergeable positions that have passed review.
- fnordpiglet 3y agoIn branch based development you do have both, in your branch. But if you’re working with other people, do they want your undo log? Is there any value to your undo log in say 10 years? The git repo on the main branch is your shared artifact, and as a matter of good practice is should be treated a shared resource that’s presentable to all, presents an easily understood and consumed interface, and is free of individual noise. If you want an undo log for your work on X, then when you merge your branch with main, don’t delete your branch. But the shared artifact shouldn’t be filled with everyone’s individual work process artifacts.
- dasil003 3y ago> "Cleaner", for some definition of "clean". In this case, pretty, not accurate. What do you mean "accurate"? The developer decides when to commit and what message to write, rebasing just enables more control over the final artifact that is shared. Have you ever heard the writing advice: don't write and edit at the same time? Rebasing allows one to use the full power of git during development, committing frequently, and creating a very fine grained record of progress while working, without committing to leaving every commit on the permanent record. The official record of development history is more useful if it's distilled down to the essence of changes, with a strictly linear history, and no commits that break CI or were not shippable to production (at least in theory). Doing so makes future analysis and git-bisect operations much more efficient, and allows future developers to better understand the long arc of the project without wading through every burp and fart the programmers did during their individual coding process. To those who say, "don't commit until you have a publishable unit of work," I say, you are depriving yourself of a valuable development tool. To those who say, "don't rebase, just squash", I say, squashing is rebasing, just without curation. To those who say, "rebasing is more error prone than merging", I say, if a merge commit turns out to have a problem you will have a much harder problem debugging it because it could be caused by either branch, or an interaction which no one considered. The beauty of rebasing is that it forces the developer to think about all the intervening changes commit by commit as if they started their feature development from the current state of the main branch. This is a more healthy mental model and puts more responsibility on the developer to ensure their code reflects the current state of the world, and not just hastily merging without recognition of what has changed since then. After all, production can only have one commit on it at a time, and given many investigations hinge on understanding what SHAs were in production at what point in time, it makes everything a lot easier with a linear history that hews closely to what was actually shipped. I realize that there's a learning curve for rebasing, but once you understand it, it allows conflict resolution to be resolved much more precisely with roughly the same level of effort. You can dismiss this as an aesthetic preference, along with good commit messages, changelogs and other points of software craftsmanship, but in my experience that there is real value in maintaining a high quality history on a long-lived project.