I usually just `git rebase origin/main -i` after the base branch has been merged there, and this means I need to explicitly drop the merged commits, but I can inspect what's happening.
I'm a heavy user of git-spice: https://abhinav.github.io/git-spice (created by a former coworker) and can't really go back to a time without it. While still not nearly as good as Facebook's Phabricator, it's probably the best workflow for small, focused stacked PRs you can achieve in a Github / Gitlab based repository.
Running a git command on one branch and multiple branches being affected is really unusual for me! This really does look like it is designed for just this problem, though. Simple overview: https://blog.hot-coffee.dev/en/blog/git_update_refs/
This marker branch step feels like a workaround to a missing capability. It's something I can easily see one forgetting especially if they haven't been doing stacked diff workflows regularly.
Even if `--update-refs` didn't exist, my experience is that git can identify duplicate commits produced by rebase, and knows to skip them when rebasing the same commits to the same place again. Am I imagining that?
Every time I see one of these nifty git tricks or workarounds I find myself wondering, “why not just use jj?”
You get a nicer, significantly simpler interface. You don’t need any tricks. You don’t have to google how to work yourself out of a bad state, ever. And you get near-perfect git compatibility (ie you can use jj on a shared git repo, doing all the same things, and your teammates won’t know the difference).
I’ve wondered if there is a psychological thing here: someone who spent time memorizing all the git nonsense may have some pride in that (which is earned, certainly), that introduces some mental friction in walking away???
The most common reason is that the git user has no idea it exists. I am in this bucket alongside 99% of git users.
There are an assortment of other potential reasons:
- It is a new tool that hasn't been battle tested as much as git has, which can decrease confidence.
- Git has inertia. People have learned it, it takes less effort to add a new git skill to your repertoire than learn a new tool from scratch, even if the new tool is easy to pick up.
- Due to its novelty, the auxiliary tooling ecosystem for jj is smaller (does it have plugins for all the popular editors? Lots of people like those, git's are high quality)
- Git is good enough. It's not perfect, but its popularity means that its shortcomings have readily available fixes or tweaks from users. It simply isn't bad enough and there are bountiful resources on how to use it effectively.
Is there any reason besides merge commits ending up in history to not do this with merges instead? ie merge main into feature-1, then feature-1 into feature-2.
Sounds like using --update-refs would let you do all that in a single operation, but you still need to force-push and don't maintain an explicit merge/conflict resolution history, both of which could be considered sub-optimal for collaborative scenarios.
I've settled on a workflow that reverses the situation. I simply commit all my work to the main branch and cherry pick commits into temporary feature branches only when submitting PRs.
This way I only need to worry about maintaining a single consistent lineage of commits. I've been using this workflow for about a year now and find it to be much easier than juggling and rebasing feature branches.
Stacked commits is awesome, but it sucks that with git you need all these workarounds, and that the servers (GitHub etc) are not designed with that workflow in mind.
I left Google a few months back to work on another project. I missed the internal version control so much that I dropped that other project and am now focused http://twigg.vc
It's very similar to what's used at Meta and Google. Atomic commits, linear history, auto rebase, code review compatible with stacked commits.
I’ve been meaning to write this article for a long time @flexdinesh . Thanks for taking the time to share this technique for managing stacked diffs using vanilla git rebase!
Eh I just use `git rebase -i` and delete the commits I don't want. Much easier to think about.
But the real problem with this workflow is that neither Github nor Gitlab support it at all. Not even Forgejo does. Which blows my mind because is such an obvious way to work.
As far as I know only Tangled actually supports it, but unfortunately Tangled is tangled up in its own weird federation ideas. There's no way to privately host it at all.
What I would really like is a Git equivalent to Mercurial's "fold" operation. I usually make a bunch of commits as I work, just as checkpoints, which I then want to turn into a single final commit when it's done, which could be quite some time later, e.g. "started on thing", "broke it", "broke it more", "Add thing to improve foo of bar".
Mercurial's "histedit" command offers two operations to combine the commits: "fold" and "roll", where "fold" brings the earlier commit into the later commit and "roll" does the reverse. The "fold" operation does exactly what I want in this case: It brings all the changes into the final commit from when I actually finished it, using the commit date of that commit.
Git's "rebase -i" command offers "squash" and "fixup" and "fixup -c" and "fixup -C", but they all do the same thing as "roll", i.e. they keep the earliest commit's date, the date when I started working on the thing and not the date when I finished working on it. (So I then cut and paste the correct date and update the commit afterwards. This may not be the best way to do it.)
This made good sense, though by the time I got to bits saying "that last step is critical. Without it, your next sync will break" and "Don't forget!" I was laughing out loud. I love git :-)
I feel that stacked PR in git should be common knowledge, however many of the documentations are scattered. I found stacked.dev but feels that its not exploring pure-git workflow that well. thats why I tried to collect all of those docs into a single website that's dedicated into that singular workflow. would love to get any feedback:
40 comments
[ 2.5 ms ] story [ 62.0 ms ] threadThis seems like a house of cards whose juice isn’t worth the squeeze. But I would love to split up my PRs into smaller pieces.
I just would hate to waste time with an incomprehensibly goofed git history because I forgot a command.
Running a git command on one branch and multiple branches being affected is really unusual for me! This really does look like it is designed for just this problem, though. Simple overview: https://blog.hot-coffee.dev/en/blog/git_update_refs/
https://github.com/jj-vcs/jj
https://www.stavros.io/posts/switch-to-jujutsu-already-a-tut...
Also found https://github.com/gitbutlerapp/gitbutler
I used to dutifully read release notes for every git release, but stopped at some point. Apparently that point was more than three years ago.
https://blog.gitbutler.com/stacked-branches-with-gitbutler
Perhaps not an option if you need to release the work in branch 1 before the work in branch 2 is ready/reviewed/etc.
You get a nicer, significantly simpler interface. You don’t need any tricks. You don’t have to google how to work yourself out of a bad state, ever. And you get near-perfect git compatibility (ie you can use jj on a shared git repo, doing all the same things, and your teammates won’t know the difference).
I’ve wondered if there is a psychological thing here: someone who spent time memorizing all the git nonsense may have some pride in that (which is earned, certainly), that introduces some mental friction in walking away???
The most common reason is that the git user has no idea it exists. I am in this bucket alongside 99% of git users.
There are an assortment of other potential reasons:
- It is a new tool that hasn't been battle tested as much as git has, which can decrease confidence.
- Git has inertia. People have learned it, it takes less effort to add a new git skill to your repertoire than learn a new tool from scratch, even if the new tool is easy to pick up.
- Due to its novelty, the auxiliary tooling ecosystem for jj is smaller (does it have plugins for all the popular editors? Lots of people like those, git's are high quality)
- Git is good enough. It's not perfect, but its popularity means that its shortcomings have readily available fixes or tweaks from users. It simply isn't bad enough and there are bountiful resources on how to use it effectively.
This makes it unusable for me.
Sounds like using --update-refs would let you do all that in a single operation, but you still need to force-push and don't maintain an explicit merge/conflict resolution history, both of which could be considered sub-optimal for collaborative scenarios.
This way I only need to worry about maintaining a single consistent lineage of commits. I've been using this workflow for about a year now and find it to be much easier than juggling and rebasing feature branches.
In case anyone's interested, I made a tool that automates this workflow. The worfklow and tool are described here: https://github.com/bjvanderweij/dflock/
Having to rebase again and again is a symptom that a dev branch is living for too long.
I left Google a few months back to work on another project. I missed the internal version control so much that I dropped that other project and am now focused http://twigg.vc It's very similar to what's used at Meta and Google. Atomic commits, linear history, auto rebase, code review compatible with stacked commits.
But the real problem with this workflow is that neither Github nor Gitlab support it at all. Not even Forgejo does. Which blows my mind because is such an obvious way to work.
As far as I know only Tangled actually supports it, but unfortunately Tangled is tangled up in its own weird federation ideas. There's no way to privately host it at all.
Mercurial's "histedit" command offers two operations to combine the commits: "fold" and "roll", where "fold" brings the earlier commit into the later commit and "roll" does the reverse. The "fold" operation does exactly what I want in this case: It brings all the changes into the final commit from when I actually finished it, using the commit date of that commit.
Git's "rebase -i" command offers "squash" and "fixup" and "fixup -c" and "fixup -C", but they all do the same thing as "roll", i.e. they keep the earliest commit's date, the date when I started working on the thing and not the date when I finished working on it. (So I then cut and paste the correct date and update the commit afterwards. This may not be the best way to do it.)
https://stacked-pr.github.io
(disclaimer: partly supported by AI, but most of the writing structure were made by hand)
It's fashionable at the moment so there is a bit of herd mentality going on. In 2 years they'll be a reinvention of the wheel again against it.