Posted by Bogdanp 8/31/2025
Which is what `jj undo` does, and why I brought it up.
I'm well aware git doesn't have a unified concept of undoing, but I'm equally aware that it's possible. (Even before jj, GitUp and git-branchless offered universal undo.)
This seems like the ideal case for revert, but the way that it alters the history stings - the fact that reverting a commit essentially means that said commit can’t show up later as a change is frustrating - also I’m sure it’s a case of user error, but…”merge branch, realize there’s a big oops in there, revert, fix the oops on the branch” feels like it shouldn’t result in a state where git doesn’t accept the branch as mergable.
>The command jj log shows you a visual representation of your version history. If you run it, you should see something like this:
> @ mkmqlnox alice@local 2025-07-22 20:15:56 f6feaadf
To:
> The command jj log shows you a visual representation of your version history.
> $ jj log
> @ mkmqlnox alice@local 2025-07-22 20:15:56 f6feaadf
Much better for learning when the command itself is part included in the output.
to the extent that niche things might not be supported (yet), so be it. It suits the needs of the vast majority of people. Moreover, if you need those things, I BET you can just do it via git and then carry on working in jj, since jj sits on top of git
CRLF autoconversions especially shouldn't have been supported, imho. Git on Windows's default setup still converts LF to CRLF to this day, even though every program on Windows has understood LF for a number of years.
Of course, it also requires users to use the official Git client, so that .gitattributes are actually observed. But that's more likely to be the case in practice.
Git for Windows configures core.autocrlf to true by default. This is a terrible idea, imho. "input" is a bit more justifiable.
I agree that the default on Windows is wrong. “Input” isn’t much better.
It is a layer on top of git that adds its own terminology and processes. The DX doesn't come close to git in understandability, what is `jj bookmark move main --to @-` and how is it more understandable than git?
> jj bookmark move
Presumably, this part is reasonably clear. “I want to move a bookmark.”
> main
Which bookmark to move. I think this is probably clear from context?
> --to @-
And here’s the part that looks foreign to a non-jj user. This syntax is familiar to jj users because the same revset syntax is used across the whole CLI. `@` is the working copy commit (the commit you have “checked out” that you are currently editing), and `@-` specifies its parent.
Because it’s just a revset, you could specify that same command in a number of ways:
- If you know the parent’s commit ID or change ID, you could use that instead: `--to abc123`
- If there happens to be another bookmark already there, you can use its name: `--to other-feature`
- You probably wouldn’t type this in the terminal, but you could do exotic things like `--to 'mutable() & description("foobar2000")'` to assign the bookmark to the work-in-progress commit that has "foobar2000" in the commit message.
Revsets used pervasively are one of the things that make the DX lovely.
This got me thinking why I have not tried Jujutsu yet (which was mentioned in the episode briefly) even after I saw it multiple times on HN over the years. It is probably because it just looks like another Git, maybe a less complicated one. But nonetheless it's going to be Git-like, and I know enough about Git and am competent with it. So there is little incentive to learn and try another Git-like tool.
I would love to try a VCS vastly different from Git. But it cannot be just different, it should be better than Git. That, I think, is hard to come by.
By the way, if anyone is interested which podcast I am talking about, this is the episode. "The Real Problems w/ Git" on ThePrimeTime. https://youtu.be/t6qL_FbLArk?si=GOkixm86rFADoc4x
What was described was actually quite close to developing through a Dropbox folder + (implied) tooling. And that makes a lot of sense for Casey Muratori's current work, which I believe is a small 5-10 person team doing game (engine) development. Such teams can quite easily work all in the same source branch (and this could in fact be preferred, e.g. for large assets in gaming).
However, as organizations grow, you want different levels of staging (that could be provided by your version control). A split between local and _the common_ stream, release branches, splits between projects or features, review states, etc.
So I believe the optimum is somewhere in the middle, and jj does take a step towards it. But still some friction remains.
My impression is, that git is ok for managing shared history. And thats what i was using it for mostly. But jj added another whole dimension for managing my work in progress, different paths of code that i am working on in the moment. jj made it almost frictionless. I did not know I was missing it, but i'm not going back.
Depends on your workflow, I guess. I need to sign with different security keys, and for that I use "defaultKeyCommand" in git. Doesn't exist in Jujutsu.
I have already found more things that I cannot do with jj (and can do with git) than things that are nicer to do in jj. Though admittedly, there are things in jj that are better.
Hopefully it will get there eventually :-). But these things take time. JJ already seems to get a lot of attention, so that's something.
If rebasing correctly is enough to support your use case, then the answer is ‘it’ll work’.
I've heard good things about https://abhinav.github.io/git-spice/
If I use git from zed and jj from the command line, will that just work?
You might want to set up this feature: https://github.com/jj-vcs/jj/discussions/3549
jj new main@github
jj describe
jj git push -c {prefix}
jj git fetch
my couple thoughts:- i'm forced to be "strict" with my changeset since there's no `git add -P`
- bookmarks are a pain to keep up-to-date with `jj new`, i don't know if i even want to do that. for multi-commit changes, i've defaulted to `jj new` a couple times as needed, and `git push -c` the latest.
- i'm sure some day i'll understand the `@..` and `roots()` incantations but for now the most complex thing i've successfully pulled off is `jj rebase -s 'roots(main@github..@)' -d main@github`
i don't think trying to map your current git flows 1-to-1 onto jj is going to be a very fruitful exercise.
JJ split is your friend
> - bookmarks are a pain to keep up-to-date with `jj new`, i don't know if i even want to do that. for multi-commit changes, i've defaulted to `jj new` a couple times as needed, and `git push -c` the latest.
Check out the common community alias JJ tug. It's used enough they're considering adding it as a feature
> - i'm forced to be "strict" with my changeset since there's no `git add -P`
Check out the command `jj split` and possibly `jj squash --interactive`.
> - bookmarks are a pain to keep up-to-date with `jj new`
There's a neat alias a lot of people use:
```toml [aliases] tug = ["bookmark", "move", "--from", "heads(::@- & bookmarks())", "--to", "@-"] ```
It finds the closest bookmark and moves it to `@-`. Pretty much exactly what you want when adding more commits on top of an existing branch.
A significant other thing jj does is introduce change IDs (i.e. a (randomly-generated) ID that stays stable even as a commit is amended), with which it should be easier to track changed commits across forks/rebases/edits/pulls/fetches, though I've yet to use jj for collaborative projects to see how much that pans out.
Generally jj makes rebasing things, and generally editing history so much more easy than git, so force-pushes messing with branches is much nicer to "fix" however needed. Being able to leave commits in a conflicted state and resolving only when actually needed also should help.
The relevance, btw, is that you can use git commit IDs in place of change IDs anywhere and they won't conflict; jj knows which you mean by the character set. (They could still conflict with bookmark names, sadly. But I haven't heard of that being an issue in practice.)
In git, your repo is the canonical repo and that is where you work. You work on a new feature and when you are ready, you "git format-patch" and "git send-email" to the community via the mailing list or other developers. A discussion may happen and people may or may not decide to apply the patch to their own repositories, with "git am." This doesn't break every other developers' working copy because they decide how to apply the patches they got in their email. No central repo, no trunk, guaranteed by the d in git dvcs.
Since you're rebasing, you're intending stuff; nothing is lost until you force-push.
> A force-push to trunk breaks every other developer's working copy.
Which is why you avoid it and set the remote main branch to protected. You can't force push by accident, you intend to forcibly overwrite the remote branch.
> A force-push to trunk breaks every other developer's working copy.
Only if they pull your broken trunk as well. Otherwise you're just wrong.
> A rebase may unintentionally overwrite someone's work.
No only your copy of someone's work.