Back

Jujutsu VCS: Introduction and patterns

228 points2 dayskubamartin.com
bkolobara2 days ago

I have used jj for about one month on a project with a colocated git repo.

So far, I enjoy it a lot. Previously I would have a bunch of 'wip', 'fix typo' commits, but my commit history got much cleaner with jj. It's very easy to jump to old changes and apply fixes right in the place they belong. All changes + tests are now committed as one unit. I would never bother to do it in git, but because it's so easy now, I do it all the time.

Also, the possibility to add a description/message at any time, not only when you "commit" something at the end, is super useful. I use it as some kind of TODO list. Basically, I create a bunch of empty commits with a message and then just start from the first and work my way through all of them with `jj next --edit`.

If you have some extra time and would like to try out something new, I would very much recommend it.

Asraelite9 hours ago

> my commit history got much cleaner with jj

In my opinion the traditional git workflow is flawed, but wanting the commit history to be cleaner is fundamentally the wrong approach.

There are basically two main reasons to look at a chronological list of commits: reading it as a changelog, and bisecting to find a bug. As a changelog, you want the history to be clean, organized, and concise. Rebase-based workflows are good for this and it's what Jujutsu excels at. For bug hunting, every commit should build successfully and be a faithful representation of what was on the developer's machine at the time they made the commit. Merge-based workflows are good for this.

But these are conflicting requirements. If you optimize for readability, you lose reproducibility and vice-versa. To achieve both you need to stop trying to use commit histories as a changelog. It doesn't matter how messy it is if you never look at it.

But then you need some kind of history to look at. Personally I haven't seen any open source tools that solve this well enough to be a viable alternative. Stacked diffs are probably the closest.

I like Jujutsu but it feels more like an incremental improvement over Git as opposed to something that actually addresses the deeper issues of VCS.

gechr2 days ago

Although it is currently undocumented, since v0.21.0 you can make `--edit` the default by adding the following to your configuration:

  [ui.movement]
  edit = true
Ref: https://github.com/jj-vcs/jj/pull/4283
hinkley2 days ago

The config file needs a lot of love. As does the help.

If you clone the repo there’s a bunch of documentation and example files, but if you install jj, those do not exist.

ilyagr2 days ago

Each release comes with a file with all the help, for example:

https://github.com/jj-vcs/jj/releases/download/v0.25.0/jj-v0...

In theory, packagers could put these docs somewhere. I'm not sure what the best way is to make it convenient to use.

Also, I'm not sure about jj v0.25, but in the upcoming (probably tomorrow) 0.26, you can get a lot of docs by doing e.g. `jj help -k config`. Ironically, this fact is currently somewhat under-documented. `jj help` just says at the end:

`'jj help --help' lists available keywords. Use 'jj help -k' to show help for one of these keywords.`

hinkley2 days ago

Yeah I made it as far as planning out a change to the `jj config --help` output to at least link to the documentation and then realized that my copy didn't have the documentation. Oops. And then I got occupied by some other FOSS bug and forgot to circle back.

Unfortunately there was a gap between installing it and trying it for the clever merge resolution use case I had in mind, and I've forgotten how I installed it, so I need to reverse engineer that to figure out how I got here.

nulld3v1 day ago

If your editor supports TOML schemas you can add the following to the config file to get autocomplete and in-editor documentation:

    "$schema" = "https://jj-vcs.github.io/jj/prerelease/config-schema.json"
More info here: https://jj-vcs.github.io/jj/prerelease/config/#json-schema-s... (I changed latest -> prerelease in the URL as this feature has been merged but not yet released).
diego8982 days ago

How does this work with a shared remote branch? I know in git when you alter your history you can do a `git push --force-with-lease` as a safer `--force` - do you have to do this frequently with jj "rewriting history" (from a git perspective)?

gcr2 days ago

Jj lets you edit:

- History you haven't pushed yet, or

- Bookmarks that you're tracking (meaning you're actively working on these branches), except "main"

History behind "main" or behind any untracked remote bookmark is immutable by default.

If you want to move "main" or any remote bookmark to a commit that isn't a descendant, you have to use `--allow-backwards`. If you want to edit one of these commits, you can use `--ignore-immutable`. Using either of these options is like a `push -f` in git terminology.

cube22222 days ago

Yeah, `jj git push` acts as an equivalent (according to my experience and understanding) to `git push --force-with-lease`, always.

You don't have to pass an explicit --force, nor an explicit lease-related argument. As you say, rewriting the commits underlying changes happens frequently, so this would otherwise be pretty annoying.

There's also `jj git push --dry-run` to preview which branches would be updated in what way.

diego8982 days ago

Thanks that makes sense - just feels weird to go from years of “be very careful rewriting history with git, especially at work” to jj rewriting everything all the time - can’t shake the feeling there’s a clash somewhere waiting to happen

cube22222 days ago

You can customize the immutable commits option to specify which commits you want to be blocked from changing.

