Most active commenters
  • paradox460(16)
  • baq(14)
  • steveklabnik(14)
  • nchmy(12)
  • sunshowers(12)
  • BeetleB(11)
  • 1718627440(10)
  • stavros(10)
  • KingMob(9)
  • adastra22(8)

Jujutsu for everyone

(jj-for-everyone.github.io)
408 points Bogdanp | 355 comments | | HN request time: 3.696s | source | bottom
1. marcuskaz ◴[] No.45084298[source]
> Jujutsu is more powerful than Git. Despite the fact that it's easier to learn and more intuitive, it actually has loads of awesome capabilities for power users that completely leave Git in the dust.

Like? This isn't explained, I'm curious on why I would want to use it, but this is just an empty platitude, doesn't really give me a reason to try.

replies(7): >>45084316 #>>45084327 #>>45084439 #>>45084678 #>>45088571 #>>45092597 #>>45093098 #
2. jennyholzer ◴[] No.45084302[source]
I've seen some posts about Jujutsu recently, but I haven't gone deep into specific workflows.

Are there specific advantages to using Jujutsu over Emacs Magit?

All other Git UIs I've used have been severely lacking, but Magit has made me significantly more productive with Git, and has convinced me of the "magic of git".

Is Jujutsu interested in competing with this experience? Or is it intended as an alternative to the (to be clear, extremely poor) git user experiences outside of Emacs?

replies(7): >>45084369 #>>45084385 #>>45084405 #>>45084556 #>>45084694 #>>45085631 #>>45089962 #
3. jennyholzer ◴[] No.45084316[source]
I agree. I'm willing to give them the benefit of the doubt to some extent because existing Git UIs are pretty poor in my opinion. But I'd like to see some more meat on the bone, in particular a demonstration of why this is easier/more powerful/more convenient to use than the alternatives.
replies(1): >>45084835 #
4. kyrra ◴[] No.45084327[source]
One example: Merge conflicts can be submitted as a proper entry and dealt with later: https://jj-vcs.github.io/jj/latest/conflicts/
5. paradox460 ◴[] No.45084369[source]
Jujutsu isn't really a git UI, and in some ways it's rather bad at being one (no support for making tags, submodules, or a few other things)

It's a whole new VCS, that just so happens to be backwards compatible with git, and uses git as it's backend

Similar to how git brought us cheap branching over svn, JJ brings cheap rebasing. Conflicts are no longer stop the world operations, and you can rebase, rearrange, and manage commits like never before.

If you've used tools like stacked diffs before, JJ will feel right at home. Making stacked diff PRs is almost trivial in jj

replies(2): >>45084398 #>>45091434 #
6. jennyholzer ◴[] No.45084385[source]
Are there "central concepts" in the Jujutsu design?

I'm having a hard time wrapping my mind around what specific details would cause me to choose Jujutsu over Git, in particular because of Git's industry standard status.

I think this is a very interesting concept, but I think it could go farther with some more targeted marketing along these lines. Of course, if Git power users are not Jujutsu's intended audience, then this comment may be irrelvant.

I think one of Git's great weaknesses is its unfriendliness to newcomers (jargon, deep features, lack of discoverability, lack of accessible GUI frontends), so there's probably a lot of potential for a VC solution that is easier for a newcomer to jump into.

replies(3): >>45084557 #>>45084575 #>>45084937 #
7. paradox460 ◴[] No.45084388[source]
I've been enjoying JJ recently, after giving it another try. I'd tried it when it was new, and the sharp corners were still a bit too sharp for my liking.

JJ seems to be part of a new "era" of tooling that's just really good. I mused about this a bit in a blog post:

https://pdx.su/blog/2025-08-13-the-quiet-software-tooling-re...

replies(4): >>45084426 #>>45084764 #>>45085176 #>>45085669 #
8. jennyholzer ◴[] No.45084398{3}[source]
Stacked diffs is a great feature. Thanks for the response!
replies(1): >>45084511 #
9. nchmy ◴[] No.45084405[source]
here's a few links that you might find useful. But it would be worth exploring the other recent hn discussions on the topic - i and other unabashed evangelists have shared a lot.

A great "Megamerge" workflow

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

https://ofcr.se/jujutsu-merge-workflow

And an absolutely fantastic TUI that wraps jj cli. Might be the best TUI ive ever used, and consistently getting better.

https://github.com/idursun/jjui

10. nchmy ◴[] No.45084426[source]
im glad to see you also use and love mise.

Mise, jj (and its phenomenal jjui TUI, which I see you mentioned there), and uv for python are nothing short of revolutionary, as far as I'm concerned. Just beautiful tools.

replies(2): >>45084516 #>>45088240 #
11. senekor ◴[] No.45084439[source]
Hi, author here. Since the target audience is people with little to no Git experience, a detailed comparison would not make sense. I did simply make that claim because the weirdness of Git's UI is usually justified by saying how powerful it is. So this statement is just intended to ease the readers mind that they're not missing out on power by choosing a tool that's easier to learn.
replies(3): >>45084539 #>>45084685 #>>45087146 #
12. ivanjermakov ◴[] No.45084465[source]
> Jujutsu is easier to learn than Git. Git is known for its complicated, unintuitive user interface. Jujutsu gives you all the functionality of Git with a lot less complexity.

Being easier to use does not mean being easier to learn. Complicated workflows requiring deep understanding might be harder to learn because of a higher abstraction.

replies(2): >>45085945 #>>45089712 #
13. quectophoton ◴[] No.45084466[source]
Would it be accurate to describe Jujutsu as "a Mercurial-inspired frontend for Git"?

Also, another question I have for people who have used Jujutsu: Is it focused on interactive use, or is it also convenient to use for automatic/non-interactive use?

For example, situations like:

* A CI/CD pipeline that periodically adds stuff to the repo, or a pipeline that modifies files when triggered by specific events.

* Server setup scripts that clone a repo with common config and then make a new commit after applying patches for host-specific changes.

replies(3): >>45084541 #>>45088807 #>>45089534 #
14. firtstea ◴[] No.45084488[source]
I wish it were jiu-jitsu for everyone. I was disappointed to see it was a Rust project.
15. paradox460 ◴[] No.45084511{4}[source]
No worries.

JJ tends to fit modern software engineering a bit better than git, I've found. Here's sort of an example of what I do when I'm working with it

I'll open an empty change on top of the main branch, not really sure about where this feature is going to go but knowing that it needs to be new changes. Usually this is already done for me automatically because my main synced with the remote, and therefore is an immutable commit. As I'm writing code I'm not really concerned with how I'm going to commit it to the graph, it's just a big pile of things all changing at the same time. Maybe I do features that are unrelated, or only tangentially related, to the actual story I'm working on.

If I get interrupted, I might go over to JJ and describe my changes, typically something along the lines of work in progress with a list of what still needs to be done and what I've been trying to do, basically a capture of my state of mind at the time. I'll then create a new change on top of that, for when I can come back later.

Once I'm finished with all of the story and it's working to my satisfaction, I probably have a big ball of changes that need to be split up and organized properly. First thing I do is look at what changes are necessary for this story, which are dependence for it, and which are just things I did because it was convenient to do them at the time. I'll split the dependencies out first putting them into their own changes. Then I'll make the actual story related changes, and then I'll make the ones that are just one offs. With a little bit of rebasing, I now will typically have three or four different bookmarks, which is the JJ analog of branches, some in parallel and some dependent on each other, that I can push up to my remote repository and open up pull requests.

I made a couple shell scripts to handle some common things, like recursively splitting a change into parts, but you don't really need them, as they're just wrappers around built in JJ stuff or adaptations to some of my workflows. I touch on them in a blog post that's mostly about JJ: https://pdx.su/blog/2025-08-13-the-quiet-software-tooling-re...

replies(1): >>45085579 #
16. paradox460 ◴[] No.45084516{3}[source]
I probably should mention things like atuin and fish, which bring, if not joy, absence of frustration when using my computer
replies(1): >>45085498 #
17. thewisenerd ◴[] No.45084536[source]
i've been using jj for the past few weeks on a feature heavy project. my entire "workflow" is no more than these 4 commands, rinse and repeat:

    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.

replies(3): >>45084604 #>>45084626 #>>45084805 #
18. jennyholzer ◴[] No.45084539{3}[source]
I appreciate this perspective.

IMO, the authors and evangelists of Git are essentially correct when they argue about its power.

However, I think that it's extremely difficult to gain practical experience with using Git in a high-powered, high-agency way, mostly because there are a lot of abstract concepts at play and there is no easily accessible place where these concepts can be "discovered".

Basically, Git is as good as it's cracked up to be, but only if you're an expert.

If you're interested in becoming a Git expert, I cannot recommend Emacs Magit strongly enough.

If not, I think Jujutsu could be an quicker road to a high-agency version control workflow. It's at least worth considering. I feel confident that Jujutsu can succeed, in particular because of Git's harsh difficulty curve.

replies(3): >>45084724 #>>45087011 #>>45088109 #
19. paradox460 ◴[] No.45084541[source]
Not really, but it's a strong enough description that people who know what both are would understand

JJ is a whole new VCS, that just so happens to be backwards compatible with git, and currently uses git as it's storage engine

It builds atop the knowledge we gained since mercurial and git were both new, not making mistakes that the others made, as well as knowledge from newer VCS systems like fig and sapling

20. veqq ◴[] No.45084556[source]
jj is similar to a magit cli, brought to the rest of the world. There's no benefit to switching from magit.
21. senekor ◴[] No.45084557{3}[source]
> I'm having a hard time wrapping my mind around what specific details would cause me to choose Jujutsu over Git, in particular because of Git's industry standard status.

Jujutsu is Git-compatible, so there's nothing to lose. It can literally create the `.git` directory next to the `.jj` directory to fool all your existing tools into thinking this is a git repository.

There are a few limitations... Jujutsu currently ignores submodules, for example. So you have to run `git submodule update` sometimes. And when you yourself update the submodule, you need to `git commit` instead of `jj commit`.

Git LFS is also not supported. Apart from that, it's smooth sailing AFAIK.

> I think this is a very interesting concept, but I think it could go farther with some more targeted marketing along these lines. Of course, if Git power users are not Jujutsu's intended audience, then this comment may be irrelvant.

Git power users are definitely part of the target audience, most Jujutsu users today are retired Git power users. Because that's not the target audience of my tutorial though, I didn't write much about that. Some of the features jj users are most excited about include: - Conflicts are non-blocking. Merge and rebase always succeed, conflicts are recorded in the commit itself. You can work on something else and come back later to solve them. - There is `jj undo` and `jj redo` which work like Ctrl[+Shift]+Z in GUI apps and text editors. They affect the whole repository, because that has basically its own linear history. Reflog on steroids, basically. - `jj absorb` can find the most recent commit that touched the same lines and squash your changes into it. It's magical if you're working on several things in parallel (by merging the separate branches together, just for development.)

These are just some examples that come up the most in the "appreciation" channel on the Jujutsu discord.

> I think one of Git's great weaknesses is its unfriendliness to newcomers (jargon, deep features, lack of discoverability, lack of accessible GUI frontends), so there's probably a lot of potential for a VC solution that is easier for a newcomer to jump into.

Yes! I think Jujutsu has a lot of potential there as well. But there's a lack of learning material for that target audience... hence why I wrote this tutorial :-)

22. veqq ◴[] No.45084575{3}[source]
jj is akin to a wrapper of coherent aliases thrown over git. No one else knows you're using it, because it uses git under the hood. It's just a simpler abstraction without insane naming and flag conventions.
replies(2): >>45084773 #>>45084893 #
23. senekor ◴[] No.45084604[source]
Just a few tips:

> - 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.

24. paradox460 ◴[] No.45084626[source]
> - i'm forced to be "strict" with my changeset since there's no `git add -P`

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

25. 0-R-1-0-N ◴[] No.45084651[source]
I would like to see an example of someone showing the workflow for using jj and doing feature branch. I don’t really get that yet. Most examples only show that they commit once and then push. But what if it requires multiple commits.
replies(1): >>45087584 #
26. pkulak ◴[] No.45084678[source]
Say you start on Main, then make a new branch that you intend to be a PR someday. You make commit 1. Then another. Maybe 6 more. Now you realize that something in commit 1 should have been done differently. So, you "edit" commit 1. All the other commits automatically rebase on top and when you go back to your last commit, it's there. Same with _after_ you PR and someone notices something in commit 3. Edit it, push, and it's fixed.

You can do all that in Git, but I sure as hell never did; and my co-workers really appreciate PRs that are broken into lots of little commits that can be easily looked over, one by one.

replies(4): >>45084727 #>>45084733 #>>45084935 #>>45085106 #
27. marcuskaz ◴[] No.45084685{3}[source]
I suppose I want the article written for the experienced developer, convince me why I should try something different than the huge defacto standard that is git. I'm totally open to trying something new, but need a compelling case.

Beyond `jj undo` everything else in this thread feels just as complicated as git.

replies(3): >>45084810 #>>45088010 #>>45089061 #
28. wry_discontent ◴[] No.45084694[source]
I switched from Magit to jujutsu. The only difference for me is that stacking PRs has become simpler. It's also made me more conscious of making smaller changes and shrinking what I consider "worth shipping".

Overall a positive experience, but if Magit is working for you, there's no killer feature that makes it worthwhile.

I'm also using https://github.com/bolivier/jj-mode.el which lets me do enough of what I need with jj from within Emacs.

replies(1): >>45085750 #
29. njaremko ◴[] No.45084716[source]
Has anyone who's enjoying Jujutsu tried Meta's Sapling? I've been using it lately with the VS Code plugin, and it's been great. My understanding is that Jujutsu is pretty heavily inspired by Sapling and Google's patch-based git workflow?

https://sapling-scm.com/

replies(3): >>45084752 #>>45085004 #>>45089623 #
30. senekor ◴[] No.45084724{4}[source]
Thanks, but I consider myself a Git expert already :-) I read the Pro Git book cover to cover. I have a gluten-free, artisanal, free-range git config that I've grown and cared for over years. single character aliases all the important commands, "log all graph oneline", "commit amend no-edit", interactive rebase (ofc. with autosquash, autostash, updaterefs and rebasemerges), reset hard, push force-with-lease... Also: commit signing, url rewriting, conditional configs for different orgs, all that jazz. I was super productive with it and loved it.

And then Jujutsu came along and casually doubled my VCS productivity. I didn't see it coming!

replies(1): >>45085346 #
31. lrobinovitch ◴[] No.45084727{3}[source]
You have to force push each time you do this, right? How do your coworkers find the incremental change you made to commit 1 after you force push it, and how do you deal with collaborative branches effectively this way? And if I don't want to work this way and force push, are there other benefits of jj?
replies(2): >>45084790 #>>45085517 #
32. adastra22 ◴[] No.45084733{3}[source]
I do this every day in git. “git rebase -i [hash]” fyi.
replies(1): >>45084782 #
33. sunshowers ◴[] No.45084752[source]
I used to work on Sapling and Mercurial while at Facebook. I've been using Jujutsu full time for the last two years.

