Stacked Diffs with git rebase —onto
Posted by flexdinesh 11 days ago
Comments
Comment by swaits 7 days ago
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???
Comment by mwcz 7 days ago
jj is harder to adopt for people with a thorough mental model of git, because it's harder to accept jj commands at face value. I know it's modifying my git tree, so I feel compelled to grok exactly what it's doing, but that's distracting and time consuming.
People like me should probably trial jj exclusively for two weeks, to shed that antijjotic resistance and form a more clear-headed opinion.
Comment by stouset 7 days ago
No, it really isn’t. I have used git since shortly after it was first released and I’ve written a git implementation.
I switched to jj in one day. And the amount of git arcana I have to keep in working memory is now basically nil. My VCS now works in almost a 1:1 mapping with how my brain wants to interact with my repo rather than having to go through a translation layer.
If you understand what git commands are doing, what jj does is essentially trivial to add to your mental model.
I also get the benefit of being able to use workflows that I always want to use in git but which are an enormous pain in practice. And I get access to wildly powerful new workflows I didn’t even consider because they would be outlandish in git.
Comment by steveklabnik 7 days ago
What I will say is this: there is certainly an adjustment period, and I also totally hear you about how learning internals can be time consuming.
I think you can get a lot of the way there with at least the core concept with something like this, if you'll indulge me:
With git, you build up stuff on your filesystem. You then select which bits go into a diff in your index, and then when you're done, you stamp out a commit.
With jj, you instead start by creating a commit. In this case, it's empty. Then, every time you run a jj command, it does a snapshot, and this produces a new commit. However, because we want to have a stable identifier for a commit, we introduce a new one: the change id. This is basically an alias for the latest commit snapshot that was made. Your git history is just made up of each of these latest commits for each change.
... does that make any sense? Obviously there's more to all of the features than this, but that's sort of the core way that the histories map to each other.
Comment by Valodim 7 days ago
Comment by jhhh 7 days ago
Comment by hansvm 7 days ago
I don't think it's just mental friction. Suppose you've learned git well enough that everything you do in it is automatic and fast, and the things which aren't fast by default you've built aliases and tooling for over the years. Yes, starting from ground zero you might want something like jj, but at the current point in your life you're not starting from ground zero. Switching to jj means learning another tool to achieve similar outcomes on your workflows.
Comment by stouset 7 days ago
Comment by steveklabnik 7 days ago
Comment by tcoff91 7 days ago
Comment by jhhh 7 days ago
Comment by tcoff91 7 days ago
With `jjui` this strategy takes only a few keystrokes to do operations like adding/removing parents from merge commits.
It's so nice to have like 4 parallel PRs in flight and then rebase all of them and all the other experimental branches you have on top onto main in 1 command.
Also, I cannot even stress to you how much first-class-conflicts is a game changer. Like seriously you do NOT understand how much better it is to not have to resolve conflicts immediately when rebasing and being able to come back and resolve them whenever you want. It cannot be overstated how much better this is than git.
Also, anonymous branches are SOOOO much better than git stashes.
Comment by 1718627440 7 days ago
You can do anonymous branches in Git as well. I use both for different use cases.
Comment by tcoff91 7 days ago
Also git has no equivalent to the operation log. `jj undo` and `jj op restore` are so sweet.
Comment by 1718627440 6 days ago
> Also git has no equivalent to the operation log.
For easy cases it's just git reset @{1}, but sure the oplog is a cool thing. I think it will be just added to git eventually, it can't be that hard.
Comment by martinvonz 6 days ago
I agree that Git could gain an operation log. I haven't thought much about it but it feels like it could be done in a backwards-compatible way. It sounds like a ton of work, though, especially if it's going to be a transition from having the current ref storage be the source of truth to making the operation log the source of truth.
Comment by 1718627440 6 days ago
I also tend to have the builtin GUI log equivalent (gitk) open. This has the behaviour, that no commit vanishes on refresh, even when it isn't on a branch anymore. To stop showing a commit you need to do a hard reload. This automatically puts the commit currently selected into the clipboard selection, so all you need to do is press Insert in the terminal.
> It sounds like a ton of work, though, especially if it's going to be a transition from having the current ref storage be the source of truth to making the operation log the source of truth.
I don't think that needs to be implemented like this. The only thing you need to do is recording the list of commands and program a resolver that outputs the inverse command of any given command.
Comment by tcoff91 6 days ago
When I’m exploring a problem I end up with complex tree of many anonymous branches as I try different solutions to a problem and they all show up in my jj log and it’s so easy to refer to them by stable change ids. Often I’ll like part of a solution but not another part so I split a change into 2 commits and branch off the part I like and try something else for the other part. This way of working is not nearly as frictionless with git. A lot of times I do not even bother with editor undo unless it’s just a small amount of undoing because I have this workflow.
Git is to jj like asm is to C: you can do it all with git that you can do with jj but it’s all a lot easier in jj.
Comment by 1718627440 6 days ago
Comment by tcoff91 6 days ago
Within a day of switching I was fully up to speed with jj and I never see myself going back. I use colocated repos so I can still use git tools in my editor for blaming and viewing file history.
Sure even rebasing a complex tree in git can be done by creating an octopus merge of all leaf nodes and rebasing with preserve merges but like that’s such a pain.
Comment by feelamee 6 days ago
Comment by martinvonz 6 days ago
Comment by brooke2k 7 days ago
I don't mind other people using jj, but I simply don't feel a need to try it. There's nothing prideful about that, it's just pragmatism.
Comment by swaits 6 days ago
My advice is to try it. You should like it, if you're in the majority of folks to give it a sincere shot. If you don't like it, cool. But then my advice changes. jj is probably the future. Adapt or become one of those old-timers who froze their learning in time.
Comment by zavec 2 days ago
Comment by steveklabnik 6 days ago
Comment by RadiozRadioz 6 days ago
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.
Comment by LVB 6 days ago
jj is sort of a bag of git tricks for me that I use when needed. It's no different than some things being easier with the git CLI vs others being easier in Sublime. I'll be at a stage where my committing/branching/rearranging wants are something that jj nails perfectly, and I do those there. As far at the other tools are concerned, I just did a bunch of sophisticated git operations.
The "colocated with git" capability of jj is probably it's most amazing feature tbh, and is key to any sort of adoption.
Comment by IshKebab 7 days ago
1. It's very new; I haven't had time to learn it properly yet.
2. It's very new and tooling doesn't support it well, e.g. VSCode. There aren't many GUIs yet.
3. I tried it once co-locating with Git and you definitely can't use both at the same time, even if it can use a `.git` directory. It ended up in a huge mess.
I'm definitely in favour of better-than-Git alternatives but I don't think it's reasonable to expect everyone to switch to JJ right now. It isn't so much better that abandoning the de facto standard is obviously worth it yet. (In contrast to things like the iPhone, Rust, SSDs, etc.).
Also I really wish they would focus on some of the bigger pain points of Git. I can deal with rebasing and whatnot. Sometimes it's painful but it's usually not that bad.
What I can't deal with are submodules and LFS. Both are awful and fixing them properly requires fundamental changes to the VCS which aren't going to happen in Git. JJ has an opportunity to do that. Imagine if JJ could say "we have submodules but they aren't awful!" or "you can check in large files!". Those are the sort of huge advantages that would mean you can say "why not just use JJ".
Comment by steveklabnik 7 days ago
Both submodules and LFS are things that jj wants to address, but they take time.
Comment by swaits 6 days ago
Comment by tcoff91 7 days ago
Comment by smcameron 7 days ago
Comment by max_k 7 days ago
Comment by michaelbuckbee 7 days ago
Comment by Hendrikto 7 days ago
Also, I dislike all of the alternate git frontends I tried, because they are opinionated in a way they clash with my workflow.
Moreover, I don’t think the git CLI is that bad. Once you learn some basic concepts, it makes a lot of sense and is pretty consistent.
Most problems people report stem from a refusal to learn the underlying structure and models. That is on them. And when using a different frontend, they don’t disappear either. They are just abstracted, to allow you to avoid learning them. But they are still there, and you will probably still need to know them at some point.
Comment by quietbritishjim 7 days ago
It's very easy to fall into the trap of believing this: git's implementation fits together neatly enough that it feels like the best you could do. Like, yes it's complex, but surely that's just intrinsic complexity of the problem? (Also, I think we all sometimes feel like someone with a different view must just not know as much as us.)
But if you have used other version control systems (I'm thinking particularly Mercurial here) you realise that actually some of that complexity is just totally made up by git.
Comment by Hendrikto 7 days ago
I explicitly said that git IS NOT the best we can do. But it is universal and good enough. Not nearly as bad as some people make it out to be.
Comment by sunshowers 7 days ago
Comment by _zoltan_ 6 days ago
Comment by yegle 7 days ago
Comment by swaits 6 days ago
I recognize that some folks are stuck with it, and yeah, in your case jj probably just doesn't make sense. Stay tuned though.
Comment by steveklabnik 7 days ago
Comment by yegle 7 days ago
Comment by steveklabnik 6 days ago
Comment by YmiYugy 7 days ago
Comment by gcr 7 days ago
i went from being a "jj cli power user" to relying on jjui for all of my complex rebase needs so quickly that i now have to read the man page to recall basic commands
Comment by baq 7 days ago
Comment by wakawaka28 7 days ago
Git is simple enough and has features and capabilities that jj does not have. Contrary to popular belief, git is not hard to use. I refuse to use any "simpler" system that is slower or less feature-rich than git. I don't even want to learn another commit graph model, because git's model is very good. About 95% of what people like yourself call "git nonsense" consists of useful features that many people would be annoyed to not have.
I believe that a large number of git or general VCS users have no idea about commit hygiene. They have not had to cherry-pick or edit commits, and have no idea what to do about conflicts. To people like that, git's features and methods will appear especially foreign.
I looked over jj specifically many moons ago and concluded it would annoy me and not function at my job. I forgot what the reasons were. One reason was most likely because I need submodules and worktrees to work. I just looked at its FAQ, and saw a bunch of nonsensical new terms as well. Nothing is more compatible with git than git itself, and I am very satisfied with how well git works for me.
Comment by steveklabnik 7 days ago
worktrees are called "workspaces" in jj, but are the same.
A lot of people find jj easier to have good commit hygiene with, and find it simpler and more powerful, not less. But that said, if you're happy with git, you should continue to use it.
Comment by wakawaka28 6 days ago
Comment by steveklabnik 6 days ago
But for me, it's not so much features that git doesn't have, it's that the core is factored in a way that's more focused and orthogonal. The stuff that I used to like to do with git is even easier and more straightforward with jj. This is more of the result of a bunch of different design decisions and how they fit together rather than just some specific feature that's great.
Comment by wakawaka28 5 days ago
Comment by martinvonz 5 days ago
I've heard this a few times. But from what I've seen, it seems like often it's the Git enthusiasts who seem to be unfamiliar with jj. I haven't heard from anyone who used jj for a few months and knew it well and then switched to Git.
Comment by wakawaka28 5 days ago
I have used other VCS systems that made similar claims about being simpler, such as Mercurial. I think I got to be fairly expert at Mercurial before becoming expert at git, and even used it to interact with SVN repositories. (I've also used git for SVN. This is another thing git does better.) After actually learning git past the first few commands, I would never go back to Mercurial. I don't want to mix and match systems either. There are things I want git to do better, but I would not call these simple feature requests... They are more along the lines of advanced features that need to be extended or polished.
Comment by martinvonz 6 days ago
Comment by wakawaka28 6 days ago
If you have others in mind then go ahead lol. I was just trying to make it easy.
Comment by martinvonz 6 days ago
Yes, it's only meant for local branches. When I used Git, I had a script for rebasing dependent branches. I remember that a coworker had written a similar script.
I think jj is generally more useful for people like me who often have lots of independent and dependent work in progress. If you mostly just have a one review at a time, there's much less benefit. Perhaps I would say that `jj undo` might be the most useful feature for users with simpler development (yes, I know about the reflog, but see the video I linked to in the other message).
Comment by 1718627440 6 days ago
If you don't have anything to update then that would be somewhat pointless to me. You can also just rebase them, when you start working on the branch again or want to merge them.
--
For me branches also represent features, that should have clear boundaries, so when I work on one feature and that means, I need to rebase another one on top instead of being able to just merge them later, this indicates a spaghetti codebase where the interfaces between features are not properly defined and things depend on internals of other things.
Comment by wakawaka28 6 days ago
I try to do this too but I often end up in situations where I have multiple incomplete (in testing, not merged) features with outstanding patches. Instead of one branch per topic, I end up with one branch for a bunch of related stuff. I then rebase and pause at the feature boundaries to do more testing. Sometimes, if I find myself doing this a lot, I will use the `exec` feature of `git rebase` to automate my testing.
I think rewriting all related branches can cause problems. It would be really weird to do interactively for one thing. The other problem is that you may have unrelated topic branches broken by such a change. If you have a broken patch X that reveals problem Y on branch Z1, but you are working on fixing that on Z2, you may lose your ability to reproduce the Z1 issue if X is fixed on every branch. What if you get conflicts on all those branches? What does this do to the reflog? Yikes! It seems more dangerous than git itself.
These complaints are very niche of course, but the problem of rewriting many branches at once is also very niche. It can cause more problems than it solves.
Comment by steveklabnik 6 days ago
Comment by wakawaka28 5 days ago
Comment by steveklabnik 4 days ago
If you don’t want to modify a branch, then you shouldn’t rebase it, same as git.
Comment by wakawaka28 4 days ago
What does it even mean to not resolve conflicts? Your branch code and/or history is broken until you come around and fix it? If so, forcing you to fix it immediately is better. Aside from the practical implications of deferring conflict resolution, I just can't think of a reason I would do it in the first place. If a branch can't be rebased with zero work, and I don't have time to do that work, I just don't do the rebase.
Comment by steveklabnik 4 days ago
It looks like this: when a git rebase would pause the rebase to make your resolve a conflict, jj keeps going. When the rebase finishes, if there are any conflicts, they’re displayed in the log with ?? after their ID. jj won’t let you push a branch with conflicts until the conflict is resolved. You can fix the conflict by either editing that commit directly, or my preferred way, which is to make a new change on top and then squash the resolution back in after you read it over.
So there’s a few thins about why this is useful: because children are also rebased, you can see immediately how much work, if any, you have to do. I have an alias to rebase all open branches on top of trunk, and will often pull trunk, run it, and see if I have any work to do to update them. “Oh, only one of my three branches needs work, I’ll work on the two that are fine first” can happen, and that third can just sit there. Or, as I said before, I could move it back on top of its old base, and the conflict disappears. Or, say I suspected none of them conflict, but all three do, and I don’t want to do it at all right this moment, I’ll just jj undo and they’re all unconflicted and back in place.
Comment by martinvonz 6 days ago
``` A---B---C main \ D---E---F feature1 \ \---G---H feature2 \ \---I---J feature3 ```
(sorry about the formatting here. I guess you'll have to copy & paste it to read it)
What I'm saying is that if I want to fix something in D, I do `jj new D` to create a new commit on top of D. Then I make the fix, run tests, etc., and then I run `jj squash` to amend the changes into D. The descendant commits (E through J) then get automatically rebased and the feature bookmarks/branches get updated.
I didn't follow what you about it other changes needed for updating the ancestor. Can you explain in the context of this example?
Comment by 1718627440 6 days ago
--
The other thing I am saying is that I don't really let features depend on each other, I let them specify the API between them first and then develop them independently. Otherwise it is easy to violate boundaries. So the ideal is that any of G,H and I,J works with D,E,F and vice versa. Of course that is tangential and it doesn't always work that way.
Comment by steveklabnik 6 days ago
Comment by wakawaka28 6 days ago
Comment by martinvonz 6 days ago
I agree. That's why jj uses practically the same model. That's how Git can quite easily be used as a backend.
> I just looked at its FAQ, and saw a bunch of nonsensical new terms as well.
Like what? Perhaps we can improve it.
Comment by wakawaka28 6 days ago
I'm sure it is stuff that makes sense to a jj user. Since I have not read the manual, it is nonsense to me. I'm just drawing attention to the fact it's a different set of non-obvious terminology and features as compared to git. I'm sure anyone who read the manual for either tool could figure it out. The trouble with git is that people don't read the manual, and hardly try to do anything with it, then loudly complain about it being tricky. Anything as complicated as version control is going to be tricky if you don't read the manual. I don't think making another tool entirely is the right solution. Perhaps a different set of git porcelain tools could help, or some git aliases. Maybe better documentation too. But some people just can't be pleased.
Comment by martinvonz 6 days ago
> I don't think making another tool entirely is the right solution.
I considered making the changes to Git but the changes I wanted to make would make the UX so different that it would basically mean introducing a whole parallel command set to Git. I figured it would take ages to get Git to that state, if I could sell the ideas to the Git community at all. By the way, the video above talks about an proposed `git history` series of commands inspired by Jujutsu (also see https://lore.kernel.org/git/20250819-b4-pks-history-builtin-...).
Comment by wakawaka28 6 days ago
I think I saw Scott Chacon talk about his git config file and advanced git features. Whoever it was, it mentioned GitButler. That was a good talk. I would certainly expect someone like that to have a lot of interest and expertise in git. But it seem to me that there is also a potential commercial angle to making a new/alternative VCS.
I looked at the mailing list entry you linked to about `git history` commands and thought to myself, it sounds all wrong and redundant. `git history` sounds like too broad of a name for one thing. I'd want to have it be `git <verb>` instead. All the operations listed can be done with rebase:
- `git history drop`: Instead, rebase interactively and drop one or more commits.
- `git history reorder`: Interactively rebasing makes this work already.
- `git history split`: Insert a pause in the interactive rebase. Do a soft reset or something to the previous commit, and use `git restore` to unstage the changes (there might be a more efficient way to do this in one step, but idk). Then, do `git add -p` to add what you want, commit, as many times as you want to split the patch. Then continue the rebase.
- `git history reword`: There is a reword option in interactive rebase mode, and also a fixup-like option to do it as well if you want to postpone the rebase.
- `git history squash`: Rebase can do this now in multiple ways.
Rebasing is not that hard. It is the Swiss Army knife of VCS tools. Once you realize that you can just break in the middle of a rebase and do nearly anything (except start another rebase), the world is your oyster. We don't need to spam people with many more single-purpose tools. We need people to understand why the way things are is actually pretty damn good already, if only they read the manual.
Comment by eddd-ddde 5 days ago
No, but it takes like 5 manual operations to do something as simple as amending my changes to a commit that's 3 parents before my current HEAD.
There's simply no excuses for git being this unusable for the very simplest operations I do day to day.
Comment by wakawaka28 5 days ago
- `git commit --fixup HEAD~3`
- `git rebase --autosquash HEAD~4`
The "less than 2 steps" part comes from fixing up more than one commit having no conflicts before the rebase. It is very common to want to stop or run test scripts at various points in the newly modified history, and rebase can trivially help you automate that (but only if you want).
Rebasing literally just does a sequence of steps in the order you specify. You can make those steps complicated, or not, without learning yet another tool. The complexity that is in the rebase tool is practically all necessary.
After using git for many years, I realize now that a lot of thought went into its design. The way it works, especially with the rebase tool, is superior to having a dozen single-purpose tools.
I don't think this particular thing is against the UNIX philosophy either. All of these operations are intimately related just like the operations that a FTP client might do. I can just imagine someone like you looking at FTP or rsync, and saying "This can be 20 different commands! Why don't they make my particular use case into its own command!" There is a place for that kind of logic, but all of the things jj supporters have proposed to me are way too niche to have their own separate commands. My commit edits are complicated, and `git rebase` makes them super simple.
Comment by martinvonz 5 days ago
Comment by wakawaka28 5 days ago
Comment by martinvonz 6 days ago
Comment by 1718627440 6 days ago
What we should do instead is provide a bunch of primitives, that as high-level are as possible so to not end up with duplicate commands, which is what git does currently. `git history` as a name is somewhat pointless, since the whole point of git is to produce and modify the history. In that sense `git history` already exists, it is called `git`.
I think the issue newbies have is not that git commands are hard per se, but that they don't think in terms of modifying the graph yet, or that they don't know which primitives are available.
Comment by steveklabnik 6 days ago
Comment by wakawaka28 6 days ago
Comment by swaits 6 days ago
Comment by wakawaka28 5 days ago
Comment by steveklabnik 4 days ago
For whatever it’s worth, the general stance of the project and most of the community is “git is good, we like jj more, but you should use whatever you prefer.” Lots of us loved git before jj came along, and there’s a lot of cross pollination between the two projects. I hope your parent takes the feedback and chills out.
Comment by dlisboa 7 days ago
Comment by stouset 7 days ago
The jj CLI is very easy to grok, even for a seasoned git user. Maybe even especially so for a seasoned git user.
Comment by swaits 6 days ago
Comment by unshavedyak 7 days ago
1. I had quite a bit of trouble figuring out a workflow for branches. Since my companies unit of work is the branch, with specifically named branches, my `jj ls` was confusing as hell.
`jj st` might have helped a bit, but there were scenarios where creating an commit would abandon the branch... if i'm reading my post history correctly. My coworker who was more familiar explained my jj problems away with "definitely pre-release software", so at the time neither of us were aware of a workflow which considered branches more core.
Fwiw, I don't even remember when the jj workflow had branches come into play.. but i was not happy with the UX around branches.
2. iirc i didn't like how it auto stashed/committed things. I found random `dbg!` statements could slip in more easily and i had to be on guard about what is committed, since everything just auto pushed. My normal workflow has me purposefully stashing chunks when i'm satisfied with them, and i use that as the visual metric. That felt less solid with jj.
Please take this with a huge grain of salt, this is 10 month old memory i scavenged from slack history. Plus as my coworker was saying, jj was changing a lot.. so maybe my issues are less relevant now? Or just flat out wrong, but nonetheless i bounced off of jj despite wanting to stick with it.
Comment by steveklabnik 7 days ago
Auto-commit is still a thing, but you can regain the stuff you like with a workflow change, this is called the "squash workflow" and is very popular: https://steveklabnik.github.io/jujutsu-tutorial/real-world-w...
Comment by oscillonoscope 7 days ago
Comment by conradludgate 7 days ago
Comment by steveklabnik 7 days ago
Comment by wild_egg 6 days ago
IIRC forcing some specific branch name to point to my changes with `jj` was non-obvious and what made me give up and go back to git when I tried it last year.
Comment by oscillonoscope 6 days ago
Comment by feelamee 6 days ago
Comment by steveklabnik 6 days ago
> You rebase feature-1 onto main:
feature2 moves as well. Basically, the behavior here is just the default.
Comment by swaits 6 days ago
Comment by bilalq 7 days ago
It's not something I can just shift away from.
Comment by swaits 6 days ago
Comment by bilalq 4 days ago
To go back to your question around why people still use these workarounds on top of git, it's because the CLI is just one piece of it. With Graphite, I also get a stack-aware merge queue and review dashboard.
Comment by qff 6 days ago
This makes it unusable for me.
Comment by steveklabnik 6 days ago
Keeping these things in a private commit is one option.
Configuring auto-tracking to be off is another option.
Comment by lowboy 6 days ago
Comment by dimitrieh 7 days ago
https://www.stavros.io/posts/switch-to-jujutsu-already-a-tut...
Also found https://github.com/gitbutlerapp/gitbutler
Comment by crabmusket 7 days ago
I wrote up a little way to use JJ's revsets to make it easy to push an entire stack of branches in one command:
https://crabmusket.net/2025/jj-bough-a-useful-alias-for-stac...
Comment by ndr 7 days ago
Does jj help with that at all?
I've experimented a bit with git-town.com (OSS) and now everyone at $DAYJOB uses graphite.com (SaaS) which does that part very well.
Comment by baq 7 days ago
Comment by gcr 7 days ago
Comment by arccy 7 days ago
this is less of a problem if you're more into the 1 change == 1 commit workflow.
Comment by pimeys 7 days ago
tug = ["bookmark", "move", "--from", "heads(::@- & bookmarks())", "--to", "@-"]
It moves the nearest bookmark to the commit before the current one (which should be your working commit).Comment by mhitza 7 days ago
Comment by steveklabnik 7 days ago
That doesn't mean it couldn't be a core command someday, but given that the alias works well for people, there's not a ton of reason to make a whole new command. You configure the alias and you're off to the races.
Comment by stavros 7 days ago
Comment by lima 7 days ago
Comment by WorldMaker 7 days ago
Comment by 1718627440 7 days ago
Comment by nopurpose 7 days ago
Comment by happytoexplain 7 days ago
Comment by fwip 7 days ago
Comment by onionisafruit 7 days ago
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.
Comment by nopurpose 7 days ago
Comment by onionisafruit 7 days ago
Comment by 1718627440 7 days ago
Comment by ananthakumaran 7 days ago
Comment by imron 7 days ago
Comment by duskdozer 7 days ago
Comment by nopurpose 7 days ago
Comment by duskdozer 6 days ago
Comment by WorldMaker 7 days ago
Comment by jbjbjbjb 7 days ago
Comment by lelandfe 7 days ago
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/
Comment by YmiYugy 7 days ago
Comment by enbugger 7 days ago
Comment by jbjbjbjb 7 days ago
https://andrewlock.net/working-with-stacked-branches-in-git-...
Another commenter posted this link which was a bit more succinct
https://blog.hot-coffee.dev/en/blog/git_update_refs/
There isn’t much to it though, you just go to the branch and run git rebase with the update refs flag.
Comment by sirsuki 7 days ago
Comment by ptx 7 days ago
$ git checkout feature-1
$ git rebase --update-refs main
Successfully rebased and updated refs/heads/feature-1.
Updated the following refs with --update-refs:
refs/heads/feature-2-base
But all it did was update feature-2-base. It still left feature-2 pointing to the old commits. So I guess it automates "git branch -f feature-2-base feature-1" (step 3), but it doesn't seem to automate "git rebase --onto feature-1 feature-2-base feature-2" (step 2).Presumably I'm doing something wrong?
Comment by mhw 7 days ago
$ git rebase --update-refs main feature-2Comment by ptx 7 days ago
Comment by happytoexplain 7 days ago
Second, you run it on the outermost branch: feature 2. It updates all refs in the chain.
Comment by bvdw 7 days ago
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/
Comment by JimDabell 7 days ago
Comment by bvdw 7 days ago
Comment by schacon 7 days ago
Comment by the_gipsy 7 days ago
Comment by Xophmeister 7 days ago
Comment by WorldMaker 7 days ago
(Plus, of course, if you have multiple branches stacked, `--update-refs` makes it easier to update all of them if you start from the outermost branch.)
Comment by Xophmeister 6 days ago
Comment by WorldMaker 5 days ago
I picked up `--update-refs` to not lose my mind working in squash merge repos. I much prefer merge commits. I often make good, well documented commits and I like having access to the original commits if I need them, so when in a squash merge repo I become a branch hoarder renaming merged branches with a `zoo/` prefix (to drop them low in sort order, among other reasons it is name `zoo/`).
I will often keep experiment branches around and `--update-refs` helps me manage that because if I see commits that might update-ref a `zoo/` branch I know to drop them from the experiment branch.
All of that discovery of already merged commits would be automatic/cheap in rebases with merge commits. I was very frustrated before discovering `--update-refs`. I'm still often frustrated with squash merging, but keeping a large `zoo/` and having `--update-refs` is extra work that almost replicates the experience of just using merge commits in the first place. I don't know why so many think squash merge workflows are "simpler".
Comment by perspectivezoom 7 days ago
Comment by happytoexplain 7 days ago
Comment by adrianN 7 days ago
Comment by 1718627440 7 days ago
Comment by dfawcus 6 days ago
As such, using it for the situation described in the piece then becomes a trivial matter, especially if one also habitually runs a graph viewer - I generally have one or more instances of gitk running all the time.
Comment by ptx 7 days ago
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.)
Comment by WorldMaker 7 days ago
One way to accomplish that is to reorder the commmits in `rebase -i`: "pick" the last commit first and squash/fixup the rest after. That can produce some very weird merge conflicts, but it works well more often than you might think, too.
At the very least, you can do your hand editing of the commits during the rebase instead of after by switching the earliest commit from "pick" to "edit" to have the full power to amend the commit before it moves on (with `git rebase --continue` when you are satisfied). (Versus "reword" if you just want to change the commit message only.)
Also, instead of naming commits things like "broke it" and "broke it more" an option is to use `git commit --fixup={previous commit hash}`. That auto-generates a "fixup!" name based on the previous commit and auto-marks it as a fixup if you use the `--autosquash` flag to rebase.
Comment by ptx 7 days ago
But in the case I'm describing, the earlier commits are essentially just temporary snapshots on the way to making a proper commit. Just a more explicit undo history, basically. I usually don't even bother naming them anything as meaningful as "broke it", actually. Maybe most people wouldn't even bother making these commits – or maybe they would if Git supported "fold".
Comment by WorldMaker 7 days ago
Though there are also times I don't mind working in a very dirty worktree and `git add -p` (interactive add that also makes it easy to stage only parts of files) pieces only once they feel complete and start to tell a story. (And in those cases I may use a lot of `git stash -u` and `git stash pop` snapshots, too, especially if I need to switch branches in that worktree.)
Comment by ptx 7 days ago
$ git checkout feature-1
$ git reset main
$ git commit -a -C feature-1@{1}
This squashes all the changes and keeps the date and and message of the final commit on the branch.Weirdly, although the "-c" and "-C" flags for the fixup operations sound like they should correspond to the same flags for "git commit", which grabs the date along with the message from the specified commit, the fixup flags only affect the message and not the date.
It would be nice if it worked the same for rebase as for commit. Then "fixup -C" would essentially correspond to Mercurial's "fold".
Comment by WorldMaker 7 days ago
Also yeah, I would expect the fixup -c and -C flags to be more aligned with commit in a rebase.
Comment by 1718627440 7 days ago
Comment by steveklabnik 7 days ago
Comment by 1718627440 7 days ago
Comment by foobarian 7 days ago
Comment by andrebianchessi 7 days ago
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.
Comment by azophy_2 7 days ago
(disclaimer: partly supported by AI, but most of the writing structure were made by hand)
Comment by mytailorisrich 6 days ago
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.
Comment by nrhrjrjrjtntbt 7 days ago
Comment by taejavu 7 days ago
Comment by nrhrjrjrjtntbt 7 days ago
Comment by flr03 7 days ago
Comment by icejunior 5 days ago
Comment by buster 6 days ago
People should really try to work on trunk.
Comment by mytailorisrich 7 days ago
Having to rebase again and again is a symptom that a dev branch is living for too long.
Comment by happytoexplain 7 days ago
And, as others have pointed out, the modern solution is `--update-refs`, so there's no need for complicated workflows any more anyway.
If you mean rebasing as each PR in the stack is merged, most Git platforms have the ability to do that automatically.
Comment by mytailorisrich 7 days ago
A rebase before a merge can always happen, of course. But there are not stacked commits then it is just a standard rebase of your small chunk and that's it. And this will also have a shorter life than a stack so rebases will be rarer and simpler.
Comment by happytoexplain 7 days ago
Comment by mytailorisrich 7 days ago
This is quite orthogonal to developing parts serially or not, and it is perfectly fine to change or refactor ealier parts when you work on later parts. Development is an iterative process.
It seems to me that "stacked diffs" are an example of looking for a technical solution to process issue.
Comment by glenjamin 7 days ago
Stacked diffs seems like a solution to managing high WIP - but the best solution to high WIP is always to lower WIP
Absolutely everything gets easier when you lower your work in progress.
Comment by happytoexplain 7 days ago
Comment by SideburnsOfDoom 7 days ago
Oh sure, many bad ideas and poor practises such as that one are quite "normal". It's not a recommendation.
Comment by glenjamin 7 days ago
It is entirely viable to never have more than 1 or 2 open pull requests on any particular code repository, and to use continuous delivery practices to keep deploying small changes to production 1 at a time.
That's exactly how I've worked for the past decade or so.
Comment by thunderfork 6 days ago
Comment by motoboi 7 days ago
Comment by tdiff 5 days ago
Comment by dspillett 7 days ago
Perhaps not an option if you need to release the work in branch 1 before the work in branch 2 is ready/reviewed/etc.
Comment by happytoexplain 7 days ago
Comment by politelemon 7 days ago
Comment by imron 7 days ago
Just use git rebase --update-refs ...
Comment by url00 7 days ago
Comment by imron 7 days ago
Comment by sublinear 7 days ago
I don't usually want to rewrite history. I just want the target branch with all my commits on top (I usually squash the feature branch into one commit anyway). I have yet to run into a situation where this isn't good enough.
If the branch diverges so much and has so many commits that this simpler approach doesn't work, that might not be a git problem, but a project management one. It's still always nice to know git has tools to get me out of a jam.
Comment by 1718627440 6 days ago
> and end up with a "bad" merge.
They end up with exactly the same merge when using cherry-pick directly?
> I don't usually want to rewrite history. I just want the target branch with all my commits on top
That's ... what rewriting history is?
Comment by davidfstr 7 days ago
Comment by hahahacorn 7 days ago
This 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.
Comment by sockbot 7 days ago
Comment by ragebol 7 days ago
Comment by IshKebab 7 days ago
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.
Comment by taejavu 7 days ago
Comment by IshKebab 7 days ago
Comment by Fang_ 7 days ago
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.
Comment by happytoexplain 7 days ago
Comment by saint_yossarian 7 days ago
This is functionally the same as rebasing, except that the new changes show up at the tip of the commit chain rather than the base. And because it doesn't rewrite history you don't need to force-push.