By default that’s only the main branch and untracked remote branches. For me that makes sense as I’m generally fine with constantly rewriting my feature branches.

However, if you want it to e.g. block you from rewriting anything that you’ve already pushed to the remote, you can set it to treat anything that’s pushed to the remote as immutable, too, as another commenter described [0].

[0]: https://news.ycombinator.com/item?id=42940329

+1
gcr2 days ago
cupofjoakim2 days ago

I think I'm at risk of sounding like a broken record here. I've read about jj many times now but i'm still confused as to what problem it actually solves. I get the same feeling as when some dude wants to sell me on using vim as my primary code editor - arguments are there but it doesn't really solve an issue.

I'll go over to jj when that's the primary tool for the job, or when I can see something that beats the git cli hands down. I'm 100% on the path of least resistance when it comes to tool adoption.

mamcx2 days ago

I was a happy mercurial user until market forces and consulting needs made me, regrettably, use the inferior, more complex, more error-prone git.

I have used it already for a few years and can't for the life of me figure out how to solve problems with it. I just have a bunch of aliases that abstract away for the inconsistency of its commands.

Now, I switched to `jj` a month ago and basically, I have learned around 60%-ish of it. And more importantly, I have gotten out of a bunch of serious trouble (you can UNDO!!!!) already.

But if this rant is not enough:

* You can UNDO. Everything. That is the major thing

* You can switch from one `branch` to `another` and leave things incomplete, even conflicts, that is nice.

* The command made sense

* Nobody else knows you use `jj`, so no barrier to adoption.

* Rebases are not painful anymore. Stacking prs are finally nice to author, even of NOBODY ELSE KNOW IT!

The major troubles:

* Not yet support for major tools, ides for it. It stays in `detached head` which is not as nice as showing the current branch on IDEs, and when you are fixing conflicts some tools do not get the diffs.

* No mature UI tool. I use `gg` which is fine enough

But the above is just temporal problems. Git will never be `fixed` or improved, so is pain forever.

12_throw_away2 days ago

> I was a happy mercurial user until market forces and consulting needs made me, regrettably, use the inferior, more complex, more error-prone git.

Right? I even started with git, and had been somewhat comfortable with it for years. Then I joined a mercurial shop, and was more proficient with `hg` in 1 month than I had been with `git` after 5 years. It ruined me on git forever, I can't see it as anything other than a mess of wrong abstractions.

And unfortunately, these days I'm back using git with the rest of the world. Can't wait for the day when Jetbrains gets a good plugin for either `jj` or `sapling`, and I never have to google another git command again ...

pkulak2 days ago

I just use the CLI, even when using IntelliJ. I may be wrong, but JJ seems to have recently moved to a new model where your most recent change is (always?) stored in git as "unstaged". That means when you use an editor, you can be using the CLI to add a description, diff, or other things, and the editor will still show all the UI hints about changes, let you see the diffs there, revert there, etc. You can blame, see history, everything. There's almost no need for a dedicated plugin.

When I first started using jj, I remember just doing a "jj st" would commit everything and I'd lose all my editor support. It was pretty much the only part that got worse after switching. And now it's "fixed". If anyone knows more about jj than me, I'd love to know if I'm right about this, or just hallucinating. :D

+1
cube22222 days ago
tripple61 day ago

I would never work with hg anymore since and I consider git much and much more flexible from both user and scripting perspective. Yes, git also suffers of command inconsistency, and unfortunately it seems to be never fixed.

> You can UNDO. Everything. That is the major thing

Everything is what? The things git cannot undo is removing untracked files (well they're untracked) on git-clean, or files added to the index for the first time and then reset. Maybe rebase in mid-rebase is a way to lose some changes so one has re-rebase again (it's really annoying, but reflog holds all rebase changes if I recall). I can't really see what you mean.

> You can switch from one `branch` to `another` and leave things incomplete, even conflicts, that is nice.

It's nice. I use git-worktree for multiple branches I work on, so my working copies may remain dirty even in conflict stage.

> The command made sense

It's nice.

> Nobody else knows you use `jj`, so no barrier to adoption.

It's really nice, but I'm not sure whether I understand it, but does it work as a front-end tool over git/other VCS?

> Rebases are not painful anymore. Stacking prs are finally nice to author, even of NOBODY ELSE KNOW IT!

I don't get it. What does make rebase hard? It's just re-applying a bunch of patches or possibly merges on top of the new base, and it doesn't even require interactive mode. Interactive mode makes magic I'm happy with. Seriously, what's wrong with it?

cube22221 day ago

Generally, most things jj does can be done in Git, it's just much more pleasant / seamless / consistent in jj - maybe with the exception of the "Working on Two Things at the Same Time" pattern[0], that one might be really hard to achieve in Git.

> but does it work as a front-end tool over git/other VCS?

citing from the article

> Before we dive in, one last thing you should take note of, is that most people use jj with its Git backend. You can use jj with your existing Git repos and reap its benefits in a way that is completely transparent to others you’re collaborating with. Effectively, you can treat it like a Git frontend.