Jujutsu is in a sense the final form of that style of VCS, which I characterize as making commits first-class rather than branches, and providing powerful tools for managing long queues of stacked changes (git rebase -i is nice but has many limitations that don't exist in Jujutsu).

I go into some more detail in my testimonial, the first one at https://jj-vcs.github.io/jj/latest/testimonials/#what-the-us....

34. sunshowers ◴[] No.45084764[source]
Nice article! I do think standards for tooling have gone up a lot in the last few years. Rust is certainly a part of it.
35. senekor ◴[] No.45084773{4}[source]
I mean, it certainly is that, but not only that. Jujutsu has more to offer than a consistently designed CLI. You can't do non-blocking merge/rebase conflicts with Git aliases. You can't do `jj absorb` with Git aliases. And you can't automatically rebase all descendants of a branch without first merging them all together, which may cause conflicts that then prevent you from performing the actual rebase. Just some examples of "more than a bunch of Git aliases" ;-)
36. baq ◴[] No.45084782{4}[source]
you think you do, but you don't; jj edit is much, much better than an edit step in a rebase - it essentially keeps rebasing while you're editing, so you can always see which changes get conflicts, then you are free to resolve them, or not, at your convenience.
replies(3): >>45084799 #>>45086057 #>>45086723 #
37. baq ◴[] No.45084790{4}[source]
the heuristic is 'if you know about rerere and especially if you use it, you should try jj'. if you never force push, you might not see value in jj. (I basically always force push.)
replies(1): >>45084944 #
38. adastra22 ◴[] No.45084799{5}[source]
So you get all merge conflicts at once? How is that better?
replies(1): >>45084858 #
39. layer8 ◴[] No.45084800[source]
One showstopper for me is that Jujutsu doesn’t support .gitattributes (https://jj-vcs.github.io/jj/latest/git-compatibility/).
replies(3): >>45085384 #>>45085408 #>>45087806 #
40. epolanski ◴[] No.45084805[source]
Can't you just add an alias that takes two arguments that takes a message and a bookmark name? I aliased the entire describe, commit, bookmark and push to a single command.
41. baq ◴[] No.45084810{4}[source]
do you know about git rerere? if yes, you might like jj.
replies(1): >>45089012 #
42. tcoff91 ◴[] No.45084835{3}[source]
The main thing that makes it more powerful imo is that you can accomplish insane rebases with 1 command that would be really difficult with git.

Like let’s say you have 4 separate PRs in review that have no dependency on each other. You then work on new stuff on top of an octopus merge of all 4. You are exploring different approaches to a solution so you have several anonymous branches where you have tried different things. You want to rebase on master, so you just run jj rebase -d master. All 4 PR branches, the octopus merge, the anonymous branches, they all get rebased with that 1 command. If there are conflicts the first class conflicts mean that you can fix the conflicts whenever you want. If one of your experimental anonymous branches is in conflict but you are unlikely to go with that approach, just leave it in a conflicted state unless you change your mind that you want to actually go that direction.

43. baq ◴[] No.45084858{6}[source]
it's exponentially better because you don't need to resolve them until you're ready. conflicts are committed to the local repo like everything else, commits with conflicts are noisily warned about and you can fix them whenever instead of having no other option than immediately.
replies(2): >>45084917 #>>45085054 #
44. Ericson2314 ◴[] No.45084874[source]
I want to read "Jutustsu for Git experts"

For example, will the committing of conflicts (a good idea I agree), mess up my existing git rerere?

Also I agree that the staged vs unstaged distinction is stupid and should be abolished, but I do like intentionally staging "the parts of the patch I like" while I work with git add -p. Is there a a lightweight way to have such a 2-patch-deep patch set with JJ that won't involve touching timestamps unnecessarily, causing extra rebuilds with stupid build systems?

replies(3): >>45085487 #>>45088024 #>>45088309 #
45. collinmcnulty ◴[] No.45084889[source]
I’ll hop on the train and say that I’ve been using jj recently as well, and it’s given me that feeling of safety and freedom I got when I first started using version control 10 years ago. That sense that “well this is a crazy idea but I’ll just commit now and try it, I can always roll back”.
replies(1): >>45089237 #
46. baq ◴[] No.45084893{4}[source]
it's much more than that. jj decouples the tree snapshot (git commit) from the patch id (jj change id); this allows workflows that are possible in git, but hard or inconvenient; if you add deferring conflict resolution, the most efficient jj workflow is similar but very much not the same as the most efficient git workflow.
47. adastra22 ◴[] No.45084917{7}[source]
How does your repo work with conflicts? How does it compile?
replies(2): >>45085447 #>>45086310 #
48. philwelch ◴[] No.45084935{3}[source]
I do that in Git all the time. JJ might be easier in some sense but “more powerful” implies that it can do things that are impossible in Git.
replies(3): >>45085525 #>>45085869 #>>45086493 #
49. thramp ◴[] No.45084937{3}[source]
(disclosure: I started a jj company but I don’t have anything to sell yet.)

> Are there "central concepts" in the Jujutsu design?

I think a central concept in jj is that everything is a commit: there’s no staging index, stashes, or unstaged files, only commits. This has a few pretty neat implications: - changes are automatically to recorded to the current commit. - commits are very cheap to create. - as others have already said, rebases (and history manipulation in general!) are way cheaper than in git. It helps that jj as a whole is oriented around manipulating and rewriting commits. Stacked diffs fall out of this model for free. - Since everything is now a commit, merge/rebase conflicts aren’t anything special anymore because a commit can simply be marked as having “a conflict” and you, as the user, can simply resolve the conflict at your own leisure. While I’m sure you know already know thus, I’d rather make subtext text: in git and mercurial, you’d need to resolve the conflict as it got materialized or abort the rebase entirely. - because everything is a commit, jj has a universal undo button that falls out effectively, for free: `jj undo`. It feels so incredibly powerful to have a safety net!

> All other Git UIs I've used have been severely lacking, but Magit has made me significantly more productive with Git, and has convinced me of the "magic of git".

I can’t speak to this personally, but I’ve had friends who are magit users tell me that jj isn’t a big enough improvement (on a day-to-day, not conceptual, basis) over magit to switch at this time. It’d be really cool to have a magit-but-for-jj, but I don’t think anyone has written one yet.

50. lrobinovitch ◴[] No.45084944{5}[source]
That makes sense, good to know, thanks.

> I basically always force push

How do your colleagues deal with this, or is this mostly on experimental branches or individual projects?

replies(4): >>45085108 #>>45085122 #>>45085521 #>>45086437 #
51. alabhyajindal ◴[] No.45084953[source]
I don't know anything about Jujutsu but the Git equivalents for some of these commands are more readable:

  jj bookmark move main --to @-
  git add . 

  jj git init --colocate
  git init
Also using `jj git` everytime feels repetitive.
replies(1): >>45085492 #
52. zamalek ◴[] No.45084963[source]
I'm a through-and-through JJ convert. One papercut I have experienced a few times (though I am getting much better at avoiding) is what to do when I forget to `jj new`. Assuming that I have pushed the current bookmark to the remote, is there any way to recover a new change that is the diff against the remote? I have tried rebasing, but that leads to bookmark ambiguity instead of the solution I'm looking for.
replies(5): >>45084985 #>>45085090 #>>45085256 #>>45085704 #>>45085910 #
53. AndrewHampton ◴[] No.45084985[source]
Is `jj split` a good option?
replies(1): >>45085542 #
54. BeetleB ◴[] No.45084995[source]
I had used jj for only a month before I switched back to git for a particular project at work. I felt that jj was *really* nice, but nothing more.

After returning to vanilla git, I was missing the jj convenience within hours.

https://blog.nawaz.org/posts/2025/Aug/the-jujutsu-effect/

replies(2): >>45085022 #>>45085178 #
55. thramp ◴[] No.45085004[source]
I’d second Rain’s reply, but having gone from git to sapling to jujutsu, I feel like the jump from sapling to jujutsu was as big as the jump from git to sapling, in terms of “oh, this is a way nicer workflow”. I really like and miss Sapling’s interactive smart log, but I found jj’s conceptual simplicity to be more compelling than ISL. That said, VisualJJ and Jujutsu Kaizen (both listed on https://jj-vcs.github.io/jj/latest/community_tools/) might give you the ISL-style experience you’re looking for.
56. incognito124 ◴[] No.45085022[source]
Something about appreciating only after losing
replies(1): >>45085265 #
57. ec109685 ◴[] No.45085054{7}[source]
To the sibling comment that is too deep to reply to, this covers why delayed conflict resolution is advantageous: https://news.ycombinator.com/item?id=45084835

It’s for other branches that hang off the commit that introduced the conflicts.

58. dylanhu ◴[] No.45085090[source]
I also run into this often, I wonder if there is a simple built-in feature that would allow you to diff against the remote.
replies(1): >>45089118 #
59. ileonichwiesz ◴[] No.45085106{3}[source]
Okay, sure, but if I realize I should’ve done something differently in commit 1, why wouldn’t I just make a new commit with the fix?
replies(3): >>45085225 #>>45085789 #>>45087384 #
60. whateveracct ◴[] No.45085108{6}[source]
People barely ever work off my branches.
61. smw ◴[] No.45085122{6}[source]
It's generally fine if you force push a branch that you're the only one working on. In many projects, there's an expectation that the 'PR Branch' you create in order to make a github pull request is owned by you, and can be rebased/edited/force-pushed at will. It's very common to do things like `git commit --amend --no-edit` to fix a typo or lint issue and then force push to update the last commit.

This has it's problems, and there's a reason things like Geritt are popular in some more sophisticated shops, as they make it much easier to review changes to PRs in response to reviews, as an example.

62. notallm ◴[] No.45085145[source]
thought i was gonna learn how to fight for a second lol
replies(1): >>45085228 #
63. yard2010 ◴[] No.45085176[source]
Bun leads this movement.
replies(2): >>45085829 #>>45085871 #
64. andai ◴[] No.45085178[source]
>but nothing more

What does that imply?

replies(2): >>45085222 #>>45085466 #
65. palata ◴[] No.45085211[source]
> Jujutsu is more powerful than Git

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.

replies(1): >>45085386 #
66. xmonkee ◴[] No.45085219[source]
Does jj effectively support stacked diffs on github? I've been paying through the nose for Graphite lately because it does, and the feeling I get is I'm just paying for their nice cli tool. `gt create` and `gt sync` is all I need. I don't really fully agree that their interface is entirely necessary or worth the $3500 per year it costs right now for my team.
replies(4): >>45085493 #>>45085886 #>>45085908 #>>45089246 #
67. lemonberry ◴[] No.45085222{3}[source]
From their post:

"Within hours, I found myself being exceedingly cautious about everything. I missed the confidence jj undo gave me. I missed the simplicity of jj new and jj describe."

replies(1): >>45085468 #
68. tomstuart ◴[] No.45085225{4}[source]
Do you want another person (or yourself in the future) to be able to read your commits, in order, to get a clear account of what changed & why? If so, you should fix up those commits to address mistakes. If not, it doesn’t matter.
replies(3): >>45085388 #>>45085450 #>>45090987 #
69. efilife ◴[] No.45085228[source]
same, I'm kinda disappointed
70. abound ◴[] No.45085256[source]
There's a few different ways to fix this, but here's what the official docs suggest: https://jj-vcs.github.io/jj/latest/FAQ/#i-accidentally-chang...

TL;DR - `jj evolog` and then restore changes from the relevant secret commit ID

replies(2): >>45085537 #>>45086931 #
71. ysofunny ◴[] No.45085265{3}[source]
also, demonstrating a marked improvement in the experience.

it really does seem like we all gonna be using jj soon enough

I recall pijul.org that was another working prototype of better git

and I wonder how much overlap is there in the way they have made the improvements.

replies(4): >>45085444 #>>45085469 #>>45085608 #>>45085649 #
72. cube2222 ◴[] No.45085269[source]
Jujutsu is pretty excellent. I’ve tried it a couple months back (and written about, along with a couple common patterns [0]) and have been using it since.

It’s just a very “consistent” experience. I never really had issues with git (so didn’t expect I’d stay with jj), but for anything more advanced I’d generally have to google. In jj everything is based on a couple primitives, and it’s easy to combine them to do history reshaping of arbitrary complexity.

My workflow used to be very stash-oriented, and with the way jj changes work, auto tracking, and letting me switch between changes, it ends up being much more pleasant than git.

Rebasing with conflict resolution is generally much nicer too (esp. in the stash-like workflow).

Anyway, very recommended, and effort required to switch is very small.

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

73. lvl155 ◴[] No.45085286[source]
OT, but are there agent-specific versioning systems? Or are people just retrofitting git/jj?
replies(2): >>45085300 #>>45085389 #
74. KolmogorovComp ◴[] No.45085300[source]
Why would agents need a specific versioning system?
replies(1): >>45085436 #
75. ivanb ◴[] No.45085317[source]
I haven't yet given jj a proper trial, so pardon the ignorance. All I've seen are conveniences regarding the working the copy. Besides the subjectively terrible UI, my biggest gripes with Git have to do with collaboration. A rebase may unintentionally overwrite someone's work. A force-push to trunk breaks every other developer's working copy. With "distributed" being in the name of this whole class of VCSes, I would expect that such things just wouldn't happen, but here we are. As I understand it, jj inherits all of these problems and adds better concepts and UI for manipulating the working copy. I'm not sure this alone is a good enough justification for a switch. Of all the Git's features I use a tiny, proven subset and stay on the beaten path. It makes it bearable enough.
replies(5): >>45085462 #>>45085736 #>>45085741 #>>45086030 #>>45090001 #
76. nocman ◴[] No.45085346{5}[source]
Is there a particular pain point (or set of pain points) that you have using git which is removed when you use Jujutsu?

I am interested to know, because there seem to be a small number of people who really seem to like it, and up to this point I haven't been able to understand what it is that they are all so excited about.

replies(4): >>45085518 #>>45085779 #>>45087984 #>>45091664 #
77. IshKebab ◴[] No.45085363[source]
Another question for jj people: one concrete thing I find annoying with Git is that it's completely incapable or rebasing anything with merge commits. Can jj do this? Even very simple merges are too much for git to rebase. It's especially annoying with `git subtree`. If I want to rebase anything to do with that I don't bother now - I just start again from scratch.
replies(3): >>45086048 #>>45086902 #>>45086938 #
78. a022311 ◴[] No.45085375[source]
Nice work! I've been using Jujutsu for almost 5 months and have completely replaced git. In fact, I've interacted with Git only 52 times (41 if we exclude my invocations of `--help` xD) since then vs 582 with Jujutsu.

Instead of having to adapt to a particular workflow, Jujutsu is flexible enough to support pretty much any workflow. Even when mostly using it like git, I have the freedom to jump around commits and branches (no stashing required), rebase easily (this may be simple for git power users, but I really appreciate being able to do this without VSCode's git tools) and just... get work done without extra headaches caused by VCS. And all that while writing my changes to an actual git repository just like everybody else's, that git tooling can interact with. When was the last time you used a different VCS than your coworkers without having them switch to it too?

While there are some rough edges regarding tags, submodules and LFS (I might be missing something else here), it's certainly worth a try.

replies(2): >>45085458 #>>45085620 #
79. verdverm ◴[] No.45085384[source]
or LFS, hooks, submodules, shallow clones
replies(1): >>45087811 #
80. mdaniel ◴[] No.45085386[source]
I was one of the early adopters (cause I enjoy kicking the tires on things) and brought awareness of the need for gpg signing at all into JJ, so I wouldn't overlook the value in bumping https://github.com/jj-vcs/jj/issues/6688 so they can eventually get you there
81. KallDrexx ◴[] No.45085388{5}[source]
Not the OP but for me, no I don't actually.

In a PR branch, my branches usually have a bunch of WIP commits, especially if I've worked on a PR across day boundaries. It's common for more complex PRs that I started down one path and then changed to another path, in which case a lot of work that went into earlier commits is no longer relevant to the picture as a whole.

Once a PR has been submitted for review, I NEVER want to change previous commits and force push, because that breaks common tooling that other team mates rely on to see what changes since their last review. When you do a force push, they now have to review the full PR because they can't be guaranteed exactly which lines changed, and your commit message for the old pr is now muddled.

Once the PR has been merged, I prefer it merged as a single squashed commit so it's reflective of the single atomic PR (because most of the intermediary commits have never actually mattered to debugging a bug caused by a PR).

And if I've already merged a commit to main, then I 100% don't want to rewrite the history of that other commit.

So personally I have never found the commit history of a PR branch useful enough that rewriting past commits was beneficial. The commit history of main is immensely useful, enough that you never want to rewrite that either.

replies(2): >>45088997 #>>45089139 #
82. jdmg94 ◴[] No.45085389[source]
what does "retrofitting" git implies? I am confused by the agent-specific systems bit.
83. mdaniel ◴[] No.45085408[source]
In every one of these threads I learn some new axis of git that JJ doesn't support, so I really do wonder if JJ should have squatted upon a different DVCS given how much it wants to do things the JJ way leading to folks with git experience being sad
replies(3): >>45085626 #>>45089103 #>>45090266 #
84. verdverm ◴[] No.45085436{3}[source]
Perhaps because Zed thinks it's a good idea?

https://zed.dev/blog/sequoia-backs-zed

DeltaDB (CRDT based)

replies(1): >>45087818 #
85. amadeuspagel ◴[] No.45085438[source]
> Jujutsu is compatible with Git. You're not actually losing anything by using Jujutsu. You can work with it on any existing project that uses Git for version control without issues. Tools that integrate with Git mostly work just as well with Jujutsu.

If I use git from zed and jj from the command line, will that just work?

replies(1): >>45085881 #
86. fiddlerwoaroof ◴[] No.45085444{4}[source]
pijul uses a completely different model of version control than git (stores diffs rather than snapshots). And so the cost of switching and interoperation is a bit higher than jj which basically acts like a nice UI over git.
87. baq ◴[] No.45085447{8}[source]
The edited commit, assuming it doesn’t have conflicts with predecessors, builds just fine. Successor commits with conflicts that you just introduced predictably don’t - but you aren’t editing them, so it isn’t a problem. In fact, that’s exactly why this feature is so compelling.
replies(1): >>45088030 #
88. christophilus ◴[] No.45085450{5}[source]
It’s useful for me to see the mistake and the fix, as it is a good way to jog my memory about the “why” of things. Pristine commit history is not important to me.
89. BeetleB ◴[] No.45085457[source]
If people want an example of jj being more powerful yet simpler than git:

jj doesn't have an explicit concept of the staging area or the stash, but it supports both and in a more powerful way.

In jj, the staging area is simply a terminal node in the graph. You make the changes you want. And when you are ready to commit, you simply push all the changes to the parent node. The terminal node is your index, the parent is the committed node. This is a construct that is entirely in your mind. You don't have to work this way, but if you really like the concept of a staging area, that's how you do it.

Have you ever done a git add, then made some changes, done a subsequent git add, only to realize you clobbered some important code that was in your first git add? How are you going to recover from this? I don't know if the git reflog has this information.

In jj, everything is saved. Think of each commit in jj as a supernode. Inside the supernode are a bunch of atomic nodes. You can think of each atomic node as the equivalent of doing a git add, with each git add being another atomic node in that supernode. So if you've ever clobbered your changes like this, you simply go and remove the last atomic node or modify it however you want to resolve it.

Effectively, jj gives your index its own version control.

Similarly, in jj, a stash is simply a branch. It's a node in a branch that stores the state of the working directory. If you're in the middle of a feature and suddenly need to stop working on it to work on something else, you simply make a new node off the relevant node you're going to work from. In other words, you will just create a new branch while retaining your work in its own branch.

Have you ever popped from the stash, made some changes, and then realized you clobbered something really important and wished you'd applied the stash instead of popping it? That's not at all a concern in jj. Effectively, you have a version controlled stash, and you naturally stash in jj without ever having to know the concept of a stash.

After I'd been using jj, having a separate concept called "index" and a separate concept called "stash" suddenly seemed ridiculous. I don't know why Git decided to have these distinct concepts. At the end of the day, it's all a graph and you are manipulating nodes and the contents within each node. What you need are operations to help you manipulate those.

I have to really emphasize that a typical jj user gets all this power just by learning a few operations - they don't have to learn so many different concepts.

How many different ways are there to do a git reset? In jj, I've only ever had to do jj undo and it covers pretty much all those use cases.

replies(1): >>45089392 #
90. nchmy ◴[] No.45085458[source]
try jjui out and you'll interact with jj nearly 0 times going forward. Its amazing.
replies(1): >>45085508 #
91. dzaima ◴[] No.45085462[source]
Working copy handling differences is certainly an aspect of jj, but, at least for me personally (esp. given me having grown up with git), it's probably my least favorite difference from git. And yet I've moved most of my personal things from git to jj (albeit with a bunch of custom scripts to make the working copy stuff more git-y).

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.

replies(1): >>45085962 #
92. BeetleB ◴[] No.45085466{3}[source]
What I mean is that using jj didn't seem like a radically new thing - it was simply a nicer git.

But when I had to go back to git, I learned that all those little niceties really added up.

93. nchmy ◴[] No.45085468{4}[source]
You missed the point - the question was, if jj is really nice, then why "nothing more"?
94. ivanb ◴[] No.45085469{4}[source]
Supposedly, Pijul doesn't have the "force-push to trunk" problem. This alone makes it interesting.
95. mdaniel ◴[] No.45085487[source]
> Also I agree that the staged vs unstaged distinction is stupid

...

> I do like intentionally staging "the parts of the patch I like" while I work with git add -p

is a mysterious perspective to me. I guess with enough $(git worktree && git diff && vi && git apply) it'd be possible to achieve the staging behavior without formally staging anything but yikes

I just checked and it seems that mercurial 7.1 still doesn't believe in $(hg add -p) so presumably that 'worktree' silliness is the only way to interactively add work in their world

replies(2): >>45088621 #>>45093843 #
96. a022311 ◴[] No.45085492[source]
This feels like a troll, but I'll answer anyway.

The first command you mention is `jj bookmark move main --to @-`, which apart from the `@-` part, is easily read and does exactly what is written. It moves the main bookmark to the parent commit. The main difference to git here is that updating a branch is an explicit action. Jujutsu supports creating aliases and many users have a `jj tug` alias that moves the current closest branch to point to the parent commit. `git add .` just adds all changed files in the current directory to the current commit. That's an entirely different thing and Jujutsu takes care of doing that automatically.

The second command creates a git repository and while it is longer, I doubt the extra keystrokes will hurt you much. If it's too long, just alias it.

As for using `jj git`, since Jujutsu supports multiple backends, backend-specific commands require the `git` namespace. You generally only need it for cloning, pushing and fetching (with the option for aliases again). You'll rarely need to create a new repository or add a remote.

replies(1): >>45086258 #
97. baq ◴[] No.45085493[source]
GitHub needs to redesign PRs to properly support stacking them. It isn’t a client problem, but jj does make rebasing multiple branches correctly trivial, so if GitHub supported this workflow in a non-pretend fashion, it’d work.

If rebasing correctly is enough to support your use case, then the answer is ‘it’ll work’.

98. nchmy ◴[] No.45085498{4}[source]
Atuin is great too! I recently switched to it from fzf.

zoxide is similar and beautiful as well. I use both all day.

replies(1): >>45085805 #
99. a022311 ◴[] No.45085508{3}[source]
haha, not interacting with a UI is a benefit for me. I do plan on trying it though.
100. Disposal8433 ◴[] No.45085517{4}[source]
IIRC it's push force with lease, ie non destructive push force. No one will be bothered or notice what you did.

And if you have conflicts, it's really easy to rebase and fix any issue.

101. nchmy ◴[] No.45085518{6}[source]
for me, everything git is a pain point. But its not so much painpoints that it addresses, as it is that it just makes entirely new things dead-simple to do, especially via jjui.

"megamerges" are one such example. ive shared many links, here and in other posts

replies(1): >>45086891 #
102. baq ◴[] No.45085521{6}[source]
The PRs are either small enough that it isn’t a problem or large enough that it isn’t a problem… the odd in-between PR experience sucks and it’s one of the cases when I sometimes add more commits instead of force pushing.

+1 to sibling gerrit recommendation; I used to use it a decade ago and it was better then than GitHub PRs today.

103. Vinnl ◴[] No.45085525{4}[source]
I think generally when people say that for jj, they mean it can do the same things with fewer concepts.
replies(1): >>45085860 #
104. zamalek ◴[] No.45085537{3}[source]
But that would undo what I've done, right? I Wang to keep the changes but only diff them against what I've pushed.
replies(2): >>45085852 #>>45086170 #
105. zamalek ◴[] No.45085542{3}[source]
I've tried it, but then I have to remember what has been pushed.
106. nchmy ◴[] No.45085579{5}[source]
Glad I'm not the only one who uses it to tidy up the chaos that is my iteration process... I have almost exactly the same workflow. jj just lets me work and not worry AT ALL about commits, vcs etc...
replies(1): >>45085856 #
107. idoubtit ◴[] No.45085587[source]
I've now seen a dozen of articles that explain that jj is wonderful and better than Git for everything. This tutorial is of the same kind. Now that I've read extensively about the good part, I'd be more interested by the bad and the ugly. Because my experience with jj was more balanced.

When I tried jj, I found a few pain points that made me return to Git. For instance, I was sharing a branch with a co-worker where we were just piling commits as soon as they were ready (after `pull --rebase` if necessary). Since jj doesn't have names branches, that workflow was easy with git and tedious with jj – even with the `tug` alias. The process in the "Tracking remote bookmarks" chapter of this tutorial still doesn't look nice to me.

Another pain point was that jj could not colocate with light clones, like `git clone --filter=blob:none`. Maybe that's fixed now.

replies(6): >>45085702 #>>45085823 #>>45085842 #>>45085897 #>>45086038 #>>45089721 #
108. baq ◴[] No.45085608{4}[source]
pijul is one of the projects that I’d just sponsor a team for a few years if I was a megacorp or a government research agency because it’s just so damn cool in theory, but has too many rough edges in day to day practice (IOW I’d like to try it but would need pijul colocate for it to make sense)
109. wocram ◴[] No.45085620[source]
What is the replacement for `git branch --set-upstream-to`?
replies(1): >>45086330 #
110. nchmy ◴[] No.45085626{3}[source]
nah, git was absolutely the right thing to squat on - its ubiquitous, so making it seamlessly powerful and easy to use makes for easy adoption. If it was built on pijul or whatever, I'd never have given it a try.

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

111. aseipp ◴[] No.45085631[source]
If you are already a heavy Magit user, then most of the basic ideas will probably be appealing and jj will let you bring those ideas to the command line. It will let you do some acrobatics you thought weren't possible before, ones that many users have come to love -- like rebasing a 5-way octopus merge with 2 extra leaves that result in conflicts you don't have to solve yet. (This is a technique known and popularized as "Mega Merge", which I suppose I am responsible for "inventing" along with its semi-silly name.)

I think Magit is an interesting parallel to jj. You say the "magic of git", but for both of them I think most of the "magic" has less to do with Git and more to do with the "design language" exposed to users, and by that I mean the tools that allow you to manipulate, navigate, and arrange commits (and diffs!) Git is more like a physical storage layer for both Magit and jj, but beyond that a lot of the special sauce is unique to their algorithms, their UX, and their "nouns and verbs".

In my experience, Magit users are generally harder to sell because for them jj isn't so revolutionary; die-hard Git powerusers who have been exclusively slugging it out on the command line to perform 10-stack rebases for the last 5+ years tend to be very easy to sell, in comparison. But all these users tend to understand and appreciate the power of a well-designed set of tools for commit graph manipulation. Git powerusers tend to be some of our most hardcore converts and advocates, really.

Full disclosure but I'm one of the jj maintainers and my opinion is that it's pretty good. Maybe try it out for a few days (perhaps without using Magit, if you can :)

replies(1): >>45092179 #
112. tombert ◴[] No.45085648[source]
Interesting; despite being chronically online I hadn't actually heard of Jujutsu before. I like the idea of a better VCS still using Git under the covers, if for no other reason so that I can continue to use Github or Gitlab to back up and share my code with people. I've never been able to stick with Fossil or Bitkeeper even though they are both very neat because they've never been "sufficiently better" to override the network-effect of Git's popularity.

I'll need to play with this. I don't know that I'll stick with it but I'd be happy enough to be wrong.

113. hooper ◴[] No.45085649{4}[source]
Jujutsu has "first class conflicts", but it's different from Pijul's "theory of patches". As far as I know, the other big stuff like "working copy is a commit" and the "operation log" (which allows for `jj undo`, safe concurrency, etc) is not present in Pijul. The approaches to Git interop are very different.
114. ItsHarper ◴[] No.45085669[source]
I'd definitely include nushell in this
replies(1): >>45089449 #
115. wocram ◴[] No.45085702[source]
https://github.com/jj-vcs/jj/discussions/3549 exists to simplify the tug workflow somewhat.
116. benoitg ◴[] No.45085704[source]
I fixed this by changing the default immutable heads to include all remote bookmarks. This way, you cannot edit them and the new commit is automatically created when you push to the remote branch.

  [revset-aliases]
  "immutable_heads()" = "trunk() | tags() | remote_bookmarks()"
117. trueismywork ◴[] No.45085736[source]
The ability to resolve merge conflicts one part at a time is a game changer itself.

> 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.

118. paradox460 ◴[] No.45085741[source]
JJ let's you have multiple "versions" of the same branch, although not directly. Typically it comes about when you've made changes and someone else has made changes, and the conflict resolution can't happen cleanly, due to each path taking a difference, unreconcilible approach. You'll have to resolve those and unify the different "branches", but it's no harder than any rebase in git land (generally easier because jj's conflict markers are even better than git's 3 part system)
119. trueismywork ◴[] No.45085750{3}[source]
How do you incrementally resolve merge conflicts in magit?
replies(1): >>45092984 #
120. BeetleB ◴[] No.45085779{6}[source]
I would think the obvious answer is how jj deals with merge conflicts.

In git, if you get a conflict, you feel like you have to resolve it now.

With jj, most of the times I get merge conflicts, I simply ignore them and deal with them later. A conflict is not at all a blocker.

replies(2): >>45086184 #>>45086767 #
121. paradox460 ◴[] No.45085789{4}[source]
You can actually do that in JJ too. And you can take a change that's full of changes to other files, run a single command, and have those changes automatically put into the most recent change (save the one you're working on) that modified it recently.
122. paradox460 ◴[] No.45085805{5}[source]
I love zoxoxe, and even have it plugged into my Alfred, with minor tweaks
123. stouset ◴[] No.45085823[source]
I’m slightly confused. jj has named branches. They’re just called “bookmarks”.

Once you track the remote bookmark, `jj git fetch` will update your local one to match the remote.

replies(2): >>45087124 #>>45089727 #
124. paradox460 ◴[] No.45085829{3}[source]
While bun is extremely impressive, I'd not say it leads the movement more than anything else. For me then biggest sea change in developer QoL has to be Mise, followed by JJ. Everything else is more niche, but I use mise and JJ in every project.

Don't get me wrong, I really like bun. I run my blogs asset pipeline on bun, and run my home automation on it as well, but at the end of the day it's a js runtime, competing with the also excellent deno

replies(1): >>45086011 #
125. shepherdjerred ◴[] No.45085842[source]
One thing that has bitten me a few times is jj randomly losing my changes. As in, I'll be working in Cursor, not run any mutating jj command (maybe I'll run jj status or jj log, but nothing else), and later on my changes are gone from my repository (often times with a message about a stale workspace).

I'm not sure if this is somehow related to my IDE, working in a huge monorepo, or something else, but it has been quite painful.

Aside from that, though, I do really like the flexibility jj provides.

replies(3): >>45086378 #>>45087511 #>>45087664 #
126. ec109685 ◴[] No.45085843[source]
This tutorial is excellent. It’d be helpful to also include how jj flows map to pull requests in the GitHub/GitLab model, since many new jj users collaborate via PRs with main protected by default.

What’s the best way to represent a stack? How do you model jj’s editing a commit at the bottom where everything above implicitly rebases when pull requests are open?

One of the most intimating things is fumbling around with your vcs when an expert is reviewing your code.

replies(1): >>45090004 #
127. dzaima ◴[] No.45085852{4}[source]
You can do a `jj new; jj restore --restore-descendants -f commit_id -t @-` with a commit id (not change id!) from the evolog to "inject" a snapshot from the evolog below your current @, leaving the file state at @ (and everywhere else in the log) intact.

Or if you have a commit ID (or otherwise revset; so a "main@origin" or whatever would work) of a specific commit that you want already, can use that instead of course too.

128. paradox460 ◴[] No.45085856{6}[source]
What's even more interesting is how ally coworkers now know I'm "the guy who can sort out branch complexity"

They'll ask me to merge a dozen PRs into a test branch or whatever and it's a piece of cake in jj

129. sswatson ◴[] No.45085860{5}[source]
The author lists that as a separate benefit, though.

My interpretation is that jj makes certain useful operations convenient to use that would be so complex in git as to be completely impractical. Something like jj undo would be a simple example: jj users can do it, and git users can’t, even though it’s logically possible in both systems.

130. sunshowers ◴[] No.45085869{4}[source]
If you're in the middle of a git rebase -i of a stack of 20 commits, and realize while editing commit 15 that you made a mistake at commit 8, how do you go back and edit commit 8 without having to complete the rebase -i?

This is not contrived — this is an entirely realistic scenario that I use jj to handle all the time.

replies(1): >>45086836 #
131. shepherdjerred ◴[] No.45085871{3}[source]
Bun is great, but that's overselling it
132. paradox460 ◴[] No.45085881[source]
Generally. JJ will sometimes leave git in a detached head state, but it's usually pretty easy to recover from on the git side. The one thing I'd caution on is making sure you keep your bookmarks on the JJ side up to date. These translate to branches, which git needs to know where it is

You might want to set up this feature: https://github.com/jj-vcs/jj/discussions/3549

133. shepherdjerred ◴[] No.45085886[source]
jj is great, but it doesn't quite fill the void of Graphite.

I've heard good things about https://abhinav.github.io/git-spice/

replies(1): >>45093952 #
134. Disposal8433 ◴[] No.45085897[source]
> Since jj doesn't have names branches

False. You need to call `jj branch set -r@ XYX` manually which can be a PITA but you only need to do that once you push. Or there is `jj git push --named XYZ=@` which moves the branch.

replies(1): >>45087010 #
135. paradox460 ◴[] No.45085908[source]
Kind of. You can do most of it by hand, and there are some community made tools that improve the experience and basically give you graphite lite

https://github.com/keanemind/jj-stack

136. aseipp ◴[] No.45085910[source]
If I understand you correctly, then you can use `jj interdiff --from name-of-branch@remote --to name-of-branch@git` in order to recover the diff between the remote branch commit and your local version of it; you could then turn that invocation into a Git .patch file and reapply it with patch(1). This only works for one commit.

The more general solution you're looking for I guess is some kind of "split based on interdiff" or something like that. I don't believe there's any way to accomplish this in a single stroke, though.

137. Disposal8433 ◴[] No.45085945[source]
> deep understanding

It took me less than one day to switch from git.

138. paradox460 ◴[] No.45085962{3}[source]
One of the quiet bit of genius is that JJ change ids use a different set of characters for base16. Instead of 0-9A-F they use k-z
replies(1): >>45089573 #
139. 8n4vidtmkvmk ◴[] No.45086011{4}[source]
It's more than a runtime. It's a test runner, package manager and build tool and shell interpreter. The first 2 in particular are big for me. Build tool needs work.
140. boltzmann64 ◴[] No.45086030[source]
I am think you are misunderstanding the entire premise of git. There is no "force push to trunk breaks every other developers' working copy." There is no central repo/trunk where all the commits are pushed. You are probably thinking of svn.

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.

replies(2): >>45086511 #>>45087314 #
141. pokipoke ◴[] No.45086038[source]
> Another pain point was that jj could not colocate with light clones, like `git clone --filter=blob:none`. Maybe that's fixed now.

It's not

replies(1): >>45092217 #
142. aseipp ◴[] No.45086048[source]
Yes. I have one repository, which is a fork of an OSS project, with a 7-way octopus merge of various patches I carry, and another 2-way merge on top of that one. Every week or two I pull the latest changes from upstream and I rebase all 9+ branches in a single command. If there are conflicts on any one of the parallel streams of work, I can trivially fix each conflict individually in any order I want.

Jujutsu does not treat merge commits any more or less special than non-merge "1-parent" commits. (We used to do this, actually, and occasionally special case merges, but most users found it very confusing.) This regularity means most commands work fine on merges. 'jj new X' is a new commit on top of X. 'jj new X Y' is a merge commit with X and Y as parents. 'jj new X Y Z...' is a 3-way merge, and so on and so forth for any number of commits. Similarly, 'jj rebase' can handle moving commits and preserving the graph structure no matter how many edges are involved in particular, and can add or remove parents from a given commit. This means that conceptually the jj commit graph is merely a simple, ordinary DAG, and operations are transformations on the DAG like you expect.

Actually, this exact workflow is beloved by many community members, and I guess I can take responsibility for popularizing it originally, the "Mega Merge" technique. Instant, easy rebase is an essential part of making this technique viable.

- https://ofcr.se/jujutsu-merge-workflow - https://v5.chriskrycho.com/journal/jujutsu-megamerges-and-jj...

143. 8n4vidtmkvmk ◴[] No.45086057{5}[source]
Jj edit isn't even the jj way of doing things. Should be jj new. Unless you have changes stacked after then you'd do jj new -A. And squish when you're done
replies(1): >>45086389 #
144. _bent ◴[] No.45086170{4}[source]
i guess you can run `jj duplicate` before?
145. touristtam ◴[] No.45086184{7}[source]
> With jj, most of the times I get merge conflicts, I simply ignore them and deal with them later.

Sorry? You what? How do you know which bit from which source goes where?

replies(1): >>45086227 #
146. BeetleB ◴[] No.45086227{8}[source]
Here's a typical scenario.

You do a git pull, just so your branch isn't so out of sync. Immediately you get merge conflicts. You then tell jj "Hey, I'll deal with this later", and make a branch off of the last commit that was conflict free and continue your work there. jj stores the conflict as is, but your branch is conflict free.

When you feel you have the energy to deal with the conflict, you switch to the branch that has the conflict, and fix the issue(s). Then you can manipulate the graph (rebase, whatever) so you can have everything in one branch - your changes and the changes you pulled in.

replies(3): >>45086667 #>>45087058 #>>45091805 #
147. alabhyajindal ◴[] No.45086258{3}[source]
Thanks for your reply! I wasn't trying to be dismissive in my original comment. Just making an observation as someone new to Jujutsu.
replies(1): >>45086353 #
148. aseipp ◴[] No.45086310{8}[source]
You have this commit graph

    B --> X --> Y (main) --> Z --> @
     \
      --> G --> H
B is a base; yesterday the name "main" pointed to it, and today "main" points to Y. Z is a commit you wrote that you haven't published yet. "@" means "Working copy", which is a way of saying "what your filesystem looks like." So, at this time, you see the changes from B, X, Y, Z, but not G or H.

You want to rebase G --> H from B to Y. But unfortunately, G conflicts with X. H does not conflict with anything. When you run this rebase in Git, you will actually have to immediately fix the conflict between G and X in order for the rebase to continue. If you do not solve it right then, the entire rebase fails. Git's rebase is actually an algorithm represented by a state machine; you must solve the conflict to proceed from "conflicted state" and `git rebase --continue` the rebase algorithm. (If you imagine what you would need to do to actually implement 'git rebase' as it works today in your own code, this state machine model makes immediate sense.)

In Jujutsu, rebase is a non-stop operation and it always succeeds. There is no state machine. It will update the commit graph to look like this:

    B --> X --> Y (main) --> Z --> @
                 \
                   --> G --> H
                       C     C
Now G and H are marked as "conflicted". If any commit is marked as conflicted, then all (transitive) children are marked as conflicted, too. If you "switch over" to working on G, then you can solve the conflict and commit the solution. That will solve the conflict in G, and also H as well.

But you don't have to do anything. In the above graph, G and H are conflicted, but because they are not a parent of `@`, then it does not matter. They exist in a parallel universe that does not influence your own. You can keep compiling code as usual. If you "switched over" to G, then the conflict is "materialized" in your working copy (filesystem) by putting conflict markers in the files, and so you have to solve it to keep compiling.

In short, Jujutsu separates conflict computation (do patches X,Y have a conflict?) from conflict materialization (make the conflict appear with markers in a file), and materialization of conflicts is "lazy" -- it only happens if a conflict exists transitively in the history of your working copy. Resolution is then done at your leisure.

A more brainiac way of thinking about it is that Jujutsu is a tool for manipulating _commit graphs_, and that is a purely computational notion; adding edges, removing edges, etc are all just basic algorithms. The graph's nodes contain "content" and states like "conflicts" are just defined as a relationship C(X,Y) on nodes in the graph. But all of this is "purely computational." Imagine implementing Jujutsu's rebase command; it is just a trivial reparenting of some graph nodes, something an amateur programmer could do. Calculating the relationship `C` is a bit more involved, but not complete black magic. But none of this involves "reading files from disk" or whatever. The side effect of "update the files on your filesystem to look like state XYZ in the graph" is just that: a side effect that the tool does when it is needed. Git, in contrast, only works through "side effects" in that it tends to only operate on the working copy, and never the "holistic commit graph". And so Jujutsu works at a higher, more "pure" level.

-----

Fun fact: in some cases, you do not actually have to "switch over" to G in order to solve this conflict, either. It is actually possible to craft a "solution" to the conflict in G while on top of Z. Then you can do `jj squash --from @ --into G` and you can "teleport" the resolution into the conflicted commit, solving both G and H, without ever making it appear in the working copy. This happens in cases like "G modified a file named readme.txt that was deleted by commit X"; all you have to do is "re-delete" the file inside commit G and it is trivially solved. This is something that is, quite literally, impossible to do in Git.

replies(2): >>45086806 #>>45089453 #
149. a022311 ◴[] No.45086330{3}[source]
Unfortunately there is no replacement for it and I miss the feature too. The docs [1] mention:

> Unlike in Git, the remote to push to is not derived from the tracked remote bookmarks. Use `--remote` to select the remote Git repository by name. There is no option to push to multiple remotes.

I think this was an explicit design decision, which I'm guessing might be because bookmarks in other storage backends (non-git) may not have a notion of an upstream URL. I'm no expert on this, you'll probably get a better answer by asking the maintainers themselves.

Currently the best you can probably do is creating an alias to push a branch to a specific remote to save you some keystrokes. I hope that helps!

[1]: https://jj-vcs.github.io/jj/latest/cli-reference/#jj-git-pus...

150. a022311 ◴[] No.45086353{4}[source]
In that case, I apologize for treating your comment that way. Written text can make communicating intent so hard...
151. Filligree ◴[] No.45086378{3}[source]
That's surprising. I have no real insight, but... was that a colocated repo? Is there any chance Cursor was creating/modifying the Git commits?
replies(1): >>45088110 #
152. BeetleB ◴[] No.45086389{6}[source]
As a relatively new jj user, I'm curious. Why is the jj new -A + squash better than just a jj edit?
replies(3): >>45086416 #>>45087841 #>>45089421 #
153. baq ◴[] No.45086416{7}[source]
Separation of concerns and performance; when you edit, the commit in the middle of the branch is your working copy and you’ll update the whole branch unnecessarily many times vs just once when you’re on a logical checkpoint.
replies(1): >>45088090 #
154. andrewaylett ◴[] No.45086437{6}[source]
JJ has the concept of "immutable changesets" -- if it sees a commit is referenced from a branch that it's not tracking, it assumes it ought not rebase that commit. Changesets on branches that look like the main branch are immutable too. And you can edit the revset that JJ considers immutable if you need it to be different from the default.

The net effect is that I can change "my" branches as I wish, but I can't change stuff that's been merged or other folks' branches unless I disable the safety features (either using `--ignore-immutable` or tracking the branch).

JJ also makes it really easy to push a single changeset as a branch, which means as you evolve that single commit you can keep the remote updated with your current work really easily. And it's got a specific `jj evolog` command to see how a specific changeset has evolved over time.

155. andrewaylett ◴[] No.45086493{4}[source]
JJ has first-class support for conflicted trees, changesets, branches, and operations. The op log itself is a (really useful) feature not present in Git.

You can always end up with the same set of published commits, guaranteed. But the tools you have for manufacturing them and for interacting with their history definitely include things that are possible in JJ but not in Git.

156. baq ◴[] No.45086511{3}[source]
Not sure if you’re trolling, but with the benefit of doubt: that isn’t how almost everyone works, though. Almost everyone treats the repository which runs CICD as the central hub repo and everything else is a spoke.
157. 1718627440 ◴[] No.45086667{9}[source]
So you essentially do a fast-forward until the first commit with a merge commit and then take the previous one?

Sounds like something that could also become a flag for git merge.

replies(2): >>45087968 #>>45088746 #
158. thibran ◴[] No.45086703[source]
jj clicks almost for me. I still struggle to understand when do I switch to a "branch" using 'jj new' and when do I use 'jj edit'. Also the manual setting of bookmarks after 'jj git pull' seems strange.
159. 1718627440 ◴[] No.45086723{5}[source]
You can use all the git commands while doing a rebase.

When you want to work on an older commit for a longer time and don't want to stay in a rebase, you just check it out and work normally, when you are done and want to propagate your changes, then you do a single rebase.

replies(1): >>45086814 #
160. nocman ◴[] No.45086767{7}[source]
> In git, if you get a conflict, you feel like you have to resolve it now.

I guess I view that as a positive rather than a negative. I'm not saying that dealing with merge conflicts is a picnic -- it isn't. I just find it difficult to believe that ignoring them and resolving them later will improve the situation in the long run.

replies(4): >>45087538 #>>45087964 #>>45089871 #>>45090681 #
161. 1718627440 ◴[] No.45086806{9}[source]
Thanks, for the long explanation.

[G: original, G' with conflicts, G" resolved]

What value do you get from G' and H' existing with conflicts when you can't use the working tree until after you have resolved the conflicts?

So in Git it would be G -> G", but in JJ you can do G -> G' -> G". But G" in both cases only exist, until after you have put in the work of solving the conflict. And G' only ever exists without a usable working tree. So what do you get from having G' earlier, when you still have G" only after the same work?

replies(1): >>45088072 #
162. baq ◴[] No.45086814{6}[source]
I’m reminded of the Dropbox comment.

You can do anything with a Turing machine. That you can isn’t the point. The point is the tool does all the things you can automatically and correctly so you don’t have to. There’s no ’just do this or that during rebase, or outside of it’. There’s only ‘it rebased everything correctly without a single thought, nice’.

replies(1): >>45086981 #
163. 1718627440 ◴[] No.45086836{5}[source]
True nested rebase doesn't exist (yet) in Git. What I do is create another commit with the parent commit 8 with commit --fixup and then complete the rebase. The I can just autosquash it, without reediting anything.

You could also keep the rebased commits, abort the rebase, rebase the already rebased commits and then continue the first rebase.

replies(1): >>45086939 #
164. nocman ◴[] No.45086891{7}[source]
> for me, everything git is a pain point

Yeah, I was looking for something (or "things") specific. An "I hate everything about it" explanation doesn't really compel me to try out the alternative.

> "megamerges" are one such example. ive shared many links, here and in other posts

I read through one megamerge link you shared ( https://v5.chriskrycho.com/journal/jujutsu-megamerges-and-jj... ). So the argument seems to be (forgive me if I'm reading this wrong), if you have multiple versions of a single set of source files that all have differing changes, for you JuJutsu makes it easier (easier then git, that is) to merge them into the final commit you want to end up with. Is that correct?

Just trying to make sure I understand. Honestly, after reading that article I am still not feeling the need to try Jujustu out. I'm still open to being convinced, but have yet to see anything that makes me go "wow, I need to try that!".

replies(2): >>45087517 #>>45089162 #
165. Human-Cabbage ◴[] No.45086902[source]
To reinforce the sibling reply with a concrete example:

Let's say you've got a few feature branches, all based of the trunk branch.

  $ jj
  @  ozywpwxm samfredrickson@gmail.com 2025-08-31 13:21:59 b2f1364d
  │  (empty) (no description set)
  │ ○  qxoklwxv samfredrickson@gmail.com 2025-08-31 13:21:27 9cda0936
  ├─╯  (empty) Feature C
  │ ○  ukqynvts samfredrickson@gmail.com 2025-08-31 13:21:26 ceee7029
  ├─╯  (empty) Feature B
  │ ○  nwtxnvxp samfredrickson@gmail.com 2025-08-31 13:21:24 9ccbedf6
  ├─╯  (empty) Feature A
  ◆  yxuvtolz samfredrickson@gmail.com 2025-08-27 09:49:16 master git_head() 8e80b150
  │  Update Claude Code to 1.0.93.
One neat workflow supported by Jujutsu is "working on all branches at the same time."

  $ jj new q u n
  Working copy  (@) now at: zzxxqlzr fb73d4dc (empty) (no description set)
  Parent commit (@-)      : qxoklwxv 9cda0936 (empty) Feature C
  Parent commit (@-)      : ukqynvts ceee7029 (empty) Feature B
  Parent commit (@-)      : nwtxnvxp 9ccbedf6 (empty) Feature A
  $ jj
  @      rzouzmyw samfredrickson@gmail.com 2025-08-31 13:25:56 fb73d4dc
  ├─┬─╮  (empty) (no description set)
  │ │ ○  nwtxnvxp samfredrickson@gmail.com 2025-08-31 13:21:24 9ccbedf6
  │ │ │  (empty) Feature A
  │ ○ │  ukqynvts samfredrickson@gmail.com 2025-08-31 13:21:26 ceee7029
  │ ├─╯  (empty) Feature B
  ○ │  qxoklwxv samfredrickson@gmail.com 2025-08-31 13:21:27 git_head() 9cda0936
  ├─╯  (empty) Feature C
  ◆  yxuvtolz samfredrickson@gmail.com 2025-08-27 09:49:16 master 8e80b150
  │  Update Claude Code to 1.0.93.
  ~
Now you can use the merge revision as a scratch space, and then squash changes from it into one of the feature revisions.

  $ vim README.md
  $ jj squash --into n
  Working copy  (@) now at: rzouzmyw 30ff9b0f (empty) (no description set)
  Parent commit (@-)      : qxoklwxv 9cda0936 (empty) Feature C
  Parent commit (@-)      : ukqynvts ceee7029 (empty) Feature B
  Parent commit (@-)      : nwtxnvxp fb3cca28 Feature A
  $ jj
  @      rzouzmyw samfredrickson@gmail.com 2025-08-31 13:27:41 30ff9b0f
  ├─┬─╮  (empty) (no description set)
  │ │ ○  nwtxnvxp samfredrickson@gmail.com 2025-08-31 13:27:41 fb3cca28
  │ │ │  Feature A
  │ ○ │  ukqynvts samfredrickson@gmail.com 2025-08-31 13:21:26 ceee7029
  │ ├─╯  (empty) Feature B
  ○ │  qxoklwxv samfredrickson@gmail.com 2025-08-31 13:21:27 git_head() 9cda0936
  ├─╯  (empty) Feature C
  ◆  yxuvtolz samfredrickson@gmail.com 2025-08-27 09:49:16 master 8e80b150
  │  Update Claude Code to 1.0.93.
Later, you decide to fetch changes from your remote, and notice that your revisions are based on an out-of-date version of the trunk.

  $ jj git fetch
  remote: Enumerating objects: 17, done.
  remote: Total 12 (delta 6), reused 0 (delta 0), pack-reused 0
  bookmark: master@origin [updated] tracked
  $ jj
  @      rzouzmyw samfredrickson@gmail.com 2025-08-31 13:27:41 30ff9b0f
  ├─┬─╮  (empty) (no description set)
  │ │ ○  nwtxnvxp samfredrickson@gmail.com 2025-08-31 13:27:41 fb3cca28
  │ │ │  Feature A
  │ ○ │  ukqynvts samfredrickson@gmail.com 2025-08-31 13:21:26 ceee7029
  │ ├─╯  (empty) Feature B
  ○ │  qxoklwxv samfredrickson@gmail.com 2025-08-31 13:21:27 git_head() 9cda0936
  ├─╯  (empty) Feature C
  │ ◆  zvpmmzru samfredrickson@gmail.com 2025-08-29 15:59:55 master 658a3d12
  │ │  Update Claude Code to 1.0.98.
  │ ~  (elided revisions)
  ├─╯
  ◆  yxuvtolz samfredrickson@gmail.com 2025-08-27 09:49:16 8e80b150
  │  Update Claude Code to 1.0.93.
  ~
With Jujutsu, you can run one command to rebase _everything_ against the latest trunk revision.

  $ jj rebase -s 'roots(trunk()..mutable())' -d 'trunk()'
  Rebased 4 commits to destination
  Working copy  (@) now at: rzouzmyw 88ed8085 (empty) (no description set)
  Parent commit (@-)      : qxoklwxv 005442c3 (empty) Feature C
  Parent commit (@-)      : ukqynvts 23923cf2 (empty) Feature B
  Parent commit (@-)      : nwtxnvxp 769d0539 Feature A
  Added 0 files, modified 2 files, removed 0 files
  $ jj
  @      rzouzmyw samfredrickson@gmail.com 2025-08-31 13:32:08 88ed8085
  ├─┬─╮  (empty) (no description set)
  │ │ ○  nwtxnvxp samfredrickson@gmail.com 2025-08-31 13:32:08 769d0539
  │ │ │  Feature A
  │ ○ │  ukqynvts samfredrickson@gmail.com 2025-08-31 13:32:08 23923cf2
  │ ├─╯  (empty) Feature B
  ○ │  qxoklwxv samfredrickson@gmail.com 2025-08-31 13:32:08 git_head() 005442c3
  ├─╯  (empty) Feature C
  ◆  zvpmmzru samfredrickson@gmail.com 2025-08-29 15:59:55 master 658a3d12
  │  Update Claude Code to 1.0.98.
  ~
I use this command so much that it's aliased as "jjsr", "Jujutsu Super Rebase".
replies(1): >>45087229 #
166. sgjennings ◴[] No.45086931{3}[source]
I recently updated that FAQ so it preserves the original commit ID. It’s in the prerelease docs now and will move to “latest” with the release this week: https://jj-vcs.github.io/jj/prerelease/FAQ/#i-accidentally-c...
167. 1718627440 ◴[] No.45086938[source]
What does git rebase --rebase-merges do in this case? How does it botch up the repo?
replies(1): >>45090521 #
168. sunshowers ◴[] No.45086939{6}[source]
With jj, because it doesn't have modal states of any kind, you can just go back to commit 8, edit it, and everything dependent on it gets auto-rebased. You can also do jj squash --into for a workflow similar to fixup commits.

I would consider the first workflow to be impossible to do by most mere mortals in Git [1]. Meanwhile in jj it's downright trivial.

[1] There technically is a way to do this by setting a temporary branch, aborting the rebase, starting another rebase -i, carefully editing the interactive instructions, going to commit 8, editing that commit, then cherry-picking 9-15 from the temporary branch. But it's too hard to do in practice, and far too easy to get wrong.

replies(1): >>45087026 #
169. 1718627440 ◴[] No.45086981{7}[source]
SSH is definitely easier than Dropbox :-).

Yes, and my point is that having a rebase and edit everything isn't too different from first modifying everything and then doing an automatic rebase.

170. 63stack ◴[] No.45087010{3}[source]
False. The branch subcommand has been removed. You have to use bookmarks now.
replies(1): >>45088599 #
171. 1718627440 ◴[] No.45087026{7}[source]
> [1] There technically is a way to do this

That's what I've described?

> rebase -i, carefully editing the interactive instructions

You neither need to use interactive rebase nor carefully edit, since there is rebase --onto.

> But it's too hard to do in practice, and far too easy to get wrong.

I do this often it's not more complicated then any other rebase.

What is annoying in Git is rebaseing across multiple merges while forging committer and date information. Can JJ do that better?

replies(1): >>45087470 #
172. EMM_386 ◴[] No.45087058{9}[source]
> When you feel you have the energy to deal with the conflict

So you just kick the can down the road and end up with possibly an even more difficult conflict resolution?

replies(2): >>45087653 #>>45088598 #
173. adito ◴[] No.45087124{3}[source]
Yeah, I don't get GP either, maybe their setup is different and more complex?
174. j2kun ◴[] No.45087146{3}[source]
> more powerful than git

> not missing out on power

Two very different claims, and it only makes me more skeptical.

175. 1718627440 ◴[] No.45087229{3}[source]
A commit with multiple parents is called a merge commit. :-)

The "super rebase" seams to be nice though. However I just tested it and achieved the same with git rebase --rebase-merges --update-refs. Have I missed something?

replies(1): >>45087481 #
176. IshKebab ◴[] No.45087314{3}[source]
I don't think they are misunderstanding the premise. They're just thinking at a higher level than you are. It's not too hard to imagine a VCS where rebases can't unintentionally overwrite someone else's work. In fact, Git has flags to avoid that! They're just off by default.
177. devnullbrain ◴[] No.45087384{4}[source]
Some repos merge/pull changes by rebasing them on main and want eventual history not actual history.
178. holyshitsss ◴[] No.45087450[source]
Great point about performance. I think the next step is figuring out how to make this accessible to smaller teams without the big tech resources.
179. sunshowers ◴[] No.45087470{8}[source]
Ah I misinterpreted what "keep the rebase commits" meant.

I'm glad you don't find it too difficult to do. It's a workflow that seemingly works well for you!

180. Human-Cabbage ◴[] No.45087481{4}[source]
Jujutsu distinguishes between "revisions" and "commits." Revision IDs like "rzouzmyw" stay stable, whereas commit IDs of course change with the content. You can see in my example how the commits associated with a revision change over time: rzouzmyw starts as fb73d4dc, then 30ff9b0f, and finally 88ed8085. I used "merge revision" to by consistent with Jujutsu's terminology.

Anyway, I just tried that command you suggested, but it didn't seem to work?

  $ jj new
  $ jj bookmark create woot -r @-
  $ jj
  @  wxkrvmxs samfredrickson@gmail.com 2025-08-31 14:53:19 4bbb7f5a
  │  (empty) (no description set)
  ○      rzouzmyw samfredrickson@gmail.com 2025-08-31 13:27:41 woot git_head() 30ff9b0f
  ├─┬─╮  (empty) (no description set)
  │ │ ○  nwtxnvxp samfredrickson@gmail.com 2025-08-31 13:27:41 fb3cca28
  │ │ │  Feature A
  │ ○ │  ukqynvts samfredrickson@gmail.com 2025-08-31 13:21:26 ceee7029
  │ ├─╯  (empty) Feature B
  ○ │  qxoklwxv samfredrickson@gmail.com 2025-08-31 13:21:27 9cda0936
  ├─╯  (empty) Feature C
  │ ◆  zvpmmzru samfredrickson@gmail.com 2025-08-29 15:59:55 master 658a3d12
  │ │  Update Claude Code to 1.0.98.
  │ ~  (elided revisions)
  ├─╯
  ◆  yxuvtolz samfredrickson@gmail.com 2025-08-27 09:49:16 8e80b150
  │  Update Claude Code to 1.0.93.
  ~
  $ git checkout woot
  $ git log --graph
  *-.   commit 30ff9b0f274c9adaca4eeadcf21d5e918e4e3578 (HEAD -> woot)
  |\ \  Merge: 9cda093 ceee702 fb3cca2
  | | | Author: Sam Fredrickson <samfredrickson@gmail.com>
  | | | Date:   Sun Aug 31 13:27:41 2025 -0700
  | | |
  | | * commit fb3cca2823b4dffe374b67d28e3c91c206828d47
  | | | Author: Sam Fredrickson <samfredrickson@gmail.com>
  | | | Date:   Sun Aug 31 13:21:24 2025 -0700
  | | |
  | | |     Feature A
  | | |
  | * | commit ceee7029730c49ae30890c1641c7d6645e60fca4
  | |/  Author: Sam Fredrickson <samfredrickson@gmail.com>
  | |   Date:   Sun Aug 31 13:21:26 2025 -0700
  | |
  | |       Feature B
  | |
  * | commit 9cda09363efe257a954b5563ce6af287a506d808
  |/  Author: Sam Fredrickson <samfredrickson@gmail.com>
  |   Date:   Sun Aug 31 13:21:27 2025 -0700
  |
  |       Feature C
  |
  * commit 8e80b15010c4d9373c5828fdf8a83c53df75ec00
  | Author: Sam Fredrickson <samfredrickson@gmail.com>
  | Date:   Wed Aug 27 09:48:45 2025 -0700
  |
  |     Update Claude Code to 1.0.93.
  $ git rebase --rebase-merges --update-refs master
  Trying simple merge with cfa85486fa3d53401be518cb42936fb6fd3c128c
  Trying simple merge with ffebef1cb34ca1670b48c594b2014e71be7d1b2b
  error: Empty commit message.
  Not committing merge; use 'git commit' to complete the merge.
  Could not apply 30ff9b0... rev-ceee702 rev-fb3cca2 #
  $ git status
  interactive rebase in progress; onto 658a3d1
  Last commands done (10 commands done):
     pick 9cda093 # Feature C # empty
     merge -C 30ff9b0f274c9adaca4eeadcf21d5e918e4e3578 rev-ceee702 rev-fb3cca2 #
    (see more in file .git/rebase-merge/done)
  No commands remaining.
  
  All conflicts fixed but you are still merging.
    (use "git commit" to conclude merge)
  
  Changes to be committed:
          modified:   README.md
  $ jj
  Reset the working copy parent to the new Git HEAD.
  @  tymwqlyq samfredrickson@gmail.com 2025-08-31 14:57:22 d083f61e
  │  (no description set)
  ○  vmnnlqro samfredrickson@gmail.com 2025-08-31 14:56:55 git_head() f92f7505
  │  (empty) Feature C
  ◆  zvpmmzru samfredrickson@gmail.com 2025-08-29 15:59:55 master 658a3d12
  │  Update Claude Code to 1.0.98.
  ~  (elided revisions)
  │ ○      rzouzmyw samfredrickson@gmail.com 2025-08-31 13:27:41 woot 30ff9b0f
  │ ├─┬─╮  (empty) (no description set)
  │ │ │ ○  nwtxnvxp samfredrickson@gmail.com 2025-08-31 13:27:41 fb3cca28
  ├─────╯  Feature A
  │ │ ○  ukqynvts samfredrickson@gmail.com 2025-08-31 13:21:26 ceee7029
  ├───╯  (empty) Feature B
  │ ○  qxoklwxv samfredrickson@gmail.com 2025-08-31 13:21:27 9cda0936
  ├─╯  (empty) Feature C
  ◆  yxuvtolz samfredrickson@gmail.com 2025-08-27 09:49:16 8e80b150
  │  Update Claude Code to 1.0.93.
  ~
Maybe I'm using the git command incorrectly?

Also, though, I'm assuming that git command will only rebase the branch you have currently checked out, whereas the jj command I gave will rebase _everything_, not just revisions that are parents of HEAD.

Edit: I figured out my issue. Git doesn't like empty commits & merge commits with no description. After addressing that, then the `git rebase --rebase-merges --update-refs master` command worked.

There's still the caveat though that the Git command will only rebase the "woot" branch. If I had some other "feature D" commit that wasn't included in "woot", that commit wouldn't be rebased. But the `jjsr` command would see and rebase that commit as well.

  $ jj
  @  qsvwusnk samfredrickson@gmail.com 2025-08-31 16:17:35 6fd02707
  │  (empty) (no description set)
  ○      rzouzmyw samfredrickson@gmail.com 2025-08-31 16:17:35 woot git_head() e6d64886
  ├─┬─╮  (empty) Merge
  │ │ ○  nwtxnvxp samfredrickson@gmail.com 2025-08-31 13:27:41 fb3cca28
  │ │ │  Feature A
  │ ○ │  ukqynvts samfredrickson@gmail.com 2025-08-31 16:16:36 9957dca2
  │ ├─╯  Feature B
  ○ │  qxoklwxv samfredrickson@gmail.com 2025-08-31 16:16:44 4361de8b
  ├─╯  Feature C
  │ ○  nwoxyzlx samfredrickson@gmail.com 2025-08-31 16:14:42 ce8de62d
  ├─╯  Feature D
  │ ◆  zvpmmzru samfredrickson@gmail.com 2025-08-29 15:59:55 master 658a3d12
  │ │  Update Claude Code to 1.0.98.
  │ ~  (elided revisions)
  ├─╯
  ◆  yxuvtolz samfredrickson@gmail.com 2025-08-27 09:49:16 8e80b150
  │  Update Claude Code to 1.0.93.
  ~
  
  $ git checkout woot
  Switched to branch 'woot'
  
  $ git rebase --rebase-merges --update-refs master
  Trying simple merge with f339926f729437f78ac407c28fc84ce3b0441eb7
  Trying simple merge with 4372b2c4f538164a10bb9d8e81899bf61eeecaf2
  Merge made by the 'octopus' strategy.
  Successfully rebased and updated refs/heads/woot.
  
  $ jj
  Reset the working copy parent to the new Git HEAD.
  Abandoned 4 commits that are no longer reachable.
  Done importing changes from the underlying Git repo.
  @  zlxqyrmq samfredrickson@gmail.com 2025-08-31 16:17:59 970c80f8
  │  (empty) (no description set)
  ○      qumulkxr samfredrickson@gmail.com 2025-08-31 16:17:42 woot git_head() cc189c82
  ├─┬─╮  (empty) Merge
  │ │ ○  vkuwsssr samfredrickson@gmail.com 2025-08-31 16:17:42 4372b2c4
  │ │ │  Feature A
  │ ○ │  lmsrxxzm samfredrickson@gmail.com 2025-08-31 16:17:42 f339926f
  │ ├─╯  Feature B
  ○ │  wrsnrokn samfredrickson@gmail.com 2025-08-31 16:17:42 53ec4dc0
  ├─╯  Feature C
  ◆  zvpmmzru samfredrickson@gmail.com 2025-08-29 15:59:55 master 658a3d12
  │  Update Claude Code to 1.0.98.
  ~  (elided revisions)
  │ ○  nwoxyzlx samfredrickson@gmail.com 2025-08-31 16:14:42 ce8de62d
  ├─╯  Feature D
  ◆  yxuvtolz samfredrickson@gmail.com 2025-08-27 09:49:16 8e80b150
  │  Update Claude Code to 1.0.93.
  ~
Versus:

  $ jjsr
  Rebased 6 commits to destination
  Working copy  (@) now at: qsvwusnk e793b1e4 (empty) (no description set)
  Parent commit (@-)      : rzouzmyw 3927be34 woot | (empty) Merge
  Added 0 files, modified 2 files, removed 0 files
  
  $ jj
  @  qsvwusnk samfredrickson@gmail.com 2025-08-31 16:18:58 e793b1e4
  │  (empty) (no description set)
  ○      rzouzmyw samfredrickson@gmail.com 2025-08-31 16:18:58 woot git_head() 3927be34
  ├─┬─╮  (empty) Merge
  │ │ ○  nwtxnvxp samfredrickson@gmail.com 2025-08-31 16:18:58 12f659cf
  │ │ │  Feature A
  │ ○ │  ukqynvts samfredrickson@gmail.com 2025-08-31 16:18:58 5c0ce98f
  │ ├─╯  Feature B
  ○ │  qxoklwxv samfredrickson@gmail.com 2025-08-31 16:18:58 f9f217e8
  ├─╯  Feature C
  │ ○  nwoxyzlx samfredrickson@gmail.com 2025-08-31 16:18:58 373d6ab1
  ├─╯  Feature D
  ◆  zvpmmzru samfredrickson@gmail.com 2025-08-29 15:59:55 master 658a3d12
  │  Update Claude Code to 1.0.98.
  ~
replies(1): >>45090448 #
181. steveklabnik ◴[] No.45087511{3}[source]
My best guess: something (likely cursor) is running git commands that mutate the repo behind your back. I don’t use cursor so I can’t say for sure, though.
182. nchmy ◴[] No.45087517{8}[source]
"multiple versions" = feature branches, possibly all in progress, probably all related. In a couple seconds, you can create a merge on top of all of them to join up their combined functionality/changes, work on top of that ON ALL OF THEM AT ONCE, and then squash all the relevant changes into the respective PRs that others (who are just using git) can review and merge into main.

At this point A LOT has been written in this and other threads, as well as lots of essays and tutorials about how jj just completely transforms your workflow. If you're curious, you'll seek it out. If not, that's fine as well.

replies(1): >>45088708 #
183. hellcow ◴[] No.45087538{8}[source]
It's not about "ignoring" conflicts. In jj you're often working with stacked diffs, and merge conflicts can impact a dozen "branches" all at once. This is trivial to resolve in jj and a nightmare in git. It lets you work on them one piece at a time and upon resolving it, instantly see the conflicts fixed across all your branches at once.
184. hellcow ◴[] No.45087584[source]
Here:

  $ jj new -m 'build my feature'
  $ touch my_feature.c
  $ jj new -m 'add some other feature'
  $ touch other_feature.c
  $ jj bookmark move main -t @
  $ jj git push
185. BeetleB ◴[] No.45087653{10}[source]
> So you just kick the can down the road and end up with possibly an even more difficult conflict resolution?

That sentiment is true for pretty much anything in life one may decide to defer till later :-)

More concretely, it's often not hard to tell if deferring it will make it worse, or merely the same.

The whole point of version control is to give your mind some peace in not worrying about things ("Let's make this change and we can always revert if it doesn't work out"). Conflicts are no different. There's no fundamental reason a conflict needs to be treated like an emergency.

186. gcr ◴[] No.45087664{3}[source]
Your changes are almost certainly in the op log, even as just bare snapshot operations. Have you been able to search that and recover them?

(i've found jj undo quite robust, i'd be surprised if you ever actually lost work tbh)

replies(1): >>45088113 #
187. cynicalsecurity ◴[] No.45087762[source]
Thank you, but no. I'm not going to learn your technology just because you want fame and money. Everyone wants it, you are not unique. Prove your technology can successfully solve real-life problems and is loved by many. So far it looks like an unsolicited call to me.
replies(1): >>45088638 #
188. odie5533 ◴[] No.45087806[source]
.gitattributes has only caused me trouble, so I wouldn't really miss it. IMO it's a footgun.
replies(1): >>45092573 #
189. odie5533 ◴[] No.45087811{3}[source]
So it doesn't have most of the worst features git ever invented. Got it.
replies(2): >>45089987 #>>45090153 #
190. odie5533 ◴[] No.45087818{4}[source]
I'm waiting for DeltaDB. Sounds game-changing to me. jj seems more like a facade for people who aren't good at git.
191. hdjrudni ◴[] No.45087841{7}[source]
I forget where I read it (maybe from Martin), but one reason I like to `jj new` before I start any work is just because it makes it easy to revert or abandon if I don't end up liking it. And also easy to diff. `jj new` is pretty much 'free' anyway, every time you make an edit you're creating new commits (not changes!) anyway.
replies(1): >>45088700 #
192. steveklabnik ◴[] No.45087964{8}[source]
It’s about giving you choice. If you want to then fix them immediately, you can. But you don’t have to if you don’t want to.

But really, it’s about something deeper: rebase is a first-class operation in memory, and not a serious of patch applications via the file system. They’re therefore lightning quick, and will always succeed, which is nice. If you get partway through resolving a conflict and want to change to something else for some reason, that’s possible and simple.

193. steveklabnik ◴[] No.45087968{10}[source]
Git won’t let the portion of the branch that’s still conflicted remain in conflict while you go and work on the other part.
replies(1): >>45090471 #
194. stavros ◴[] No.45087984{6}[source]
For me, it's two things:

1. I understood git better after ten minutes of jj than after fifteen years of git. Git just doesn't expose its underlying data model as well as jj does. I guess, if you already know git well, this isn't going to make a difference for you.

2. This question is a bit like asking what can I do with a calculator that I can't do with pen and paper? Technically, nothing, but everything will be so much easier that you'll be much more likely to use it. Even though I can, technically, stash my worktree and jump to another commit with git, it's so fiddly to unstash (especially with multiple stacked switches/stashes) that I just never did it.

With jj, I leave commits in the middle and jump to other commits (to fix a bug or make a small change I noticed I need while working on a larger change) all the time, because there's zero friction.

jj just removes all the friction that's prevalent in git. Things are easy in jj that in git are merely possible.

replies(2): >>45088720 #>>45089117 #
195. stavros ◴[] No.45088010{4}[source]
There isn't a single thing in jj that's as complicated as git. I could go on to list a few features, but it would sound underwhelming, because you could do all that in git.

It's kind of like asking "why would I buy a digital camera when my film camera does all the same things? I can already see what the photo will look like when I take it, and developing my own film isn't that much of a hassle", yet film cameras have gone the way of the dodo, except for the occasional nostalgic enthusiast.

replies(1): >>45089289 #
196. steveklabnik ◴[] No.45088024[source]
1. You wouldn’t use git rere with jj, so that’s sort of a nonsequitor.

2. You treat @ (the working copy) like the staging area, @- (the parent of the working copy) as the commit you’re working on, and then jj squash -i (“interactive”) the parts of the diff you want back into @.

replies(2): >>45088172 #>>45089207 #
197. adastra22 ◴[] No.45088030{9}[source]
Sorry if I’m being dense, but I still don’t get how this is any different. If I interactive rebase and stop to edit a commit, isn’t that the same thing?
replies(1): >>45089153 #
198. adastra22 ◴[] No.45088072{10}[source]
To expand on this(since I asked the question), I see mostly downside as the branch with G is unusable until the conflicts are resolved. Being able to keep a merge halfway resolved is a nice CLI shortcut. There should be a stash command for this, and I expect there probably is, or it can be done with work trees.
replies(1): >>45092493 #
199. adastra22 ◴[] No.45088090{8}[source]
Not everyone works by “logical checkpoint” commit strategy. I myself want my tree clean and commits being atomic state transitions. Otherwise git bisect (which I rely on) would break.

A lot of the jj strategies in this thread are a bit more cowboy, and I’m surprised.

replies(1): >>45089163 #
200. johnisgood ◴[] No.45088109{4}[source]
> If you're interested in becoming a Git expert, I cannot recommend Emacs Magit strongly enough.

Yes, Emacs' Magit and Git Cola.

201. shepherdjerred ◴[] No.45088110{4}[source]
Yeah, it was a colocated repo. I've been using Cursor, but this also happened with VS Code at least once. I don't think that either tool should be doing anything with Git aside from maybe running git fetch.
replies(1): >>45091928 #
202. shepherdjerred ◴[] No.45088113{4}[source]
If this happened with Git I feel confident enough to recover my work. I'm not familiar enough with jj, so usually when this happens I've used VS Code's timeline feature to recover my work.
replies(1): >>45089847 #
203. Ericson2314 ◴[] No.45088172{3}[source]
If I use JJ in my existing git clone, and then use the occasional git command, I don't want rerere to be messed up
replies(1): >>45088356 #
204. stavros ◴[] No.45088240{3}[source]
I never managed to get into mise. I tried, but I think its documentation wasn't good enough for me to be able to configure the things I wanted, and, with uv, I barely ever see a need to use it...

Does anyone have a use case where they use mise with Python? What do you use it for?

replies(2): >>45089209 #>>45090259 #
205. hu3 ◴[] No.45088275[source]
Personally I don't think adding a layer on top of git is a good idea.

I know it's hard to break git's hegemony but it would be much more powerful and less constrained to build on top of a new foundation.

replies(2): >>45088871 #>>45089270 #
206. sfink ◴[] No.45088309[source]
Yes, and this is the most commonly used workflow these days with jj (the "squash workflow"). You have a top commit, which is also your working directory, and you make changes freely. To "stage" something, you squash it down into the next commit (all changes, or interactively selected changes with -i aka --interactive).

This generalizes to using a whole stack of "stages", by doing ´squash --into´ to select the patch to put the changes into if it's not just the next one down.

207. steveklabnik ◴[] No.45088356{4}[source]
In general you don’t want to mix git mutating commands with jj commands. I believe that there might be a way to get jj to resync its understanding of the word, but I’m not sure what it is off the top of my head.

In rere’s case specifically, I’d expect you’d just be using jj’s rebase, so it shouldn’t be needed though of course want and need are different things.

replies(1): >>45093882 #
208. interroboink ◴[] No.45088571[source]
Jujutsu has many Mercurial-style features, one of them being revsets[1] - a set-notation-style DSL for selecting changesets (eg: which ones to log, etc). I have never found anything as powerful as revsets in Git.[2]

[1] https://jj-vcs.github.io/jj/latest/revsets/

[2] https://stackoverflow.com/questions/22520751/what-is-the-git...

replies(2): >>45088581 #>>45089737 #
209. capitainenemo ◴[] No.45088581{3}[source]
Yeah, I'm excited by the mercurial-style features as a mercurial user looking for something to transition to in a world where git has, sadly, become the standard. The revsets were definitely a joy to discover. The main thing I'm hoping they add is mercurial's diff format which, according to the jujutsu dev discussions, is considerably better than git's, and allows for cleaner "absorb" and, apparently, the clearness of mercurial's "hg fa --deleted".

And, well, it's silly, but I do like revnums. It's a compact way to compare changes over time, even if it's only useful for the local repo. Would be nice to have those too.

210. sunshowers ◴[] No.45088598{10}[source]
Or none at all, if you decide to abandon that work (as has happened to me a bunch).
211. comonoid ◴[] No.45088599{4}[source]
Ok, you need to call `jj bookmark set -r@ XYX` (or `jj b s -r@ XYX`), so what?
replies(2): >>45089900 #>>45091092 #
212. sunshowers ◴[] No.45088621{3}[source]
In Mercurial you'd do hg commit -i and squash further changes down incrementally via hg amend -i, similar to Jujutsu.

(The first thing about Jujutsu that was earth-shattering for me was learning that jj amend is an alias for jj squash. I swore aloud for several minutes when I first learned that.)

replies(1): >>45088687 #
213. sunshowers ◴[] No.45088638[source]
> fame

I think it's great when people become famous for building useful, high-quality works, and other pro-social activities!

> money

Jujutsu is free software in both senses of the word.

replies(1): >>45091719 #
214. stevage ◴[] No.45088661[source]
In case the author is reading, can I suggest reformatting this kind of thing:

>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.

215. mdaniel ◴[] No.45088687{4}[source]
How would anyone possibly know that? https://mercurial-scm.org/help/commands/commit says no such thing but I also recognize that I am obviously a fool for thinking that one should add the whole file but only commit part of it

I guess that also places the burden upon the user to .. I dunno, go through that whole TUI dance again?, if one wishes to amend one more line in the file

In some sense, I do recognize it's like showing up to an emacs meeting and bitching about how it doesn't do things the vim way, but seriously, who came up with that mental model for committing only part of the working directory?

replies(1): >>45089053 #
216. BeetleB ◴[] No.45088700{8}[source]
Oh I'm good with jj new for any new thing I'm working on. But I was wondering why one would use jj new -A to fix a commit in the past vs jj edit,
replies(1): >>45088815 #
217. typpilol ◴[] No.45088708{9}[source]
What does the history look like then? Just a single merge from a ton of branches or?
replies(1): >>45089169 #
218. BeetleB ◴[] No.45088720{7}[source]
> With jj, I leave commits in the middle and jump to other commits (to fix a bug or make a small change I noticed I need while working on a larger change) all the time, because there's zero friction.

For git users who are wondering "What friction? I just git stash and jump to another branch":

In jj, you just jump without needing to type any command like git stash.

replies(1): >>45089997 #
219. riwsky ◴[] No.45088746{10}[source]
No. The GP making a commit off of the first non-conflict isn’t the essence of the feature he’s talking about, just an example of what he can do next.

He’d also be free to edit upstream to not commit, or split a change in two so that parts unrelated to the conflict are in their own change. The big idea is that he doesn’t need to blindly decide that before seeing what the conflict is.

220. Rebelgecko ◴[] No.45088807[source]
IMO its inspired by the best parts of git, hg, and darcs. jj split alone is worth the price of admission IMO

I haven't done a TON of scripted use of jj, but when I have the built in templating has come in clutch. If you want a command to return in a certain format (or include/exclude certain metadata) it's usually easier- and probably more future proof- to tweak the jj command's template than to parse the string in your script.

221. kps ◴[] No.45088815{9}[source]
Mostly to keep the new changes isolated from the original commit until they're ready, I think. The only time I use `jj edit` is to resume a leaf I left in the middle of something (where I might have used `git stash pop` but without needing to stash).
222. rich_sasha ◴[] No.45088844[source]
Having disliked Git for a long time, I concluded that looking for alternatives is actually a waste of time, unless you do it as a hobby.

VCSes are natural monopolies. There is a natural and unforced benefit from using the same thing as everyone else, unless it is so terrible that you literally can't work it. Because:

1. You use it for collaboration with others

2. You use it on new or under-configured machines where suddenly you're stuck with the lowest common denominator, ie got

3. 99%+ of online materials assume you're using git.

4. The RoI is actually not that great. If you do learn the system, you're only in the same place professionally as if you learned git in the first place.

Git is famous for its user interface, and not in a good way. But the 5-8 commands I use daily, I now remember. I still have to Google for interactive rebase or how to nuke last commit - but I do it rarely enough that it doesn't matter.

Jujutsu sounds really nice, likewise I really wanted to like mercurial. But in the end I decided it wasn't worth my time.

replies(1): >>45089233 #
223. dcre ◴[] No.45088871[source]
jj’s design is not constrained by git. git is one possible backend, but you don’t even need to use jj with git. I will eat a shoe if you can name something jj is prevented from doing by its relation to git.
replies(1): >>45090291 #
224. sltr ◴[] No.45088923[source]
I can also recommend Koppel's JJ Workshop

https://github.com/jkoppel/jj-workshop

225. tux3 ◴[] No.45088997{6}[source]
When you force push a PR, Gitlab shows the changes from the last push. So it also depends which forge you use. I could see that working less well on Github or simpler Git forges
replies(1): >>45092976 #
226. spooneybarger ◴[] No.45089012{5}[source]
This made me laugh. Thank you.
replies(1): >>45090168 #
227. sunshowers ◴[] No.45089053{5}[source]
Well, git add is super overloaded, because it lets you add untracked files (especially with -N), or all or parts of tracked files to the staging area. Mercurial is a different system with different primitives, where each command tends to do one thing, and add is only meant to operate on untracked files.

I strongly prefer JJ's approach of simply doing away with the concept of untracked files, though note that this is one of the features that is designed around developers having NVMe drives these days. It wouldn't have been possible to scan the working copy with every command back in 2004.

replies(1): >>45093932 #
228. monster_truck ◴[] No.45089058[source]
I'm not using a cli to manage my code, it's 2025. I want gittower and gitlab, I want shortcut keys and first class support in _everything_, I'm a professional. If anybody paying me ever says I have to use it, I'll be using git anyways just like I did with clearcase, svn, mercurial, etc etc

A quick scan of this thread reveals no less than 4 show stopping missing features. By the way, where are submodules? I see sentences like "you can work on every branch at the same time" and think "wow this would be great if I really wanted to fuck something up, now show me how you'd track this down to unfuck it a few years later in an unfamiliar codebase. With a gui. For use as evidence in a criminal court case"

Keep plugging away. If this is how you think you can make the world of software a better place, I'm cheering for you.

replies(2): >>45089177 #>>45089593 #
229. Izkata ◴[] No.45089061{4}[source]
A couple of them seem more complicated, like the example further up on the page for postponing merge conflicts. In git I'd just abort the merge and do it later.

I also found the exchange about named branches funny, that ends with:

> Ok, you need to call `jj bookmark set -r@ XYX` (or `jj b s -r@ XYX`), so what?

Apparently this is excusable, but people like to complain about git's commands being too obtuse - as far as I understand the git version is "git checkout -b XYX", right? (Or I guess "git switch -c XYX" with the new commands)

replies(2): >>45089144 #>>45091961 #
230. jaberjaber23 ◴[] No.45089085[source]
i thought you're talking about the new jjk trailer
231. sunshowers ◴[] No.45089103{3}[source]
Ehh these are mostly edge case features that are very important to some, but not relevant to most. I've been using Jujutsu full time with dozens of repos and the only one that's come close to mattering is submodules (which are easy to work around with a colocated repo + `git submodule update`).

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.

232. KingMob ◴[] No.45089117{7}[source]
I've taken to summing it up as "jj makes everyone the git guru".
233. paradox460 ◴[] No.45089118{3}[source]
Interdiff
234. Izkata ◴[] No.45089139{6}[source]
> Once the PR has been merged, I prefer it merged as a single squashed commit so it's reflective of the single atomic PR (because most of the intermediary commits have never actually mattered to debugging a bug caused by a PR).

While working on a maintenance team, most of the projects we handled were on svn where we couldn't squash commits and it as been a huge help enough times that I've turned against blind squashing in general. For example once a bug was introduced during the end-of-work linting cleanup, and a couple times after a code review suggestion. They were in rarely-triggered edge cases (like it came up several years after the code was changed, or were only revealed after a change somewhere else exposed them), but because there was no squash happening afterwards it was easy to look at what should have been happening and quickly fix.

By all means manually squash commits together to clean stuff up, but please keep the types of work separate. Especially once a merge request is opened, changes made from comments on it should not be squashed into the original work.

replies(1): >>45093055 #
235. steveklabnik ◴[] No.45089144{5}[source]
That’s backwards, your git command is “move to this branch” and the jj command is “update where the branch points to,” so git reset —hard.
replies(2): >>45089262 #>>45091987 #
236. steveklabnik ◴[] No.45089153{10}[source]
You have to finish the rebase before git will let you move in to other work. jj will not. With git you can stop in the middle to make changes, but you must continue until the rebase is done.
replies(1): >>45089925 #
237. KingMob ◴[] No.45089162{8}[source]
> I'm still open to being convinced, but have yet to see anything that makes me go "wow, I need to try that!".

You might not find that feature, but I'd suggest giving it a go anyway. The list of jj technical superiorities is short, but the numerous quality-of-life DX improvements all add up to pleasant, fearless version control.

Even without editor support or a UI, I abandoned git forever last year after using jj for a couple weeks.

Just my $.02.

replies(1): >>45091651 #
238. steveklabnik ◴[] No.45089163{9}[source]
The key is to be cowboy until you’re happy with things, and then get clean, just like with git. It’s way easier to slice and dice commits with jj and so you can be sloppy at first and it’s easy to turn beautiful afterward.
replies(1): >>45089939 #
239. KingMob ◴[] No.45089169{10}[source]
The parent is describing the megamerge pattern, which is a way to work on multiple branches at once.

You don't have to do that, and you rarely push it to others. History looks the same as git, usually, although I end up rebasing more than I ever did in git, since it's easier and safer.

240. steveklabnik ◴[] No.45089177[source]
There are many different guis you can use with jj.

It’s true that submodules aren’t directly supported, but you can use the git commands to manage them hntil native support comes.

241. didibus ◴[] No.45089207{3}[source]
I think the issue for me will be that IDEs tend to show diff with head, I'm not even sure you can configure them otherwise.
replies(1): >>45089655 #
242. KingMob ◴[] No.45089209{4}[source]
uv is amazing, but mise is good if you need non-python tools too (e.g., bringing in prettier to format your HTML/Js/templates, adding Js tools, etc.)

It's also not either/or. mise can defer to uv for setting up all the python stuff.

The mise docs are pretty good these days, I think. Task dependencies are still a little rough, though.

replies(2): >>45090517 #>>45091672 #
243. KingMob ◴[] No.45089233[source]
This over-estimates the lock-in, because jj uses git under the hood. You can try jj, and your colleagues need never know. If you hate it after a couple weeks, you can return to git, no sweat.

It's much easier to try out than earlier VC systems.

244. KingMob ◴[] No.45089237[source]
It's still insane to me that universal `undo` is not a basic git command.
replies(1): >>45089983 #
245. KingMob ◴[] No.45089246[source]
No, sadly, that's more of a Github limitation.
246. Izkata ◴[] No.45089262{6}[source]
The git commands are "create new branch at HEAD and switch to it", and the context of the thread above sounded like that's the functionality they wanted?
replies(1): >>45089628 #
247. KingMob ◴[] No.45089270[source]
You might be interested in pijul then, which has a theory of patches that is interesting.
248. cortesoft ◴[] No.45089289{5}[source]
I don't think this is what the person you responded to is asking.

Their question is more, "why would I buy a digital camera that takes pictures in a new format that only a few cameras understand? All my tooling, 3rd parties, and other camera I own use the standard format. Even though I can see why the new format has advantages, I am still going to have to use the other format for all these other photos I have to work with, and there aren't equivalent tools in the new format for all these other photos things I need to do. Even if I buy this new camera, I am still going to have to work with the old format, so I'll have to learn how to use two formats now, and get used to two tool chains. Since the existing format is something I am going to have to use either way, how is it worth it for me to have to use two formats?"

replies(1): >>45090523 #
249. DAddYE ◴[] No.45089298[source]
How does JJ works with LazyGit? I can’t see myself living without it.
250. ygouzerh ◴[] No.45089392[source]
Thanks for the explanation!

For the defense of git add and the index, I actually personally enjoy it. I discovered git add -p recently, and it's quite nice.

It's like a way to say: let's prepare a commit with only this part of the file, but not yet this one as it's not yet finished.

For example, you can git add -p only the dependencies of a package.json, push that so others have it, and continue working on the scripts part.

I use often as well the index in order to have more granular commits that follows well Conventional Commits, when I want to have a `docs:...` for the readme update and a `feat: ...` for the src change

jj looks interesting, I will take a look!

replies(2): >>45089668 #>>45089947 #
251. sfink ◴[] No.45089421{7}[source]
My personal opinion: `jj new` is better because it's non-modal. If you use `jj edit`, you're sort of switching to "edit mode": any change you make will trigger a rebase of all descendants.[1] You're live-mutating the core graph structure rather than a harmless appendage node. Also, if you notice something else that needs to be fixed, you can do it but then you'll need to remember to split it out into a separate commit before leaving edit mode.

With `jj new` + `jj squash`[2], you're collecting work that you can review as a separate thing anytime as you go along. You don't have to remember anything. If you throw in an unrelated change, you'll notice it if you review the changes before squashing them, so you can split it out then. And I'm pretty much always working in this state even when I'm at the top of my branch, so `jj new some-deep-node` doesn't really change anything. If I get called away and have no memory of what I was doing when I return, it doesn't matter: my jj state tells me exactly where things are and what I was doing.

[1] Which is not a huge problem, you have deferred conflict resolution so if something goes wrong you can probably just repair it with normal editing or your editor's undo functionality.

[2] I don't usually bother with `jj new -A`, since I'm going to squash my "out of line" temporary commit into the linear chain anyway. `jj new -A` is more similar to `jj edit` than `jj new` -- it shares some but not all of the modal disadvantages. So perhaps my answer to your actual question is: "yeah, I dunno either."

replies(1): >>45089922 #
252. GCUMstlyHarmls ◴[] No.45089449{3}[source]
I used zsh for gosh, over 15 years probably now. I swapped to fish at the start of this year on a bit of a whim (new year, new computer, new ~~me~~ shell).

It's pretty good in a "gets out of the way" kind of way, and the `abbr` feature is preferred over aliases but it has some annoying quirks like no dictionary type, the inability to "background blocks of code", requiring spawning a subshell + string which has other issues and in a way it feels sort of stuck between being new and friendly but still carrying over enough baggage to be annoying in other ways.

I tried Nushell a week or so ago and after getting my head around it (and $it and $in, haha) I think it feels like the future. It's not quite there ergonomically in some ways eg: reedline can't edit the current command line, but it's quite close.

What really sold me on it was when I

- Had a flat text file of "url\nartist\ntitle"

- Had to download these urls and insert the artist & title into a database

and I realised I could do it all with Nu primitives,

    open new-landings.txt
    | lines
    | each { $in | str trim}
    | chunks 3
    | each {{title: $in.0, artist: $in.1, url: $in.2}}
    | enumerate
    | par-each {
      http get ($in.item.url | str replace large medium) | save $"landing-($in.index + 101).jpg"
      # also insert artist title into database
      run-app ... etc
    }
Now, could you do the same thing with zsh/bash/sh? Yep. Some kind of awk/sed thing with xargs I would guess. But I'd have to look it up. With Nu, after a few hours of playing with it I already "knew" how to do it. That felt really powerful.

For reference, I also tried Murex and Elvish. Elvish doesn't support ctrl-z, so its disqualified right there though I like the syntax. Murex seemed fine but Nu was bigger with wider support. I think I saw Nu had pattern matching and it immediately got a big desirability bump.

I remember seeing another comment, wishing we had STDIN, STDOUT, STDERR and STDDATA, maybe one day.

replies(1): >>45090913 #
253. codethief ◴[] No.45089453{9}[source]
I had been wondering for quite some time what people meant when they said "you can resolve conflicts later". Thanks so much for this excellent explanation!
254. sfink ◴[] No.45089534[source]
> Would it be accurate to describe Jujutsu as "a Mercurial-inspired frontend for Git"?

In my opinion: yes. (Maybe with a slight caveat that I think some of the Mercurial inspiration came through Sapling.) And it's not limited to Mercurial/Sapling ideas, it has some new stuff too, but I think that still counts as "Mercurial-inspired".

> Also, another question I have for people who have used Jujutsu: Is it focused on interactive use, or is it also convenient to use for automatic/non-interactive use?

I use it in automation. I'm hampered because the primary VCS for the automation is git, so I'm doing more of a compatible layer and can't go too crazy with the jj-specific stuff.

But jj has some features that are excellent for automation.

Example: you can manually wrap things in a discardable transaction: grab the current operation ID, do all kinds of weird mutating operations that add and remove patches, maybe even merge and rebase, and then at the end (maybe in a `finally` block) do `jj operation restore <id>` and everything will go back to the way it was.

Another example is that if you want to tweak something, say a config file, in N different ways, you don't have to mess around with naming branches and switching between them or whatever. Just create N independent child commits and make the appropriate changes in each.

Or conversely: make M children for the M values of configuration value 1 and N children for the N values of configuration value 2, then create a new multiparented child (a merge commit) for each combination. `jj new <a> <b>` creates a merge commit with parents <a> and <b>, so:

    jj new $base -m 'var1=a'
    jj new $base -m 'var1=b'
    jj new $base -m 'var2=x'
    jj new $base -m 'var2=y'
    jj new 'description("var1=a")' 'description("var2=x")' -m 'var1=a, var2=x'
    # run scenario 1 with those settings
    jj new 'description("var1=a")' 'description("var2=y")' -m 'var1=a, var2=y'
    # run scenario 2 with those settings
Sorry, I'm using the `description(...message...)` revset expression to retrieve the appropriate commits when in practice you'd probably record the IDs and do `jj new $a $x` with no descriptions anywhere. But hey, the above would work as written!
255. sfink ◴[] No.45089573{4}[source]
Technically, it's z-k (z=0, k=15). It makes for a nice root change ID: zzzzzzzz. Less choking than kkkkkkkk.

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.)

