![]() |
|
![]() |
| I'll add what should be obvious to any seasoned git user: rebase is only possible for "private" commits. If the commits were published, then merge is the only solution, because otherwise with a rebase you will "change (git) history" and break people's repos that have yours as upstream.
I recommend reading this 2008 exchange between top Linux developers learning to use git, and Torvalds's... characteristic language when talking about rebasing a "public" repo: https://www.yarchive.net/comp/linux/git_rebase.html (I'm putting "private" and "public" in quotes because they're really on a spectrum) |
![]() |
| Never use git push —force. Alway use git push —force-with-lease.
Then if someone changed your remote branch, git will always let you know :) |
![]() |
| I think feature branches typically should be "mostly" private - but sometimes a few quick fixer are better to push, rather than just describe as a review comment. |
![]() |
| This. --force-with-lease is what --force should have been in the first place. Hopefully they will eventually make it so, and rename --force to something less accessible. |
![]() |
| In practice it’s fine to rebase and force push to your own published branch, even when it’s an open PR.
Just mark it draft or otherwise communicate when it’s no longer a moving target. |
![]() |
| I try not to rebase and force push for branches under active review though. It makes it much harder for the reviewer to see the changes that have happened since they last reviewed. |
![]() |
| Use a temporary branch for those. When you come back, undo the commit (git test -- hard if memory serves but i just have an "uncommit" alias) and commit the fully finished work to the real branch. |
![]() |
| And messy intermediate states won't help him at all. It won't help him either when related commits are interwinned with unrelated commits in history rather then being together. |
![]() |
| No, that describes rebasing and preserving the intermediate commits. Of course, if you squash into one commit at merge time, this won't happen. |
![]() |
| If you want the full history of someone's work, you need all the edits. Including all the times they backspaced over the typo. With down-to-the-millisecond timestamps attached! |
![]() |
| CI will usually only run the latest commit. Even if commit a, b, and c were all tested, the resulting commits a', b', and c' after rebase would usually only have the last one tested. |
![]() |
| Since you shouldn't publish commits that weren't tested, this suggests you should publish only one commit at a time.
(Unless you think "works on my machine" is good enough testing. Sometimes it is.) |
![]() |
| The problem I run into is when a developer merges the main line branch into their feature branch repeatedly to get new changes. Now your branch is completely tangled up and difficult to change |
![]() |
| I do this like... all the time. At least weekly for the last 5 years maybe? I can't remember a time where this has caused anything to get tangled or difficult. |
![]() |
| Maybe? But it's also what merge is for.
If I used rebase like this regularly it would become difficult to determine if a given commit is an ancestor of HEAD. Sometimes I like to do that. |
![]() |
| If I'm making a change that people are likely to wonder about, I tend to add comments. Basically, anything that's not obvious tends to get a comment, change or no. |
![]() |
| > How many years have you worked in the software industry?
Professionally, 21 years. Seems like you're implying that someone with an opinion that is different than your own is not experienced. |
![]() |
| Stashing always makes me nervous.
For some reason it feels ephemeral and I worry that I’ll lose the stashed changes like when you accidentally overwrote the copy/paste clipboard |
![]() |
| Git stash is indeed more dangerous than branching. Stashes don’t go into the reflog, and so you lose the primary safety net that git offers. I’ve watched people get into a bad state with stash and end up losing work. The stash documentation says “If you mistakenly drop or clear stash entries, they cannot be recovered through the normal safety mechanisms. However, you can try the following incantation to get a list of stash entries that are still in your repository, but not reachable any more: ” https://git-scm.com/docs/git-stash
I know stash feels easier to a lot of people, and that’s a valid reason, but it’s really no more typing or thinking to use branches instead, it just might require changing habits. |
![]() |
| > I had a colleague (smart guy, lots of impressive Ivy League creds after his name) who just _insisted_ that rebasing was evil.
There is an historical reason for this. It is a hyperbolic marketing point of view invented by Richard Hipp, the author of SQLite, and of Fossil, in order to try to sell Fossil as being superior to Git. “Rebase is a lie” has been a meme ever since he published an article titled “Rebase Considered Harmful”, which has been posted to HN many times. Because he has contributed a lot to open source software development and because SQLite is so widely used, a lot of people respect everything he says and so this unfortunate idea has spread and gets repeated, even by otherwise very smart people. Dr. Hipp has made appearances on HN arguing that rebase is bad, and even told me it should be likened to criminal activity. (https://news.ycombinator.com/item?id=29133188) The Fossil documentation still has remnants of it, but it has been softened over the years. Luckily it seems to be dying, and I think the Fossil team might be coming around to the realization that this negative attack smear hyperbole is ultimately not helping Fossil grow. This is the primary sticking point for me and the reason I won’t use Fossil. I’m actually quite interested in trying it, but not until the (ironically) dishonest claims about Git and it’s goals are taken down. |
![]() |
| What is your definition of a shared branch? Is it shared the moment you push it (assuming a github like workflow). Is it shared if you check it out on two separate machines just for yourself? |
![]() |
| There are some downsides.
https://github.com/libexpat/libexpat/pull/789/commits is a PR of 16 related commits. Many of them make sense individually, but are depended on by subsequent commits. Reviewing them separately makes sense. It's easier on the reviewer (and future readers) to handle multiple smaller changes, especially since each is more tightly coupled to a rationale in the commit message. Unfortunately, github's PR-focused UI doesn't really make this per-commit review as convenient as Gerrit does. Additionally, the last two commits are authored by another contributor. This metadata would be lost in a squash. Of course, each intermediate commit must build and pass tests. WIP and cleanup commits are squashed locally before final review/merge. You could argue that all of these could be separate PRs, but I think there's value in grouping them up with that final merge commit, showing what one was trying to do at a larger scale. |
![]() |
| I have never seen stacked PRs work that well in practice. Two reasons:
- Namely there would be review comments on the first PRs that then cause a cascade of merge conflicts in the follow-on PRs - Somehow reviewers never seem to like the stack of PRs, my experience is they always react with disdain and/or confusion. There is the counter-question too, why not stack commits cleanly? A third reason against, not a good reason, but for many developers Git is super difficult (IMO largely a skill issue, not taking the time to learn basic tools that they need everyday; otherwise I have no clue why software developers do not learn their IDEs and VCS tools very well). Stacking PRs requires some Git skills, a simple feature-branch workflow can be a challenge for many.. Ultimately, I think the solution to stacked PRs is to change review policy to "ship/show/ask": https://martinfowler.com/articles/ship-show-ask.html In other words, if someone is skilled enough to do a set of stacked PRs, the team likely benefits by letting that person merge the stack on their own when each bit is ready and do a post-merge review instead of pre-merge. (Side-note, my unsolicited perspective: I'm personally convinced that the benefits of linear history is a magnitude more important than all the other peeves & nits combined between merge vs rebase.) |
![]() |
| > - Somehow reviewers never seem to like the stack of PRs, my experience is they always react with disdain and/or confusion.
Are people sending multiple branches of the stack for review at once? It should only ever be the "bottom" branch out-for-review at any time. > - Namely there would be review comments on the first PRs that then cause a cascade of merge conflicts in the follow-on PRs This can still happen in the model above of course as you need to make changes to the bottom branch in response to review comments/requests. However, as I noted elsewhere, `--update-refs` is an absolute god-send in those situations: https://andrewlock.net/working-with-stacked-branches-in-git-... It reduces a ton of manual work (scaled by how many branches you have stacked!) to one operation. |
![]() |
| To work on the same code base as the rest of my team? Like everyone else. Certainly not to nitpick about how history is written, in what order or what is omitted. |
![]() |
| I find merges more confusing than rebasing. At the end of rebasing you just have commits. Merges are commits with multiple parents, which is a confusing concept. |
![]() |
| Why haven't more people heard of git revise?
Unless you actually want to transplant your changes onto a new base, honestly, do yourself a favor and use this instead:
https://github.com/mystor/git-revise Its inability to change your worktree (operates in memory instead) is a big speed and safety feature: It won't invalidate your build, and you can't screw up the end state. It also has features that regular rebase lacks, like splitting a commit and editing all commits at once. I'm more than a big fan of it. |
![]() |
| And a fairly quick way to do the same sort of thing is `git fetch && git rebase -i origin/main`. You never bother updating `main` because you kind of don't care for the task at hand. |
![]() |
| Your commit history is part of your product, not scaffolding. Well designed commits make a codebase a pleasure to work on and reduce the accrual rate of technical debt. |
![]() |
| This may be rose-tinted glasses, but I was fine with merges with Mercurial / https://tortoisehg.bitbucket.io/ but subsequently using git (with gitlab/GitHub ) I've been rebase + `--force-with-lease` every time. I'm happy with my current git workflow, but I can't help but feel I'm missing a trick through finding git merge commits so difficult to understand
|
![]() |
| Use `git log --first-parent` and if you merge, write useful merge commit messages. There, I've resolved the difference between merge and rebase forever. |
I, too, much prefer a rebase-heavy workflow. It allows me to both have a dirty "internal" history and clean up for publication.
As a side-effect, it also makes me comfortable having a mostly linear-history for what I publish, as opposed to a many-branched, merge-heavy one, which I dislike, and makes history confusing.
I reject the argument that a no-rebase, merge-only history "preserves the true history of how commits were created", because I believe that is irrelevant. What is relevant is what the tree looks like once the merge (or rebase) lands.
Should a merge conflict arise, in a rebase workflow the conflict resolution is folded into the rebased commit, so it looks like it was fine all along. In a merge workflow, the fix is in the separate merge commit. In both cases you still have to handle the merge conflict. And in my opinion it is not significant for the merge conflict resolution to be separate from the original commit itself because, again: what's important is the final state of the repo.