by steveklabnik | 2025-07-22 03:55:04+00:00
> 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.