replies(1): >>45090215 #
256. sfink ◴[] No.45089593[source]
You can have most of that stuff, but it doesn't sound like that would sway you anyway. (And those 4 missing features may truly be showstoppers for you, in which case you're absolutely correct to stick with git.) There are early adopters and late adopters, and both have good reasons for being the one they are. You do you.
257. sfink ◴[] No.45089623[source]
I'm mostly git -> mercurial -> sapling -> jj. For me, Mercurial -> Sapling was a mostly lateral, slightly upward move. Sapling -> jj felt really good, though oddly not because anything felt horribly wrong or missing in Sapling. Things just feel nicer and lighter in jj. Once I know a couple of things, I can combine them to do lots and lots of other things without learning any new commands. In fact, the total command set is pretty small, yet covers about the same range as other systems (a little more here, a little less there). Also, improvements come in at understandable and predictable places: at its current stage of development, it's not growing brand new commands. It is instead slightly improving existing commands to enable significant new capabilities. It has the feel of a well-designed system with mostly orthogonal components. (Give it a decade, and perhaps it'll start crumbling under its own weight too...)
258. steveklabnik ◴[] No.45089628{7}[source]
“jj bookmark set” doesn’t create a new branch and switch to it. It updates the head of an existing branch to a new place.

jj doesn’t have a “name a new branch and switch to it” command, because you usually don’t bother naming branches until you’re using them up to a forge, and there’s no “current branch” concept. I creat new named branches with “jj git push -c” which names it for me, and switching branches is closest to jj new or jj edit.

259. steveklabnik ◴[] No.45089655{4}[source]
I’m not a big “VCS in an IDE” person but visualjj works for vs code, and there’s other plugins to, I hear. If your IDE isn’t configurable and assumes certain things then it might not work, yeah.
260. steveklabnik ◴[] No.45089668{3}[source]
I love the index and git add -p too, jj does thus far better than git imho. You just have to realize that the index is a commit, not its own separate feature.
261. sfink ◴[] No.45089712[source]
> Complicated workflows requiring deep understanding might be harder to learn because of a higher abstraction.

That's a fair complaint, and where other git porcelains have failed in the past.

I think the trick is that jj isn't raising the level of abstraction. Well, maybe in one place: there are change IDs in addition to commit IDs, and most of the time you'll want to be working with the change IDs. But even then, you could just use commit IDs, you'd just need to be looking them up all the time.

jj adds on a minimum of new functionality. It just turns out that if you make the already-existing git graph the object of your CLI commands, you can actually expose all of the operations you need with a much smaller set of primitives, even those for constructing your complicated workflows. git has accreted commands and flags to handle everything you need because it restricts its focus to a single commit or ref. Sometimes a command will use that commit to imply a whole branch, sometimes not. It avoids mutating things because that'd make you lose track of the holy commit, but then it has to provide lots of tools for when you want to mutate things. It has a staging area so that you can delay creating a commit until it's stable enough -- but now you have another concept. Stashes are similar.

(This actually undersells jj a bit. The op log and deferrable conflicts, for example, are bizarrely useful in practice even though they aren't fundamentally "needed". Nor do they raise the abstraction level. But going into that would dilute the message.)

262. caspar ◴[] No.45089721[source]
Another major caveat is that JJ doesn't work with Git LFS yet - though some progress is being made: https://github.com/jj-vcs/jj/issues/80
263. MrJohz ◴[] No.45089727{3}[source]
Bookmarks are similar to branches (and I believe implemented as branches?) but aren't quite the same thing. In particular, you can't check out a bookmark, you can only check out commits. If you do something like `jj edit <bookmark>`, it'll resolve <bookmark> to figure out which commit it points to, and then check out that commit. If you create a new commit now, the bookmark won't automatically be moved along, because jj doesn't know if you want to add a new commit to the <bookmark> branch, or if you're trying to create a new branch that forks off <bookmark>.

Whereas `git checkout <bookmark>` puts you in a state where you've checked out the _branch_ and not the commit, which means if you create more commits, they'll automatically be added to <bookmark>. To create a branch forking off from <bookmark>, you need to first create a new named branch, then start adding commits to it. (There are other ways as well, but this is the most standard approach.)

The tradeoff with Jujutsu's approach is that it's very easy to create lots of lightweight spin-off branches (just `jj new <fork point>`, no need to come up with names first). But it's slightly harder to do the traditional linear approach where you're consistently committing in a straight line on a single branch, and then syncing that branch with another remote. This is because for each new commit you create, you also need to update the bookmark manually.

In the parent poster's case, it's probably complicated further by them working on the same branch as someone else. Assuming there's no rebasing going on, this shouldn't be too complicated, but it's another case where in git you would check out a branch, and `git pull` will automatically forward you to the head of the branch, whereas `jj git fetch` will just move the bookmark without moving you.

It sounds like the workflow that this person is using doesn't fit that well with how jj "wants" to be used. I believe there's been some talk in jj of a way to automatically update branches when creating a new commit, which would help somewhat, but I think it's also a natural effect of having built their workflow around git, and that flow just not quite working so well in jj.

replies(1): >>45091477 #
264. exclipy ◴[] No.45089737{3}[source]
git-branchless implements revsets for git https://github.com/arxanas/git-branchless/wiki/Reference:-Re...
replies(1): >>45089907 #
265. raylu ◴[] No.45089847{5}[source]
`jj op` has `log` to show you the op log, `show`/`diff` which work like their non-op versions, and `jj op restore` which is sorta like `git reset --hard HEAD@{whatever}` but for your whole repo state
266. raylu ◴[] No.45089871{8}[source]
the thing about rebasing/cherry-picking (including just popping the stash) in git is that you only have 2 choices: fix the conflict now or abandon the entire rebase/cherry-pick

with jj, you have the option to fix half of it and come back later. you can take a look and see how bad the conflicts are if you go a certain route and compare to another option

replies(1): >>45091729 #
267. MrJohz ◴[] No.45089900{5}[source]
They're not quite the same thing as named branches in Git. In jj, a branch is a chain of commits. This chain of commits cannot be named. It's possible to create a bookmark, which is a name for a particular commit, and it's possible to use that bookmark to refer to a branch of commits (e.g. jj rebase's -b flag), but this is different from naming a branch like you would in git.

This is a subtle difference, and most of the time it doesn't matter at all, but in this case there is a difference between how bookmarks work in jj, and how branches work in git.

replies(1): >>45093301 #
268. MrJohz ◴[] No.45089907{4}[source]
Git branchless is basically jj as a git subcommand - it's no coincidence that arxanas is also involved in jj's development!
269. BeetleB ◴[] No.45089922{8}[source]
Indeed, that was my point. jj new -A would also trigger rebases.
270. adastra22 ◴[] No.45089925{11}[source]
Ok. There are simple workarounds. I git rebase —quit and then make a branch for this saved rebase progress to return to later. I then restart the rebase from the corresponding commit. It would be much nicer if this was all wrapped up a simple command.

There is value here, but I think it is more like “add a new command consisting of 50-100 lines of code” not “write an entirely new VCS.”

replies(1): >>45090285 #
271. adastra22 ◴[] No.45089939{10}[source]
That’s not how I use git, at all. I have a messy workspace with a lot of things going on simultaneously, and only selectively stage when something crosses the finish line. Sounds very difficult to do this in jj.
replies(2): >>45090278 #>>45091465 #
272. BeetleB ◴[] No.45089947{3}[source]
> For the defense of git add and the index, I actually personally enjoy it.

I'm not criticizing it. In fact, I'm pointing out how, if you like the index/staging workflow, you can have it in jj with much more power (your index is version controlled).

Oh, if you thought this line was the criticism:

> After I'd been using jj, having a separate concept called "index" and a separate concept called "stash" suddenly seemed ridiculous.

I'm not saying the index/stash workflow is ridiculous. I'm saying that having them as separate constructs, and giving them names, is silly. The index need not be distinct from your overall graph.

273. arialdomartini ◴[] No.45089962[source]
You will be probably excited to give https://github.com/bolivier/jj-mode.el a try.
274. Cthulhu_ ◴[] No.45089983{3}[source]
That's because there is none, because you're dealing with 4 locations (local, staging, local branch, remote branch); which one do you want to undo?
replies(1): >>45091054 #
275. Cthulhu_ ◴[] No.45089987{4}[source]
I can understand the criticism to submodules but the rest of your comment feels like unwarranted snark.
replies(1): >>45090038 #
276. socalgal2 ◴[] No.45089997{8}[source]
git stash is not that simple. you'd need to remember what branch that stash applies to to get back to where you were.

I'm new to jj. I'm still mixed on if I like it not. I think it's mostly familiarity. For example, switching to a commit puts things in the state before the files were committed. All my projects have a presumit step that says "hey! commit your files!" so they are all incompatible with jj at the moment or at leas the default. I end up having to do temp stuff like `jj new` (ok, now they're committed). Now run my presubmit scripts. Then `jj undo` so I don't have this unneeded commit. That said, I'm sure there's a better way, I just haven't gotten used jj yet.

