For anyone who's debating whether or not jj is worth learning, I just want to highlight something. Whenever it comes up on Hacker News, there are generally two camps of people: those who haven't given it a shot yet and those who evangelize it.
You will be hard-pressed to find someone who stuck with it for a week and decided to go back to git. You will not find a lot of people who say they switched but just stayed out of inertia. Of course both of these do happen—nothing is perfect—but they are by far the exception. From my own personal anecadata, I have seen a 100% conversion rate from everyone who gave it a serious try.
I encourage you to let today be the day that you decide to try it out. It is far less effort to make the switch than you probably think it is: I was productive the same day I switched and within a week I had no remaining situations where I needed to fall back to git commands. You will quickly be more productive and you will find yourself amazed at how you ever got by without it.
tgv · 1m ago
> I have seen a 100% conversion rate from everyone who gave it a serious try.
Cognitive dissonance is a thing.
palata · 2h ago
> For anyone who's debating whether or not jj is worth learning
I don't have any productivity issues with git, like... at all. It's not like I spend an hour running git commands every day.
I can totally imagine that some people spend their day manipulating repos with git, and jj is better for them. But that's not my case, and git is already everywhere.
To me it sounds like telling me: "You HAVE TO move to bim, the better vim. It's very similar to vim, but different enough that you have to learn new stuff. But you will be infinitely more productive: when you start bim, you're already in edit mode, so you don't have to type i! And the auto-complete in Julia is objectively a lot better in bim!".
Sure, but typing "i" a few times more is really not a concern for me, and I don't use Julia. But if it's better for you, please enjoy bim!
stouset · 2h ago
For a lot of people, making small and tightly-focused branches that are easy to review and merge is very important.
This is where jj excels. Especially if you find yourself often doing large chunks of work between convenient checkpoints, but you still want to create commits as if this work was all done in tiny and discrete chunks. It's also very helpful if you're the kind of developer who makes lots of unrelated changes in a single coding session, and wants all those changes to be in parallel branches that can be reviewed and merged independently. I greatly prefer working with (and being) the kind of developer that puts out a large number of very tiny and easy to review PRs, eve. jj makes doing that a breeze.
There are lots of people for whom these things aren't important. I will be slightly judgmental and say I don't really enjoy working with them. They tend to write very large PRs that are difficult and time-consuming to review. And it's a frequent source of frustration for everyone when 95% of the work is uncontroversial but a merge is being held up because of legitimate concerns with an unrelated 5%. This is even worse when there's later work that builds upon it that can't happen until a merge (or that needs to be constantly rebased as the PR is improved).
This is not to say if you do this you’re a bad developer. There are plenty of great developers who don’t care about these things and still do great work. This is also not to say you can’t follow my preferred approach with git. I did it with git for a decade and a half.
sethammons · 1h ago
Nice commits can really tell a development story that makes reviews easier. That said, I want all teams to squash merge their feature into master after tests pass. One commit at the end, and one commit to remove in case of an issue affecting customers related to the release.
A very, very large problem at five out of six companies I have worked at is casual code improvement and refactoring. Devs would say, "we will address that minor and unrelated thing in a separate PR" - one that never comes. At one company, a single PR could address unrelated fixes and it was encouraged to "take out the trash" on the code. Unrelated metrics added, logging improvement, or code simplified, or test robustness improved, etc. That company had vastly better code. Easier to read. Easier to maintain. Easier to observe. And easier to test.
jon-wood · 12m ago
I'm honestly baffled by this. You're a proponent of dealing with chores as you encounter them during development rather than putting them off til later (great! I love this!), but also when that PR lands you want it all squashed down into a single commit, which presumably will have a message like "Implemented Important Feature, also did a bunch of unrelated work".
That sort of workflow is ideal for making sure you've got a set of isolated commits each looking at a single subject so that when someone is reading through the history later they can quickly see where something was introduced or why, and jj is perfect for doing that because it makes crafting those commits so much easier.
normie3000 · 1h ago
> That company had vastly better code.
It's very frustrating that what IMO is the inferior approach was producing better results in those teams!
RUnconcerned · 3m ago
It's because what you see as the inferior approach involves less effort and friction for the developers.
When you are told to separate general code improvements to another PR, or worse, to not do them, and create a Jira task for them so they can be adequately prioritized, it just saps your will to do so. You just won't do any improvements that fall outside the scope of the feature, because even just thinking about the hoops you have to jump through to get work done is mentally draining.
beng-nl · 1h ago
Wow, that does sound like a big improvement. My git commits are, when not forced to be otherwise, very sloppy, even though I’d prefer them to be neatly self contained. But as you imply, there is friction to making these in git, while making lots of unrelated changes in a single coding session.
As an example of another ‘unnecessary’ switch, Pip with venvs was also completely solving all my Python dependency problems. I just needed to copy paste a few lines from my README to create and populate the venv, and remember to run pip and Python from that venv. And run pip freeze after package installs or upgrades. No problem. But switching to uv was a huge life improvement. No more copy pasting (‘uv sync’ does everything and even that isn’t needed) nor remembering extra steps and everything is fast so I’m never waiting and forgetting what I was going to do.
I could’ve been GP talking about pip but still I would be missing out.
I still see people clinging to cvs because it works for them (netbsd why?), which I respect but don’t understand/believe.
Fish don’t know what water is.
So.. I’m gonna give jj a go and trust life will be better again.
palata · 1h ago
I tend to `git add -p` and create different commits that I put on different branches if they are unrelated.
Doesn't feel painful in git, but I'd like to see an example doing that with jj. Maybe I'll try.
normie3000 · 1h ago
> ...making small and tightly-focused branches that are easy to review and merge is very important.
> This is where jj excels. Especially if you find yourself often doing large chunks of work between convenient checkpoints, but you still want to create commits as if this work was all done in tiny and discrete chunks.
This is exactly how I like to use git. Sounds like I should be recommending jj to colleagues who struggle with this approach.
IshKebab · 16m ago
It's totally possible to have no issues with Git, e.g. if you are only using it for small or slow moving repos.
There definitely are lots of big issues with Git though. I dunno how many jj solves but it doesn't seem unreasonable to suggest people move to a better system.
xelxebar · 2h ago
It's certainly a solid improvement in the space of VCS UI, but beware that jj has some current limitations which might prohibit switching, especially for the git power users.
Lack of gitattributes support precludes git-crypt and git-lfs usage or anything that needs filters; line ending settings will get ignored, making Windows interop a little less smooth; etc.
Also note that auxillary tooling, such as git-annex and git-bug, becomes second class, i.e. no oplog integration and they might mess up your log with internal-use commits and heads.
steveklabnik · 2h ago
> line ending settings will get ignored, making Windows interop a little less smooth; etc.
Arguably a good thing - git's autocrlf setting causes way more issues than it solves. I highly recommend setting it to "input" (basically bans CRLF).
Ayesh · 2h ago
I use gitattributes quite a lot (lfs, various diff engines, and export-ignore). Thanks for the heads-up, jj looked very interesting but I'm not going to give up gitattrivutes.
stouset · 2h ago
These are all really excellent points, and well worth calling out.
resonious · 4h ago
I used jj for several months, then eventually went back to git.
The biggest killer was performance. jj operations took several seconds for me, whereas git is instantaneous no matter how big the project. Maybe this is fixed now.
But also honestly I felt like there was a bit more mental burden to using jj. When I switched back to git, it was like a weight off my shoulders. Maybe that's just due to the decade of constant use though.
npinsker · 4h ago
I used to have the same problem (on Windows only, not Mac) but it seems to have been fixed as of a month or two ago.
weinzierl · 3h ago
"You will be hard-pressed to find someone who stuck with it for a week and decided to go back to git. "
Reporting in. Doesn't mean I will not end up with jj eventually, but so far I always went back to git after a while.
For me it is the staging area and the workflow it allows. Most people hate it and love jj because it does away with it. That is just not me. I don't see the staging area as a hack that was necessary to overcome some superfluous technical limitations but as a workflow tool.
Could I change my ways? Sure. jj just did not provide enough benefit for me so far to do it, but we will see. I am still open to give jj another try some day.
stouset · 3h ago
The staging area is a hack in that it is its own unique concept that doesn't work with any of the rest of git's tooling without needing special, inconsistent flags for commands to target it (git stash, git reset, etc.).
I use a staging area with jj! I would surmise most jj users do too. It's just a real, honest-to-god commit in the repo instead of a special snowflake.
# do some work
…
# prepare a new, empty commit if you don't already have one
jj new --no-edit --insert-before @
# move changes into it; repeat ad nauseam
jj squash --interactive
Since it's just a commit, all your tools work with it out of the box. And you don't need to stash changes when you jump around between branches.
turboponyy · 42m ago
Can you edit commits by adding on only parts of your "staging" area?
Because that's my workflow. I produce a few focused and semantically coherent commits that I'd like to apply to the codebase, where each keeps the codebase in a working state. I might be working on more than one thing at once, but I know which commit each set of changes should "live in."
An append-only log of commits is decidedly not something I want. Is jj amenable to this?
nchmy · 1h ago
Yeah, I regularly have MANY "staging commits" at the same time in jj, in which I just throw ideas in that I don't want to pollute or yet know how to integrate with the other ideas. I use squash, split, interactive split, rebase etc to move lines and changes around etc...
When I eventually figure it all out, I can tidy it all up into completely coherent, discrete, sequential commits that make it look like I knew exactly what I was doing from the start, then push to remote.
Conversely git staging around stashing was always an absolute, terrifying mystery to me. I could never remember what was stored where, and would consequently end up losing work, repeating work, and, most of all, just making massive commits that make no sense.
I was sold on jj from essentially the moment I found it, but I remain routinely amazed at how much more powerful (yet simple!) it is than I realized.
1718627440 · 21m ago
You can use commits instead of the stash the same way in git. Isn't this just a way of using, why do you need jj for that?
You now you can give stashs a commit message?
weinzierl · 44m ago
Hmm, never saw it that way. Maybe I just need a different perspective on commits. Thanks for your comment, that was insightful.
weinzierl · 1h ago
Thanks for the clear and concise summary. It is helpful for some workflows.
For me the staging area being a snowflake is the feature. It is a special temporary singleton commit if you will, but I can rely on its temporary and singleton nature. As that it should have a different interface from regular commits.
drcongo · 2h ago
I found it hard to stick to due to pre-existing workflows too. I find I'm often switching between branches in git, and our CI depends on pushing to specific branches - I constantly found myself a bit lost with jj about which underlying branch I was on and where that would get pushed to. I did Steve's tutorial and came out of that really liking the concepts, but still unable to map it all in my head when it came to pushing my work to a remote.
esperent · 3h ago
> there are generally two camps of people: those who haven't given it a shot yet and those who evangelize it.
This sounds exactly like something a person evangelizing it would say.
0x457 · 7h ago
Reason I like git is because I use like 2% of its features. I don't fall for propaganda that I need to use bisect and co. 99% of git commands I call are aliased to 3 characters, so it's dense terminology doesn't bother me.
ykonstant · 3h ago
Bisect is a wonderful feature. Git is full of tedium and frustration, bisect is not part of that.
falconertc · 3h ago
Bisect is a very cool feature that I used once over 5 years ago. The collection of git tools that I use often is very small. I've found that the more experienced I got with git, the less I found myself in scenarios where I needed git's more complex tools.
theLiminator · 1h ago
Hmm, bisect is definitely not one of those.
1718627440 · 19m ago
Yes not using bisect, seams like you do all the work to record information, but then never use that information to save work?
Valodim · 5h ago
There were (are?) people who say the same thing about svn
globular-toast · 3h ago
I relish the day I get to use bisect. It's like, finally I get to use all this version data I've been collecting.
I don't understand why anyone would say you have to use it. It does a very specific thing, namely finding the source of a regression between two commits. If you need it you'll know.
tcoff91 · 3h ago
It’s really nice to use the —-first-parent flag with bisect.
globular-toast · 2h ago
Yeah this is good if you, for some reason, rebase then merge without fast forwarding. I never understood doing this. If I'm going to be ignoring those commits when bisecting then they are useless commits just using up disk space IMO.
Does it work well with a classic merge workflow? I haven't worked that way (without rebasing) for a long time.
1718627440 · 16m ago
For me a merge is about grouping commits, when you have just a list of commits, the list can be very long.
That's also useful for bisecting, as you can first find the feature that is buggy and then find the commit that introduced it.
anticodon · 3h ago
I used bisect once in my life but it was extremely helpful. Without it I'd spend weeks trying to find regression. With bisect I found and fixed it in under 1 hour.
It's a command that is needed rarely but there's no replacement for it in some situations.
bjackman · 7h ago
Counter point: I adopted it internally at Google (there's a backend for Piper, Google's monorepo Perforce thingy). I don't do my day-to-day work in the monorepo but I still jump in there once or twice a week. I adopted JJ because the existing frontend (Mercurial-based) is slow while JJ is fast.
It's nice, I really like it! I'll probably switch to it as my main VCS eventually. But it doesn't feel that important to me. Even though my main work involves quite a lot of annoying rebases which is where JJ really seems to shine.
I dunno I guess it's just that a) I've really mastered git and have a deeply-rooted workflow in it and b) despite my project involving annoying rebases, version control still isn't very high on the list of problems I have.
So yeah I'm basically bullish on JJ as a technology but I think movement from Git is inevitably gonna be slow and steady.
sethammons · 1h ago
Curious, I was under the impression that Google was all a monorepo, but your phrasing suggests that there are others. As my company is pushing for a monorepo, I'd love to know what causes someone at big G to not be in the monorepo. Thanks for any insights you can help me with!
rfoo · 49m ago
Mainly open source stuff. Chromium, Android, etc.
stouset · 7h ago
I'm not entirely sure that "after using it I really like it and I'll switch eventually" is that much of a counterpoint :)
What really kept you from staying with it? It does seem like if your workflow involves a lot of nasty rebases you'd reap dividends from something like jj. I was also someone who'd mastered git (hell, I've written a git implementation) so I get having its patterns deeply ingrained.
bjackman · 6h ago
Yeah I guess it confirms your point in a way too.
There's nothing keeping me from switching except the activation energy cost. Almost every aspect of working in my area (Linux kernel) is painful so I'm constantly investing in tooling and workflow stuff. So usually I just don't feel like investing EVEN MORE in the area of tooling that's probably least painful of all.
So yeah this is still basically a recommendation for people to try JJ!
esafak · 7h ago
Do you use an IDE with git support but not jujitsu? I do (intellij), and I'm not sure how useful it would be.
Disposal8433 · 3h ago
I've always hated the git CLI, and that's why I used Jetbrains tools or Sublime Merge.
Now that I switched to jj (colocated with a get repo, very useful), I went back to using the CLI again for jj and I don't miss the graphical tools.
exclipy · 1m ago
But how do you split commits? Command line tools for splitting commits suck because they force you to look at one hunk at a time without getting the context from the rest of the change.
fedeb95 · 23m ago
this still doesn't imply it is worth checking out.
globular-toast · 3h ago
I tried it and didn't switch. The funny thing is I immediately recognised that it was forcing me to use git in basically the same way I use it anyway. I have more than 15 years experience with git at this point. I never had to do the "delete repo and reclone" thing after the first year. In other words, I actually understand git, so I don't really need Jujutsu.
I also already use very good tooling for git, namely Magit. IMO Magit is a much better git frontend than Jujutsu. It guides you down the right path but doesn't take away any of the power of git at all. It's quite remarkable.
Maybe I should recommend jj to some of my colleagues, though. Trouble is I'm already on the hook for helping them with git, but I don't have the experience with jj.
stouset · 3h ago
> In other words, I actually understand git, so I don't really need Jujutsu.
This is kind of a poor take. By all means use what you prefer! But understanding git and knowing the "right" way to use it doesn't make jj obsolete.
I am (or was) a git expert. I’ve used it since pre-GitHub. I’ve written a git implementation. I know (or knew) the interface inside and out. I haven’t deleted and re-cloned a repo in as long as I can remember.
jj is still leagues better. Things I want to do and know how to do in git are dramatically faster and easier. They require less mental overhead. They’re less error prone. And I get superpowers with workflows that are super useful but wildly impractical in git.
This isn’t even really opinion at this point. Any task you can give me in git, I can with almost near certainty give you a shorter and more elegant alternative with jj that is an intuitive and obvious interaction with its core primitives.
A month ago our company split off a division. They needed to take a specific repo and sanitize out all of the parts that aren’t relevant to the new company, going back to the beginning of its commit history. You can do this with git. It wouldn’t be fun. I’d have to spend a lot of time reading the filter-branch manpage, and people have written countless wrappers of varying quality that try and make it a bit more ergonomic.
It took me like ten minutes to come up with:
declare -rA paths=(
…
)
# for every commit that touched a file
# we want to excise
for id in "$(
jj log \
--revisions '..' \
--no-graph \
--template 'change_id ++ "\n"' \
"${paths[@]}"
)"; do
# restore those paths’ contents from the
# the empty root commit; this happens
# entirely in-memory without having to check
# out each revision into the working copy to
# do file operations so it is FAST
jj restore \
--from 'root()' \
--to "${id}" \
"${paths[@]}"
done
# remove any commits that are now empty
# (except for the implicit root commit)
jj abandon \
--revisions 'empty() ~ root()'
Three dumb, simple commands that I already use every day: list some commits, copy file contents from one commit into another, and remove some commits from the tree.
Not only was this more or less obvious to do, but it was exceedingly fast to perform on a pretty highly-trafficked repo due to not having to thrash around in the working directory with checkouts.
> Magit is a much better git frontend than Jujutsu. It guides you down the right path but doesn't take away any of the power of git at all.
jj is more powerful than git. It’s not simply a dumbed-down alternative. By having a more carefully chosen set of primitives that compose better, you gain a lot of abilities that are technically possible with git but never used in practice due to the complexity. The above is IMO a fantastic example of how and why.
And you can still do all the normal git things too.
latexr · 1h ago
> This is kind of a poor take.
Why? They’re sharing what they prefer in response to someone claiming that everyone who tries the other thing comes to prefer that. They’re offering a reply in context and not in the slightest saying everyone should follow what they do.
> By all means use what you prefer!
That’s exactly what they’re doing.
> But understanding git and knowing the "right" way to use it doesn't make jj obsolete.
Which they haven’t claimed at all.
You seem to be objecting to an argument which hasn’t been made.
darthrupert · 34m ago
You can consider your perfect conversion rate now broken; I tried jujutsu on a personal project and found it to be a hassle without any upsides.
I believe your perception is flawed because the people who just try it and throw it away don't tend to talk about it because it's not popular enough to warrant even a twitter comment. This is the first time I got the impulse to share this but only because you claimed a rather silly 100% conversion rate.
sngz · 2h ago
I always go back to using mercurial for personal projects. Better than both
windward · 2h ago
'Doesn't use Mercurial' was top of my requirements list last time I left a job that did use it.
pjmlp · 2h ago
If I had an option, I would still be using something like Subversion or Mercurial.
As it is, I go with whatever our clients require of us, and that isn't jj.
nchmy · 1h ago
You're misunderstanding. You can use it with any git repo without anyone knowing.
forrestthewoods · 5h ago
> You will be hard-pressed to find someone who stuck with it for a week and decided to go back to git.
I’ve tried jj on three occasions and I always get confused by something and just bounce. It hasn’t clicked for me.
I’ve only tried it solo hobby projects. For which git is perfectly tolerable.
I have many many many complaints about git. But jj doesn’t move the needle for me.
Reading this post I am extremely annoyed. Almost every single section is “but more on that later”. It’s still far more complicated than it needs to be.
I also really really really hate the jj log rendering. The colors are a sea of barf. Bolding the leading character that represent uniqueness is stupid and adds noise. The username being second is dumb, I almost never care about that. And the bright neon green (empty)(no description set) is such bad spew. Kinda nit picky, but blech.
Jujutsu suffers the same thing Git suffers. Every god damn blog post that tries to explain how simple it is just my eyes gloss over and think “this is too complex for me to care”.
viraptor · 4h ago
> I also really really really hate the jj log rendering
You can fully change/customise it. Sounds like you just don't like the default.
forrestthewoods · 4h ago
Some people love to tinker and fine tune their tools to behave exactly like they want. I am not one of those people. I hate it. It does not spark my joy.
Most people use the default for most of their tools. It’s why Google pays Apple over $20 billion per year to be the default search engine. Because defaults matter.
viraptor · 8m ago
But you can't make a default that everyone will like. The best overall interface will have someone hating it. If you want to always use defaults, you're basically guaranteeing that you'll hate some genuinely great software one day.
stouset · 2h ago
FWIW, I am also one of those people who does not want to fine tune their tools. I want my tools to work well out of the box.
> I’ve only tried it solo hobby projects.
If I can surmise a bit, this is probably why it's never felt "worth it". I'm guessing that for a hobby project you don't really care that much about crafting small, easily-digestible PRs or keeping a clean history. `git commit -a` every now and then is good enough, and that's entirely reasonable.
For team projects, jj becomes a much bigger deal. I find it invaluable for splitting up a day's worth of work into small, parallel, easily-reviewable changes. While working on one feature I might run across a dozen other things that should be fixed, improved, or otherwise changed. Bundling them into one giant PR is bad practice and causes reviews to take much longer.
Carving those up into single-purpose PRs that can be reviewed in seconds and tested independently is super helpful on a project with multiple teammates. But that's not something that really matters or that most people care to take the time do to in personal projects. Hell, it's something a lot of people punt on a lot in git due to the extra burden of doing so.
baq · 1h ago
my heuristic is:
do you know about git rerere?
if yes - try jj.
xdfgh1112 · 3h ago
I tried it and I went back to git. I know how git works though. I don't put much stock into your 'anecdata'
lawn · 4h ago
Eh, not really.
I've used it and I like it but I couldn't find a good plugin for Neovim so I reverted back to my old more well-supported workflow.
nephalegm · 7h ago
I started doing jujitsu a few months ago. The title of the article and this top comment had me bewildered until I remembered what jujitsu was in this space LOL
WhyNotHugo · 1h ago
The main things that drives me crazy about jj is that all changes are always staged implicitly. This is what SVN did back in the day, and git was a huge improvement by staging changes explicitly.
I almost always have more changes in my repository that those which I want to include in the next commit. With git, I just add the changes I want. With jj (and svn), there’s not obvious way around it—you have to manually copy-paste changes outside of the repository before committing.
padenot · 59m ago
`jj commit -i` (or a lot of commands `-i`) and maybe `snapshot.auto-track="none()"` in the config, to a certain extent is what I use. I used to do the same with mercurial. In practice, I also use `absorb` a lot, that leaves unrelated files and chunks alone.
kirici · 56m ago
You could disable autom snapshots or use `jj split -i`, which I use almost exclusively
emresahin · 8m ago
I tried jj a few days but I noticed my lazygit based workflow and scripts already make me productive enough not to deal with another mental model.
jj looks cool in general, I'd start with it if I were just going into this _version control_ thing but for most of us older folks, that doesn't provide enough motivation to change.
stavros · 1m ago
Eh, I can see how, if you use GitButler, the porcelain is fairly irrelevant to you, but a few days ago I decided to try Jujutsu, asked Claude how I could do a few things that came up (commit, move branches, push/pull to Github). It took me ten minutes to become proficiend in Jujutsu, and now it's my VCS of choice.
I still use Lazygit for the improved diffing, but, as long as you don't mind being in detached HEAD all the time, there's really no issue with doing that. JJ interoperates fine with git, but why would I use the arcane git commands when JJ will do the same thing much more straightforwardly?
Also, the ability to jump from branch to branch with all my uncommitted files traveling with me is a godsend. Now I can breeze between feature development, bug fixing, copy changing, etc just by editing the commit I want. If I want multiple AI agents working on that stuff, I just make a worktree and get on with it.
Not to mention that I am really liking the fact that I can describe changes (basically add commit messages) before I'm done with them, so I can see them in the tree.
JJ is just all around great.
Lyngbakr · 9h ago
I feel pretty dense, because I still struggle to get my head around automatically adding changes to a revision. Sometimes, I'll make a change locally to a file that I'll use during the development process that I have no intention of committing. With regular git, I never stage that file so there's no danger of accidentally pushing my change to the remote repo, but it seems with jj I'll need to somehow unstage that change or something to prevent this. Perhaps it's just habit, but I feel more comfortable explicitly saying what I want to commit rather that defaulting to everything. Or have I totally misunderstood jj?
simonmic · 1m ago
[delayed]
stouset · 9h ago
In jj you tend to use `jj split` to break changes apart. The selected bits become the first revision, the remaining bits become the second revision.
I tend to do a bunch of work then split into small, bite-sized revisions. Often I split something out to a parallel revision (e.g., a separate branch) if it’s an independent thread of work like a bugfix elsewhere or a documentation fix. This is an obvious one-liner in jj but a bunch of annoying branch-switching and stashing in git.
You can also use a `git add`-style workflow. Create a new revision with `jj new`. Do it again. Make your changes, then `jj squash -i/--interactive` to select the bits you want to include. Keep making changes and squashing into the previous commit you’re building up until you’re happy. Conceptually just think of @ (the current revision) and @- (the previous revision) as the working copy and the staged copy, respectively.
palata · 2h ago
I feel like I already lost more time reading this than jj would make me win if I switched :-).
rtpg · 9h ago
I usually work, then do `jj split` to review changes I want to make commits to. This generally makes the workflow look like `git add -p`.
A decent mental model is that the top-most commit isn't generally going to get pushed up anywhere. It's like your working copy but also you get stashing "for free" (change back to main to make a new commit? All the WIP stuff stays on that branch instead of being carried over to main!)
0cf8612b2e1e · 9h ago
No that is correct, and also a habit I am trying to break. The reasonable argument is that we should stop running code with untracked state. Either the changes are important and should be committed or not. Otherwise you are recording code versions that never truly existed during development.
Where this gets extra sticky for me is tooling which refuses to distinguish repo wide config vs a local only version. VSCode being a huge offender where there is only a ‘launch.json’ and no ‘launch.local.json’ suitable for per host customization (eg maybe I am already running something on port 8888, so I need to map it to 9000, that does not mean a quirk of my environment should be committed).
oxidant · 6h ago
Counterpoint: Why should my println debugging get committed? They're not "important" for the final product but important for development.
sunshowers · 3h ago
I think most people would use a logging library (maybe at the "trace" level) at that point.
0cf8612b2e1e · 6h ago
The logic is once you are ready to commit you delete all of the debugging stuff. Otherwise you are committing an illusionary state of the repo that only existed by manipulating the stage.
I am a black kettle here as I frequently commit individual lines amongst a sea of changes, but I do appreciate the theoretical stance of jj.
steveklabnik · 5h ago
Just to be clear, jj makes it really easy to carry this sort of thing as a separate patch, so while it may be "committed," that doesn't mean it has to go into what you send upstream.
(though for debug printfs in particular, the Right Thing is proper logging with log levels, but I myself love printf debugging and so sometimes don't do that either. Which is why carrying local patches is nice.)
baq · 2h ago
There is no Right Thing here. Practicality beats purity. The product (a snapshot of the source tree) should do what it needs to do, but getting there is not the product. It can be if you want it to be, but there is no upside to that.
do_not_redeem · 9h ago
If you want this workflow, you can treat jj's `@` (nominally equivalent to git's HEAD) as the git index, then at commit time, manually squash changes to `@-` just like you would with `git add --patch`.
000ooo000 · 4h ago
FWIW, @ is a shortcut for HEAD in Git.
zamalek · 9h ago
I've been using jj for a few weeks, and recently made an auto-commit-message script for it[1]. Just today I started working in an old git repo and thought that I should port the script to git. It turns out that jj made the script trivial because of immutable commits, and mt script would need to automatically do that with git: I use a rebase/amend/clean history workflow at work, so I would need the script to determine when to commit or when to amend. It's obviously possible, but I don't want to expend the effort - I just re-cloned it with jj.
It's amazing how quickly I forgot about the commit vs. amend papercut.
You don't even need to re-clone. You can add jj to an existing git repo.
ants_everywhere · 7h ago
I'm confused what this is?
Is it just a git frontend for people who are confused by git?
It says it abstracts the backend, but it's not clear how something so git-influenced will have abstractions that work with something like a centralized system like Perforce or Piper that has auto-increment numeric commits.
Some of the design decisions are also not great. Working copy as commit means you have no quality control. The whole point of a commit is that... you commit it. So now you need a separate repo or filter to remove the worthless changes from the ones that you actually intend to commit. Bad commits polluting git histories is already a big problem.
The biggest problems with git IMO are it scales poorly and it encourages commit pollution. Presumably jj isn't trying to tackle those problems, which is totally fine. But I am confused about which problems it is tackling. That's why I'm wondering whether it's just a frontend that the author finds more to their liking.
Filligree · 7h ago
> It says it abstracts the backend, but it's not clear how something so git-influenced will have abstractions that work with something like a centralized system like Perforce or Piper that has auto-increment numeric commits.
Its Piper backend honestly works better than the Git backend. Which isn't a knock on the Git backend, but the impedance mismatch is worse there.
> Some of the design decisions are also not great. Working copy as commit means you have no quality control. The whole point of a commit is that... you commit it. So now you need a separate repo or filter to remove the worthless changes from the ones that you actually intend to commit. Bad commits polluting git histories is already a big problem.
Sorry, but saying this implies you haven't tried it. :-)
Jujutsu commits aren't equivalent to Git commits. They're implemented with a mixture of Git commits and the Git working tree, yes (if you use the Git backend!), but when you see 'commit', you should read 'named diff'.
Jujutsu also has a notion of immutable commits, by default meaning (roughly) commits which have been pushed upstream.
You can and should rewrite the un-pushed commits to clean up history prior to pushing changes upstream. jj makes that much MUCH easier than rebases and history edits could ever be with git. Most of my nontrivial jj work involves at least three or four commits at some point, everything from experiments to documentation branches, which with git I would have needed to awkwardly fit into stash or inconvenient throwaway branches.
alwillis · 2h ago
> Jujutsu also has a notion of immutable commits, by default meaning (roughly) commits which have been pushed upstream.
This makes sense to me as a longtime Mercurial user. In short, by default, when you push a commit, it becomes public and therefore immutable [1].
Other awesome Mercurial features that appear in JJ are revsets [2], filesets [3] and templates [4]. Looking forward to giving JJ a try.
Thanks, can you link me to their perforce backend code? I don't see the string "perforce" or "p4" anywhere in the code and it's not coming up on search.
> You can and should rewrite the un-pushed commits to clean up history prior to pushing changes upstream.
My concern isn't just about pushing upstream. What I'm saying is that the typical change to a file shouldn't be committed to my local repo until I indicate that it's ready. The FAQ suggests this isn't possible and that you should work around it by using a separate branch and then merge into your target branch, which is a pretty ugly workflow.
Their GitHub branches are a mess of auto-generated strings, which suggests to me that this problem isn't just an abstract concern but is a form of technical debt that the jj devs are currently piling up.
baq · 2h ago
> My concern isn't just about pushing upstream. What I'm saying is that the typical change to a file shouldn't be committed to my local repo until I indicate that it's ready.
jj doesn't have the concept of a non-committed file.
instead you don't track the HEAD as the tip of the branch (@ in jj), you track HEAD^ (jj: @-) or something earlier and since jj rebase isn't dumb as a rock you can relatively easily keep the working set of changes (as in, multiple commits/WIP branches; not necessarily the index or the working copy) on top of what you are pushing and squash or advance the branch (bookmark).
I'm reiterating because it's a very important and super confusing for advanced git users: there is no uncommitted state in jj and yes, it does make sense, but you need to stop thinking in git.
1718627440 · 8m ago
Can't this be a security risk as there might be secrets that should never be recorded by jj (You can probably configure this, but I want my software to only do what I tell it to, not do everything until I tell it not to.)
How does that work in practice, is there a daemon running constantly and monitoring? How does that interact with other users and changes from other computers?
baq · 18s ago
Every jj invocation stores a snapshot before doing anything else - thus anytime you run jj, you tell it to store a snapshot in the local repo.
I don't see a problem with secrets TBH. .gitignore is respected if that's what you have in mind. Otherwise store your secrets out of tree (you probably should with git too, anyway.)
stouset · 6h ago
> What I'm saying is that the typical change to a file shouldn't be committed to my local repo until I indicate that it's ready.
Yes, it's "committed", but that doesn't mean all that much. I have the fsmonitor feature enabled that continually watches my repos as I edit files in them. This means that over the course of a coding session, the revision I'm working on has probably pointed at dozens or more ephemeral underlying git commits. Those are only kept around for the purpose of the evolution log, which lets me look back at my edit history throughout the day even without having interacted with the repo.
When you're ready to "finalize" your work, you have two common workflow options: you can `split` the out parts you want to keep as one consistent commit, which dumps the rest in a subsequent revision. Spiritually this is equivalent to `git add -p`. Alternatively, you can create an empty "staging" revision before your "working copy" revision and `jj squash -i` pieces you're happy with into there. In practice, many people use both. I generally do the former for new work, and the latter to make changes to earlier work.
Thinking that `git add -p` was absolutely a deal-breaker is the main reason I passed over jj for so long. I care deeply about maintaining a clean commit history with small, isolated, and individually-tested changes. I thought jj would make that harder due to not having a staging area. I was wrong. It is actually far easier to be principled about your commit history with the tools that jj gives you.
viraptor · 4h ago
> What I'm saying is that the typical change to a file shouldn't be committed to my local repo until I indicate that it's ready.
You're thinking in very git-specific terms. JJ is just different in many ways so such comparison doesn't work well. I didn't get it from explanations before, but trying it in practice, it's a non-issue. It may be easier to just give it a go.
wredcoll · 4h ago
> My concern isn't just about pushing upstream. What I'm saying is that the typical change to a file shouldn't be committed to my local repo until I indicate that it's ready
Since you brought this up, I've noticed some people seem to work this way but I've never found anyone to ask why they do this.
I get the idea behind clean, readable git logs with nice consistent messages, but isn't that what rebase/amend is for?
To me, the status of my local git log is mostly irrelevant, the thing that really matters is the commit(s) I submit for merging.
Also, probably for related reasons, I've never truly understood why git has this whole separate staging concept...
1718627440 · 1h ago
I also work this way. A commit is really about, well, committing that these are exactly the right lines of code I intended to write. I also read them in another program, so it gets easier to not think that I already know the code and skip over. I basically start from the clean state (no changes) again and approve the changes line-by-line according to whether they fit the domain model of the program, whether they are in the right places, are the right amount of complexity/abstraction, if they are readable/understandable. I typically also add/remove comments at that point, because I've now tried to read the code with a "second pair of eyes".
I also use this to compare the changes to the commit messages. Ideally the changes follow from the message, not the other way around.
While a VCS is also useful for adding a time-axis to code, for me the main selling point is, that it's adding causality to the code. You can ask 'Why is this code how it is?'. (Of course causality is a side-effect of time.) When you don't curate the commits you completely loose this feature. (You can still ask, but the answers will be confusing and need way more work, which for me amounts to manually comparing commits to grasp the big picture and intention.)
> rebase/amend
When you messed up, you can of course change commits, but this has a danger of creating a version that was never there or mixing history, for example combining a single physical change, that are multiple logical changes. In order to test that the code at least works I use ```git rebase --exec="make -C build distcheck"```, not sure if that is common.
I also don't use MS GitHub -style merges, I find them inferior. (I also don't use MS GitHub, but that's for another reason.) I think they try to make merges the actual unit of change, which is stupid, because that is what a commit is for. To me merges are about semantic grouping of commits, i.e. maintaining a tree of commits representing the logical evolution of the code.
Yes the staging area is exactly designed for this approach and I don't like people telling me I'm holding it wrong and don't need it. To use a metaphor, I think the staging area is like my desk were I'm producing stuff (I'm not producing code, I'm producing changes). Committing is about having produced a complete opus and then cleaning the desk. Cleaning isn't seen as annoying, it is important for the mind to process completing/validating and then starting afresh. Stashing is in this metaphor about switching to an alternate desk. This is different from putting the opus aside and having also a clean desk. (I think that is what Jujutsu wants you to do?) Having the staging-area/stash different from commits is a feature, the fact that the stash is the same storage-wise is an implementation detail.
dzaima · 2h ago
> What I'm saying is that the typical change to a file shouldn't be committed to my local repo until I indicate that it's ready.
I was extremely hostile to this aspect of jj too, but I've settled on a workflow where @ (i.e. the change/commit referring to the working copy) is always the same, named ".WIP: …", and as such is clearly never supposed to break out of the local system, and can never accidentally get pushed down the log. (..my jj workflow is just blatantly copying my git workflow (with the same-name shell aliases/helpers even), but even with said git emulation I'd say it's nicer than git itself)
Still may be weird/undesired to have the local repo preserve the changes (especially annoying if it happens to find an unignored build artifact, though there is a size limit for automatic tracking by default), but it's also rather neat that you can dig out your old deleted printf debugging or whatnot later on if you wanted to.
MrJohz · 1h ago
If you leave the WIP head change blank, i.e. don't give it a description at all, then JJ will block you from pushing it unless you pass a specific flag, which is useful for sanity checking this sort of stuff.
You might be able to configure JJ to also block commits with certain descriptions as well, which might be useful in your case.
dzaima · 50m ago
I specifically have the WIP head description include the name of bookmark that it's attached to, if any (emulating git's non-detached head), so can't leave it empty (and generally wouldn't want to, for easy way to identify it, esp. with multiple workspaces).
Some push blocker for certain commit name patterns would make sense to look into.
steveklabnik · 6h ago
> I'm confused what this is?
jj is a version control system. It is backend-agnostic. The most common backend is a git one, because git is so popular. This allows you to use jj on a git repository, allowing for individuals to adopt it without forcing their teammates to.
> Is it just a git frontend for people who are confused by git?
I used git since before github existed. I considered myself a git lover before I found jj. I will not be going back to git.
The thing is, jj is both simpler and more powerful than git, at the same time. People who are confused by git may like its simplicity, but I like its power.
> It says it abstracts the backend, but it's not clear how something so git-influenced will have abstractions that work with something like a centralized system like Perforce or Piper that has auto-increment numeric commits.
You already got some replies on this one, so I'll leave that to them :)
> Some of the design decisions are also not great. Working copy as commit means you have no quality control. The whole point of a commit is that... you commit it. So now you need a separate repo or filter to remove the worthless changes from the ones that you actually intend to commit. Bad commits polluting git histories is already a big problem.
This is an understandable misunderstanding. The right way to think of it is "the index is also a commit." A common way of working with jj is to do something like this:
Imagine I am working on adding some feature x to my codebase. I'll first make a change, and give it a description:
jj new -m "working on feature x" trunk
Working copy (@) now at: lqqlysul c6756b49 (empty) working on feature x
Parent commit (@-) : ylnywzlx 8098b38d trunk | (empty) foo
Now I will make a new empty change on top of that:
jj new
Working copy (@) now at: pxrvoron c823d73a (empty) (no description set)
Parent commit (@-) : lqqlysul c6756b49 (empty) working on feature x
Now, @- is the change that I intend to push publicly, but @ is my index. Say I add foo.rs:
touch foo.rs
Now, when I run `jj status`, jj takes a snapshot, and it now lives in @:
jj st
Working copy changes:
A foo.rs
Working copy (@) : pxrvoron ba7ad8c6 (no description set)
Parent commit (@-): lqqlysul c6756b49 (empty) working on feature x
We can see the diff here with `jj diff`:
jj diff
Added regular file foo.rs:
(empty)
So, let's say I'm happy with its contents. I want to stage it into my final commit. I can do this with `jj squash`, which by default takes all the diff of @ and puts it into @-:
jj squash
Working copy (@) now at: pxkqmsww 9f7e1ef2 (empty) (no description set)
Parent commit (@-) : lqqlysul 41dc1531 working on feature x
Now that change is in @- instead of @. we can see that by passing -r (for revision) to `jj diff`:
jj diff -r @-
Added regular file foo.rs:
(empty)
I don't have to move the whole change; I can do the same thing as git add -p by using jj squash -i, and only move the portions of the diff.
What's the advantage here? Well, because the index is just a commit, I can use any tools that I use on commits on the index. There's nothing like `git reset` needing to have `--hard` vs `--soft` vs `--mixed` to deal with index behavior: everything is in a commit, so everything acts consistently.
jj makes it very trivial to carve up commits into exactly what you want. It is far easier and more powerful than using the index for the same purpose.
> The biggest problems with git IMO are it scales poorly and it encourages commit pollution. Presumably jj isn't trying to tackle those problems, which is totally fine. But I am confused about which problems it is tackling. That's why I'm wondering whether it's just a frontend that the author finds more to their liking.
IMHO, commit pollution is more due to the pull request workflow than git itself, though I do think that git doesn't do that much to help you. jj can help with this kind of thing, but also on some level, it can only do so much.
wredcoll · 4h ago
I want to appreciate this cool-sounding new tool, but everything you've said sounds exactly the same as using git rebase, what am I missing?
baq · 2h ago
You can do everything with git rebase, the question is how much pain are you willing to tolerate. If you've ever solved the same conflict twice and/or are aware of git rerere, you should try jj.
stouset · 2h ago
Git rebase is, frankly, a massive pain in the ass. It's worth doing if you care about it. I used a rebase-heavy workflow for a decade and a half. But having to drop everything you're in the middle of to fix an endless list of rebase conflicts sucks. And it sucks even worse when you screw up the conflict resolution halfway through and have to start over from scratch.
Rebases also don't play nicely with stacked branches. Branches based off your original changes don't get rewritten to be on top of the rebased change, so now multiple rebases are in your future.
It turns out that basically none of the rebase friction most people experience is necessary in practice. Rebases can be automatic, implicit, and conflicts can be fixed at your leisure and in whatever order that you feel is best.
jj also has a ton of other powers, but you asked about rebase :)
1718627440 · 2m ago
> having to drop everything you're in the middle of to fix an endless list of rebase conflicts sucks
You can use git commit --fixup to record what you want to change in an earlier commit.
> start over from scratch
rerere
> Branches based off your original changes don't get rewritten
As written elsewhere: git rebase --update-refs. If you want to do it manually git rebase --onto.
do_not_redeem · 7h ago
> Working copy as commit means you have no quality control
The working copy doesn't get automatically pushed to GitHub or anything crazy like this seems to be implying. You review/curate your commit when you give it a description.
doritosfan84 · 9h ago
I’ve been trying unsuccessfully to convert my team to jujutsu. I feel like what would be great is a page that really shows some common but complicated operations in git and how much easier they are in jujutsu. Something like the elevator pitch here but expanded on without the depth of Steve’s tutorial.
Maybe what I need to do is do a demo so people can see and ask questions.
Filligree · 7h ago
> I feel like what would be great is a page that really shows some common but complicated operations in git and how much easier they are in jujutsu.
What I find isn't that common git operations are easier in jujutsu. They're not; sometimes they're slightly harder, due to the impedance mismatch with the git backend.
Rather, what git makes easier are operations that are next to impossible — or at least highly inconvenient — in git, and which therefore next to no-one does. That makes it harder to explain, because you're telling them there's this great new workflow that does stuff that... they don't think they need (they have workarounds), and the notion of which triggers their ick reflex if they're good at programming.
palata · 2h ago
I do understand that point, but to me it sounds like "you should use jj because it's a lot better at solving problems you don't have".
If there are really common use-cases where git is annoying and jj is great, it shouldn't be that hard to explain, should it? If you can say "remember how in the last few days you struggled with this? Jujutsu solves it", then I'm happy to try.
If your argument starts with "imagine you are in a team that looks like X (but your team does not), with a project that looks like Y (but your project does not), and now imagine that you need to do this thing that you have never done before...", then maybe I actually don't need jj?
gabrielgio · 15m ago
> "you should use jj because it's a lot better at solving problems you don't have"
It is more like "you should use jj because then you won't have a lot of problem with git that you'd need git to solve"
> it shouldn't be that hard to explain, should it?
It is not. There are plenty of example on this page. For me the biggest one is stacked PR, jj makes it trivial since it tracks the change sets and not commit ids (which are immutable). So you can work on any level of the stacked PR independently, once you're done run "jj git push -r '(trunk()..@ | @::)'" and it will update all the remote branches accordingly. Another feature that works great with stacked PR is that you don't need to solve conflicts right away. You will see a marker in the "jj log" and you can solve it later down the road.
Also another great feature is the operation log, you can just rewind your actions. F'd up a conflict resolution? Just go "jj op undo". That goes for everything, including file changes and rebases. Want to go back the state it was 15 min ago because you didn't like what you did? Merge to the wrong place? "jj op undo"
Adding to that there are hundreds paper cuts that jj fixes, like:
* Simpler mental model for local change, no git stash/add necessary.
* Simpler commit process, you can just work and use "jj describe" whenever where git forces you to write message before creating commit (again because commits are immutable).
* Starting a work is much easier, I can just go "jj new" away without caring about detached head. Nowdays I just use branches (jj bookmarks) for git compatibility reasons.
* Revsets are amazing, much more powerful and expressive than git logs, and since the UX is more consistent you can always work with set of rules that expects a revset with "-r".
Ofc, you can do all of that with git, but it just works better, easier and more consistent with jj.
nchmy · 56m ago
Here's a few great links that might be what you're looking for
I don't especially like git. I stuck with mercurial for a long time.
But that was ten years ago. Now git is kind of hard-wired in my brain. By and large, it works well enough.
It's not really clear to me that Jujutsu offers a significant enough of a benefit to spend the time re-wiring my brain, never mind dealing with the initial setup (e.g. the unreadable colours, setting up some scripts/aliases for things I like).
bravesoul2 · 8h ago
Yeah I find abstraction layers suck a bit. Same with miso and uv (for python). They don't "just work TM" and now I have 2 problems. By just work I couldn't install miso onto a fresh Ubuntu without 404 errors and uv kept bitching about my pyproject file instead of just working or trying to fix it.
Some of these tools do just work. E.g. nvm seems to just work and is much nicer than raw node installs.
sunshowers · 8h ago
Note that jj is not a frontend for git or an abstraction layer over git — rather, the git object store format is one of at least two backends to jj.
bravesoul2 · 8h ago
Thanks. That would make me interested in it for a personal project where I only use jj.
stouset · 8h ago
There's really no cost to trying it out on an existing project with other people. It natively interoperates with git. Nobody needs to know you're using something different, and you can always fall back to using git commands if you need to do something you haven't learned yet.
what · 8h ago
What is miso?
jph · 5h ago
Possibly means mise (https://mise.jdx.dev/) which is a tool similar to uv in the sense of managing versions.
nchmy · 54m ago
Mise and jj (and the jjui TUI) are my two favourite tools, by far
bravesoul2 · 4h ago
Correct mise. Sorry!
sunshowers · 8h ago
I think jj just uses the 4 bit (16 color) terminal palette, so if a color is unreadable, you can update your terminal theme accordingly.
nchmy · 52m ago
Jjui is by far the best TUI for jj and just released a Themes feature yesterday. It's well worth checking out.
This has been the case, but it's worth noting that very recently, support for theming with the 256-color palette has landed too https://github.com/jj-vcs/jj/pull/6763
sunshowers · 7h ago
Cool! Don't imagine it would ever be the default, though.
steveklabnik · 6h ago
I hope not, for sure.
arp242 · 8h ago
Some programs set the background colour and some don't. For example pamix sets the background to black, or tmux's statusline, or ngrok. It's not really possible to define one terminal scheme that always works.
sunshowers · 7h ago
I think it's reasonable to assume that all 12 colors that aren't black or white will be readable on the default terminal background. I sympathize with those for whom that isn't true, but please do find a different theme in that case.
arp242 · 7h ago
How would that work with applications that hard-code the background to black (or some other colour, like tmux's green statusline)? If you change the colours to work on a light background then those break.
You have to choose what you want to break. Once you start pulling on one thread lots of stuff start to unravel. It's really not a simple matter of "choosing a better theme".
I've spent a long time looking at this, and my conclusion is that there is no safe default that will work for everyone, other than bold/reverse and 256/true colours and setting both the foreground and background.
sunshowers · 7h ago
If you hardcode the background to a fixed color, the 4-bit palette for foreground colors is generally to be avoided -- instead, use the 8-bit (256 color) or bigger palettes.
arp242 · 6h ago
I don't control these applications.
sunshowers · 3h ago
If I understand correctly, you're in the situation where you've chosen your 4-bit colors so that they render properly on an application which (incorrectly) uses an 8-bit background color but a 4-bit foreground color, and also that the colors don't render well on the default background that's part of the same 4-bit palette.
I think you'd be a great candidate for jj's ability to customize colors.
arp242 · 2h ago
I don't choose anything; I just use the xterm defaults. Some colours inherently conflict. Blue on white is fine. Yellow on black is fine. Yellow on white is not. White on white is even worse. There are many combinations that don't really work brilliantly. Some applications set background colours, some don't. pamix, ngrok, and npm are examples of applications that hard-code background colours. Configuring the terminal to use text colours that work well with both the default white and hard-coded black (for some applications) is hard.
That I need to spend a bunch of time setting all of this up (among other things) is exactly how this thread started.
wredcoll · 4h ago
What situation do you have where jj is rendering colored text on top of a background you don't control? I'm perplexed.
I think that comparison is unclear and unfair. The core is that instead of:
jj rebase -r @ -B abc
the recommended Git alternative is:
git rebase -i abcd1234
# move the last line to the desired position
This is a process I use heavily, and one of the rare cases where I prefer the Git way: less cognitive load (I don't need to memorise options b/s/r/d/A/B for `jj rebase`) and the interactive editing of the history feels simpler (especially if I move several commits).
I've used jj for a few weeks, but switched back to git. I'm fluent enough with Git so I never struggle any more. jj mostly felt nice, but the added value was not enough to replace years of expertise.
baq · 2h ago
I find
jj rebase -b @ -d master@origin
to be an excellent improvement over anything git provides, including rerere. It's the first patch queue for git implementation that actually worked for me, which in turn makes github PR UI somewhat bearable.
I occasionally use -r, too, but most of the time it's better to
jj squash --from ... --into @
wredcoll · 4h ago
This article convinces me that what he wants to do is easier in jj, I just don't understand why he wants to do it.
My quick summary is that in one case he's try to avoid "extra" commits and in another case he's trying to re-order some commits. In my usual work flow, both of those problems would be handled by the git-rebase-squash I do after the feature works.
doritosfan84 · 6h ago
Thanks. This is a really good one. It outlines an operation that would resonate well with most developers and clearly demonstrates how much simpler, easier, and faster this is in jujutsu vs git. I think most devs just wouldn't even bother to do it in git, they'd leave the test out of order and call it a day.
dcre · 6h ago
Yep, I think that's right. As the other reply says, this is part of what makes it hard to explain. If you told me jj makes it easy to rebase all the time, I would ask: why do I want to do that? Now I don't even think about it, I just rebase all the time.
maddiemort · 41m ago
I think you'll probably like the next part I'm going to add to this post, then! I'm sure it'll get posted separately, but it'll also appear at the bottom of the page linked here when it's up (hopefully soon, as in the next few days).
_bent · 9h ago
I've been using jj for two weeks now and it's kinda exciting, because for the first time I'm comfortable with using version control just via the command line.
With Git I always had to use a GUI (preferably Git Graph in VSCode) and launch all operations by right clicking items, but jj was simple and consistent enough that I could just start using it after reading Steve Klabniks tutorial.
The thing I'm running into right now is that I should really learn the revset language, so that I don't have to constantly copy paste ids from jj log
noroot · 4h ago
I'm in a very similar situation: been using git for a long time, but anything more complicated always via some kind of UI (often intellij).
Been using jj without significant issues for about a month and been super happy to be comfortable using the cli and slowly ramping up to more complicated operations.
The documentation still assumes a lot of inherent knowledge which sometimes makes it a little difficult. I love seeing blog posts like these and hopefully some more in depth resources will appear over time. Steve's guide is good, but there are still gaps for me :).
Next I want to learn some more revset language and become a bit more fluent with rebase operations. I love the more simplified cli, conflict resolution and op log!
benoitg · 5h ago
I had the same copy paste problem, now I use jjui (https://github.com/idursun/jjui). The daily operations become even smoother, it looks like I’m flying over the log.
nchmy · 46m ago
This is The Way. jjui makes jj's power and simplicity even more powerful, simple and beautiful
mihaic · 1h ago
For a decade now I've been lamenting that git won the source control war. My complaints fall in two main categories:
1. The mental model is too complex: rebase or merge, detaching head, etc. A good UI can hide away some of this uglyness. It sounds like jj helps here.
2. Any source control in a terminal is just horrible, since you have no view of the state you're working with. I honestly never fully understood why developers have such religious elitism for the command line. Where would you draw the line on something having a complex enough state to need a UI?
The change to git that I'm hoping for is a GUI on top of a simpler wrapper like Jujutsu.
nchmy · 1h ago
jjui is what you're looking for. jj is amazing and jjui makes it incredibly better. The maintainer is extremely receptive and responsive to feedback, and constantly moving it forward.
There's other TUIs and GUIs, but jjui is by far the best.
I’ve had a lot of success using https://graphite.dev/. Been pretty easy to pick up and slots right into our usual GitHub workflow. I end up using the vscode extension to manage it. Anyone have opinions how jujutsu compares?
ramon156 · 2h ago
Isn't graphite a pr review tool that still uses git?
accelbred · 8h ago
Is there a magit equivalent? I heavily use magit's interactive features and extensions to the git UI (like spinoff, absorb, or the auto-backup thing)
stouset · 7h ago
I am not a magit user but from doing a little bit of reading, all of these commands are essentially first-class citizens in jj. Spinoff seems to be `jj split` (or in most cases literally nothing because the default is to edit a new, empty revision off the trunk), absorb is probably `jj rebase` or `jj squash`, and the auto-backup is either the evolog (which tracks file changes that haven't been explicitly commmited) or the op log (which lets you reset the entire repo to what it looked like before or after any operation).
There is also lazyjj as an interactive UI.
Filligree · 7h ago
magit-merge-absorb is one of rebase (if that's what you want), or new (which gets you a merge when you specify multiple parents). The 'delete the branch' part of it doesn't really apply, because jj doesn't have branches; though you might need to delete a tag.
Note for other readers: jj also has a literal `jj absorb` command. That one does what you'd expect from mercurial, i.e. moves diffs from the current commit into the most recent ancestral commit where that file was changed.
stouset · 7h ago
Note also that jj does have branches, they are just anonymous. You don't checkout a branch or edit a branch, you edit a revision or make a new one. One revision with multiple children is a branching point, one revision with multiple parents is a merge.
You name them with bookmarks which are sort of like branch names except they don't follow along automatically as you make new revisions. You can point an existing bookmark at a later revision when you're ready to push new changes.
do_not_redeem · 7h ago
jjui is the best VCS TUI I've ever used. It's even smoother than magit, but I think most of that is because of jj itself. Spinoff and absorb are both native jj features (jj rebase and jj absorb, respectively)
Completely agreed. I've been evangelizing jjui even more than I do jj!
And, you're right, it's powers largely come from the underlying jj - it mostly just runs jj commands behind the scenes, parses the output, and displays it. But it's all so beautiful, seamless etc..
I can't wait to really dig into the big additions released yesterday in v0.9.0 - themes, vim-like leader key shortcuts, other shortcuts etc...
tcoff91 · 3h ago
I’ve got to check this out.
I’ve really been loving these two neovim JJ plugins
Any tips for a magit fan who wants a positive second experience with jj?
I’m used to sorting through the changes I have made and making separate commits for each unrelated one (eg bug fix for several files, some WIP work, other parts actually done and ready to be “shipped”/reviewed)?
Do I need a better workflow?
Should I just be using the command line?
fxmc · 1h ago
I came from magit and completely switched to jj for the much easier handling of sets of changes that you describe. jjui (https://github.com/idursun/jjui) made this a lot faster and more enjoyable.
nchmy · 44m ago
This x1000. Jjui is beautiful
atkailash · 4h ago
I tried it out on one of my personal repos and I need to give it more time. I do like the way it tracks work, it feels like it would help me to make more commits as I work, cause I’m really bad about that
eulgro · 34m ago
I've never tried jj, but recently I discovered lazygit which in my opinion comes pretty close to perfection.
Rebasing can be done instantly (reorder commits, fixups, squash), I can extract and reverse custom patches from previous commits, I can partially stage files, undo.
One think I like is that it explains what it does in terms of git commands. So it helps a little to have a good git mental model, and as such I guess it doesn't solve the same thing as jj.
xcubic · 25m ago
This is the reason I have not gone the jj way.
sieve · 8h ago
JJ is brilliant.
I reluctantly moved to git from mercurial when host-after-host dropped mercurial support. git is a user-hostile POS that I never got used to. A thousand needless complications including nightmare merges, and an inscrutable command interface.
JJ eliminates all the git bullshit. Some incredible features:
* Merges never fail as conflicts are a first class feature. You can come back and resolve them at any time.
* You can reset the state of the repo to any particular point in history as JJ maintains a complete operations history.
* You can insert new changes anywhere and deal with the repercussions at leisure.
I started with Steve's tutorial but found it a bit wordy. So I used Gemini to produce a simple guide for me with: "You are an expert at DVCS systems like JJ. Act as my guide for the system and answer my questions with simple cookbook-style recipes."
I have been using JJ for a month and am never going back to git. It is such a pleasure to work with a DVCS that you don't have to fight at every turn.
fouc · 5h ago
Lately I've been hearing more people are getting into jj because of how much easier it is to keep track of the code generated with tools like cursor or claude code.
stouset · 2h ago
Yes!
I have found it incredibly helpful when using an agent. It's a great case for the "staging-area" style workflow. I `jj new` an empty revision where the LLM can go hog-wild. When I'm generally happy with the LLM's work, I make a new revision. Then I ask it to make some improvements. Sometimes I have to discard those. No problem, I just `jj abandon` the whole thing. Sometimes I like a few pieces; I `jj squash -i` those into the parent revision with the bulk of the work and abandon the rest. And then I repeat until I'm happy with the overall output.
It's also super useful when I need to go fix something in an earlier (but still unmerged) revision. `jj new {id}` creates a new revision whose parent is the older change, even if it already has children. Again, I let the agent loose. When I'm happy, I `jj squash -i` what I want and the fixes are not only incorporated back into the parent but then propagated automatically down through the rest of its children through automatic rebasing.
1718627440 · 1m ago
I wonder when LLMs will start messing with VCS. That will be fun :-/
flockonus · 5h ago
I legit ask myself how many folks avoid Github Desktop for some dogmatic reasoning equivalent to "having an UI makes it worse", when it does the core of common flows extremely easily and clear.
To be clear where it ties to this post: it makes git far more convenient with nearly 0 learning curve.
MoltenMan · 3h ago
When I was first learning coding, git, etc. and had no clue how git worked I downloaded GitHub desktop and used that. It's true that I was much younger and less knowledgeable then so it's possible that's influencing me but man when you actually understand git the cli just feels so much smoother, cleaner, and faster. It really doesn't take much dedicated to time to learn the top 5 commands you'll use 95% of the time and if you don't know git currently I think it's probably one of the most leveraged ways you can spend your time.
nottorp · 2h ago
Judging by the name, Github Desktop works only with github... ?
aniforprez · 40m ago
I'm not sure if there's advanced commands that GitHub Desktop does more with but for the most part it's just a porcelain frontend that works great for doing simple operations but can't do things like interactive rebases, reflogs, blames and so on. It's a pretty simple frontend to get started if you're just learning git. There's not much it does that's GitHub specific. If you're logged in, it will easily checkout your upstream repos.
I tried twice to switch for a project, both times came back to git. But then, I am still not fully grokking the "why bother", and suspect that if I had a UI it would be both less friction, and more understanding.
nchmy · 22m ago
There's lots of TUIs and even some GUIs (including a vs code extension), but the best by far is jjui
I want to be excited about Jujutsu, but what always bothers me about JJ is that if you search the page you can find 'jj' 47 times and 'git' - 49.
Is there a good explanation of Jujutsu without referring to git? May be with some pictures? Am I the only one bad with memorizing SHAs when reading? Also, does it work with other people? Would it work in repo with 5 contributors? 20? 500?
EDIT: I am looking for tutorials with explanation on how JJ works, preferably without referring to Git, and even more preferably with some visual explanations, which, there are plenty for Git. It seems I am not very good in reading text trees. It is my issue, not yours, but may be you know something which would help.
stouset · 8h ago
Git is nearly universal, so highlighting the areas where jj makes things that are painful in git trivial and obvious isn't exactly a bad strategy. It's also worthwhile pointing out that it is interoperable with git, so your company and team don't have to change just because you do.
> Am I the only one bad with memorizing SHAs when reading?
I haven't ever needed to memorize a SHA or change ID with jj. What are you referring to?
> Also, does it work with other people? Would it work in repo with 5 contributors? 20? 500?
I have used jj for two years on teams without anyone else needing to be aware. Other teammates that I've convinced to give it a shot have switched, and we all work together happily alongside the git users. If anything, it's easier to work with others when they switch to jj because you no longer have to worry about rewriting history of branches that have been pushed. If you have a branch that multiple people are working on, that's bad form in git but it's just another day with jujutsu.
Svoka · 7h ago
I was referring to tutorial(s), which refer to commits just by hashes. It is very hard to read for me. While git is nearly universal, my concern was about leaning jj and what it is useful for, what are its limitation etc.
nchmy · 24m ago
But in jj you can refer to commits, changes etc by the shortest unique substring in its hash, and it automatically highlights what that is
sjenfidb
skeifixu
These could be referred to as sj and sk. And their associated git commit hash might be
2748dn49
48jdj40r
Would be 2 and 4
Again, the output highlights those unique prefixes.
But there's also jjui, which is an incredible TUI for jj. Makes everything even simpler and efficient than it already was.
"At the time of writing, most Jujutsu tutorials are targeted at experienced Git users, teaching them how to transfer their existing Git skills over to Jujutsu. This blog post is my attempt to fill the void of beginner learning material for Jujutsu."
dcre · 8h ago
That’s very fun. I’ve been wondering what would happen if someone new to programming started with jj directly.
Svoka · 7h ago
Thank you!
steveklabnik · 8h ago
> Is there a good explanation of Jujutsu without referring to git?
There can and will be, but at this stage in the project's life, git familiarity can be assumed.
> Am I the only one bad with memorizing SHAs when reading?
Nope! The CLI has nice syntax highlighting to show you the shortest valid prefix, so for example, right now I have something that looks like
lzrvnkxl
but the initial l is in purple, while the zrvnkxl is in grey. This means I can just use the l when referring to the change. That can be harder to demonstrate in a blog post, which can't know how to highlight this, and so often they have no highlighting.
> Also, does it work with other people? Would it work in repo with 5 contributors? 20? 500?
Yes. Because it's backed by a git repo, nobody else needs to know you're using jj. Everyone can use the tool they choose.
plandis · 8h ago
> The CLI has nice syntax highlighting to show you the shortest valid prefix, so for example, right now I have something that looks like
lzrvnkxl
but the initial l is in purple, while the zrvnkxl is in grey. This means I can just use the l when referring to the change. That can be harder to demonstrate in a blog post, which can't know how to highlight this, and so often they have no highlighting.
This is such a simple UX feature that I have ended up using all the time after I switched to jj a few months back.
jackblemming · 9h ago
I must be getting old because I really don’t think git needs a simplified model. But hey, if people find value from this, more power to them. The blog is well written too.
stouset · 9h ago
It’s not just that it’s simpler, it’s that the primitives and interaction model are also strictly more powerful. A ton of extremely useful workflows that are an utter pain in the ass with git are absolutely trivial with jj.
One example is a series of dependent PRs. This is excruciating in git if you ever need to make a fix to an earlier PR because you have to manually rebase every subsequent change. It’s trivial in jj: you either fix the revision directly or you insert a new revision inbetween. The subsequent revisions are updated automatically. You don’t even have to think.
Another is splitting work into parallel branches. So often when I’m working on one area I end up making unrelated tweaks and improvements as I go. Pulling these out into their own branches is painful in git, so people just make omnibus branches that include a handful of unrelated work alongside the main task. In jj it’s basically zero work to split out the unrelated stuff onto a new branch off main, and it doesn’t require you to task-switch to a new branch. So I end up making lots of one-line PRs that are trivial to review whenever I’m doing deeper work.
deredede · 3h ago
> This is excruciating in git if you ever need to make a fix to an earlier PR because you have to manually rebase every subsequent change.
Spreading the word about `git rebase --update-refs` that will automatically update any branches that point to commits along the path (very useful with stacked branches). It is less convenient than what jujutsu offers (you need to know the branches to update, where jujutsu automatically updates any dependency), but still a very useful if you don't want to or can't switch to another tool.
nchmy · 33m ago
There isn't really any "can't switch to another tool" when it comes to git + jj. You can use it today without anyone on your team knowing - other than that your PRs suddenly became much cleaner
1718627440 · 1h ago
> The subsequent revisions are updated automatically
How do you make sure, that a commit isn't changed under you, because someone thought, it would be a good idea to change an earlier revision? I think having immutable commits including all the previous history is a feature, not a bug.
nchmy · 32m ago
Jj treats any public/pushed commits/branches as immutable by default.
krackers · 8h ago
>you have to manually rebase every subsequent change
What do you mean by this? You can do an interactive rebase in git as well. The real issue is non-trivial merge conflicts which is going to be an issue no matter you use.
stouset · 8h ago
Let's say I have five commits in a row ready to go, but they should be reviewed and merged one-by-one. Upon review, I need to make a change to the first commit. How much work do you think this would be in git? How much of a pain in the ass do you think it would be if a later change conflicted with this earlier change?
It is essentially zero work in jj. I `jj edit` the revision in question, make the change, and `jj push`.
packetlost · 8h ago
It's really not a lot at all. The weakness is in "forge" tools like GitHub that add a PR/changelist abstraction on top of git and don't support sequences of patches. If you're using just commits and maybe (mailed) patches you only do a single `git rebase` (with -i if you want) and you're done. Unless jj is literally magic and can automatically fix conflicts, I can't see how it would actually reduce the work involved in a meaningful way.
stouset · 6h ago
I'll be honest, it's been so long at this point since I've used git that it's hard for me to remember the exact details of many of the challenges. I know that I avoided a lot of workflows I wanted to do because of the complexity and mental overhead, and the scenario I described was absolutely one of them. Maintaining a stack of changes that needed to be merged one after another was painful if there were any unexpected hiccups along the way. Now it is painless.
Arbitrarily-complicated rebases just happen automatically with jujutsu. Inserting, moving around, and directly editing commits are first-class operations, and descendants automatically rewrite themselves in-place to incorporate changes made to their parents. Rebase conflicts are also first-class citizens so they don't block you and force you to deal with them right now or in any particular order. Having to rebase seven related conflicts one-after-another is no longer a thing, nor is realizing you fucked something up halfway through and having to restart.
Coming from git it honestly feels like magic even if it strictly isn't. It genuinely hard to understand how much unnecessary toil git makes you put up with on a day to day basis until you suddenly don't need to deal with it any more.
plandis · 7h ago
As far as I know, by default Git doesn’t enable the “reuse recorded resolution” feature so if you made a change to the first commit you’d have to manually do the same thing for any subsequent commits.
packetlost · 7h ago
If you have 5 different branches, sure. Again, the reason you create a bunch of branches for separate review is because that's what the "git forge" abstraction generally expects. It's not actually how code reviews are done by the people who wrote it.
You can also just enable that feature (rerere).
wredcoll · 4h ago
What is the "right" way to do it then?
Also probably most of us are stuck with whatever git*.com supports anyways...
globular-toast · 3h ago
Use `--update-refs`.
Think about the word "branch". If you have a linear sequence of commits that's one branch by definition. But you go and label the middle of that branch as branches too and then get annoyed when git, you know, does some branching there.
deredede · 3h ago
> It's not actually how code reviews are done by the people who wrote it.
What do you mean by that? Even if you do review by emailing patchsets those are still managed locally using branches, to my knowledge.
windward · 2h ago
No work, I set `rebase.updateRefs = true` years ago.
sunshowers · 8h ago
git rebase -i is a much worse experience than jj's autorebasing of descendants. For example, with git rebase -i you can't decide to do something else in the middle of the rebase — you're in that state until the rebase is completed or abandoned.
Merge conflicts are also significantly better with jj because they don't interrupt the rest of your flow.
> with git rebase -i you can't decide to do something else in the middle of the rebase
You absolutely can, to some degree. At any point in an interactive rebase you can just make your changes and do a normal `git commit` then do `git rebase --continue` along on your merry way. Unless you're talking about suspending the rebase and like switching branches, messing around, and then resuming the rebase, which is kind of a weird thing to do.
sunshowers · 7h ago
I do mean suspending the rebase and going to do something else, yes.
It's a weird thing to do in git, because the conditions that git creates makes it weird. It is completely natural with jj, because jj doesn't have any modal states at all. (This is one of the key reasons jj is both simpler and more powerful than git — no modal states.)
1718627440 · 1h ago
You can absolutely do that in git:
git checkout master
git rebase [whatever]
[rebasing stuff]
git tag rebasing
git checkout --detach master
[do random other stuff]
git tag todo
git checkout rebasing
[continue rebasing]
When you are not trying to modify the same branch you're rebasing, you can omit --detach and also git tag todo.
(To clarify, it has never occurred to me that I even want to do that, so I didn't knew how to do it. Yet I didn't even needed to consider a manual, it just follows naturally from the git user model even if it seams completely unidiomatic.)
frizlab · 6h ago
Modal states are actually good IMHO.
sunshowers · 6h ago
I feel like I just outlined a situation where something that's weird in git is completely natural in jj. Maybe I miscommunicated?
frizlab · 1h ago
I think you communicated well, probably I did not.
> suspending the rebase and going to do something else
This is what I find to be weird, personally. When doing a rebase, there’s no way I want to do something else in the middle of it, and having a modal state feels totally natural to me.
At first approach (I read a (very good) intro[1]; I did not try), it seems there’s a lot of new things to learn (for instance the revsets language), for a very minimal gain, so I’m (still) gonna pass on it. It feels like jj is solving a lot of problems that do not exist, or are mostly solved with worktrees.
That being said it’s true that everybody’s way of working is different, so I don’t know! Maybe jj will be picked up by the younger generation and it will become the new de facto standard. Time will tell…
Personally, the current VCS tool I’d like to try now instead of jj is fossil. It seems much more interesting as it promises to allow bypassing GitHub/other forge completely by being the full forge itself. In these days, having ownership of one’s data feels primordial to me.
I literally will have like 4 PRs in flight at once and have an octopus merge of all my separate PRs that I can then work on top of. JJ can rebase all 4 separate branches, the octopus merge, and the work on top of the octopus merge in a single command: jj rebase -d main.
If there are conflicts I can then resolve them whenever I want.
You have no idea what you are talking about if you think git’s interactive rebase holds a candle to what jj rebase can do.
doritosfan84 · 9h ago
I don’t think simplified model is even the best selling point, but it’s definitely up there. IMO one of the killer features that you absolutely cannot get in git is universal undo. For example, rebases can always be a little tedious and tricky no matter how experienced you are. Not only does jj make that whole process easier and safer to work through, but if you do still manage to get to a state where you just want to go back it’s literally just an undo away.
It's not a "simplified model". The jujutsu model is more capable, but also more generic and thus easier to use and re-use. It's just better.
landr0id · 7h ago
Honestly one of the biggest selling points of jujutsu for me is its `op log`. You can fuck up your repo in git and that's it -- you're screwed. Or you find some extreme magic on the internet that saves you.
With jj you just "jj op undo <operation_id_that_fucked_your_repo>" and you're fine.
Editing prior commits is also pretty easy, which in turn makes fixing merge conflicts pretty easy too.
frizlab · 6h ago
Like… reflog?
steveklabnik · 5h ago
Kinda!
jj has two kinds of these logs: the evolog and the op log.
The git reflog is based on, well, refs. Whenever a ref is updated, you get an entry. This log is per ref. That's HEAD, your branches, your tags, and your stash.
jj's evolog is sorta similar, but also different: it's a log, but per change (think commit in git). This means it is broader than per ref, as it includes not just commits that correspond to a ref, but all of them.
jj's oplog is a log per repository. This lets you do things like `jj undo`, which lets you get the entire repository, not just one ref or commit, back to the previous state, easily.
1718627440 · 55m ago
Yeah this doesn't make sense in git terms. A commit is immutable, it never changes so it doesn't have a history, it's just always there.
baq · 1h ago
no. reflog stores snapshots with descriptions; op log stores operations.
simonebrunozzi · 3h ago
Isn't it supposed to be called Ju-Jitsu, instead of Jujutsu?
You will be hard-pressed to find someone who stuck with it for a week and decided to go back to git. You will not find a lot of people who say they switched but just stayed out of inertia. Of course both of these do happen—nothing is perfect—but they are by far the exception. From my own personal anecadata, I have seen a 100% conversion rate from everyone who gave it a serious try.
I encourage you to let today be the day that you decide to try it out. It is far less effort to make the switch than you probably think it is: I was productive the same day I switched and within a week I had no remaining situations where I needed to fall back to git commands. You will quickly be more productive and you will find yourself amazed at how you ever got by without it.
Cognitive dissonance is a thing.
I don't have any productivity issues with git, like... at all. It's not like I spend an hour running git commands every day.
I can totally imagine that some people spend their day manipulating repos with git, and jj is better for them. But that's not my case, and git is already everywhere.
To me it sounds like telling me: "You HAVE TO move to bim, the better vim. It's very similar to vim, but different enough that you have to learn new stuff. But you will be infinitely more productive: when you start bim, you're already in edit mode, so you don't have to type i! And the auto-complete in Julia is objectively a lot better in bim!".
Sure, but typing "i" a few times more is really not a concern for me, and I don't use Julia. But if it's better for you, please enjoy bim!
This is where jj excels. Especially if you find yourself often doing large chunks of work between convenient checkpoints, but you still want to create commits as if this work was all done in tiny and discrete chunks. It's also very helpful if you're the kind of developer who makes lots of unrelated changes in a single coding session, and wants all those changes to be in parallel branches that can be reviewed and merged independently. I greatly prefer working with (and being) the kind of developer that puts out a large number of very tiny and easy to review PRs, eve. jj makes doing that a breeze.
There are lots of people for whom these things aren't important. I will be slightly judgmental and say I don't really enjoy working with them. They tend to write very large PRs that are difficult and time-consuming to review. And it's a frequent source of frustration for everyone when 95% of the work is uncontroversial but a merge is being held up because of legitimate concerns with an unrelated 5%. This is even worse when there's later work that builds upon it that can't happen until a merge (or that needs to be constantly rebased as the PR is improved).
This is not to say if you do this you’re a bad developer. There are plenty of great developers who don’t care about these things and still do great work. This is also not to say you can’t follow my preferred approach with git. I did it with git for a decade and a half.
A very, very large problem at five out of six companies I have worked at is casual code improvement and refactoring. Devs would say, "we will address that minor and unrelated thing in a separate PR" - one that never comes. At one company, a single PR could address unrelated fixes and it was encouraged to "take out the trash" on the code. Unrelated metrics added, logging improvement, or code simplified, or test robustness improved, etc. That company had vastly better code. Easier to read. Easier to maintain. Easier to observe. And easier to test.
That sort of workflow is ideal for making sure you've got a set of isolated commits each looking at a single subject so that when someone is reading through the history later they can quickly see where something was introduced or why, and jj is perfect for doing that because it makes crafting those commits so much easier.
It's very frustrating that what IMO is the inferior approach was producing better results in those teams!
When you are told to separate general code improvements to another PR, or worse, to not do them, and create a Jira task for them so they can be adequately prioritized, it just saps your will to do so. You just won't do any improvements that fall outside the scope of the feature, because even just thinking about the hoops you have to jump through to get work done is mentally draining.
As an example of another ‘unnecessary’ switch, Pip with venvs was also completely solving all my Python dependency problems. I just needed to copy paste a few lines from my README to create and populate the venv, and remember to run pip and Python from that venv. And run pip freeze after package installs or upgrades. No problem. But switching to uv was a huge life improvement. No more copy pasting (‘uv sync’ does everything and even that isn’t needed) nor remembering extra steps and everything is fast so I’m never waiting and forgetting what I was going to do.
I could’ve been GP talking about pip but still I would be missing out.
I still see people clinging to cvs because it works for them (netbsd why?), which I respect but don’t understand/believe.
Fish don’t know what water is.
So.. I’m gonna give jj a go and trust life will be better again.
Doesn't feel painful in git, but I'd like to see an example doing that with jj. Maybe I'll try.
> This is where jj excels. Especially if you find yourself often doing large chunks of work between convenient checkpoints, but you still want to create commits as if this work was all done in tiny and discrete chunks.
This is exactly how I like to use git. Sounds like I should be recommending jj to colleagues who struggle with this approach.
There definitely are lots of big issues with Git though. I dunno how many jj solves but it doesn't seem unreasonable to suggest people move to a better system.
Lack of gitattributes support precludes git-crypt and git-lfs usage or anything that needs filters; line ending settings will get ignored, making Windows interop a little less smooth; etc.
Also note that auxillary tooling, such as git-annex and git-bug, becomes second class, i.e. no oplog integration and they might mess up your log with internal-use commits and heads.
This one got fixed just a few days ago! https://github.com/jj-vcs/jj/pull/6728
Arguably a good thing - git's autocrlf setting causes way more issues than it solves. I highly recommend setting it to "input" (basically bans CRLF).
The biggest killer was performance. jj operations took several seconds for me, whereas git is instantaneous no matter how big the project. Maybe this is fixed now.
But also honestly I felt like there was a bit more mental burden to using jj. When I switched back to git, it was like a weight off my shoulders. Maybe that's just due to the decade of constant use though.
Reporting in. Doesn't mean I will not end up with jj eventually, but so far I always went back to git after a while.
For me it is the staging area and the workflow it allows. Most people hate it and love jj because it does away with it. That is just not me. I don't see the staging area as a hack that was necessary to overcome some superfluous technical limitations but as a workflow tool.
Could I change my ways? Sure. jj just did not provide enough benefit for me so far to do it, but we will see. I am still open to give jj another try some day.
I use a staging area with jj! I would surmise most jj users do too. It's just a real, honest-to-god commit in the repo instead of a special snowflake.
Since it's just a commit, all your tools work with it out of the box. And you don't need to stash changes when you jump around between branches.Because that's my workflow. I produce a few focused and semantically coherent commits that I'd like to apply to the codebase, where each keeps the codebase in a working state. I might be working on more than one thing at once, but I know which commit each set of changes should "live in."
An append-only log of commits is decidedly not something I want. Is jj amenable to this?
When I eventually figure it all out, I can tidy it all up into completely coherent, discrete, sequential commits that make it look like I knew exactly what I was doing from the start, then push to remote.
Conversely git staging around stashing was always an absolute, terrifying mystery to me. I could never remember what was stored where, and would consequently end up losing work, repeating work, and, most of all, just making massive commits that make no sense.
I was sold on jj from essentially the moment I found it, but I remain routinely amazed at how much more powerful (yet simple!) it is than I realized.
You now you can give stashs a commit message?
For me the staging area being a snowflake is the feature. It is a special temporary singleton commit if you will, but I can rely on its temporary and singleton nature. As that it should have a different interface from regular commits.
This sounds exactly like something a person evangelizing it would say.
I don't understand why anyone would say you have to use it. It does a very specific thing, namely finding the source of a regression between two commits. If you need it you'll know.
Does it work well with a classic merge workflow? I haven't worked that way (without rebasing) for a long time.
That's also useful for bisecting, as you can first find the feature that is buggy and then find the commit that introduced it.
It's a command that is needed rarely but there's no replacement for it in some situations.
It's nice, I really like it! I'll probably switch to it as my main VCS eventually. But it doesn't feel that important to me. Even though my main work involves quite a lot of annoying rebases which is where JJ really seems to shine.
I dunno I guess it's just that a) I've really mastered git and have a deeply-rooted workflow in it and b) despite my project involving annoying rebases, version control still isn't very high on the list of problems I have.
So yeah I'm basically bullish on JJ as a technology but I think movement from Git is inevitably gonna be slow and steady.
What really kept you from staying with it? It does seem like if your workflow involves a lot of nasty rebases you'd reap dividends from something like jj. I was also someone who'd mastered git (hell, I've written a git implementation) so I get having its patterns deeply ingrained.
There's nothing keeping me from switching except the activation energy cost. Almost every aspect of working in my area (Linux kernel) is painful so I'm constantly investing in tooling and workflow stuff. So usually I just don't feel like investing EVEN MORE in the area of tooling that's probably least painful of all.
So yeah this is still basically a recommendation for people to try JJ!
Now that I switched to jj (colocated with a get repo, very useful), I went back to using the CLI again for jj and I don't miss the graphical tools.
I also already use very good tooling for git, namely Magit. IMO Magit is a much better git frontend than Jujutsu. It guides you down the right path but doesn't take away any of the power of git at all. It's quite remarkable.
Maybe I should recommend jj to some of my colleagues, though. Trouble is I'm already on the hook for helping them with git, but I don't have the experience with jj.
This is kind of a poor take. By all means use what you prefer! But understanding git and knowing the "right" way to use it doesn't make jj obsolete.
I am (or was) a git expert. I’ve used it since pre-GitHub. I’ve written a git implementation. I know (or knew) the interface inside and out. I haven’t deleted and re-cloned a repo in as long as I can remember.
jj is still leagues better. Things I want to do and know how to do in git are dramatically faster and easier. They require less mental overhead. They’re less error prone. And I get superpowers with workflows that are super useful but wildly impractical in git.
This isn’t even really opinion at this point. Any task you can give me in git, I can with almost near certainty give you a shorter and more elegant alternative with jj that is an intuitive and obvious interaction with its core primitives.
A month ago our company split off a division. They needed to take a specific repo and sanitize out all of the parts that aren’t relevant to the new company, going back to the beginning of its commit history. You can do this with git. It wouldn’t be fun. I’d have to spend a lot of time reading the filter-branch manpage, and people have written countless wrappers of varying quality that try and make it a bit more ergonomic.
It took me like ten minutes to come up with:
Three dumb, simple commands that I already use every day: list some commits, copy file contents from one commit into another, and remove some commits from the tree.Not only was this more or less obvious to do, but it was exceedingly fast to perform on a pretty highly-trafficked repo due to not having to thrash around in the working directory with checkouts.
> Magit is a much better git frontend than Jujutsu. It guides you down the right path but doesn't take away any of the power of git at all.
jj is more powerful than git. It’s not simply a dumbed-down alternative. By having a more carefully chosen set of primitives that compose better, you gain a lot of abilities that are technically possible with git but never used in practice due to the complexity. The above is IMO a fantastic example of how and why.
And you can still do all the normal git things too.
Why? They’re sharing what they prefer in response to someone claiming that everyone who tries the other thing comes to prefer that. They’re offering a reply in context and not in the slightest saying everyone should follow what they do.
> By all means use what you prefer!
That’s exactly what they’re doing.
> But understanding git and knowing the "right" way to use it doesn't make jj obsolete.
Which they haven’t claimed at all.
You seem to be objecting to an argument which hasn’t been made.
I believe your perception is flawed because the people who just try it and throw it away don't tend to talk about it because it's not popular enough to warrant even a twitter comment. This is the first time I got the impulse to share this but only because you claimed a rather silly 100% conversion rate.
As it is, I go with whatever our clients require of us, and that isn't jj.
I’ve tried jj on three occasions and I always get confused by something and just bounce. It hasn’t clicked for me.
I’ve only tried it solo hobby projects. For which git is perfectly tolerable.
I have many many many complaints about git. But jj doesn’t move the needle for me.
Reading this post I am extremely annoyed. Almost every single section is “but more on that later”. It’s still far more complicated than it needs to be.
I also really really really hate the jj log rendering. The colors are a sea of barf. Bolding the leading character that represent uniqueness is stupid and adds noise. The username being second is dumb, I almost never care about that. And the bright neon green (empty)(no description set) is such bad spew. Kinda nit picky, but blech.
Jujutsu suffers the same thing Git suffers. Every god damn blog post that tries to explain how simple it is just my eyes gloss over and think “this is too complex for me to care”.
You can fully change/customise it. Sounds like you just don't like the default.
Most people use the default for most of their tools. It’s why Google pays Apple over $20 billion per year to be the default search engine. Because defaults matter.
> I’ve only tried it solo hobby projects.
If I can surmise a bit, this is probably why it's never felt "worth it". I'm guessing that for a hobby project you don't really care that much about crafting small, easily-digestible PRs or keeping a clean history. `git commit -a` every now and then is good enough, and that's entirely reasonable.
For team projects, jj becomes a much bigger deal. I find it invaluable for splitting up a day's worth of work into small, parallel, easily-reviewable changes. While working on one feature I might run across a dozen other things that should be fixed, improved, or otherwise changed. Bundling them into one giant PR is bad practice and causes reviews to take much longer.
Carving those up into single-purpose PRs that can be reviewed in seconds and tested independently is super helpful on a project with multiple teammates. But that's not something that really matters or that most people care to take the time do to in personal projects. Hell, it's something a lot of people punt on a lot in git due to the extra burden of doing so.
do you know about git rerere?
if yes - try jj.
I've used it and I like it but I couldn't find a good plugin for Neovim so I reverted back to my old more well-supported workflow.
I almost always have more changes in my repository that those which I want to include in the next commit. With git, I just add the changes I want. With jj (and svn), there’s not obvious way around it—you have to manually copy-paste changes outside of the repository before committing.
jj looks cool in general, I'd start with it if I were just going into this _version control_ thing but for most of us older folks, that doesn't provide enough motivation to change.
I still use Lazygit for the improved diffing, but, as long as you don't mind being in detached HEAD all the time, there's really no issue with doing that. JJ interoperates fine with git, but why would I use the arcane git commands when JJ will do the same thing much more straightforwardly?
Also, the ability to jump from branch to branch with all my uncommitted files traveling with me is a godsend. Now I can breeze between feature development, bug fixing, copy changing, etc just by editing the commit I want. If I want multiple AI agents working on that stuff, I just make a worktree and get on with it.
Not to mention that I am really liking the fact that I can describe changes (basically add commit messages) before I'm done with them, so I can see them in the tree.
JJ is just all around great.
I tend to do a bunch of work then split into small, bite-sized revisions. Often I split something out to a parallel revision (e.g., a separate branch) if it’s an independent thread of work like a bugfix elsewhere or a documentation fix. This is an obvious one-liner in jj but a bunch of annoying branch-switching and stashing in git.
You can also use a `git add`-style workflow. Create a new revision with `jj new`. Do it again. Make your changes, then `jj squash -i/--interactive` to select the bits you want to include. Keep making changes and squashing into the previous commit you’re building up until you’re happy. Conceptually just think of @ (the current revision) and @- (the previous revision) as the working copy and the staged copy, respectively.
A decent mental model is that the top-most commit isn't generally going to get pushed up anywhere. It's like your working copy but also you get stashing "for free" (change back to main to make a new commit? All the WIP stuff stays on that branch instead of being carried over to main!)
Where this gets extra sticky for me is tooling which refuses to distinguish repo wide config vs a local only version. VSCode being a huge offender where there is only a ‘launch.json’ and no ‘launch.local.json’ suitable for per host customization (eg maybe I am already running something on port 8888, so I need to map it to 9000, that does not mean a quirk of my environment should be committed).
I am a black kettle here as I frequently commit individual lines amongst a sea of changes, but I do appreciate the theoretical stance of jj.
(though for debug printfs in particular, the Right Thing is proper logging with log levels, but I myself love printf debugging and so sometimes don't do that either. Which is why carrying local patches is nice.)
It's amazing how quickly I forgot about the commit vs. amend papercut.
[1]: https://codeberg.org/jcdickinson/nix/src/branch/main/home/co...
Is it just a git frontend for people who are confused by git?
It says it abstracts the backend, but it's not clear how something so git-influenced will have abstractions that work with something like a centralized system like Perforce or Piper that has auto-increment numeric commits.
Some of the design decisions are also not great. Working copy as commit means you have no quality control. The whole point of a commit is that... you commit it. So now you need a separate repo or filter to remove the worthless changes from the ones that you actually intend to commit. Bad commits polluting git histories is already a big problem.
The biggest problems with git IMO are it scales poorly and it encourages commit pollution. Presumably jj isn't trying to tackle those problems, which is totally fine. But I am confused about which problems it is tackling. That's why I'm wondering whether it's just a frontend that the author finds more to their liking.
Its Piper backend honestly works better than the Git backend. Which isn't a knock on the Git backend, but the impedance mismatch is worse there.
> Some of the design decisions are also not great. Working copy as commit means you have no quality control. The whole point of a commit is that... you commit it. So now you need a separate repo or filter to remove the worthless changes from the ones that you actually intend to commit. Bad commits polluting git histories is already a big problem.
Sorry, but saying this implies you haven't tried it. :-)
Jujutsu commits aren't equivalent to Git commits. They're implemented with a mixture of Git commits and the Git working tree, yes (if you use the Git backend!), but when you see 'commit', you should read 'named diff'.
Jujutsu also has a notion of immutable commits, by default meaning (roughly) commits which have been pushed upstream.
You can and should rewrite the un-pushed commits to clean up history prior to pushing changes upstream. jj makes that much MUCH easier than rebases and history edits could ever be with git. Most of my nontrivial jj work involves at least three or four commits at some point, everything from experiments to documentation branches, which with git I would have needed to awkwardly fit into stash or inconvenient throwaway branches.
This makes sense to me as a longtime Mercurial user. In short, by default, when you push a commit, it becomes public and therefore immutable [1].
Other awesome Mercurial features that appear in JJ are revsets [2], filesets [3] and templates [4]. Looking forward to giving JJ a try.
[1]: https://wiki.mercurial-scm.org/ChangesetEvolution
[2]: https://jj-vcs.github.io/jj/latest/revsets/
[3]: https://jj-vcs.github.io/jj/latest/filesets/
[4]: https://jj-vcs.github.io/jj/latest/templates/
> You can and should rewrite the un-pushed commits to clean up history prior to pushing changes upstream.
My concern isn't just about pushing upstream. What I'm saying is that the typical change to a file shouldn't be committed to my local repo until I indicate that it's ready. The FAQ suggests this isn't possible and that you should work around it by using a separate branch and then merge into your target branch, which is a pretty ugly workflow.
Their GitHub branches are a mess of auto-generated strings, which suggests to me that this problem isn't just an abstract concern but is a form of technical debt that the jj devs are currently piling up.
jj doesn't have the concept of a non-committed file.
instead you don't track the HEAD as the tip of the branch (@ in jj), you track HEAD^ (jj: @-) or something earlier and since jj rebase isn't dumb as a rock you can relatively easily keep the working set of changes (as in, multiple commits/WIP branches; not necessarily the index or the working copy) on top of what you are pushing and squash or advance the branch (bookmark).
I'm reiterating because it's a very important and super confusing for advanced git users: there is no uncommitted state in jj and yes, it does make sense, but you need to stop thinking in git.
How does that work in practice, is there a daemon running constantly and monitoring? How does that interact with other users and changes from other computers?
I don't see a problem with secrets TBH. .gitignore is respected if that's what you have in mind. Otherwise store your secrets out of tree (you probably should with git too, anyway.)
Yes, it's "committed", but that doesn't mean all that much. I have the fsmonitor feature enabled that continually watches my repos as I edit files in them. This means that over the course of a coding session, the revision I'm working on has probably pointed at dozens or more ephemeral underlying git commits. Those are only kept around for the purpose of the evolution log, which lets me look back at my edit history throughout the day even without having interacted with the repo.
When you're ready to "finalize" your work, you have two common workflow options: you can `split` the out parts you want to keep as one consistent commit, which dumps the rest in a subsequent revision. Spiritually this is equivalent to `git add -p`. Alternatively, you can create an empty "staging" revision before your "working copy" revision and `jj squash -i` pieces you're happy with into there. In practice, many people use both. I generally do the former for new work, and the latter to make changes to earlier work.
Thinking that `git add -p` was absolutely a deal-breaker is the main reason I passed over jj for so long. I care deeply about maintaining a clean commit history with small, isolated, and individually-tested changes. I thought jj would make that harder due to not having a staging area. I was wrong. It is actually far easier to be principled about your commit history with the tools that jj gives you.
You're thinking in very git-specific terms. JJ is just different in many ways so such comparison doesn't work well. I didn't get it from explanations before, but trying it in practice, it's a non-issue. It may be easier to just give it a go.
Since you brought this up, I've noticed some people seem to work this way but I've never found anyone to ask why they do this.
I get the idea behind clean, readable git logs with nice consistent messages, but isn't that what rebase/amend is for?
To me, the status of my local git log is mostly irrelevant, the thing that really matters is the commit(s) I submit for merging.
Also, probably for related reasons, I've never truly understood why git has this whole separate staging concept...
I also use this to compare the changes to the commit messages. Ideally the changes follow from the message, not the other way around.
While a VCS is also useful for adding a time-axis to code, for me the main selling point is, that it's adding causality to the code. You can ask 'Why is this code how it is?'. (Of course causality is a side-effect of time.) When you don't curate the commits you completely loose this feature. (You can still ask, but the answers will be confusing and need way more work, which for me amounts to manually comparing commits to grasp the big picture and intention.)
> rebase/amend
When you messed up, you can of course change commits, but this has a danger of creating a version that was never there or mixing history, for example combining a single physical change, that are multiple logical changes. In order to test that the code at least works I use ```git rebase --exec="make -C build distcheck"```, not sure if that is common.
I also don't use MS GitHub -style merges, I find them inferior. (I also don't use MS GitHub, but that's for another reason.) I think they try to make merges the actual unit of change, which is stupid, because that is what a commit is for. To me merges are about semantic grouping of commits, i.e. maintaining a tree of commits representing the logical evolution of the code.
Yes the staging area is exactly designed for this approach and I don't like people telling me I'm holding it wrong and don't need it. To use a metaphor, I think the staging area is like my desk were I'm producing stuff (I'm not producing code, I'm producing changes). Committing is about having produced a complete opus and then cleaning the desk. Cleaning isn't seen as annoying, it is important for the mind to process completing/validating and then starting afresh. Stashing is in this metaphor about switching to an alternate desk. This is different from putting the opus aside and having also a clean desk. (I think that is what Jujutsu wants you to do?) Having the staging-area/stash different from commits is a feature, the fact that the stash is the same storage-wise is an implementation detail.
I was extremely hostile to this aspect of jj too, but I've settled on a workflow where @ (i.e. the change/commit referring to the working copy) is always the same, named ".WIP: …", and as such is clearly never supposed to break out of the local system, and can never accidentally get pushed down the log. (..my jj workflow is just blatantly copying my git workflow (with the same-name shell aliases/helpers even), but even with said git emulation I'd say it's nicer than git itself)
Still may be weird/undesired to have the local repo preserve the changes (especially annoying if it happens to find an unignored build artifact, though there is a size limit for automatic tracking by default), but it's also rather neat that you can dig out your old deleted printf debugging or whatnot later on if you wanted to.
You might be able to configure JJ to also block commits with certain descriptions as well, which might be useful in your case.
Some push blocker for certain commit name patterns would make sense to look into.
jj is a version control system. It is backend-agnostic. The most common backend is a git one, because git is so popular. This allows you to use jj on a git repository, allowing for individuals to adopt it without forcing their teammates to.
> Is it just a git frontend for people who are confused by git?
I used git since before github existed. I considered myself a git lover before I found jj. I will not be going back to git.
The thing is, jj is both simpler and more powerful than git, at the same time. People who are confused by git may like its simplicity, but I like its power.
> It says it abstracts the backend, but it's not clear how something so git-influenced will have abstractions that work with something like a centralized system like Perforce or Piper that has auto-increment numeric commits.
You already got some replies on this one, so I'll leave that to them :)
> Some of the design decisions are also not great. Working copy as commit means you have no quality control. The whole point of a commit is that... you commit it. So now you need a separate repo or filter to remove the worthless changes from the ones that you actually intend to commit. Bad commits polluting git histories is already a big problem.
This is an understandable misunderstanding. The right way to think of it is "the index is also a commit." A common way of working with jj is to do something like this:
Imagine I am working on adding some feature x to my codebase. I'll first make a change, and give it a description:
Now I will make a new empty change on top of that: Now, @- is the change that I intend to push publicly, but @ is my index. Say I add foo.rs: Now, when I run `jj status`, jj takes a snapshot, and it now lives in @: We can see the diff here with `jj diff`: So, let's say I'm happy with its contents. I want to stage it into my final commit. I can do this with `jj squash`, which by default takes all the diff of @ and puts it into @-: Now that change is in @- instead of @. we can see that by passing -r (for revision) to `jj diff`: I don't have to move the whole change; I can do the same thing as git add -p by using jj squash -i, and only move the portions of the diff.What's the advantage here? Well, because the index is just a commit, I can use any tools that I use on commits on the index. There's nothing like `git reset` needing to have `--hard` vs `--soft` vs `--mixed` to deal with index behavior: everything is in a commit, so everything acts consistently.
jj makes it very trivial to carve up commits into exactly what you want. It is far easier and more powerful than using the index for the same purpose.
> The biggest problems with git IMO are it scales poorly and it encourages commit pollution. Presumably jj isn't trying to tackle those problems, which is totally fine. But I am confused about which problems it is tackling. That's why I'm wondering whether it's just a frontend that the author finds more to their liking.
IMHO, commit pollution is more due to the pull request workflow than git itself, though I do think that git doesn't do that much to help you. jj can help with this kind of thing, but also on some level, it can only do so much.
Rebases also don't play nicely with stacked branches. Branches based off your original changes don't get rewritten to be on top of the rebased change, so now multiple rebases are in your future.
It turns out that basically none of the rebase friction most people experience is necessary in practice. Rebases can be automatic, implicit, and conflicts can be fixed at your leisure and in whatever order that you feel is best.
jj also has a ton of other powers, but you asked about rebase :)
You can use git commit --fixup to record what you want to change in an earlier commit.
> start over from scratch
rerere
> Branches based off your original changes don't get rewritten
As written elsewhere: git rebase --update-refs. If you want to do it manually git rebase --onto.
The working copy doesn't get automatically pushed to GitHub or anything crazy like this seems to be implying. You review/curate your commit when you give it a description.
Maybe what I need to do is do a demo so people can see and ask questions.
What I find isn't that common git operations are easier in jujutsu. They're not; sometimes they're slightly harder, due to the impedance mismatch with the git backend.
Rather, what git makes easier are operations that are next to impossible — or at least highly inconvenient — in git, and which therefore next to no-one does. That makes it harder to explain, because you're telling them there's this great new workflow that does stuff that... they don't think they need (they have workarounds), and the notion of which triggers their ick reflex if they're good at programming.
If there are really common use-cases where git is annoying and jj is great, it shouldn't be that hard to explain, should it? If you can say "remember how in the last few days you struggled with this? Jujutsu solves it", then I'm happy to try.
If your argument starts with "imagine you are in a team that looks like X (but your team does not), with a project that looks like Y (but your project does not), and now imagine that you need to do this thing that you have never done before...", then maybe I actually don't need jj?
It is more like "you should use jj because then you won't have a lot of problem with git that you'd need git to solve"
> it shouldn't be that hard to explain, should it?
It is not. There are plenty of example on this page. For me the biggest one is stacked PR, jj makes it trivial since it tracks the change sets and not commit ids (which are immutable). So you can work on any level of the stacked PR independently, once you're done run "jj git push -r '(trunk()..@ | @::)'" and it will update all the remote branches accordingly. Another feature that works great with stacked PR is that you don't need to solve conflicts right away. You will see a marker in the "jj log" and you can solve it later down the road.
Also another great feature is the operation log, you can just rewind your actions. F'd up a conflict resolution? Just go "jj op undo". That goes for everything, including file changes and rebases. Want to go back the state it was 15 min ago because you didn't like what you did? Merge to the wrong place? "jj op undo"
Adding to that there are hundreds paper cuts that jj fixes, like:
* Simpler mental model for local change, no git stash/add necessary.
* Simpler commit process, you can just work and use "jj describe" whenever where git forces you to write message before creating commit (again because commits are immutable).
* Starting a work is much easier, I can just go "jj new" away without caring about detached head. Nowdays I just use branches (jj bookmarks) for git compatibility reasons.
* Revsets are amazing, much more powerful and expressive than git logs, and since the UX is more consistent you can always work with set of rules that expects a revset with "-r".
Ofc, you can do all of that with git, but it just works better, easier and more consistent with jj.
https://v5.chriskrycho.com/essays/jj-init/ https://v5.chriskrycho.com/journal/jujutsu-megamerges-and-jj... https://ofcr.se/jujutsu-merge-workflow
But that was ten years ago. Now git is kind of hard-wired in my brain. By and large, it works well enough.
It's not really clear to me that Jujutsu offers a significant enough of a benefit to spend the time re-wiring my brain, never mind dealing with the initial setup (e.g. the unreadable colours, setting up some scripts/aliases for things I like).
Some of these tools do just work. E.g. nvm seems to just work and is much nicer than raw node installs.
https://github.com/idursun/jjui
You have to choose what you want to break. Once you start pulling on one thread lots of stuff start to unravel. It's really not a simple matter of "choosing a better theme".
I've spent a long time looking at this, and my conclusion is that there is no safe default that will work for everyone, other than bold/reverse and 256/true colours and setting both the foreground and background.
I think you'd be a great candidate for jj's ability to customize colors.
That I need to spend a bunch of time setting all of this up (among other things) is exactly how this thread started.
https://lottia.net/notes/0013-git-jujutsu-miniature.html
I've used jj for a few weeks, but switched back to git. I'm fluent enough with Git so I never struggle any more. jj mostly felt nice, but the added value was not enough to replace years of expertise.
I occasionally use -r, too, but most of the time it's better to
My quick summary is that in one case he's try to avoid "extra" commits and in another case he's trying to re-order some commits. In my usual work flow, both of those problems would be handled by the git-rebase-squash I do after the feature works.
The thing I'm running into right now is that I should really learn the revset language, so that I don't have to constantly copy paste ids from jj log
Been using jj without significant issues for about a month and been super happy to be comfortable using the cli and slowly ramping up to more complicated operations.
The documentation still assumes a lot of inherent knowledge which sometimes makes it a little difficult. I love seeing blog posts like these and hopefully some more in depth resources will appear over time. Steve's guide is good, but there are still gaps for me :).
Next I want to learn some more revset language and become a bit more fluent with rebase operations. I love the more simplified cli, conflict resolution and op log!
1. The mental model is too complex: rebase or merge, detaching head, etc. A good UI can hide away some of this uglyness. It sounds like jj helps here.
2. Any source control in a terminal is just horrible, since you have no view of the state you're working with. I honestly never fully understood why developers have such religious elitism for the command line. Where would you draw the line on something having a complex enough state to need a UI?
The change to git that I'm hoping for is a GUI on top of a simpler wrapper like Jujutsu.
There's other TUIs and GUIs, but jjui is by far the best.
https://github.com/idursun/jjui
There is also lazyjj as an interactive UI.
Note for other readers: jj also has a literal `jj absorb` command. That one does what you'd expect from mercurial, i.e. moves diffs from the current commit into the most recent ancestral commit where that file was changed.
You name them with bookmarks which are sort of like branch names except they don't follow along automatically as you make new revisions. You can point an existing bookmark at a later revision when you're ready to push new changes.
https://github.com/idursun/jjui
And, you're right, it's powers largely come from the underlying jj - it mostly just runs jj commands behind the scenes, parses the output, and displays it. But it's all so beautiful, seamless etc..
I can't wait to really dig into the big additions released yesterday in v0.9.0 - themes, vim-like leader key shortcuts, other shortcuts etc...
I’ve really been loving these two neovim JJ plugins
For splitting commits: https://github.com/julienvincent/hunk.nvim
For resolving conflicts: https://github.com/rafikdraoui/jj-diffconflicts
I’m used to sorting through the changes I have made and making separate commits for each unrelated one (eg bug fix for several files, some WIP work, other parts actually done and ready to be “shipped”/reviewed)?
Do I need a better workflow?
Should I just be using the command line?
Rebasing can be done instantly (reorder commits, fixups, squash), I can extract and reverse custom patches from previous commits, I can partially stage files, undo.
One think I like is that it explains what it does in terms of git commands. So it helps a little to have a good git mental model, and as such I guess it doesn't solve the same thing as jj.
I reluctantly moved to git from mercurial when host-after-host dropped mercurial support. git is a user-hostile POS that I never got used to. A thousand needless complications including nightmare merges, and an inscrutable command interface.
JJ eliminates all the git bullshit. Some incredible features:
* Merges never fail as conflicts are a first class feature. You can come back and resolve them at any time.
* You can reset the state of the repo to any particular point in history as JJ maintains a complete operations history.
* You can insert new changes anywhere and deal with the repercussions at leisure.
I started with Steve's tutorial but found it a bit wordy. So I used Gemini to produce a simple guide for me with: "You are an expert at DVCS systems like JJ. Act as my guide for the system and answer my questions with simple cookbook-style recipes."
I have been using JJ for a month and am never going back to git. It is such a pleasure to work with a DVCS that you don't have to fight at every turn.
I have found it incredibly helpful when using an agent. It's a great case for the "staging-area" style workflow. I `jj new` an empty revision where the LLM can go hog-wild. When I'm generally happy with the LLM's work, I make a new revision. Then I ask it to make some improvements. Sometimes I have to discard those. No problem, I just `jj abandon` the whole thing. Sometimes I like a few pieces; I `jj squash -i` those into the parent revision with the bulk of the work and abandon the rest. And then I repeat until I'm happy with the overall output.
It's also super useful when I need to go fix something in an earlier (but still unmerged) revision. `jj new {id}` creates a new revision whose parent is the older change, even if it already has children. Again, I let the agent loose. When I'm happy, I `jj squash -i` what I want and the fixes are not only incorporated back into the parent but then propagated automatically down through the rest of its children through automatic rebasing.
To be clear where it ties to this post: it makes git far more convenient with nearly 0 learning curve.
And there's toppling built around jj, such as jjui https://github.com/idursun/jjui
I tried twice to switch for a project, both times came back to git. But then, I am still not fully grokking the "why bother", and suspect that if I had a UI it would be both less friction, and more understanding.
https://github.com/idursun/jjui
Is there a good explanation of Jujutsu without referring to git? May be with some pictures? Am I the only one bad with memorizing SHAs when reading? Also, does it work with other people? Would it work in repo with 5 contributors? 20? 500?
EDIT: I am looking for tutorials with explanation on how JJ works, preferably without referring to Git, and even more preferably with some visual explanations, which, there are plenty for Git. It seems I am not very good in reading text trees. It is my issue, not yours, but may be you know something which would help.
> Am I the only one bad with memorizing SHAs when reading?
I haven't ever needed to memorize a SHA or change ID with jj. What are you referring to?
> Also, does it work with other people? Would it work in repo with 5 contributors? 20? 500?
I have used jj for two years on teams without anyone else needing to be aware. Other teammates that I've convinced to give it a shot have switched, and we all work together happily alongside the git users. If anything, it's easier to work with others when they switch to jj because you no longer have to worry about rewriting history of branches that have been pushed. If you have a branch that multiple people are working on, that's bad form in git but it's just another day with jujutsu.
sjenfidb skeifixu
These could be referred to as sj and sk. And their associated git commit hash might be
2748dn49 48jdj40r
Would be 2 and 4
Again, the output highlights those unique prefixes.
But there's also jjui, which is an incredible TUI for jj. Makes everything even simpler and efficient than it already was.
https://github.com/idursun/jjui
A snippet from the intro:
"At the time of writing, most Jujutsu tutorials are targeted at experienced Git users, teaching them how to transfer their existing Git skills over to Jujutsu. This blog post is my attempt to fill the void of beginner learning material for Jujutsu."
There can and will be, but at this stage in the project's life, git familiarity can be assumed.
> Am I the only one bad with memorizing SHAs when reading?
Nope! The CLI has nice syntax highlighting to show you the shortest valid prefix, so for example, right now I have something that looks like
but the initial l is in purple, while the zrvnkxl is in grey. This means I can just use the l when referring to the change. That can be harder to demonstrate in a blog post, which can't know how to highlight this, and so often they have no highlighting.> Also, does it work with other people? Would it work in repo with 5 contributors? 20? 500?
Yes. Because it's backed by a git repo, nobody else needs to know you're using jj. Everyone can use the tool they choose.
This is such a simple UX feature that I have ended up using all the time after I switched to jj a few months back.
One example is a series of dependent PRs. This is excruciating in git if you ever need to make a fix to an earlier PR because you have to manually rebase every subsequent change. It’s trivial in jj: you either fix the revision directly or you insert a new revision inbetween. The subsequent revisions are updated automatically. You don’t even have to think.
Another is splitting work into parallel branches. So often when I’m working on one area I end up making unrelated tweaks and improvements as I go. Pulling these out into their own branches is painful in git, so people just make omnibus branches that include a handful of unrelated work alongside the main task. In jj it’s basically zero work to split out the unrelated stuff onto a new branch off main, and it doesn’t require you to task-switch to a new branch. So I end up making lots of one-line PRs that are trivial to review whenever I’m doing deeper work.
Spreading the word about `git rebase --update-refs` that will automatically update any branches that point to commits along the path (very useful with stacked branches). It is less convenient than what jujutsu offers (you need to know the branches to update, where jujutsu automatically updates any dependency), but still a very useful if you don't want to or can't switch to another tool.
How do you make sure, that a commit isn't changed under you, because someone thought, it would be a good idea to change an earlier revision? I think having immutable commits including all the previous history is a feature, not a bug.
What do you mean by this? You can do an interactive rebase in git as well. The real issue is non-trivial merge conflicts which is going to be an issue no matter you use.
It is essentially zero work in jj. I `jj edit` the revision in question, make the change, and `jj push`.
Arbitrarily-complicated rebases just happen automatically with jujutsu. Inserting, moving around, and directly editing commits are first-class operations, and descendants automatically rewrite themselves in-place to incorporate changes made to their parents. Rebase conflicts are also first-class citizens so they don't block you and force you to deal with them right now or in any particular order. Having to rebase seven related conflicts one-after-another is no longer a thing, nor is realizing you fucked something up halfway through and having to restart.
Coming from git it honestly feels like magic even if it strictly isn't. It genuinely hard to understand how much unnecessary toil git makes you put up with on a day to day basis until you suddenly don't need to deal with it any more.
You can also just enable that feature (rerere).
Also probably most of us are stuck with whatever git*.com supports anyways...
Think about the word "branch". If you have a linear sequence of commits that's one branch by definition. But you go and label the middle of that branch as branches too and then get annoyed when git, you know, does some branching there.
What do you mean by that? Even if you do review by emailing patchsets those are still managed locally using branches, to my knowledge.
Merge conflicts are also significantly better with jj because they don't interrupt the rest of your flow.
And most importantly, the two features work together to create something greater than the sum of its parts. See my testimonial (first one) at https://jj-vcs.github.io/jj/latest/testimonials/#what-the-us...
You absolutely can, to some degree. At any point in an interactive rebase you can just make your changes and do a normal `git commit` then do `git rebase --continue` along on your merry way. Unless you're talking about suspending the rebase and like switching branches, messing around, and then resuming the rebase, which is kind of a weird thing to do.
It's a weird thing to do in git, because the conditions that git creates makes it weird. It is completely natural with jj, because jj doesn't have any modal states at all. (This is one of the key reasons jj is both simpler and more powerful than git — no modal states.)
(To clarify, it has never occurred to me that I even want to do that, so I didn't knew how to do it. Yet I didn't even needed to consider a manual, it just follows naturally from the git user model even if it seams completely unidiomatic.)
> suspending the rebase and going to do something else
This is what I find to be weird, personally. When doing a rebase, there’s no way I want to do something else in the middle of it, and having a modal state feels totally natural to me.
At first approach (I read a (very good) intro[1]; I did not try), it seems there’s a lot of new things to learn (for instance the revsets language), for a very minimal gain, so I’m (still) gonna pass on it. It feels like jj is solving a lot of problems that do not exist, or are mostly solved with worktrees.
That being said it’s true that everybody’s way of working is different, so I don’t know! Maybe jj will be picked up by the younger generation and it will become the new de facto standard. Time will tell…
Personally, the current VCS tool I’d like to try now instead of jj is fossil. It seems much more interesting as it promises to allow bypassing GitHub/other forge completely by being the full forge itself. In these days, having ownership of one’s data feels primordial to me.
[1] https://ofcr.se/jujutsu-merge-workflow
https://ofcr.se/jujutsu-merge-workflow/
I literally will have like 4 PRs in flight at once and have an octopus merge of all my separate PRs that I can then work on top of. JJ can rebase all 4 separate branches, the octopus merge, and the work on top of the octopus merge in a single command: jj rebase -d main.
If there are conflicts I can then resolve them whenever I want.
You have no idea what you are talking about if you think git’s interactive rebase holds a candle to what jj rebase can do.
I’d give [1] a read if you’re interested.
1. https://zerowidth.com/2025/what-ive-learned-from-jj/#commits...
With jj you just "jj op undo <operation_id_that_fucked_your_repo>" and you're fine.
Editing prior commits is also pretty easy, which in turn makes fixing merge conflicts pretty easy too.
jj has two kinds of these logs: the evolog and the op log.
The git reflog is based on, well, refs. Whenever a ref is updated, you get an entry. This log is per ref. That's HEAD, your branches, your tags, and your stash.
jj's evolog is sorta similar, but also different: it's a log, but per change (think commit in git). This means it is broader than per ref, as it includes not just commits that correspond to a ref, but all of them.
jj's oplog is a log per repository. This lets you do things like `jj undo`, which lets you get the entire repository, not just one ref or commit, back to the previous state, easily.