You're right that git commits are snapshots.

jj is very non-modal, that is, it doesn't tend to have a lot of state that commands rely on. As an example of what I mean, because jj does not have a staging area, everything is already committed, which makes it very easy to say, move to a different commit: you don't need to stash your working copy, as jj has already stashed it for you. Similarly, due to the auto-rebase behavior, you can be working in one part of the tree, realize something somewhere else should be moved, and go rebase that without even moving to it at all!

As a small example: say I'm working on something, and I find a typo. I want to send that typo in as a PR, but I don't want to do it as part of my work. I can do that with:

1. make the change in my current working copy (@)

2. jj split -o trunk (selecting the typo contents to split off the typo fix into a new change on top of (hence -o) trunk)

3. jj log (go check out what the change id of that change is

4. jj git push -c <change id I found in 3>

No need to even move my own HEAD (in git terms), just knock it out inline in a few steps while I'm working.

Now, as for magit, I don't use it, and I know that those that do love it and it does make some of this stuff easier. But not everyone can use magit. And there are "magit, but jj" projects as well, but I can't speak to them or which is best at the moment.