@b0rk You point out some great examples of how rebase/force-push can confound peer review. That’s my main gripe with it.
My workaround is, after I’ve asked for reviews on a PR, if I have further nontrivial fixes, I ‘git checkout -b mybranch-wip’ and hack on that for a while. I can push it, see CI results, and ask for informal feedback. Once I’m done I can squash it back onto mybranch, reset —hard mybranch to it, and push to cleanly update my PR
@jamiemccarthy @b0rk I've started using "git absorb" for PRs. One absorb per nitpick, push, resolve, then next nitpick, and when approved either rebase or squash.
Then again, I REALLY hate octopus merges.
@b0rk oh, i feel very seen having messed up all these things while learning how to rebase hahaha.
One thing i didnt see is rebasing when you or someone else has pushed a merge commit/figuring out how to drop a merge commit since that commit doesn’t appear in the rebase commit log view
@b0rk Taking commits from other people with you when rebasing on a branch that corresponds to another major version (is that rebase type 3? or maybe just a special case of type 2?)
Solution: either use `--onto` or do an interactive rebase and make sure to take only your commits. You can customize the format to make them easier to spot: https://greg0ire.fr/git-gud/#/21
@b0rk Not something you missed, but maybe useful as addition to the force push section:
We sometimes intentionally force push to shared feature branches, to rebase it against the main branch. This breaks all derived branches, but they can be repaired using `git rebase --onto <feature-branch> <last-hash-of-local-feature-branch>` on a branch created from the feature branch.
Sorry if you already knew, but I saw no mention of --onto in the blog
@b0rk oh, good one!
rebase -i HEAD~4
That one doesn’t always properly count to 4 or whatever, and it isn’t necessarily because of merges. I haven’t figured out the reason yet.
rebase and tags
Tags become kind of orphaned, their history lost (easy enough to fix, just checkout the tag, create a branch from there and push, but that’s annoying)
rebase and collaboration
If anyone else is working on a branch that you keep rebasing, you’re going to have conflicts of a different kind 😈
@b0rk It sometimes speeds things up for me if I know that some changes are already on my branch, I'll delete them first on my branch and the rebase goes better. I'm a huge fan of rebase and use it all the time to make my changesets cleaner.
Thanks for the article.😀
@b0rk For "never force push to a shared branch" what is a shared branch? When I first encountered this I interpreted it as anything that anyone else could even have seen, but that's maybe too strict?
I started a new role recently and they are in the habit of having branches up for PR which are mostly one person but then someone else comes in during PR and rebases to clean up the history but also adds things that they thought would improve the PR, but then it turns out some other change is needed because a change in another repo is now causing CI to fail... This is my first encounter with a rebase heavy workflow.
@b0rk I'm reading this carefully because I like rebase, so I'm curious too as to why some people don't like it.
Plus I have a team that is new to git and I want to adopt "beginners mind" when we transition them from svn (don't ask) to git.
@b0rk the first thing I looked for when I got the idea of what this post was about was if you knew a solution to loss of gpg-signatures. It’s very much still something some people (at least me) care about a lot.
Another thing to add to the list is rebase merges can mess up the SHA of commits, making it impossible to add a .git-blame-ignore-revs in a PR containing the thing you wanna ignore.
@b0rk great post! I particularly liked "stopping a rebase wrong", because I've done that a few times but never quite gave it its own brain-category for "yes that is a specific mistake I keep making".
"complex rebases are hard": yes, when I'm polishing a personal branch to push, I often take it a lot further than 2 rebases. More like Θ(N) rebases to sort out N commits!
(That _feels_ wasteful because it's quadratic time overall! But git's working time is dominated by my thinking time.)
@b0rk Nice write-up! I learned a few things, now to remember to use them next time. 😅
> I was curious about why people would run git push --force on a shared branch.
Collaborative feature branches are a thing, especially when you’re not shipping a web app but a client side native artefact. Rebasing those feature branches on the main branch is useful as much as it is for single-developer ones. But it needs to be coordinated; I usually rename the remote branch to a backup name.
@b0rk We use git submodules extensively.
If we have a repo with multiple local (as yet, unmerged) commits that reference other local commits in a submodule, then rebasing the submodule branch requires also fixing up each commit in the parent, since those now point to invalid submodule commit hashes.
(I'm currently working on tooling to make this process easier)
@pcreux @b0rk I came here to say the same thing. I commit _a lot_ and don't ever bother thinking about my commit history until I'm getting ready to submit a PR.
If I somehow ended up with a PR that's large enough that it really should be split into multiple commits, I'll create a new branch off main, do a `git merge --squash`, only add the files/lines that should be part of that commit, and discard the rest.
@b0rk Also, you mentioned having a bunch of "wip” and "typo" commits, which I do do *sometimes*, but most of my commit messages are notes to myself. “Got most of the tests working, except for that one DB call”, “Everything runs, but need to format the new file”.
Super useful when I'm working on something a few hours at a time over the course of a week, it helps me remember what I was thinking.
@b0rk Didn't know about `git rerere`. Thanks for sharing! I suggest amending:
> use `--force-with-lease` when force pushing, to make sure that nobody else has pushed to the branch since you last fetch
to emphasize that fetching immediately before pushing this way defeats the protection of `--force-with-lease`. I've recommended this flag to less experienced developers eager to improve their Git skills, failing to make this gotcha crystal clear. On one or two occasions, it has led to lost work.
@b0rk I’m a little late to this, but...
My own feeling about rebase is not so much “what can go wrong” as a philosophical dislike of effectively re-writing history. A practical consequence of this is that when you look back at the commits, they aren’t the ones you did (or anyone did), which, for me, makes it harder to get my mind back to where it was when the changes were being made. And that’s a lot of what I want from history.
@njr i’ve heard this objection a lot of times and i’m really curious about it but struggle to relate to it — personally the history i’m rewriting is often 17 “wip” or “fix” commits which to me are totally worthless as a historical artifact. Are you saying that you already make pretty nice commits to begin with? do you ever amend commits? does amending a commit feel like rewriting history to you?
(those are all genuine questions!)
git rebase --autosquash --interactive to do a final editing round. For project with dependable and fast test suites, adding --exec "make test" to the rebase command could be an improvement to that process. 🤔