> I don't get it. What does make rebase hard?

It's hard when you work on a set of stacked PRs and make frequent changes to arbitrary PRs in that stack, because every time you make a change, you have to manually rebase and push all of the other PRs. There's SaaS's specifically built and used for solving this problem in Git[1].

Maybe hard is the wrong word, it's just annoying and tedious. jj makes this completely seamless, as shown in the article[2].

[0]: https://kubamartin.com/posts/introduction-to-the-jujutsu-vcs...

[1]: https://graphite.dev/

[2]: https://kubamartin.com/posts/introduction-to-the-jujutsu-vcs...

tripple61 day ago

Regarding the rebase thing, I guess it's more a matter of habit. Stacked branches indeed may be tedious and may be annoying to rebase, agree. I implemented a shell script to rebase branch trees on top of a new base that would cover stacked branches as well. It covered all my needs like resolving conflicts, concluding or aborting the branch tree rebase and so on. Of course, it would be nice to have such a right-done thing right in git. But as of now, if I understand what jj is, it seems like I can shell-script some of its features myself. Still a happy git user.

martinvonz1 day ago

I think the best way to answer most of your questions is just for you to try it yourself (https://jj-vcs.github.io/jj/latest/install-and-setup/).

> Everything is what?

Not interactions affecting remotes (you can't unpush a commit). Configuration is also not tracked. Neither are ignored files.

You can undo changes in the working copy, rebases, splits, squashes, etc.

> The things git cannot undo is removing untracked files (well they're untracked) on git-clean, or files added to the index for the first time and then reset.

Files added to the index can be recovered at least, but it's not easy to find them. The bigger problem is if you have not added them to the index and run e.g. `git reset --hard`. You can't undo that (via Git, but maybe via your IDE or your file system).

It's also about ease of use. I know you can undo a lot of things by using Git's reflogs, but it's not easy to e.g. undo a `git rebase --update-refs` that update many branches.

> I use git-worktree for multiple branches I work on, so my working copies may remain dirty even in conflict stage.

Yes, Jujutsu also supports that (`jj workspace`), but IMO, it's nice to not have to use them just because your working copy is dirty.

> What does make rebase hard? It's just re-applying a bunch of patches or possibly merges on top of the new base, and it doesn't even require interactive mode.

I've worked on the `git rebase` itself, so it's not like I don't know how to use it. Some things that I think are wrong with it:

* Without `--update-refs`, it only rebases a single branch. That's basically never what I want when I have branches pointing to other rebased commits.

* It's not able to rebase more than one head (no trees of commits)

* Doesn't rebase merge commits properly. Instead, it redoes the merge, discarding any conflict resolutions. (Yes, I know about rerere.)

> Interactive mode makes magic I'm happy with. Seriously, what's wrong with it?

Mostly that it's so stateful. While doing an interactive rebase (e.g. editing a commit), you're in a state where you can't easily check out another commit, for example. If you run into conflicts, you have to finish resolving all conflicts in the stack of commits, or you have to abort the whole rebase (throwing away any previous conflict resolutions).

Again, it will be easier to understand if you just try using Jujutsu for a few days.

eikenberry22 hours ago

> I was a happy mercurial user until market forces and consulting needs made me, regrettably, use the inferior, more complex, more error-prone git.

IMO Mercurial did a lot of this to themselves. I also preferred it at first but switched away from it due to their stance on no in-repo branching. They, of course, also lost due to git's author and network effects (github), but it wasn't all just "market forces".

pkulak2 days ago

> Nobody else knows you use `jj`, so no barrier to adoption.

All my PRs now have branches named "push-kjasdfkhdf", so it's not terribly well hidden. :D

BeetleB2 days ago

You can name them if you want.

+1
zellyn1 day ago
saulrh2 days ago

The thing about jj is that it doesn't actually enable any new capabilities. I tell people to use emacs or vim or vscode or whatever instead of notepad because it gives them new capabilities, things that are simply unavailable unless you're talking to an LSP or running a full-powered scripting engine. jj doesn't make anything possible the way going from notepad to a real editor does. What jj does do is it makes everything so much easier that you can now actually use all of those features that git theoretically gave you. Rebases? No more fiddling, no more wedges, no more digging through the reflog because you fat-fingered something, you just go `jj rebase` and... that's it. And if you get it wrong, you just do `jj undo` and... that's it. And if you just had a six hour manic coding marathon without committing anything and now you want to spread its batch of changes back down through your patch stack, you just do `jj absorb` and... that's it. It's not the difference between notepad and emacs where you're going from no LSP to LSP, it's the difference between emacs@2016 where LSP support is a week-long adventure and emacs@2024 where LSP support is five lines copy-pasted out of the info page.

sunshowers2 days ago

As a source control expert and jj's number one fan [1], I would count being able to defer merge conflict resolution as a new capability, FWIW. In general I think jj's greater than the sum of its (very good) parts because of how its features work together to create a coherent and pleasant user experience.

[1] the top testimonial on https://jj-vcs.github.io/jj/latest/testimonials/ is mine

saulrh2 days ago

I guess I was thinking in terms of the patches you push up to github. `jj` is a joy to use and it absolutely enables me to implement workflows that I wouldn't even vaguely consider without it helping me; the big one I think of is the one where you work in a merged dir with like six parents and use `jj absorb` to instantly spread your changes out to the different PRs. I've been forced to do that in git. It was a nightmare and took me two days. Not impossible! Just utterly impractical. `jj` takes end results that were theoretically-possible-but-practically-infeasible and makes them usable. Which I suppose counts as a new capability from the UX perspective. :P

+1
sunshowers2 days ago
ehzy2 days ago

jj is not just a new interface for git, it has a lot of new and powerful features to offer, even when you're using the git backend with a colocated repo. Just to name a few:

- deferred conflict resolution

- The very expressive revset language

- the op log and ability to undo any operation

Many things that you can do with the git cli are significantly easier and in some cases comparatively effortless using jj. If all you do is git add and git commit then you probably aren't missing out on much, but if you ever split or rebase commits you should definitely try jj.

dartos2 days ago

I switched from git to jj a few months ago, so I may have some insight.

I used it at work, where everyone used git, and there were 0 issues.

What I really like about it is how changes are sort of soft committed right away and, when I checkout a different branch or change, I don’t need to stash my changes and remember the stack of stashes.

Rebasing and merging feel a little easier, but that might just be because “ours” and “theirs” doesn’t get flipped during rebasing.

I also like the ability to checkout an old commit, make a change to it, then have all my more recent commits automatically get rebased (not exactly what happens, but the analogy works)

All in all, it’s a solid, compatible, incremental step forward in VCS.

The only tangible downside for me is that it doesn’t support git hooks

password43212 days ago

> I also like the ability to checkout an old commit, make a change to it, then have all my more recent commits automatically get rebased (not exactly what happens, but the analogy works)

Can anyone comment on how Jujutsu "rebases" multiple commits at once vs. git prompting each one be signed (eg. touch Yubikey), and how it looks afterward in git?

saghm2 days ago

One of the main innovations in how `jj` works (in my opinion at least) is that has a concept of "immutability" for commits, which if I recall correctly is configurable with an arbitrary filter in the config, but the default (which I've never messed with personally) essentially considers commits in tracked branches from remotes to be "immutable", meaning that anything purely local is "mutable". When you run a command to try to change any immutable commits, it will refuse unless you manually opt-in with `--allow-immutable`. This makes it super easy to clean up the commit history from local development without having to worry about accidentally putting stuff in a state where there will be conflicts when you try to sync the changes with a remote later.

I think what they're the parent commenter is referring is being able to make changes to older "mutable" commits without having to do a rebase. I'm not sure I find the description of this as being similar to an "automatic rebase" super helpful personally; the way I'd personally describe it is being able to do the equivalent of "git commit --amend" on any commit with a built-in safety check to prevent this from accidentally modifying commits pushed to remote branches that you need to explicitly override if you want to ignore it.

+1
justinpombrio2 days ago
+1
sunshowers2 days ago
stouset2 days ago

If you require a physical touch for every sign operation on your YubiKey and you sign all your commits, you are going to have a bad time with jujutsu. That is somewhat unavoidable. If you are open to caching your credentials and only requiring a physical touch once every so often, this pain goes away entirely. If a physical touch for every sign is nonnegotiable, this probably isn't going to be a tool that works for you.

Jujutsu rebases multiple commits at once almost exactly the same way git does it. The three huge improvements are: 1) rebases happen automatically for descendent commits when their parents are modified, 2) rebases happen entirely in-memory and so don't require filesystem operations, which is an enormous performance benefit, and 3) rebase conflicts are a first-class citizen and doesn't require interrupting everything to drop you into a wedged rebase so you can fix it.

The third aspect combined with the first one means you can just jump to where a rebase conflict occurred, fix it, and… you're done. All the children are fixed for you automatically. If there were more, you can fix them up in the order that makes sense to you and you don't need to completely finish before being able to jump around elsewhere in the repo.

renox1 day ago

> The only tangible downside for me is that it doesn’t support git hooks

Thanks for this information: at work we use them for creating gerrit review id, so I'll wait until jj get this feature.

steveklabnik1 day ago

There’s workarounds for that, including an open PR for a dedicated integration.

cube22222 days ago

So, I've felt pretty much like you - that is, I was happy with git, never really had major problems, and was generally content. Which meant that even though Jujutsu was on my radar for a while, it took me until now to take it for a spin.

In practice, for most bits and purposes, Jujutsu is just designed really well around a core set of primitives, which you use to achieve basically all your use-cases, which in git are spread across many fairly "special-case" features. This means that arbitrary "change tree modification" are pretty trivial.

The easiness of jumping into non-branch-tips and editing them with descendants automatically rebasing is also nice, to me, and fits in well with my workflow. Stacked PRs[0] are much less annoying than in Git thanks to auto-rebase as well.

Finally, I think the "Working on Two Things at the Same Time"[1] pattern is not easy (if even possible?) to achieve with git.

But all in all, as I wrote in the article's conclusion, the reason to use Jujutsu is that it just makes your VCS usage more pleasant, while generally not interfering with anything else as it effectively works as a git frontend.

[0]: https://kubamartin.com/posts/introduction-to-the-jujutsu-vcs...

[1]: https://kubamartin.com/posts/introduction-to-the-jujutsu-vcs...

saghm2 days ago

I've switched over to using it pretty much exclusively over git directly. While there are a number of minor things that I prefer about it over git, these are the two main ones for me personally:

* If I don't pass `--allow-immutable`, trying to run a history-rewriting command will immediately fail if it would touch a commit that's present on a tracked branch from a remote. When using git directly, it's way easier to accidentally make changes during a rebase that conflict with remotes and then requires me to go back and fix it when I can't do a fast forward to sync the changes *Conflicts are part of the state of individual commits rather than a property of an ongoing operation. When an operation produces a conflict in one or more commits, I can still do whatever development (or make version control changes) on any of the other commits. I'm free to fix the conflicts in any order I want, or fix some of them now and worry about the others later, which is much more intuitive to me than using `rerere` to achieve something similar with git directly. I could even imagine a scenario where I might want to push up conflicts that come from attempting to merge my own changes with ones made in parallel from someone else so that they could resolve some of them if I wasn't confident in my understanding of the changes they made, although this isn't something that I've run into yet.

A lot of this hinges on the ways I personally use git; I tend to commit often and not worry about making sure the history is clean or the commit messages are perfect until I actually want to merge them, and I absolutely loathe merge commits, so this ends up meaning I rebase a _lot_. The benefits I mentioned probably wouldn't seem very significant if you don't work on shared codebases often or if you use git in a way that you rarely rebase, but because these feature solve concrete annoyances I'd run into quite often with git, the quality of life improvement feels massive to me.

Hasu2 days ago

> when I can see something that beats the git cli hands down.

Then you should switch to jujutsu several months ago.

It's the biggest improvement to my development workflow in over a decade. The last change that was as big was git itself.

zellyn2 days ago

I can second this opinion. The moment I read "something that beats the git cli hands down" I command-F searched for "hands down" to find this comment or make it myself.

stouset2 days ago

Thirded. I relegated jj-related articles to /dev/null for a long time having believed nothing was likely to supplant git. Then… wow, actually almost exactly a year ago something moved me to read the `jj init` post here. I read it, it was intriguing, I figured I'd give it a try later that week.

I wasn't hopeful. I have a lot of git's working model in my head. Replacing it was going to be hard but hey, might as well kick the tires.

I was productive that day. I knew enough to completely eliminate git from my workflow two days later. I have never looked back, and now I come to sing its praises every time an article is posted. It's immensely fun having seen the proportion of commenters in these threads start tilting sharply in the direction of more and more of HN having used it and loving it.

(also, hey zellyn!)

abound2 days ago

> I'm 100% on the path of least resistance when it comes to tool adoption.

Different folks will have different preferences when it comes to adopting new tools. Some like "tried and true", some want to experiment and see what's out there.

I (an avid `jj` user) like to think I want some pragmatic blend: I want tools that help me get my job done, and otherwise get out of the way. So if I think there's room for improvement in my workflows (in this case, managing 10-20 ongoing changes at a given time across 5-6 projects), I'll experiment with new tools that can help reduce the friction for me.

Like you said, there's functionally nothing `jj` can do that `git` can't, Jujutsu literally uses git as the default backend. For me, the power of Jujutsu is the massively reduced cognitive overhead of using it. My thoughts more directly translate to `jj` commands and I rarely have to think about it, even with complex stacks of changes.

danpalmer2 days ago

It solves two problems. First is having a batter UI, git is well known to have a bad UI and be hard to teach. But you’re right there aren’t new capabilities.

Secondly, and more importantly, is that you can back it on to different storage mechanisms. Companies with large monotypes are often not using git (Google, Facebook,…), or are using highly specialised git (e.g. Microsoft). JJ is not just got compatible, git is a pluggable storage backend.

I mostly use JJ backing on to Piper, and the integration is excellent. I’ve also used it backing on to git. I can transfer almost all my knowledge and muscle memory between these two places.

hellcow2 days ago

Stacked PRs with multi-branch rebases are effortless. Conflicts are actual things in the history that you can come back to later. Everything is automatically committed, so I don't lose work from a bad reset/stash pop.

Maybe these don't help you with your workflow. But in my case these solve the biggest problems I had with git while still being fully compatible.

oniony2 days ago

I personally switched over in December. For me it solves a few problem in an elegant way:

1. No branches. Instead of having tunnel vision on a single branch, you can easily hop around the tree, creating revisions and switching context without having to mess about stashing files and then remembering which branch and version each stash is for. The fact that your changes are automatically added to a revision make this very easy. You can literally drop what you're doing, `jj edit` or `jj new` a different commit to switch over to working on that stuff, whether it's on a Git branch or not.

For example, you could be working on a feature 'A', push your PR and then start work on feature 'B', only to find your pipeline has failed thereafter. You can then `jj new` a commit off your old feature commit, create a fix and push it (or even `jj squash` it first) and then `jj edit` back over to what you were doing on feature 'B' with no fiddly stashes.

2. Much simpler rebasing commands. For those of us who like to rewrite history to make PRs easier to comprehend for reviewers, and for general rebasing when your target branch has changes, I find the `jj rebase` much saner than Git. It's very easy to rebase a set of commits or move a single commit about in history.

`jj squash` is also great for fixing up earlier commits. You can `jj new` a commit off an existing commit, make a fix or change and then `jj squash` it into the previous one without a fiddly `git rebase -i HEAD~2`.

3. Automatically rebasing of descendents. If I edit a commit in the history, all of the descendents are automatically rebased with this change. So if I realise I've made a mistake in commit `aaa` and edit that commit, `bbb` and `ccc` that are derived from it will automatically incorporate this change, with commits being market conflicted if this can't be done.

4. Conflict handling. Any conflicts are recorded against commits and doesn't prevent operations from working. So you don't have to immediately drop everything and work interactively through a set of conflicts. Combine that with the automatic rebasing, above, and your resolutions propagate through the descendents similar to using git rerere.

So those, for me, are the killer features.

On the downsides:

1. HTTPS Git repository handling is poor. There is no vault integration, at least on Mac, so I have to enter my username and password every time. This is not a problem for SSH, but my company prohibits SSH, so it's a pain. ** see reply below, credential helper now works **

2. Git fetches seem slower for large changes than using Git fetch directly, and I've had the tool timeout on repositories with large files. Combined with the above issue I tend to now `git fetch` rather than ever `jj git fetch`, avoiding the two problems, and just push with `jj`.

Other than that I'm loving the tool.

skitter2 days ago

> 1. HTTPS Git repository handling is poor. […] 2. Git fetches seem slower for large changes than using Git fetch directly

JJ used to use libgit2 for fetches, but it now can shell out to the git binary instead: https://github.com/jj-vcs/jj/pull/5228 (which will be the default in the future)

oniony2 days ago

Nice, good to know, thanks.

cube22222 days ago

> There is no vault integration, at least on Mac

I'm not sure when it was added, but now if you set `git config --global credential.helper osxkeychain` then it will work for jj, too.

oniony2 days ago

Oh wow, somehow missed that. Thank you.

Edit: worked a treat, thank you so much.

theLiminator2 days ago

I think the main problem it solves is stacking https://www.stacking.dev/

keybored2 days ago

That’s a problem created by GitHub or people unable to think outside of the GitHub model. It would make more sense to solve that problem through a review tool.

For what it’s worth from what I’ve read about JJ: it’s a better VCS frontend overall.

sunshowers2 days ago

There are two distinct but related issues here: GitHub's terrible support for stacked PRs, and Git's `rebase -i` user interface to manage them which is quite bad. Jujutsu fixes the latter.

I maintain a fork of a tool called `spr` which comes as close to fixing the former as GH allows. It has a number of limitations, though. https://github.com/sunshowers/spr

theLiminator2 days ago

I don't believe so, I believe it's a fundamental aspect of the git model.

sunshowers2 days ago

There is no substitute for trying it out for a few days. It will quickly become apparent why people like it so much.

christophilus2 days ago

If you switch between branches a lot, it's a game changer. I look at it as just git with a sane CLI.

mixmastamyk2 days ago

Yes, so far these jj blog posts do not sell it well at all.

Not to mention the rainbow colors are a bit of a turnoff. I like a little color here and there, but not ransom-note style. Monochrome effects are available folks.

stouset2 days ago

`jj --config ui.color=never`

Maybe the blog posts don't sell it well, but I find it pretty telling that bordering on zero people ever enter the comment section for Jujutsu posts to complain about a bad experience they had. Just dozens upon dozens of people singing its praises.

Think about literally other technology post on this site. Nothing is universally loved.

mixmastamyk2 days ago

Just said I don't want color==never.

That's the problem, no one can cogently/concisely explain why. I don't love git but have sanded its rough edges already. Gitlab did the rest.

I recommend this Spolsky classic on how to convince one to try a new VCS: https://hginit.github.io/ Doesn't have to be this long but note his writing style.

+1
stouset2 days ago
frizlab2 days ago

Well said.

sfink2 days ago

I've been using jj for a month, coming from mercurial. I have settled into a usage pattern slightly different than the article describes, so that all of those `jj edit` commands made me a little itchy. I prefer to nearly always use a separate @ commit to hold any changes I make. The mental model is that there's a graph of commits, and then @ is an auto-updated commit to hold any changes I make in an editor or whatever. Actually modifying anything in the core graph is then a conscious decision, and I might decide to update a commit with my changes (`jj squash` or `jj squash --into X`) or make the changes into a commit of their own (`jj describe` if I haven't already, followed by `jj new` to return to the stable state of having an @ commit for collecting changes).

The reason why I don't like using `jj edit` on something in the graph is that every change I make is conceptually applied immediately to all descendants. It's sort of like plucking a commit out of the graph (the commit you're editing), entering a mode where you can only modify that commit and don't have the option to use the changes in a different way (as in, squash some of them to some other commit or make a new branch or whatever).

It makes jj modal, and one of the things I most appreciate about jj is that it is not modal, you don't have a separate staging area or stash or anything, it's always in the same consistent state where you can do the same set of things.

Also, having a separate commit makes it easy to see how you've modified things, rather than only being able to see the sum total of the original commit contents plus your recent modifications. (It does make it a little harder to see that sum: `jj diff --from @--`.)

This is also the default behavior of `jj next` and `jj prev`, which are very confusing if you're expecting to be editing nodes in the graph directly. You can totally do that, by passing `--edit` to them, and if that's your preference you can default the flag in the config.

I do still use `jj edit` from time to time, but for the most part `jj new` gives me a better feeling of control. It was difficult initially to not want the "extra empty change" to go away, but once I accepted that there's really only one and it corresponds to a meaningful concept (it's the auto-updated container for any changes you make), I made peace with it.

steveklabnik2 days ago

The nickname for these two is the "edit workflow" vs the "squash workflow," and both are good to know, for sure!

sfink2 days ago

Sorry, I knew I remembered reading about the two somewhere but could not figure out where. I searched through the jj page for it. Only later did I remember that it came from your jj guide, which I still had open in a tab. I wish I had pointed to your explanation, it's much more clear.

Better late than never: https://steveklabnik.github.io/jujutsu-tutorial/real-world-w...

steveklabnik1 day ago

It’s all good! More people writing stuff out is a good thing. You never know which phrasing resonates with more people.

And to be clear I didn’t coin these nicknames, I got them from the community.

cube22222 days ago

I think I agree for the most part in practice. The vast majority of changes I make are on the nondescript (or actually description-containing) @ changes / "branch" tips, and then either become described changes, or get squashed / split / rebased / etc. into the right place.

bsder2 days ago

> It makes jj modal, and one of the things I most appreciate about jj is that it is not modal, you don't have a separate staging area or stash or anything, it's always in the same consistent state where you can do the same set of things.

Staging and stash are modal--YES! I'm going to steal this.

I've been trying (and failing) to explain what I hate about staging and stash for years and this explains the issue so succinctly.

sunshowers2 days ago

Yeah, I agree with this. I definitely prefer `jj new` and then squashing changes -- it also means I can see what changes I just made with `jj diff`.

nightscape2 days ago

I'd recommend trying it with GG, a GUI for JJ: https://github.com/gulbanana/gg It proves you with an easy way to get started with JJ, especially dragging and dropping files between commits is super handy!

hbogert12 hours ago

I've used git-branchless in the past. It seems they have in common (suprise!) that branches are a distraction. And for a lot of workflows that has a lot less friction. The only thing is, a lot of version control users are only comfortable with branches. They can't think of an alternative with changes and patchsets.

gcr2 days ago

I mentioned this on previous discussion recently, but I use jj for all my projects on github! It's really useful for my sort of workflow: chains of commits with easily-editable history. If you make a change back in time, you edit the previous commit (which puts you in a state similar to git's detached head), and any edits you make there are automatically carried forward (rebased) onto descendants. It feels way more natural, especially for newer users.

The killer feature that I love the most is a small one, but it's that commit messages can be made ahead of time rather than after-the-fact. So I can sit down at my desk, say

    jj new -m "Work on XYZ feature"
then edit my code in the editor. When I'm finished, I move on to the next commit:

    jj new -m "Working on UVW feature"
No more "oh no I accidentally started touching code and forgot to commit my work, so now I have to manually split two git commits;" it's a small way that the tooling encourages you to be intentional about your engineering philosophy.

The fact that jj snapshots the working tree on every command is a mixed blessing. It's nice and fast and so I do prefer it, but there have been times that I've been bit by accidentally adding my python virtualenv into repository history. Luckily it's easy to undo.

    jj log ./venv # see which change added it
    jj edit change-id
    echo venv >> .gitignore
    rm -r ./venv # or jj file untrack ./venv
    jj edit whichever-change-you-were-on
bmacho2 days ago

Your example videos are hard to follow. I don't have this much working memory. Can you keep showing the content of the directory, and the contents of the files too? I guess you will need a file explorer and a text editor that updates automatically, but I can't recommend any in particular.

qudat2 days ago

Editing changes in place is very handy. This is especially useful for range-diff workflows when you want to see what has changed between two patchsets (eg diff of diffs).

I’ve been slowly working on a patch-bin service that only has access to patchsets and doesn’t have access to the git repo itself. As a result the best we can offer is range-diff. It’s a pretty interesting workflow and makes the collaboration software easier to maintain: https://pr.pico.sh

0cf8612b2e1e2 days ago

What workflow do people adopt for tools which still do not have a concept of local-only config files? For example, VSCode only has a launch.json, which should be committed, but I might need to customize it to my environment/current problem without distributing those edits.

How can I not constantly fight jj with this?

cube22222 days ago

I think you could achieve this with a variation of the "Working on Two Things at the Same Time" pattern[0], also explored more widely here[1] (with e.g. using private commits).

Basically, just have a local-only merge-change that combines whatever you're working on, with all your local modifications. You can then even just create new changes on top of the `<dev>` change, and then rebase each into your branch when you're done with it, for even better ease-of-use (using `jj rebase -A name-of-branch`).

You can also mark those local-only changes (both the merge-change and the local customizations) as private changes, to make sure you never actually push them.

[0]: https://kubamartin.com/posts/introduction-to-the-jujutsu-vcs...

[1]: https://v5.chriskrycho.com/journal/jujutsu-megamerges-and-jj...

0cf8612b2e1e2 days ago

I am not proficient enough in jj to fully follow that, but it sounds promising. Especially if I can at least poison the local changes so there is no risk of sharing them.

stouset2 days ago

Honestly that does sound like a really frustrating corner case.

SatvikBeri2 days ago

I use the squash workflow for this. I do my work on change N+1, which includes all the local changes I don't want to merge. When I'm ready to make a PR, I squash my edits into earlier changes, then `jj git push -c @-`, which will send everything up to change N. Once merged, I rebase change N+1 onto the new master.

sunshowers2 days ago

The usual way this is done is by having a sample file checked into the repo (launch.sample.json?) that developers can copy locally and customize.

hinkley2 days ago

The fact that jj doesn’t tell you what files have changed until you start a new commit is holding up my plans to use it. It’s frustrating for me and I imagine it would create tech support questions if I made others use it if it’s to replace git it needs some further ergonomics refinement.

steveklabnik2 days ago

I am not 100% sure what behavior you're talking about, but if it's that `jj` doesn't snapshot until you run a `jj` command, you can add watchman, and then it will do so on every filesystem change https://jj-vcs.github.io/jj/latest/config/#watchman

Otherwise, jj status will show you the current changes at any time.

necauqua2 days ago

It's `jj status`?.

hinkley2 days ago

I'll have to try it on a blank checkout because once I've run 'jj new' then 'jj st' does something similar to git st, but I had a situation on a fresh checkout where I know I'd saved changes in the IDE, git st showed them, but jj st came up as blank.

It may be that this only happens immediately after jj git init.

But any tool that lies to people is a huge red flag. Because while I can memorize that caveat it's presumptuous to assume that an entire team of people new to a tool will remember a footgun. That's on the tool not the victims.

stouset2 days ago

With all due respect, it sounds like you might have some confusion about what happened. If you run `jj new`, `jj st` will virtually by definition show a clean working copy. You've created a new, empty revision, and as a consequence there are zero changes which have been made in that empty revision.

Asserting that `jj` "lies to people" is… probably uncalled for here. I'm not going to say it's impossible for the two commands to produce incongruent output, but knowing how they work it seems pretty unlikely. I think it's far more likely that either there was some confusion on your part (two different tabs in two separate projects?), you did something you didn't fully understand with a new and unfamiliar tool you were playing with (perhaps you misunderstood what `jj new` does?), or some other very reasonable and understandable thing occurred that falls pretty far short of "the tool lied to you".

+2
hinkley1 day ago
pkulak2 days ago

>> Changes in jj can be marked with bookmarks (what jj calls branches),

JJ actually calls its bookmarks "bookmarks". Maybe these two words got accidentally flipped?

dragonwriter2 days ago

No, the English structure "<foo> ... what jj calls <bar>" just has an annoying semantic ambiguity, and can either mean "<foo> is the name used by jj for the thing that is more commonly referred to as <bar>" or "the thing more generally called <foo> is referred to as <bar> by jj", which is expected to be resolved by the reader on the basis of whether <foo> or <bar> is the generic term they are familiar with.

pkulak2 days ago

Oh, you're right, I didn't read it in my head like that.

Artoooooor2 days ago

Nice! I'm comfortable with git, but something better can still be invented. Thank you for the information.

currymj2 days ago

it’s great that jj just looks like you use git to other people, so doesn’t inconvenience them.

how does it work the other way? if i’m using jj, and my colleagues are using git, how does it cope? in particular what if they are using git in a really depraved way (like just add all files and always commit everything, etc.)?

steveklabnik2 days ago

> how does it cope?

You're both operating on git repositories, so there's no issues. You can think of jj as an alternate git client.

frizlab2 days ago

[flagged]

lr4444lr2 days ago

That's not a polite response to a hard working person trying to share his endeavor, but I can't say I disagree with you either.