Others have said this, `jj undo` and `jj op restore` have been lifesavers though. No matter what I do I can get back to where I was before I messed up.

replies(3): >>45091695 #>>45091791 #>>45092309 #
277. Cthulhu_ ◴[] No.45090001[source]
> A rebase may unintentionally overwrite someone's work

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.

278. raylu ◴[] No.45090004[source]
I'm having a hard time understanding your question. are you asking how people who are unfamiliar with git and jj should mentally model a stack of PRs? and that this is a problem because it's intimidating for them when an expert (in git? in the codebase?) is reviewing their code?

if yes:

while jj does make stacked PRs quite easy, I think this tutorial is mostly aimed at people who are still getting their feet wet and not making stacks of PRs. and if the expert is intimidating, tell them to be helpful or fuck off

if no:

well I don't understand your question, but probably don't `jj edit` the commit at the bottom of a stack; `jj new` and, when you're ready, either `jj squash` it down or `jj rebase -s @ -A @-` your change into the stack instead

replies(2): >>45093929 #>>45094050 #
279. odie5533 ◴[] No.45090038{5}[source]
Hooks, submodules, and LFS all have huge followings of haters. I think just a week ago there was a big post on HN about how awful LFS is.
replies(1): >>45094087 #
280. wolvesechoes ◴[] No.45090153{4}[source]
Yes, jj is awesome and perfect, and if it lacks any feature, it means that feature is bad and you shouldn't really use it.

