I've been testing out Jujutsu this weekend and this will come in handy. I still need to wrap my head around the different overflow and this might make it easier.
I started using jj a few months ago. I had used Fig previously when I was at Google, but then spent over 2.5 years just using git, and using jj was like riding a bike. It's more or less exactly what I want it to be and I use it as much as I can.
That said, here are some of my pain points (despite all of which I do still prefer jj to git):
* No GitHub PR sync for stacks. Managing stacked diffs locally is great, but (a better version of) Sapling's PR syncing would be a huge value add. This is somewhat of a pain point for me directly, but even more so a weakness when I've tried to evangelize jj internally as a viable "stacked diff" solution (e.g. to be blessed by our eng tools team). Someone familiar and comfortable with Sapling (or just skeptical of jj) can easily point to this feature gap in a way that pretty much ends the conversation.
* I wish the native backend supported pushing/pulling changes. I want to be able to switch between working from my laptop and my workstation, and doing that through pushing to and pulling from GitHub is obviously lossy of jj history. Manually copying the .jj directory seems to work if I'm careful and do everything correctly (i.e. make sure both clones have equivalent working copy state when I copy .jj), but this feels brittle and it's easy to mess up.
* If you forget to start a new rev before you (or your LLM) touch the repo, it's a little bit of a pain to go back and split the changes into a new rev.
* Some of the more mature repos I've worked with have tooling/scripts/tests/etc. that seem to look for or rely on the presence of .git (perhaps indirectly). Perhaps I could have gotten around this with colocated repos (i.e. `git clone` and then `jj git init --colocate`), but in at least one case I just gave up on using jj for a given repo and just made a separate git clone. I'm not sure this is really jj's fault so much as a practical compatibility gap with git (again, possibly entirely solved by using `--colocate`).
FWIW, I think the fact that you need client support for this is a bizarre shortcoming of GitHub.
If GitHub just let you review individual commits like Gerrit does, the concept of "PR stacking" would be unnecessary (as it is with Gerrit).
The model of "a PR is just a blob of changes" is a weird baby's toy version of a code review tool IMO!
It admittedly doesn't have a lot of polish, but I do use it regularly and I'd be happy to help anybody who is interested in using it.
I ~hate how ADO handles individual commits (comment left on a single commit is hidden), but I do like the update tracking on a PR as it allows cleaning up the commit tree seamlessly without losing history or comment traceability (less you remove/move files).
All we need is GitHub to support a `Depends on: #123` annotation which would hide commits already in #123 and not let you merge until #123 is merged.
You can get an approximation by having the PR target the branch used by #123.
There's also no native UI support for it in GitHub -- I'd expect to have a navigation element for stacked PRs like in Gerrit.
IIUC the typical model is that people just upload new commits like "respond to review comments" and then eventually squash the whole PR into one commit when it's ready.
So basically you are just giving up on the idea that the commit history is part of the artifact you're working on.
Still, you're right that it makes sense to build tools to work around GitHub since there's always gonna be cases where you can't avoid using it.
Can you explain this point in detail? I've been using jj and doing stacked PRs on GitLab using `jj git push --all`. I haven't used Sapling so I'm not familiar with it's way of doing stacked PR and I'm really just curious what do you miss from it.
0.29.0 is working towards support here. It's only the first step as far as I understand (not following too closely): https://github.com/jj-vcs/jj/releases/tag/v0.29.0 git.write-change-id-header
> If you forget to start a new rev before you (or your LLM) touch the repo, it's a little bit of a pain to go back and split the changes into a new rev.
This is my biggest pain point at the moment, yes. I think it could be partially solved by better documentation already. I contributed a bit in the past but haven't had the time to look at this bit yet.
There might be some issues with jj workspaces though if you have the habit of using git worktrees as the colocated repo is not there anymore.
This has been my experience as well! I haven't had success conveying that to my peers at work (or outside), but your comment resonates with me deeply.
> If you forget to start a new rev before you (or your LLM) touch the repo, it's a little bit of a pain to go back and split the changes into a new rev.
This is one area where I broke my Fig habits and just switched to a the [Squash workflow](https://steveklabnik.github.io/jujutsu-tutorial/real-world-w...): I basically forbid myself from use `jj edit` and have been pretty successful in getting that to stick in my brain. Instead, I use `jj new` to switch contexts (and then `jj commit`, `jj squash`, or `jj absorb` depending on what I'm trying to achieve).
> Some of the more mature repos I've worked with have tooling/scripts/tests/etc. that seem to look for or rely on the presence of .git (perhaps indirectly).
Yeah, I've noticed this too. I find that the most common root cause is usually something that wants to run a lint tool only against the files that you've changed so it does something to the effect of `git diff --name-only $(git merge-base HEAD origin/main)`. I've also noticed that some precommit hooks have been working better since I enabled the `git.subprocess` option (which is on by default as of recently, I believe).
I've written about JJ[0] when I was starting to use it, and now, a couple months in, it's become an indispensable part of my workflow. Git really does feel clunky now (even though I never had major problems back when using it), whenever I see it used - with jj being compatible with it, fortunately I don't have to ever use it myself anymore.
Historically I never cared much about my git history (and always squashed PRs) - now I find myself occasionally using empty changes with good descriptions to just write out a sort of todo-list on my branch (kinda CDD, as in Change-Driven Development :) ), and it's overall much cleaner.
I've always used a ton of stashes with git for various experiments and in-progress work, now that's just normal local-only jj changes. Also solves the very unpleasant problem of rebasing stashes.
If you're reading this comment section thinking about whether it's worth trying jj out, I would strongly suggest you give it a go!
[0]: https://kubamartin.com/posts/introduction-to-the-jujutsu-vcs...
With so much VC money chasing so many established niches (eg bun -> node), and so much potential growth for jj - especially considering it has so much dev interest, and has the advantage of not requiring lock-in [a jj repo can be converted to a regular git repo].
JJ uses the git binary and maintains a git repository. In co-located mode, the jj working copy is a git working copy. If you use regular git tooling it might be a bit uncertain why you're in detached head mode, but all read-only tooling will work without any problems and if you write to the repo jj will do its best to absorb your manual changes cleanly.
You could also write e.g. a Mercurial backend to Jujutsu and it would operate similarly, but using revlog storage vs Git's content addressed store.
> JJ uses the git binary
That's a relatively recent phenomenon. The git binary is used only to fetch and push objects. Previously we used libgit2. Why do this? Because replicating all of git and OpenSSH's feature support for every little random auth flow/crypto algorithm/hardware token/whatever is a battle that can't be won. It's ultimately smarter to just have an uglier interface between the two parts (invoking a command line vs using an API) to make the end-user experience significantly better.
When working with Git objects, jj uses the Gitoxide rust library to natively manage the Git store and upstream Git is not used at all.
so to answer your question, jj allows you to do the same set of things you can do with git, but with (arguably) much better UI/UX.
https://matklad.github.io/2024/12/13/majjit-lsp.html
With that yearning in mind, jjui is the jj TUI I like best so far having tried them all. It's snappy, has useful and intuitive keybinds and presents them well, and good stability so far. Doesn't quite seem to like it if I resize the surrounding zellij/terminal pane while it's open though.
Lazyjj was a little laggy and crash-prone for me on all of the same repos, and jj-fzf is a tragic pile of bash so I can't recommend ever using that.
That said, I switched to jj after using GitUp https://gitup.co/ for many years, and I was pretty surprised not to really miss it when using the jj CLI. The things I liked most about GitUp are all covered by the jj CLI pretty well:
- Undo
- Interactive staging (well, in jj the analogue is interactive split with jj split -i)
- Feeling like you’re manipulating the DAG directly, picking this up and plopping it down over here (jj rebase)
Notably, jj already builds in a TUI for the spots where interactivity is most beneficial.
skeptrune•1d ago
anticodon•1d ago
Unfortunately, as far as I understand, jj won't help with issue of unnecessary manual merges either.
cube2222•1d ago
When rebasing a big branch in git with conflicts, you usually end up having to manually resolve conflicts in multiple commits in a row - in jj you just have to resolve the conflict in the first conflicted change, and it usually adapts subsequent changes automatically.
Overall, rebasing branches with conflicts used to be a chore for me with git, and is now mostly a nothing-burger. Of course, I don't know the exact details of your scenario, so I'm not sure if it'd help, but I'd say it's worth trying, esp. if it's a common pain-point for you.
cowsandmilk•1d ago
mtndew4brkfst•1d ago
By contrast, git rebase --update-refs does not handle sibling branches but detaches them instead, and git replay hasn't had much TLC for UX yet. Plus, the last time I tried replay it was still obliterating commit signatures.
https://git.github.io/htmldocs/git-replay.html
viraptor•1d ago