Are you a GNOME dev by any chance?

replies(2): >>45090332 #>>45092575 #
281. baq ◴[] No.45090168{6}[source]
It’s funny because it’s true! ;)
282. dzaima ◴[] No.45090215{5}[source]
Bookmarks take precedence over change ID prefixes. And indeed if you add a bookmark "xyz" and a change had a unique prefix "xyz", the change's unique prefix will grow by one. (the unique prefixes can already grow or shrink semi-arbitrarily so this doesn't "break" anything more)
283. digianarchist ◴[] No.45090259{4}[source]
If you use asdf mise is worth a look. I use uv directly.
284. digianarchist ◴[] No.45090266{3}[source]
Adoption probability without git squatting would be virtually zero.
285. steveklabnik ◴[] No.45090278{11}[source]
Selectively staging is easier in jj. I loved git’s index, jj does it better.
286. steveklabnik ◴[] No.45090285{12}[source]
One little thing here, one little thing there, it all adds up. The jj model is more orthogonal, and so ends up being easier, which also translates to more power.
287. hu3 ◴[] No.45090291{3}[source]
Interesting. How can I use jj without git?
replies(1): >>45091714 #
288. lmm ◴[] No.45090332{5}[source]
I'm all for functionality, but git should never have been a smorgasbord of random bolt-on special cases. If you have to break the basic core functionality to make a feature work, it's not a good feature.
289. 1718627440 ◴[] No.45090448{5}[source]
True, Git only rebases from one branch. You would need run the same command on all other branches, or use git for-each-ref.
290. 1718627440 ◴[] No.45090471{11}[source]
I would just abort the conflict resolution.
291. sbinnee ◴[] No.45090495[source]
I happened to listen to a podcast episode yesterday where the topic was Git rebase. There were 4 people in the panel. One person made a point that he did not understand why they even care, saying basically Git is broken, all the VCSs are broken because they are too complicated and they get in the way. He said a lot of things but my take was that all the existing VCSs are just a distraction. I personally don't agree with him. But while listening to his argument, I started imagining a perfect VCS which is not Git, JuJutsu, perforce, or whatever.

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

replies(2): >>45090686 #>>45091078 #
292. stavros ◴[] No.45090517{5}[source]
Ah interesting, I have needed node and it's been a bit of a pain, maybe mise can set it up. I'll try it again, thanks!
replies(1): >>45091684 #
293. IshKebab ◴[] No.45090521{3}[source]
I can't remember exactly but I have tried it a couple of times and couldn't get it to work at all.
294. stavros ◴[] No.45090523{6}[source]
You don't have two formats, though. Jj transparently works with git. I use it for everything and none of my collaborators is even aware that I'm using jj.
replies(1): >>45092341 #
295. MrJohz ◴[] No.45090681{8}[source]
I think the word "later" is unhelpful in this situation because it implies all sorts of different timescales. You don't want to be resolving merge conflicts three weeks after you've created them, you're right!

Typically for me, "later" means "immediately after the rebase command has finished", which is very similar to git's "while the rebase command is running", but has some subtle and important differences.

For example, because the rebase completes first, I get to see roughly what the end-state of the rebase is before I start doing the hard work of fixing conflicts. This is useful as a sanity check - sometimes the reason I'm getting a bunch of merge conflicts is because I was rebasing the wrong things in the first place and included an extra commit somewhere. Seeing the result first gives me the chance to sanity check what I'm doing.

Another thing is that my repository is never in a broken state where I can't continue doing other things. There's no way in git to stash a rebase, say because I've realised a different approach might work better or just because I urgently need to work on something different. I either need to cancel the rebase and start it again later, or keep on going until it's over. In jj, because the conflicts are automatically checked in as part of the commits, I can easily jump backwards and forwards between the work I'm doing resolving my conflicts, and anything else.

Another way of thinking about it is that git is very modal. You check out a branch and are in the "branch" mode, and then you start a rebase and are in the "rebase" mode, and then you do a bisection and are in the "bisect" mode - it's difficult to move freely between these modes, and there's certain things you can only do in some modes and can't do in others. In contrast, jj has exactly one mode: "there is a commit checked out". The different operations like rebasing all happen atomically, so you never see the halfway state. But because things like conflicts can be encoded in the commit itself, you still have all the same power that the modal approach had, just with a simpler conceptual model.

296. pampa ◴[] No.45090686[source]
I didn't think that there was something wrong with my git workflow, or that git was complicated. I switched from darcs as my primary VCS to git around 2007. I was skeptical about jj at first but gave it a try. Did not look back ever since.

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.

297. hiq ◴[] No.45090913{4}[source]
> I'd have to look it up.

I suspect most of the current coding assistants would have been able to do that for you in most languages, including something more portable and easier to maintain like Python.

298. infamousclyde ◴[] No.45090924[source]
To the author: I really enjoyed your style of writing. This had more explicit and realistic examples compared to other HN tutorials on the subject, and that made it easy to inhale in one sitting. Thanks for your work!
299. thecupisblue ◴[] No.45090987{5}[source]
Yes, but in that case, I want the fix of the original mistake to be done in a new commit.

Why?

Example #1: - I am working on implementing API calls in the client, made 3 commits and opened a PR - In the meantime, the BE team decides they screwed up and need to update the spec

If I now go and fix it in the commit #1, I lose data. I both lose the version where the API call is in its original state, and I lose the data on what really happened, pretending everything is okay.

Example #2: - I am writing a JVM implementation for our smart-lens - In commit #2 I wrongly implement something, let's say garbage collection, and I release variables after they have 2 references due to a bug. - I am now 6 commits ahead and realise "oh shit wait I have a bug"

If I edit it inline in commit #2, I lose all the knowledge of what the bug was, what the fix is, what even happened or that there was a bug.

tldr: just do an interactive rebase

300. thecupisblue ◴[] No.45090993[source]
I still do not understand why use jj.

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?

replies(2): >>45091624 #>>45093939 #
301. KingMob ◴[] No.45091054{4}[source]
Any of them. All of them. (Maybe not remote changes, but that's way less safe to attempt, even if possible.)

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.)

replies(1): >>45093852 #
302. guidopallemans ◴[] No.45091078[source]
I listened to the same podcast, and agreed with the points that Casey made. However, there were some aspects of version control that were glossed over.

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.

303. jbverschoor ◴[] No.45091092{5}[source]
Such nice community.. I think I’ll stick to git, even though Torvalds isn’t the nicest person in the world
replies(2): >>45091313 #>>45091598 #
304. 63stack ◴[] No.45091313{6}[source]
Don't base your opinion on some idiotic comments on HN, they are probably not part of a "community". Just try it, it's a legit super good tool.
305. stavros ◴[] No.45091434{3}[source]
I use jj, but I haven't gotten into conflicts much yet. What do you mean by "they aren't stop-the-world operations"? I do like how chill jj is about them (as in, it doesn't interrupt what I'm doing), but I always resolve them immediately. Can I not?
replies(1): >>45093415 #
306. sgjennings ◴[] No.45091465{11}[source]
You can work exactly this way using the “gitpatch” diff editor[1].

1. Your working copy contains whatever mish-mash of changes you want.

2. When you’re ready to stage and commit these changes, run `jj commit --tool gitpatch`

3. The iterative “stage this hunk?” UI from git lets you choose what to commit.

4. Your editor opens for a commit message.

5. The changes you selected are now in a new parent commit of your working copy, and the remaining changes are left in the working copy commit.

In addition to the _same_ workflow, jj makes it easier to have other workflows as well (you may be interested in the megamerge workflow if you’re always working on multiple tasks at once).

[1]: https://zerowidth.com/2025/jj-tips-and-tricks/#hunk-wise-sty...

307. epolanski ◴[] No.45091477{4}[source]
> In particular, you can't check out a bookmark, you can only check out commits.

That's because "branches" in git are an imaginary concept, they are simply a label you give to a certain commit, nothing else, nothing more.

Moreover, in git this is further complicated by the fact that you may have branches with the same identical name on different environments (your local and remotes). So you can have, by any means, 4 `feat/foo` branches all being the "right" `feat/foo`, and you need to decide which one is the right one (we tend to default to some origin, but what if you have local work that is ahead of origin? maybe on two different computers? Meanwhile your branch is on a fork of a different origin. Git branches do nothing but complicate a simpler model where what matters is the commit, history and diff, yet we fixate on label names which are just conveniences).

At the end of the day branches do nothing but add complexity, I much prefer the jj model, bookmarks are just _local_ names you give to some edit, you move the bookmark manually, and it _may_ point to a git branch just to preserve the git backend.

replies(2): >>45091827 #>>45091842 #
308. comonoid ◴[] No.45091598{6}[source]
I'm replying to a comment that starts with accusation: "False.". My point is that correct version differs just by a single word. It can be just a tipo in Disposal8433's comment, not "false".
309. sgjennings ◴[] No.45091624[source]
I’ll focus only on the syntax of the command. Why you might need it at all is important, of course, but it’s nuanced and specific to how some people work (I never need this command or anything like it, for example).

> 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.

310. nchmy ◴[] No.45091651{9}[source]
Jjui is an incredible TUI for jj. Its the only way I interact with jj
311. senekor ◴[] No.45091664{6}[source]
Yes and no.

Before I started using Jujutsu, I didn't have any pain points with using Git. I didn't understand what all the fuss was about. Git works well! So I totally understand how most Git users have that same reaction when hearing about Jujutsu.

I think the reason I even tried it out in the first place was because Steve Klabnik wrote a tutorial about it. I have a lot of respect for him, because the Rust book is really good. So I though: If Steve thinks it's worth it, I should probably check it out.

Now that I'm used to jj, going back reveals like 100 things that are immediately super annoying when using git. I don't feel like writing it all down TBH. :-) In a general sense, Jujutsu get's out of your way much better than Git. There are a lot of situations where Git blocks you from continuing to work. Have a merge conflict? Stop working, fix it right now. Want to check out another branch? Nu-uh, clean up your dirty worktree first. jj doesn't do that. Have a conflict? I'll record it in the commit, fix it whenever you like. Checking out another branch? No worries, I'll keep your work in progress safe in a commit.

312. nchmy ◴[] No.45091672{5}[source]
Yeah mise and uv are completely complementary - there's no overlap, whatsoever, between them...

In fact, if uv is installed (which can be done via mise), mise uses it for python related stuff.

I use mise to install as much as I can now - different language runtimes, tools/utilities etc. I even discovered yesterday that you can use the ubi backend to install directly from Git repos that aren't yet supported by the other registries.

313. nchmy ◴[] No.45091684{6}[source]
Should be as simple as mise use node.

Or mise use -g node@version (the -g installs it globally)

Same for anything else - deno, bun, golang tooling etc

replies(1): >>45091707 #
314. skydhash ◴[] No.45091695{9}[source]
> git stash is not that simple. you'd need to remember what branch that stash applies to to get back to where you were.

I use the stash for changes I like or for small experiments, not tied to anything. For any other changes, I just create a wip commit and switch. It’s trivial to switch back and soft reset.

315. stavros ◴[] No.45091707{7}[source]
Oh, this reminds me of a big problem I had: I never know the names of the packages, is there a registry somewhere? I never managed to find one.
316. tfsh ◴[] No.45091714{4}[source]
Right now there's no active public backends, there are a few prolific JJ contributors who are working on their own forge though (https://ersc.io).

At Google, JJ is natively supported for interfacing with the monorepo, this is where the proof of being backend agnostic comes from. Hopefully as the network effects catches on we'll see more and more of a desire for 3P forges.

317. cynicalsecurity ◴[] No.45091719{3}[source]
> Jujutsu is free software in both senses of the word.

It is not free for me. I would need to spend my time on learning it if I would ever encounter it in the wild. The time I could otherwise spend on my family, projects or hobbies. While having no benefits over git, it's trying to steal my time and make my life more complicated, by trying to introduce just another code versioning system no one asked for.

replies(1): >>45093325 #
318. skydhash ◴[] No.45091729{9}[source]
Are you using your editor or a special software/plugin for resolving conflicts. I used Sublime Merge in the patch, but now I’m using Magit+ediff. Resolving conflicts is quite trivial in that case. And I can always ‘rebase -i’ to revert some of the decisions I’ve taken.
319. MangoToupe ◴[] No.45091791{9}[source]
> git stash is not that simple.

It's a stack of diffs.

Anyway, I've probably used this to transfer changes between branches thousands of times. Once you grasp the underlying data model all these abstractions introduced by jujutsu seem more confusing.

That said, I do understand most people aren't going to take the day or so to read through any of the hundreds of detailed "explain the data model" articles online.

320. MangoToupe ◴[] No.45091805{9}[source]
Isn't this equivalent to simply hard resetting to before the pull? (Or the commit before the conflict?) Plus then you don't end up with an extraneous branch.
321. MrJohz ◴[] No.45091827{5}[source]
The problem is that branches _aren't_ an imaginary concept in git. The easiest way to see this is by looking at the difference between `git checkout master` and `git checkout ae596fe`. The first one checks out a branch, but the second one checks out a headless commit. I can still do many of the same operations in both states (for example, I can create new commits based on the current one), but how the repository changes will differ depending on what I've got checked out.

I agree with you that this is mostly unnecessary complexity — branches being anonymous is one of the big reasons I like jj, and why I think it's so useful to people just getting started with version control, because it is a simpler conceptual model. But it's not correct to say that branches in git are imaginary.

322. tsimionescu ◴[] No.45091842{5}[source]
> That's because "branches" in git are an imaginary concept, they are simply a label you give to a certain commit, nothing else, nothing more.

They're really not. The whole Git ecosystem makes a very clear distinction between branches and tags, even though they are virtually the same thing in the underlying data model.

For example, if you checked out a branch and then do a git commit, the branch name tag will be automatically moved to the new commit. Conversely, if you checked out a tag (or a random commit that is not the head of a branch) and then run git commit, the new commit will not get any tag.

Another example is `git rebase`, whose semantics only make sense if you consider that branch names refer to branches, not just to their latest commits, since they often modify many children of those branches.

> Moreover, in git this is further complicated by the fact that you may have branches with the same identical name on different environments (your local and remotes).

This is presented as more complicated than it is. If you are working with multiple remotes, you may end up in these complex situations, true. But Git is pretty clear about the names and identities of each of these. You have the local branch, `feat/foo`, which typically has as a default upstream the remote branch named `feat/foo` on a remote called `origin`; the local copy of that branch is the tracking branch called `origin/feat/foo`. If you have other remotes `remote1` and `remote2`, the tracking branches of those are called `remote1/feat/foo` and `remote2/feat/foo`. You will never want to commit directly to any of these tracking branches - instead, you'll create either separate local branches with each as an upstream(`feat/foo-remote1`, `feat/foo-remote2` - if you want different commits for each remote), or directly push from `feat/foo` to all of them if the intent is to keep them in sync (`git push feat/foo:remote1/feat/foo` or similar).

replies(1): >>45093434 #
323. p_l ◴[] No.45091928{5}[source]
In both cases you might have had a plugin that worked on Git - I think one is even part of default VS Code install (at least I don't recall having to install it)
324. tsimionescu ◴[] No.45091961{5}[source]
> as far as I understand the git version is "git checkout -b XYX"

The difference is actually worse than that. There is not the regular git equivalent, because this step is just done implicitly for you, normally. That is, with jj, just because you had checked out the head of main and then you added a new commit, doesn't mean your new commit is now the new head of main. `jj bookmark set -r@ main` is the way you tell jj to actually advance main to your latest commit.

But you are right - `git switch -C main` would be more or less the equivalent in git if you were working in detached head mode, which is how jj normally works (note the `-C`, not `-c`, to forcefully update main to point to this commit).

325. tsimionescu ◴[] No.45091987{6}[source]
They're closer to the right command than you are. `git reset --hard` will move HEAD to the given branch. The right command would be `git checkout -B branch` / `git switch -C branch`, to create or update `branch` to point to the current commit (except for the side effect that future commits will then go onto `branch` in git, while they won't in jj).

Basically, jj is just like working with git in detached head mode as far as I can tell.

326. skydhash ◴[] No.45092179{3}[source]
I think git was meant to be used as a base layer. You design your workflow and then add you aliases on top, making it more semantically significant for you.
327. pjjpo ◴[] No.45092217{3}[source]
I was curious to give Jujutsu a try but while procrastinating I found light clones and can't go back. Hopefully they support it eventually and then I'll get to procrastinate on trying it again.
328. arcbyte ◴[] No.45092309{9}[source]
> git stash is not that simple. you'd need to remember what branch that stash applies to to get back to where you were.

This quote confused me for a while. I was thinking "git stash isn't branch specific its just a single bucket". But I realoze you must be making lots of little changes that are highly branch specific and then not wanting to commit those, but instead stashing them. Which would leave you with a hellscape of stashes that can't just be unstashed.

The biggest problem with git is people just inventing asinine ways to do things and ending up with absolutely stupid problems like that. No sane person does these things but yet I do keep encountering people digging holes and falling in them. It's a bit like people who invent the clever idea of having one repository with multiple code bases on different root branches. It's possible but you dont deserve to be working in this industry if you think its a good idea.

Git is simple. It's stupid simple. That's its problem.

329. mcepl ◴[] No.45092341{7}[source]
If you say that you can work only with JJ and never use git, you are delusional. For one, where is that JJ forge (i.e., the equivalent of Sourcehut)?
replies(2): >>45092523 #>>45093066 #
330. baq ◴[] No.45092493{11}[source]
The branch is indeed unusable, as opposed to the whole repository being unusable - it's a very nice upside actually.
331. Macha ◴[] No.45092523{8}[source]
GitHub, or sourcehut, or whatever you prefer.

Maybe you feel that jj git fetch and jj git push are using "git" but it means you avoid the git cli in favour of the IMO better designed jj cli.

332. layer8 ◴[] No.45092573{3}[source]
It's the only way to ensure proper cross-platform EOL handling when you can't rely on users configuring their local Git appropriately.

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.

333. odie5533 ◴[] No.45092575{5}[source]
I never said jj was awesome. I've never used it. I'm already adept at git, enough to have opinions on its advanced features, and the knowledge to avoid them, so I'm not sure if I'd gain all that much from jj.
334. aniviacat ◴[] No.45092597[source]
It's also quickly followed by:

> Jujutsu is relatively new and doesn't cover 100% of the features of Git yet.

So it's more powerful except when it's not.

335. KallDrexx ◴[] No.45092976{7}[source]
Yeah I don't have much experience outside of GitHub for team projects, so maybe gitlab works better. For GitHub it just gives up and claims it can't give you diff since the last review
336. wry_discontent ◴[] No.45092984{4}[source]
I don't know what that means. I haven't changed how I resolve conflicts with jj.
337. KallDrexx ◴[] No.45093055{7}[source]
I wonder by your last comment if this is just is talking past each other.

I try very hard to keep my PRs very focused on one complete unit of work at a time. So when the squash happens that single commit represents one type of change being made to the system.

So when going through history to pinpoint the cause of the big, I can still get what logical change and unit of work caused the change. I don't see the intermediary commits of that unit of work, but I have not personally gotten value out of that level of granularity (especially on team projects where each person's commit practices are different).

If I start working on one PR that starts to contain a refactor or change imthat makes sense to isolate, I'll make that it's own pr that will be squashed.

338. stavros ◴[] No.45093066{8}[source]
I could respond to this uselessly pedantic comment, but then nobody wins.
replies(1): >>45093949 #
339. mamcx ◴[] No.45093098[source]
Other do a lot of nitpicking in workflows (with responses that are expected, like "but who cares" or "in git I could do the same"), but not answer this:

JJ is MORE powerful than git because has a BETTER SEMANTICS & ABSTRACTION.

That its. Is like when git emerge, it was more powerful than svn because was distributed.

I wanna make the point clear: Is NOT about the "amount of features or specific workflows" that with pain can be made on git and with effort could be retrofitted on jj eventually (if today are missed, like a equivalent of GitHub!).

The power is what abstraction made jj that is different to git: We work on "commits" all the time, and not need to manually sync the state. Everything derive from this.

And because is a better abstraction, it make more sense and is easier to understand.

The UX derive from this.

340. ◴[] No.45093301{6}[source]
341. sunshowers ◴[] No.45093325{4}[source]
Do you use version control professionally? Part of professional life is continuing education.
342. paradox460 ◴[] No.45093415{4}[source]
If you're doing a rebase or merge or any other operation, and a conflict comes up, in git you have to resolve it right now, or discard everything. You can't just leave the conflicts in place while you work on something else, they effectively freeze your entire repo. JJ is the opposite. You'll get conflicts, it will tell you about them, but it's up to you to choose when to work on them. You can even keep piling new changes on top of a conflicted one. Your program probably won't work, being full of conflict markers, but you can keep working still
replies(1): >>45094121 #
343. epolanski ◴[] No.45093434{6}[source]
> the branch name tag will be automatically moved to the new commit

On your specific repository.

344. Ericson2314 ◴[] No.45093843{3}[source]
It's a bit tongue in cheek, I'm saying

"I know this thing is bad, and it shouldn't exist, but I'm also personally used to it right now, and it does have some perverse silver linings"

345. SAI_Peregrinus ◴[] No.45093852{5}[source]
Not to mention Git has a reflog, so `undo` could just always revert the last operation in the reflog. Or the last `n` operations if specified. So there already is a universal list of what operations were performed, and thus what operations for `undo` to work on.
346. Ericson2314 ◴[] No.45093882{5}[source]
Mmm I see. Getting out of sync sounds very bad!

https://lore.kernel.org/git/CAESOdVAspxUJKGAA58i0tvks4ZOfoGf... I hope this happens, because then it seems like far less state would be needed on the jj side.

347. Ericson2314 ◴[] No.45093932{6}[source]
It doesn't really depend on NVMe, that's just the OS sucking.

The right way has always been FUSE, so that version control knows about every change as it happens. Push, not pull (or poll).

With FUSE passthrough, maybe this won't even be slow!

replies(1): >>45094158 #
348. SAI_Peregrinus ◴[] No.45093939[source]
`git` is a content-addressable filesystem (the "plumbing") with a VCS UI on top of it (the "porcelain"). `jj` is alternative "porcelain" using the same "plumbing". Git was initially designed to be a toolkit on which to create VCSes, over time the VCS portion of Git has become more complete but is still a very leaky abstraction, resulting in users having to use the "plumbing" commands more often than never. `jj` aims to be better "porcelain" over the Git "plumbing", and the project authors hope to eventually support other "plumbing" model(s).
349. mcepl ◴[] No.45093949{9}[source]
I didn’t mean to be pedantic. My experience with Jujutsu was as bad as with Mercurial (and hg-git, I believe?) … after couple of hours working with my repo (https://git.sr.ht/~mcepl/m2crypto), looking at it with git, I got incomprehensible mess of destroyed multiple unmergeable heads (especially for Mercurial), and completely destroyed other branches, where I was not planning to work (Jujutsu). The only resolution in both case was export my work to plain patches, rm -r the checkout, clone again with git and forget anything about those other VCSes.

So, I claim that Jujutsu actually doesn’t work well with git repositories (and forges) very well, and I would like to see a native one.

replies(1): >>45094139 #
350. SAI_Peregrinus ◴[] No.45094050{3}[source]
I'll use my case as an example (though I haven't tried this yet, maybe it'll be seamless):

I'm working in a monorepo on GitHub, where PRs get squash-merged & `main` is a protected branch, and a bot automatically tags every team for review if a commit in a PR branch has changed any of their files (anything they're in an OWNERS file for), then how do I work with `jj` so as not to tag the entire company if I develop a long-running feature branch alongside several other devs on my team? We need to periodically merge in changes from `main` as the feature is developed, but don't want to merge the feature until it's ready for release. With `git`, any use of `rebase` to get changes from `main` into the feature branch causes the bot to tag every team that merged anything to `main` between the creation of the feature branch & the use of the `rebase`. I'm not on the team that controls the bot, and I don't care to try to get them to change it.

I happily use `jj` for anything I can get away with it on, but `git rebase` + monorepo + PR-centric workflow = pain, and `jj` seems like it could cause the same sort of issues. I should just try with a dummy branch & see, but that takes time & I've been busy.

351. SAI_Peregrinus ◴[] No.45094087{6}[source]
LFS is pretty crap, but the problem it tries to solve is real: version control should be able to control all the sources of the application, not just the text-based sources. Even if it can't meaningfully "diff" between them.
352. stavros ◴[] No.45094121{5}[source]
Ahh nice, I didn't realize it'd carry the conflict markers along. That's great, I really like jj.
353. stavros ◴[] No.45094139{10}[source]
It works fine for my use case, all my Github repos are in the state I'd expect, and I sometimes use git in my local repos as well, and that works fine. Hell, sometimes I use jj to fix a mess I made in git.

The only issue is being in detached HEAD all the time, but in practice it's not a big problem for me.

354. sunshowers ◴[] No.45094158{7}[source]
> It doesn't really depend on NVMe, that's just the OS sucking.

I've spent so much of my professional career profiling source control access patterns. Hot cache tends to be OS VFS layer performance, but the moment you hit disk that dominates, unless the disk is NVMe (or, back in the day, PCIe flash storage). Further compounding this is the use of a naive LRU cache on some OSes, which means that once the cache size is exceeded, linear scans absolutely destroy performance.

> FUSE

So you might think that, but FUSE turns out to be very hard to do correctly and performantly. I was on the source control team at Facebook, and EdenFS took many years to become stable and performant enough. (It was solving a harder problem though, which was to fetch files lazily.)

I believe Microsoft tried using a FUSE equivalent for the Windows repo for a while, but gave up at some point.