ibejoeb 21 hours ago

A lot of the discussion focuses on differences from git and how it uses the git storage strategy under the hood. Honestly, I think you should just ignore all of that. Don't think about git. Here's a workflow that'll get you through your daily usage:

On a clean repo:

    $ jj
    The working copy has no changes.
    Working copy  (@) : abcdef a53ff9ba (empty) (no description set)
    Parent commit (@-): qrstuv 4bc1bf34 the last thing you did

Make some changes.

    $ jj # shows status (new, modified, deleted, moved, etc.)

If you're satisfied with your changes,

    $ jj commit -m "made changes"
    

Now `jj` is back to clean state. You don't need to do `jj new` to continue working linearly. Just make your next changes and commit. If you realize that you messed something up, fix it, then `jj squash` and it gets incorporated into that last commit.

You only `jj new -r lmnop` if you want to do some work based on the state of the codebase at revision lmnop, rather than the previous revision, which is a figurative branch. No need to manually create and name branches.

As for git history: just do `git log` to see what's actually going on. It's a clean history reflecting your `jj commit`s. You're not going to see all of the stuff the jj machinery does.

  • skydhash 20 hours ago

    If I really wanted that, I could create an alias like

      alias.save="!git add -A; git commit -m"
    

    And then use

      $ git save "made changes"
    • ibejoeb 20 hours ago

      You could. That's not the point. I suggesting that if you want to try jj, try it based on its affordances, not by comparing it to how you'd do it differently with git.

      Most of the tutorials start with git and try to teach you how to change your thinking. I think the project would find many more fans if it didn't make such a big deal about being kinda like git.

      • skydhash 19 hours ago

        I did try it, but it was not my cup of tee. Git fits my mental modal better. Branches are experiments, commits are notes in my lab notebook, stashes are notes in scrap of papers, the staging area is for things that I plan to note down and the working tree is the workbench. Then every once in a while I take the notebook and rewrite stuff in it or update it to fit recent changes in the canonical branches. I use magit so the actual operations are just a few quick key presses.

        I could probably do the same thing with jj, but why use a new tool when the old thing works well, has myriad of integrations, and it's fairly standard.

        • menaerus 13 hours ago

          The problem jj is trying to solve is not entirely clear to me but I guess there is enough people who aren't able to find their way with git so for them it probably makes switching to jj more appealing, or at least that's my first impression without going too deep into the documentation.

          • oniony 12 hours ago

            I wouldn't say it's that people are not able to find their way with Git. I was a competent Git user and would carefully and squash my commits. It's just easier and nicer with Jujutsu.

            The way all changes (except those in the ignore file) are automatically incorporated into the current commit means I don't have to faff about with stash when I need to pivot and then try to remember which commit to pop against. I can just hop around the tree with gay abandon. That alone is invaluable.

            Then add in the fact that a change to history gets rippled down the descendent commits. And the fact that conflicts are recorded in the history and can be dealt with at your leisure. Or the fact that `jj undo` is a thing.

            • whytevuhuni 10 hours ago

              > Then add in the fact that a change to history gets rippled down the descendent commits.

              This sounds interesting. Could you go into a bit more detail?

              I have 3 branches off of a single commit, update that commit, and all branches automatically rebase? Or?

              • willhbr 9 hours ago

                Yep they automatically rebase. If that creates conflicts it's marked on the child commit and you can swap over and resolve it any time.

              • oniony 5 hours ago

                Yes, exactly that. In Jujutsu you don't have Branches like you do in Git. You have branches in the sense that you have forks in the tree and you can place a "bookmark" against any revision in that tree. (When exporting to a Git repo those bookmarks are mapped to Git branch heads.)

                So yeah if I have revision `a` with two children `b` and `c`, and even if those children have their own children, a change to `a` will get rippled down to `b` and `c` and any further children. It's a bit like Git rerere if you've used it, except you're not forced to fix every conflict immediately.

                Any conflicts along the way are marked on those revisions, you just fix the earliest conflicts first and quite often that'll ripple down and fix everything up. Or maybe there'll be a second conflict later down the stack of commits and you'll just fix that one the same way.

                To fix a conflict you typically create a new revision off the conflict (effectively forking the tree at that point) using `jj new c` (let's call the result `cxy`) fix the revision in that commit and then you can `jj squash` that revision `cxy` back into `c`. This, again, gets rippled down fixing up all of the descendent commits.

            • ncphillips 9 hours ago

              There must be some kind of split in how people work or something. I’ve never had the desire to jump around the git tree. I never squash commits. I basically never stash changes. All the things that people say jj makes easier are things I never even want to do. Not because they’re not easy with git, but because it sounds hard to keep straight in my head.

              • oniony 5 hours ago

                Maybe. Different organisations work at different paces and with different contention rates. If you're on a small team and less being tugged about then you might not find value with this stuff.

                But I frequently have cases where I have some changes I'm making to repo `acme`. I'll put a PR up for review and then I'll start on a second PR for the same repo. I stack these commits on top of my previous PR. If I then notice a problem in the earlier work I can easily fix it and have the changes ripple down to the later PR. Or if somebody else merges something in against `main` it's really easy using `jj rebase` to move my commits against the new version of `main`. With a single `jj rebase` I can move all of my stacked PRs over to the new version of `main` and have every stacked PR updated.

          • steveklabnik 6 hours ago

            Some of jj's users are "I find git hard and like jj more" but a lot of us are/were git experts before switching.

            • menaerus 4 hours ago

              Yes, I understand that but what I'm saying is that the problem definition isn't completely clear to me. I'm not saying that there is none, it's just that it may not be obvious at the first read.

  • 8n4vidtmkvmk 19 hours ago

    Sometimes i like to jj describe before writing any code. It helps with mental clarity what i intend on writing or helps if i have to run away mid coding session (description will show in log so i can find it again)

    In this scenario I'd instead jj new at the end, after im finished and ready to move on to the next thing.

    • MrGreenTea 14 hours ago

      You can then also just use `jj commit`. It will see the existing commit message and just move to a new clean revision.

  • busfahrer 9 hours ago

    > Don't think about git.

    You can also make the reverse point, only think about git:

    In theory it's perfectly possible if you have to jj binary lying around somewhere, to not even use it as your git client, but to temporarily do a `jj git init --colocate`, do a complex restructuring of your repo across 3 branches, and then delete the .jj folder again :-)

    Just to highlight the other end of the spectrum, jj is flexible like that

    • steveklabnik 6 hours ago

      Colocation is the default these days, so you don't even need that flag :)

tiborsaas 1 day ago

Does JJ really prefer for me to think backwards? It wants me to start with the new and describe command, but with git I first make the changes and name the changeset at the end of the workflow.

I also often end up with in a dirty repo state with multiple changes belonging to separate features or abstractions. I usually just pick the changes I want to group into a commit and clean up the state.

Since it's git compatible, it feels like it must work to add files and keep files uncommitted, but just by reading this tutorial I'm unsure.

  • Jenk 1 day ago

    It doesn't need you to think that way at all.

    `jj new` simply means "create a new commit [ontop of <location>]" - you don't have to describe it immediately. I never do.

    I know that the intention was to do that, and I tried forcing the habit, but I too found it counter-productive to invariably end up re-writing the description.

    • surajrmal 1 day ago

      I don't usually do that right away, but I often use squash or absorb to move additional changes into a commit I already made in my stack. I think the spirit still applies if you take that course.

  • smweber 1 day ago

    My preferred workflow is to start with a new change, pick the changes I want, then use jj commit to describe the change and create a new empty one on top. Feels very similar to my old git workflow.

    If I end up with multiple features or abstractions in one change (equivalent to the “dirty repo”), jj split works very well as an alternative to the git add/git commit/repeat workflow tidying up one’s working copy.

    • pythonaut_16 1 day ago

      I also like `jj commit [paths]` to commit just a subset of files when I don't need hunk based splitting.

      Like `jj commit -m 'Feature A' file1 file2` then `jj commit -m 'Feature B' file3 file 4`

      • surajrmal 1 day ago

        I use jj commit -i a lot when writing the paths is too tedious. What's nice is you can pass -i into most commands (squash, split, absorb, etc).

  • joshka 1 day ago

    > Does JJ really prefer for me to think backwards? It wants me to start with the new and describe command, but with git I first make the changes and name the changeset at the end of the workflow.

    A good way to think of it is that jj new is an empty git staging area. There's still a `jj commit` command that allows you to desc then jj new.

    > I also often end up with in a dirty repo state with multiple changes belonging to separate features or abstractions. I usually just pick the changes I want to group into a commit and clean up the state.

    jj split allows you do to this pretty well.

    > Since it's git compatible, it feels like it must work to add files and keep files uncommitted, but just by reading this tutorial I'm unsure.

    In jj you always have a commit - it's just sometimes empty, sometimes full, has a stable changeid regardless. jj treats the commit as a calculated value based on the contents of your folder etc, rather than the unit of change.

    • saghm 1 day ago

      I often will use `jj new -B@` (which I made an alias for) followed by `jj squash -i` to split changes. I had no idea about `jj split`, so I need look into that!

    • greenicon 1 day ago

      I'm using jj exactly this way, but `jj commit -i` is still somewhat backwards compared to `git commit -i`: jj displays the commit timestamp by default instead of the author timestamp like git. In addition, in jj the author timestamp of a commit is set to the time you started and not ended a commit/change. This results in unexpected timestamps when working with git-using people or tools. Also, it's rather weird if you use a previously empty commit for your work which was created months earlier by a previous `jj commit`, resulting in a timestamp neither correlating to when you started nor ended your work.

      I guess the idea of jj's authors is that jj's commits are far more squishy and can always be changed, so a fixed finished timestamp makes less sense. I still prefer git's behaviour, marking work as finished and then keep the author (but not commit) timestamps on amends.

      I use this jj alias to get git's timestamp behaviour:

        [aliases]
        c = ["util", "exec", "--", "bash", "-c", """
        set -euo pipefail
        change_id=$(jj log -r @ --no-graph -T 'change_id')
        desc=$(jj log -r $change_id --no-graph -T 'description')
        commit_author=$(jj log -r $change_id --no-graph -T 'author.email()')
        configured_author=$(jj config get user.email)
        
        jj commit -i "$@"
        
        if [ -z "$desc" ] && [ z"$commit_author" = z"$configured_author" ]; then
            echo "Adjusting author date"
            jj metaedit --update-author-timestamp --quiet $change_id
        fi
        """]
        
        [templates]
        # display author timestamp instead of commit timestamp in log
        'commit_timestamp(commit)' = 'commit.author().timestamp()'
    • frio 22 hours ago

      > A good way to think of it is that jj new is an empty git staging area. There's still a `jj commit` command that allows you to desc then jj new.

      This always made me feel uncomfy using `jj`. Something that I didn't realise for a while is that `jj` automatically cleans up/garbage collects empty commits. I don't write as much code as I used to, but I still have to interact with, debug and test our product a _lot_ in order to support other engineers, so my workflow was effectively:

          git checkout master
          git fetch
          git rebase # can be just git pull but I've always preferred doing this independently
          _work_/investigate
          git checkout HEAD ./the-project # cleanup the things I changed while investigating

      ```

      Running `jj new master@origin` felt odd because I was creating a commit, but... when I realised that those commits don't last, things felt better. When I then realised that if I made a change or two while investigating, that these were basically stashed for free, it actually improved my workflow. I don't often have to go back to them, but knowing that they're there has been nice!

      • stavros 21 hours ago

        I think calling them "commits" is doing it a disservice because it's not the same as git commits, and the differences confuse people coming from git. I'd say "jj changes are like git commits, except they're mutable, so you can freely move edits between them. They only become immutable when you push/share them with people"..

        It's a mouthful, but it's more accurate and may be less confusing.

  • miyoji 1 day ago

    > Does JJ really prefer for me to think backwards? It wants me to start with the new and describe command, but with git I first make the changes and name the changeset at the end of the workflow.

    Yes, but this is not backwards, the way you do it in git is backwards. =)

    • SiempreViernes 1 day ago

      git promises "version control", this clearly implies that the versions predate the control: in this picture the git workflow is not backwards.

      • miyoji 1 day ago

        I don't think the term "version control" has any implication about precedence, and I don't understand what you mean by "the versions predate the control". In git, you add items to the worktree (control), then you commit (create a version), so doesn't that mean git does it "wrong" according to what you're saying? In jj, you are always on a committed version and the contents of that commit are controlled by your edits, if you want your edits to be on a different commit, you usually just change to that commit and make the edits, although there are other ways to move edits around (which is also true in git).

        The point is that there actually isn't a correct order to do these operations, just one that you're familiar with. Other orders of operations are valid, and may be superior for your or your team's workflow.

        • SiempreViernes 12 hours ago

          The versions of importance are the changes made outside of git, not the internal bookkeeping of the version control software itself.

  • saghm 1 day ago

    Nothing stops you from making changes in a commit that has no description and then at the end doing `jj commit -m` to describe them and make a new commit in one go, which is essentially the same as git. The difference is that it's essentially amending in place as you make changes rather than needing to stage first.

  • fmckdkxkc 1 day ago

    Personally haven’t used jj but as far as dvcs’s are concerned Fossil is great complement to Git because it does things differently than git and truly has a decentralized feel.

    The autosync feature is really nice too, and you can store backup repos in cloud storage folders and auto sync to those as well.

    • 59nadir 22 hours ago

      Fossil is delightful and definitely nails a feeling of decentralization that I think we ruined completely with `git` by constantly centering around centralized repositories.

      I also find it interesting that so many people want to switch to something that's not `git` but are simultaneously somehow super invested in it being basically just `git`.

      Most teams could switch to Fossil and just have a better time overall. It's made for smaller, high-trust teams, `git` is not. Fossil also manages to actually support external contributions just fine; it's just that it's not the default.

  • jezzamon 1 day ago

    That totally works and it's how I use jj. jj commit -i does what you would want

  • EliasWatson 1 day ago

    jj is very flexible when it comes to workflow. One thing to note is that commits don't have to have messages. What I tend to do is to run `jj new` frequently while I work on something and leave all of them without messages. Then when I'm ready to make my actual commit, I squash the temporary commits together and then add a message. If my changes are separable, I can split the commits before squashing. This workflow acts as a kind of undo history. I can easily go back to what I had 5 minutes ago and try a different approach, but then also jump back to my original changes if I want. It makes experimentation much easier compared to git.

  • minraws 1 day ago

    think of jj like,

    I want to build xyz,

    ```

    jj desc -m "feat: x y & z"

    ```

    do the work.

    ```

    jj split

    ```

    Split up the parts and files that you want to be separate and name them.

    This will also allow you to rename stuff.

    ```

    jj bookmark create worklabel-1 -r rev1

    jj bookmark create worklabel-2 -r rev2

    # Push both commits

    # since we just split them they are likely not inter-dependent

    # so you can rebase them both to base

    # assuming rev1 is already on top of base

    jj rebase -s rev2 -d base

    jj git push

    ```

    That is it.

    • motbus3 1 day ago

      I am dumb. why is that better than a git branch or a git worktree ?

      • cornstalks 1 day ago

        If you're already super comfortable in git, it's not. I'm saying this as someone who recently converted from git to jj and never wants to go back.

        You also don't have to follow what the GP said. I never say `jj describe` before writing code. I write the code then just say `jj commit -m "Foo stuff"`, just like I would in git.

        The bigger difference I've noticed is:

        1. Switching between changesets just feels more natural than git ever did. If I just run `jj` it shows me my tree of commits (think of it like showing you git's branches + their commits), and if I want to edit the code in one of them I just say `jj edit xyz`, or if I want to create a new commit on top of another one and branch it off in a new direction, I just say `jj new xyz`. It took a little bit for my brain to "get" jj and how it works because I was so used to git's branches, but I'm really enjoying the mental model.

        2. `jj undo`. This alone is enough to convert me. I screwed something up when trying to sync something and had a bunch of conflicts I really didn't want to resolve and I knew could have been avoided if I did things differently, but my screwup was several operations ago! So I ran `jj undo`. And ran it again. And again. And again. And then I was back to my clean state several stages ago before I screwed up, despite having made several changes and operations since then. With git? Yeah I could have gotten it fixed and gone back. But every time I've had to do something like that in git, I'm only 25% confident I'm doing it right and I'm not screwing things up further.

        3. Rebasing. When I would try to sync git to an upstream GitHub repo that used rebasing for PRs, I would always get merge conflicts. This was because I stack my changes on top of each other, but only merge in one at a time. Resyncing means my PR got a new commit hash, even though none of the code changed, and now git couldn't figure out how to merge this new unknown commit with my tree, even though it was the same commit I had locally, just a different hash. With jj? I never get merge conflicts anymore from that.

        Overall the developer experience is just more enjoyable for me. I can't say jj's flow is fundamentally and objectively better than git's flow with branches, but personally and subjectively, I like it better.

      • bastardoperator 1 day ago

        It's not, you can literally do everything this tool does with Git, and 80% of the features could be replaced with commands in your shell rc file also using vanilla git.

        This tool was described perfectly the other day. JJ is the Dvorak of Git. Most people could careless about Dvorak layout, 99.8% of people use qwerty just fine. Those 0.02% though, they're loud, and they want everyone to know how great the reinvention of bread is.

      • Balinares 21 hours ago

        In sort of the same way juggling apples is better than juggling hand grenades: it's mostly the same in the simple cases, but once you start doing the really fancy stuff, one of the two will get you a lot fewer messy explosions.

        (Your question is not dumb, BTW. The pithy answer is: UX matters, but it does so in ways that can be hard to convey since it's about the amount of cognition you need to put in a given thing to get a desired outcome, and that's tricky to express in text. Also there will always be some people for whom a new mental model just doesn't work. That doesn't make them dumb either, at least provided they have the wisdom not to petulantly piss in the cornflakes of those who get a kick out of how much better the new thing works for them.)

        • skydhash 20 hours ago

          You have to put a lot of effort to mess up a git repo. So I'm not seeing the allusion to hand grenades.

          • cornstalks 18 hours ago

            I’ve done it multiple times without much effort. Or skill. Really it was a skill issue and I tried things that I thought would work but apparently don’t.

            I screwed up jj a few times while picking it up, but jj’s undo command made that trivial to go back and try again. With git? I know a lot of things can be undone but I can never remember how to do it!

          • Balinares 8 hours ago

            I'm glad to hear you never encountered the kind of quagmire that can occur around e.g. non-trivial conflicts while rebasing a chain of git commits. On large enough codebases, those can be common.

  • baq 1 day ago

    it's actually git that makes you think backwards - in jj the working tree is a commit, in git it isn't until you at least stage it.

    the working tree being a commit has wide ranging implications as all the commands that work with commits start working with the working tree by default.

  • shermantanktop 1 day ago

    This is me! I often find that in the process of making one change, I have also made several other changes, and only recognize that they are distinct after following the ideas to their natural conclusion.

    Hence I have multiple workspaces, and I shelve changes a lot (IntelliJ. I end up with dirty repos too and that can be painful to cherry-pick from. Sometimes I just create a git patch so I can squirrel the diffs into a tmp file while I cleanup the commit candidate. I often let changes sit for several days while I work on something else so that I can come back and decide if it’s actually right.

    It’s chaotic and I hide all this from coworkers in a bid to seem just a bit more professional.

    I admire people who are very deliberate and plan ahead. But I need to get the code under my fingers before I have conviction about it.

    • chriswarbo 1 day ago

      > I often find that in the process of making one change, I have also made several other changes, and only recognize that they are distinct after following the ideas to their natural conclusion.

      I do that all the time. With git, everything starts "unstaged", so I'd use magit to selectively stage some parts and turn those into a sequence of commits, one on top of another.

      With jj I'd do it "backwards": everything starts off committed (with no commit message), so I'd open the diff (`D` in majutsu), selecting some parts and "split" (`S` in majutsu) to put those into a new commit underneath the remaining changes. Once the different changes are split into separate commits, I'd give each a relevant commit message.

    • sfink 1 day ago

      I'm about the same. jj is kind of perfect for that. Example:

      # I've finished something significant! Carve it out from the working "change" as its own commit.

          `jj commit --interactive` # aka `jj commit -i` or `jj split`, depending on how you prefer to think of it: making a commit for some work, or splitting a separate commit out of the working change.
      

      # Oops, missed a piece.

          `jj squash --interactive` # aka `jj squash -i`
      

      # Let me look at what's left.

          `jj diff`
      

      # Oh right, I had started working on something else. I could just leave it in the working change, but let me separate it out into its own commit even though it's unfinished, since I can always add pieces to it later.

          `jj commit -i`
      

      # Wait, no, I kind of want it to come before that thing I finished up. Shoot, I messed up.

          `jj undo`
      

      # Let me try that again, this time putting it underneath.

          `jj split -B @-` # aka `jj split --insert-before @-`. @ is the working change, @- is its immediate parent(s), @-- is all grandparents, etc.
      

      # Note that instead of undoing and re-selecting the parts, you could also `jj rebase -r @- -B @--` to reorder. And in practice, you'll often be doing `jj log` to see what things are and using their change ids instead of things like `@--`.

      # I also have some logging code I don't need anymore. Let me discard it.

          `jj diffedit`
      

      # Do some more work. I have some additions to that part I thought was done.

          `jj squash -i`
      

      # And some additions to that other part.

          `jj squash -i --into @--`
      

      # etc.

      There's a lot more that you could do, but once you internalize the ideas that (1) everything is a commit, and (2) commits (and changes) can have multiple parents thus form a DAG, then almost everything else you want to do becomes an obvious application of a small handful of core commands.

      Note: to figure out how to use the built-in diff viewer, you'll need to hover over the menu with the mouse, but you really just need f for fold/unfold and j/k for movement, then space for toggle.

      • em-bee 17 hours ago

        # I've finished something significant! Carve it out from the working "change" as its own commit.

            git add -p
            git commit
        

        # Oops, missed a piece.

            git add -p
            git commit --amend
        

        # Let me look at what's left.

            git diff
        

        # Oh right, I had started working on something else. I could just leave it in the working change, but let me separate it out into its own commit even though it's unfinished, since I can always add pieces to it later.

            git add -p
            git commit
        

        # Wait, no, I kind of want it to come before that thing I finished up. i didn't mess up, this is standard procedure

            git rebase -i <some older commit ref> # put commits into the desired order.
        

        # I also have some logging code I don't need anymore. Let me discard it.

        don't know what jj does here

            edit files
        

        or

            git revert -p
        

        # Do some more work. I have some additions to that part I thought was done.

            git add -p
            git commit
            git rebase -i <some older commit ref> # choose the right place and then tell git to squash into the parent
        
        

        # And some additions to that other part.

            git add -p
            git commit
            git rebase -i <some older commit ref> # as above
        

        you list a number of different commands that i do in git always with the same sequence of commands. i don't see how jj makes those examples any easier. it looks like maybe they help you get away with not understanding how git works, by instead giving you more commands that do specifically what you want.

        • sfink 5 hours ago

          Yes, and? I wasn't trying to demonstrate jj superiority. I was responding to a post about a normal messy workflow and showing how to handle it in jj.

          If I'm writing a description of how to use jj, I could take several different approaches. Am I writing for a git novice? A git expert? An expert in a different VCS? A novice in any VCS? And even within those, there's a big difference in whether you're a solo dev working alone on their own project, a solo dev working across multiple systems, a random github contributor working alone against a github repo, a group of contributors who work together before landing something in an upstream repo, or whatever. And then, it matters whether my objective is to show that jj is somehow superior, or to just show to accomplish something.

          Those are going to require rather different approaches. I was not going for "jj is better than git". I was aiming more for "here's how straightforward it is to do the sort of stuff you're talking about". Even with the example actions I described, jj does have a couple of advantages that I didn't highlight: first, your git equivalents would require looking up commit hashes whereas in jj I tend to remember the recent change ids that I've been working with. Second, `jj undo` (and its stronger variant, `jj op restore`) is easier and simpler to work with than the reflog. A longer example would have demonstrated that, but I didn't want a longer example.

          But I have no dispute with your assertion that this workflow is not harder in git. I could write a description of why I think jj is better than git, it's just that my post was not that. (I could also write a post about how jj is still missing some important functionality that git has, and therefore git is better than jj.)

          But just to sketch out what I would use if I wanted to make git look bad, I'd probably use an example of multiple independent lines of development where I want to work off of a tree with all of them applied at the same time, without artificially linearizing them because I don't know what order reviews are going to come in, and then doing fixups and incorporating new changes that turn out to conflict, and not getting stuck working through conflicts in the other patch series when I'm actively working out the finishing touches on one of them that turns out to be high priority. And then getting some of that wrong and wanting to back up and try a different path. All while carrying along some temporary logging or debugging changes, and perhaps some configuration changes that I don't ever want pushed. And keeping my patch series clean by separating out refactoring from changes, even when I actually do bits of that refactoring or those changes out of order. And doing all this without risking modifying anything other people might be using or building off of, by preventing force pushes on stuff that matters without preventing it for in-development stuff that is only relevant to me. And in the middle of this, wanting to look back on what the state of things was last Wednesday, including the whole commit graph.

          All of that is possible with both git and jujutsu. In practice, I wouldn't even try much of it with git. Perhaps I just suck at git? Very possible. I'm better with mercurial, but I wouldn't do a lot of that there either. I won't say all of that is trivial with jj, but some of it is easy, all of it is doable without thinking too hard, it's the sort of stuff that arises for me quite often as I'm working, and none of it requires more than the same handful of commands and concepts. I know what changes are tentative and what are more fixed without juggling commits vs staging vs stash, and I can freely move bits and pieces between them using the same set of commands. I could do the exact same things in git, but I wouldn't. The core git data model is very nice, so it's pretty clear what can and can't be done. jj gives me the ability to manipulate it without tangling my head or my repo in knots.

          • em-bee 3 hours ago

            i apologize if my comment came across as accusing you of claiming superiority and failing your intention. it was not meant to do that. if there is any accusation then it is the general assumption that jj is better that can be felt in the overall tone of this discussion thread.

            your examples show a particular aspect of jj, and to me they demonstrate that jj isn't better across the board for a certain group of users at least.

            i'd be interested to know if i got the impression right that jj provides more commands for specific actions that don't require you to understand how the system works underneath. that is significant because i like to understand how things work underneath. more commands then means that i have to learn more to understand each one of them.

            in a sense it is like high level languages vs low level languages. don't get me wrong, i like high level languages. i prefer them actually, but i also like minimalistic syntax, so i prefer smalltalk and lisp over, say scala which has a reputation for being complex. but scala is starting to make more sense once i learn what lower level primitives the syntax translates to.

            same goes for jj. i want to understand the primitives that the commands translate to. git puts me closer to those primitives, which makes it harder to learn but possibly easier to use or understand once you get it. since jj is built on top of the same primitives it should be possible to reach the same understanding. jj uses those primitives differently, and that's the part that i find interesting. the more i understand how jj works with git primitives the more i like it. but it also shows that git primitives are not bad if they enable such different approaches. (i wonder how different git primitives are from other approaches such as pijul which is based on a theory of patches).

            i guess what would help me would be a guide to jj for those who are familiar with how git works underneath.

            the examples in your second to last paragraph sound very interesting. please show. of link to references if you know any.

            • sfink 5 minutes ago

              Heh, sorry, I guess you triggered my defensive "book-length response" reaction. Here's the next book:

              As you say, jj uses git primives so the core data model is the same. I say "core" because jj subtracts out the staging area and stash and adds in "changes" and the operations log. But otherwise, your understanding of the git data model translates seamlessly, with one exception: git-style branches aren't really a thing, they can only be emulated. What I mean is that in git, the topological head that is associated with a branch ref (apologies if I'm getting terminology wrong), when accessed through a branch name, is taken to represent not just that commit but all ancestors of it as well. So "merging a branch" or "checking out a branch" are natural things to do.

              jj prefers to be grounded in the commit DAG. Since you're always "updating" a commit (really replacing it with new ones), the only other state is the location of that commit in the DAG. You specify that before that change exists, with the default being the same as in git: the single descendant of the last thing you were working on (the branch tip in git, the @ change in jj). `jj new` (or `jj commit`) creates a single child change of your current change, unless you pass flags putting it elsewhere in the graph -- and it is very common to put it elsewhere if that's the thing you want to do.

              An example: say you have a sequence of changes (and thus commits) where you're working on a feature: A->B->C, with C being what you're editing now (the @ change). You realize that your latest change, C, might not be the best approach and decide to try another. You do `jj new B` to create an alternate descendant and implement a C2 approach. Now you have A->B->{C,C2}. Which of C or C2 is the tip of your "feature branch"? Answer: who cares? At least until you want to push it somewhere. My default log command will show the whole DAG rooted at A, the first change off of "trunk" commits. I might do further work by creating descendants of C2 and thus have A->B->C and B->C2->D->E, and I might rebase or duplicate ->D->E on top of C if I want to try it out with that approach. Or I might inject or modify things earlier in the graph like A->X->B'->Y->{C,C2} (where B' is an updated B). If I push part of that DAG up to a server that I have declared to be shared with other people, then jj will know what parts of the graph should no longer be mutated and prevent me from doing that (by default).

              All of this follows the core git model, it's just that mutating the graph structure is expected and common with jj and only restricted modifications seem to be common with git. You can do any and all of it with git, but rebase, undo, and conflicts are better-supported and feel less exceptional with jj. And you can jump to anywhere in the graph, or move around pieces of the graph, anytime and without necessarily changing the @ (working change) you're looking at.

              This also means that jj doesn't need a `git switch` or `git checkout` command. That notion is replaced by making your current change (there's always a current change, it's the one that will be associated with any edits you make) be the child of wherever in the DAG you want, probably with `jj new <parent>`. As with git, there's a snapshot associated with it, so the other purpose of `git checkout` (to make your on-disk copy of a file match the contents of a given commit) is `jj restore filename`. In `git help checkout`, it says "Switch branches or restore working tree files". The "switch branches" part is `jj new`, the "restore working tree files" part is `jj restore`. `jj new` is equivalent to `git checkout --detach` or `git checkout <branch> <start-commit>` (or plain `git checkout <treeish>`). If you only consider the "make (part or all of) the working tree match that of this commit", then all of the different variants `git checkout` being in one command makes sense. But jj distinguishes things that operate on the DAG vs trees (file contents). And it doesn't need different flags or modes for the index/stage vs a commit; everything is a commit. `git stash` is unnecessary, the equivalent is `jj new <ancestor>` (you're just moving your working directory to be based on a different commit; the previous patches aren't lost or anything, they stay where they were in the DAG. Grab them from there if you want them and put them anywhere else in the graph, with or without changing your working directory as you wish.) `git reset` is redundant with `jj restore` -- well, mostly; the other part of what it does is handled by `jj abandon`. (Again, `restore` for tree contents, `abandon` for the graph node.) `git merge` is `jj new` with multiple parents instead of one so a command isn't needed.

              One way to say it: with jj, `new` + `describe` + `rebase` + `abandon`, you can do all the graph manipulation. Add in `squash` + `restore` and you can do all content manipulation too. That's it for local operation. Well, maybe `jj file track/untrack`. The flags are pretty consistent across commands and don't fundamentally change what they do. Everything else is either for working with servers and other repositories, convenience utilities for specific situations, details like configuration, or jj-specific functionality like the operation log.

              The other major difference is that jj adds in the notion of persistent "changes" (I wish we'd come up with a better name) that in normal operation correspond to a single "visible" commit, but which commit that is changes over time. A change has a single semantic purpose and doesn't lose its identity when rebased or merged or the contents of its files are changed. This difference is actually starting to diminish, because git (or rather, the git forges and tooling) has started preserving the footers that identify these change IDs over various operations. So if you're using git in connection with gitbutler or gerrit or whatever, you'll be getting the benefit of these persistent identifiers even if you continue using the git cli.

              Sorry, that's a lot, but it'd be my attempt at "jj fundamentals for someone who understands git primitives well".

  • chriswarbo 1 day ago

    > It wants me to start with the new and describe command

    jj doesn't "want" anything.

    I always end a piece of work with `new`: it puts an empty, description-less commit as the checked-out HEAD, and is my way of saying "I'm finished with those changes (for now); any subsequent changes to this directory should go in this (currently empty) commit"

    The last thing I do to a commit, once all of its contents have settled into something reasonable, is describe it.

    In fact, I mostly use `commit` (pressing `C` in majutsu), which combines those two things: it gives the current commit a description, and creates a new empty commit on top.

  • gcr 1 day ago

    Think of it this way: the current change is like a staging area/index in git. Leave it without a a description while you're working (just like git's staging area). Rely on jj's auto-snapshotting to capture all your changes. Then, when you're ready to do something else, give it a description ("jj describe") and switch to a new blank change ("jj new"), and that becomes your new "staging area"/index.

    The workflows are conceptually identical.

  • benoitg 1 day ago

    Not necessarily, I often make changes on unrelated commits. You can always use jj split to extract the change and put it somewhere else.

  • nchmy 22 hours ago

    JJ doesnt prefer you to do anything. I regularly just create description-free commits, do whatever, then name them later (or squash, split, absorb the changes into other commits). It is exceptionally flexible and forgiving. Even moreso if you use jjui, the best TUI ive ever used.

  • stouset 21 hours ago

    > Does JJ really prefer for me to think backwards?

    No, you run `jj new` when you’re done with your work just like you’d run `git commit`. You can even just run `jj commit` which is a shorthand for `jj describe && jj new`.

  • arialdomartini 21 hours ago

    Yes, that’s idiomatic in JJ. Honestly, I’ve been recommending the same workflow (first commit, then make changes) with Git too since years

    https://arialdomartini.github.io/pre-emptive-commit-comments

    If you want to have a workflow similar to Git with index, check out the Squash Workflow: basically, you would edit your files in a disposable commit having the same purpose of Git’s index.

  • tiltowait 20 hours ago

    I tend to approach jj commits as local PRs. Decide what it is I'm working on, make a new, empty commit on top of that :

        jj new -m 'do thing'
        jj new
    

    As I work on the "local PR", I `jj squash` my working changes into the named commit. By keeping my working commit description-free, I avoid accidentally pushing it (and potentially broken code) to origin.

  • aryehof 7 hours ago

    > Does JJ really prefer for me to think backwards?

    Perhaps it’s the other way around. I have a standard agentic instruction to summarize prompt instructions using jj describe. After it makes changes I can see both the file changes made and a log description of what prompted those changes. When done it’s just jj new and its ready for the next set of prompts.

tom_alexander 1 day ago

I'm giving jj a try but one aspect of it I dislike is edits to files are automatically committed, so you need to defensively create empty new commits for your changes. As in, want to browse the repo from a commit 2 weeks ago? Well if you just checkout that commit and then edit a file, you've automatically changed that commit in your repo and rebased everything after it on top of your new changes. So instead you create a new branch off of the old commit and add an empty commit to that branch so any file changes don't end up rewriting the past 2 weeks of history. git is much nicer in that I can do whatever I want to the files and it won't change the repo until _I tell it to_.

  • smackmybishop 1 day ago

    Just don't ever use `edit`, use `new` instead; then your changes are tracked without making a mess. I think that's much nicer than juggling stashes in git.

    • VMG 1 day ago

      ... unless you actually want to edit a change!

      • throawayonthe 1 day ago

        well, you can do jj new <revision>, make your edit, and then do jj squash which will add the changes to the prev revision

        i do this for example when i want to see a specific edit highlighted in my editor, it's a nice workflow i think

        • Aeolun 1 day ago

          This is exactly how someone explained Git to me 12 years ago or so, and I’ve finally wrapped my head around it. Not changing now.

          • mh- 1 day ago

            If I'm understanding the thread correctly, I have a git alias to `git commit --amend --no-edit`, for exactly this workflow. When I'm hacking on something locally and want to just keep amending a commit. I only ever do this if it's HEAD though.

            • steveklabnik 1 day ago

              Yes, one way to think about jj in a sort of low-level way is that every jj command does the equivalent of that, every time.

              (You can also set up watchman and have that happen on every file change...)

      • arccy 1 day ago

        still use new, and then squash your changes in. that way you can actually see what changes you made

      • BeetleB 1 day ago

        I go back and forth between the two approaches, but because of the whole "accidentally made some temporary changes and now it's a pain to separate/undo them because not all changes were temporary", I also usually do a jj new and then jj squash.

    • embedding-shape 1 day ago

      > Just don't ever use `edit`, use `new` instead

      As a git-ist (?), if I'd ever move away from git, it would be to avoid tooling that has idioms like this (like git too has), if `jj` just gonna surface a bunch of new "bad ideas" (together with what seems like really good ideas), kind of makes it feel like it isn't worth picking up unless you don't already know git.

      • surajrmal 1 day ago

        jj edit has good use cases, but it's not the default command you need. For instance, say you were working on some changes but had to change branches for a few minutes to do something. If you didn't manage to create a commit and want to go back to the previous staging area, you would use the jj edit command rather than jj new. It's very intuitive in my experience, something I can't say is true for managing git commits (unless you've spent years forcing it into muscle memory). I never need to run jj help. I run help commands with git all the time.

      • dzaima 1 day ago

        `edit` is still useful; just, for ..editing (!) something, instead of viewing it.

        If you have some unfinished changes at the tip and want to temporarily checkout something 2 weeks ago, you `jj new` to there (similar to `git stash; git switch whatever`), and then later `jj edit your-old-tip` to go back (equivalent to `git switch main; git stash pop`; I think `jj edit` being an extended replacement for stash-popping things is a reasonable way to think about it). (and if you don't have any uncommitted changes, you always `jj new`)

        jj also has a concept of immutable commits (defaulting to include tagged commits, and trunk at origin, which it'll disallow editing as a layer of defense)

      • saghm 1 day ago

        The idiom here is use `edit` if you want to edit a commit, and use `new` if you want to make a new commit. This works identically whether you specify the commit via branch name or commit id. I'm not sure why people are saying not to use `edit` ever. It's basically just a shorthand for staging and amending changes in an existing commit, and there's still a use case for that; it's just not "I want to see the changes on this old branch".

        • embedding-shape 1 day ago

          > Just don't ever use `edit`,

          > The idiom here is use `edit` if you want to edit a commit

          You know, you guys have fun with that, I'll continue using git which (probably) has the same amount of warts, but I already know them. I'll continue to refer new VCS users to jj, seems a lot easier to learn, but really don't have the interest to re-learn a bunch of ever-changing idioms.

          • saghm 1 day ago

            I disagree with the people saying "never use edit". There are plenty of people saying conflicting things about git too, and I'd argue that understanding edit versus new isn't anywhere close to the level of wart that having to get people to agree on merging versus rebasing. Like you said though, have fun with that!

          • sswatson 1 day ago

            No system is perfect, but there's nothing wrong with `jj edit` and `jj new`. Both commands are completely reasonable and do what you think they would do.

          • medler 20 hours ago

            jj has far fewer warts than git. You don’t have to learn every jj idiom, you just have to find a workflow you like, which you will, quickly, because it’s so easy to use. Personally I don’t know why anyone uses `edit` but if they like it then I’m happy for them.

            • ncphillips 9 hours ago

              But I have a workflow I like with git and I can’t see how jj would be better. I’m genuinely curious as to whether it would be or not, but the behaviours people are describing are not things that interest me.

        • joshuamorton 1 day ago

          I think it's because it's easy to make annoying mistakes (still easy to fix with undo) with edit. And it gains relatively little over new+squash. Edit is a useful power-feature, but I think for a novice, "never use it, only use the more well understood workflow of new+squash" is a good heuristic.

      • rtpg 21 hours ago

        "jj new" is like "I'm going to make some changes", then you do "jj squash" to say "OK these look good enough to commit".

        If you work this way you're "always" in a WIP state. And if you switch to another spot you won't lose your work, cuz it's persisted.

        The end result if you work like this is you don't need stashing, since you get "free" stashing in your commit tree, which is more likely what people want (and if it's not... rebasing is easy so just move the node/`jj duplicate` it!)

        `jj edit` exists but I think it's just not what people want in the default case at all. In exchange: rebasing "just works", stashing is not a thing to think about, and you don't lose your work

    • busfahrer 9 hours ago

      Once you get your head around it a bit, doing a new in this circumstance will be second nature, since you will have realized that a `new XYZ` in jj leads to the same underlying git state as a `git checkout XYZ` in git

  • jdkoeck 1 day ago

    Wow, that’s a total deal breaker to me. Using git may require a complex mental model, but at least it’s not doing anything I didn’t ask for.

    • Diggsey 1 day ago

      You would have had to run `jj edit` in order for this to happen, so I think it's a stretch to say you didn't ask for the edit?

      This is the main difference though: in git files can be `staged`, `unstaged` or `committed`, so at any one time there are 3 entire snapshots of the repo "active".

      In `jj` there is only one kind of snapshot (a change) and only one is "active" (the current working directory). When you make changes to the working directory you are modifying that "change".

      As others have mentioned, the equivalent to `git checkout` would be `jj new`, which ensures a new empty change exists above the one you are checking out, so that any changes you make go into that new change rather than affecting the existing one.

      • jdkoeck 23 hours ago

        Thanks for the explanation! I wish I could edit my comment to reflect the truth.

      • adastra22 18 hours ago

        I really, REALLY wish jj picked a different subcommand name for that than 'jj new'.

    • saghm 1 day ago

      Using `jj edit` will edit a commit you specify, and `jj new` will make a new empty commit after the one you specify. These work exactly the same whether you specify a commit by branch or by the hash. I'd argue that you're getting exactly what you ask for with these commands, and by comparison, what "checkout" is asking for is much less obvious (and depends on context). We've just internalized the bad behavior of git for so long that it's become normalized.

    • stouset 1 day ago

      `jj edit` is quite literally asking for that.

      GP is holding it wrong. If you don’t want to edit a commit, don’t ask to edit it. Use `jj new`.

  • csmantle 1 day ago

    `jj new` works like `git checkout` most by creating an empty revision on the top. `jj edit` on the other hand resembles `git checkout; [edits...]; git add -A; git commit --amend --no-edit`.

  • arianvanp 1 day ago

    You can disable the auto staging of new files since recently which removed the main grype for me

    • tom_alexander 1 day ago

      ooo that will be a nice improvement. So many times I've run `jj status`, then saw a file I wanted gitignored, so I'll edit my gitignore, but the file has already been added to the repo so I have to `mv <file> /tmp/ && jj status && mv /tmp/<file> .` to get the file out of the repo.

      • steveklabnik 1 day ago

        You can `jj file untrack` instead of that mv bit.

        • tom_alexander 1 day ago

          Oh neat, thanks! I (clearly) did not know that command.

      • kps 1 day ago
            [snapshot]
            auto-track = '~glob:**/*'
  • smweber 1 day ago

    jj edit is the biggest jj footgun I can think of, as other comments said just use jj new. But also if you do accidentally edit or change something jj undo works surprisingly well.

    I found when using jj it worked best for me when I stopped thinking in commits (which jj treats as very cheap “snapshots” of your code) and instead focus on the “changes”. Felt weird for me at first, but I realized when I was rebasing with git that’s how I viewed the logical changes I made anyway, jj just makes it explicit.

    jj auto-rebasing doesn’t matter until you push changes, and once you do it marks them immutable, preventing you from accidentally rebasing changes that have been shared.

    • saghm 1 day ago

      > jj edit is the biggest jj footgun I can think of

      Honestly, this is only because `git checkout` is so convoluted that we've collectively changed our expectations around the UX. "checkout" can mean switching to another branch (and creating it if you specify a flag but erroring if you don't), looking at a commit (in which case you have "detached HEAD" and can't actually make changes until you make a branch) or resetting a file to the current state of HEAD (and mercy on your soul if you happen to name a branch the same as one of your files). Instead of having potentially wildly different behavior based on the "type" of the thing you pass to it, `jj edit` only accepts one type: the commit you want to edit. A branch (or "bookmark", as jj seems to call it now) is another way of specifying the commit you want to edit, but it's still saying "edit the commit" and not "edit the bookmark". Unfortunately, the expectation for a lot of people seems to be that "edit" should have the same convoluted behavior as git, and I'm not sure how to bridge that gap without giving up part of what makes jj nice in the first place.

      • nightski 1 day ago

        It's not "wildly" different behavior based on the thing it's pointing to. In all 3 cases, the command is pointed at a commit and the behavior is the same. Once you know that branches/HEAD are just named pointers to commits, then it becomes obvious you are always just working on commits and branches/ids/HEAD etc are just ways of referencing them.

        • saghm 1 day ago

          > In all 3 cases, the command is pointed at a commit and the behavior is the same

              echo "something" >> foo.txt
              git checkout foo.txt
          

          What's the name of the branch this is pointed at? If I have to run another git command to find out, then it's not "pointed" at it.

          • webstrand 1 day ago

            If you don't provide it a <tree-ish> it reads from the index (staged files). So you're right its not really pointed anywhere, since the index isn't a ref.

            • saghm 1 day ago

              That's my overall point: the argument itself (with respect to the current state of the repo) is what determines the behavior. I don't think this is anywhere close to as intuitive as commands that only ever accept one "type" of argument (and erroring if it's different).

              • nightski 1 day ago

                I stand corrected by this one scenario, but I’ve been using git for over a decade and never found that useful. Just don’t use checkout on a file path, there is no need.

                • sswatson 1 day ago

                  I find this kind of advice to be a more scathing indictment of an interface than a critic could ever muster: asking users to forego available functionality so that some sense of order can be imposed.

                  • tom_alexander 1 day ago

                    < glances around at all the people telling me to never use `jj edit` >

                    • baq 1 day ago

                      edit is useful and there are good reasons to use it, 'never use edit' is like 'never use goto' i.e. false - but if you're just starting out, jj new/jj squash is the way to go indeed.

                      (my particular favorite reasons to use jj edit are git-native tools which expect to work with uncommitted files e.g. autoformatters, linters, etc. which have been scripted in CI/dev workflows such that they cannot accept a list of files as params)

                    • dwattttt 22 hours ago

                      That goes in the same bucket as rebase. Until you know what it does, you'll be fine avoiding it.

                      Since people are sharing their experiences and my recent one is relevant to edit, I'll go:

                      Working on a feature recently, I ended up making 3 changes ("commits") on top of each other and hopping between them via jj edit.

                      The first change wasn't feature specific, it was extending the base project in preparation.

                      The second change just added a doc describing all the changes needed for the feature.

                      The third change removed the doc as parts were implemented, bit by bit.

                      As I progressed on the third change & found stuff I'd missed at the start of this process, I jumped back to edit the first change (maybe I had a bug in that base project extension) and the second change (oh hey, I found something else that needed to be done for the feature).

                      It sounds crazy compared to a git workflow, but at the end of the process I have 3 changes, all tested & working. If I was doing this with git, I'd have to rebase/squash to get the final changes into a neat clear history.

                    • smackmybishop 22 hours ago

                      I suggested that since you seemed really concerned about editing the commit that you just told it to edit. Use 'edit' all you want if your goal is to edit commits, otherwise 'new' does what it seems like you're expecting...

                • tom_alexander 1 day ago

                  If you don't run checkout on file paths, how do you undo changes to specific files that you haven't committed yet? Like you've edited but not committed <foo>, <bar>, and <baz>. You realize your edits to <bar> are a mistake. I'd just run `git checkout <bar>` to revert those changes, what do you do?

                  It is also really useful when you realize you want <bar> to be the version from a commit two weeks ago. I guess you could always switch to the branch 2 weeks ago, copy the file to /tmp/, switch back, and copy the file into place, but `git checkout c23a99b -- <bar>` is so quick and easy. Or does this example not fall under the "dont run checkout on a path" since it is taking a treeish first before the path?

                • hollowcelery 1 day ago

                  Interesting - I use git checkout constantly, whenever I have a file in another branch or commit that I want to drag into this one wholesale.

                  • saghm 22 hours ago

                    It's a useful thing to be able to do! It just fundamentally shouldn't be under one command. To its credit, git did add `switch` (with `-c` for creating a new branch and `-d` for specifying detached HEAD), but after two decades I can't imagine they'll ever get rid of checkout entirely because it was so fundamental for so long, and as long as its there, it's a loaded footgun with the safety off.

                • saghm 22 hours ago

                  "Just don't accidentally do things wrong" is also the way to avoid null pointer errors, type mismatches in dynamically typed languages, UB in C/C++. It works, until it doesn't, and in practice that happens pretty quickly. Personally, I like things that have proper safety checks.

                  • nightski 20 hours ago

                    Except it's not an unrecoverable error. If you do it all it does is add that file to the working index. No commits are made and no harm is done. So no, I do not feel it's the same as a null pointer exception or type mismatch.

                    • saghm 17 hours ago

                      > If you do it all it does is add that file to the working index.

                      No, that's not at all what it's doing!

                          git init
                          touch foo.txt
                          git commit -m 'empty foo.txt'
                          echo something >> foo.txt
                          git diff --stat # Shows one line added to foo.txt
                          git checkout foo.xt
                          git diff --stat # empty output; no changes!
                      

                      It removes changes that are not yet checked in. You can only get them back by going into the reflog (which is pretty much identical to the situation described with `jj edit` at the beginning of this thread; `jj op log` will let you get them back fine).

                      edit: Actually, I was wrong; the changes will NOT be in the reflog because they were never checked in. It's fully destructive; you've lost the changes and don't have any way of getting them back through git. This is strictly worse than anything possible with `jj edit`, and even when making this point I didn't realize how bad it was, and clearly neither did anyone defending it.

                      The fact that there have already been two instances of defending git's behavior without even understanding what it's doing with this command is exactly my point. It's not your fault; the command is unintuitive, and that's my entire point. How many times should it take for people to try to explain what's happening incorrectly before we accept this?

        • MrJohz 1 day ago

          But branches are not just named pointers to a commit. If they were, then checking out the pointer would be the same as checking out the commit itself. But I can check out a commit and I can check out a branch and depending on which I've done, I'm in two different states.

          Either I'm in branch state, where making a commit bumps the branch pointer and means the commit will be visible in the default log output, or I'm in "detached head" mode, and making a commit will just create a new commit somewhere that by default is hidden into I learn what a reflog is. And the kicker is: these two states look completely identical - I can have exactly the same files in my repository, and exactly the same parent commit checked out, but the hidden mode changes how git will respond to my commands.

          In fairness, none of this is so difficult that you can't eventually figure it out and learn it. But it's not intuitive. This is the sort of weirdness that junior developers stumble over regularly where they accidentally do the wrong kind of checkout, make a bunch of changes, and then suddenly seem to have lost all their work.

          This is one of the ways that I think the JJ model is so much clearer. You always checkout a commit. Any argument you pass to `jj new` will get resolved to a commit and that commit will be checked out. The disadvantage is that you need to manually bump the branch pointer, but the advantage is that you don't necessarily need branch pointers unless you want to share a particular branch with other people, or give it a certain name. Creating new commits on anonymous branches is perfectly normal and you'll never struggle to find commits by accidentally checking out the wrong thing.

          • fragmede 1 day ago

            > these two states look completely identical

            No they don't. As you noted, one state is "detached head" and any competently set up shell PS1 will tell you that, or that you're on a branch by displaying the name of the branch vs the commit.

            > Creating new commits on anonymous branches is perfectly normal

            Sorry, that that's an example of more intuitive behavior on jj's partc, you've lost me. I've done that intentionally with git, but I know what I'm doing in that case. For someone new to version control, committing to an unnamed branch doesn't seem like a desired operation no matter which system you're using. What's wrong with requiring branches to be named?

            • steveklabnik 1 day ago

              > For someone new to version control, committing to an unnamed branch doesn't seem like a desired operation no matter which system you're using.

              We have data on this! I can't cite anything public, but companies like Meta have to train people who are used to git to use tools like sapling, which does not require named branches. In my understanding, at first, people tend to name their branches, but because they don't have to, they quickly end up moving towards not naming.

              > What's wrong with requiring branches to be named?

              Because it's not necessary. It's an extra step that doesn't bring any real benefits, so why bother?

              Now, in some cases, a name is useful. For example, knowing which branch is trunk. But for normal development and submitting changes? It's just extra work to name the branch, and it's going to go away anyway.

              • fragmede 23 hours ago

                Fascinating. The benefit it brings is you can map the branch to its name. Of the, say, 10 branches you've got checked out, how do you know which branch maps to jira-123 and which one maps to jira-234, or if you're using names, which anonymous branch maps to addFeatureA or fixBugB?

                More to the point though, what tooling is there on top of raw jj/git? Specifically, there's a jira cli (well, multiple) as well as a gh cli for github as well as gitlab has one as well. When you call the script that submits the branch to jira/github/gitlab, how does it get the ticket name to submit the code to the system under? Hopefully no one's actually opening up jira/github/gitlab by hand and having to click a bunch of buttons! So I'll be totally transparent about my bias here in that my tooling relies on the branch being named jira-123 so it submits it to jira and github from the command line and uses the branch name as part of the automated PR creation and jira ticket modification.

                • steveklabnik 23 hours ago

                  > Of the, say, 10 branches you've got checked out, how do you know which branch maps to jira-123 and which one maps to jira-234, or if you're using names, which anonymous branch maps to addFeatureA or fixBugB?

                  The descriptions of the changes. I shared some jj log output in another comment, here it is with more realistic messages, taken from a project of mine:

                      @  vvxvznow 
                      │  (empty) (no description set)
                      │ ○  uuowqquz 
                      ├─╯  Fix compiler panic in error rendering for anonymous struct methods (rue-fwi9)
                      │ ○  uvlpytpm 
                      ├─╯  Stabilize anonymous struct methods feature
                      ◆  lwywpyls trunk
                      │  Fix array return type unification in type inference
                  

                  That (rue-fwi9) is the equivalent of jira-123, if I super care about it being obvious, I might put it in the message. But also, I might not, as you can see with the other two. You could also pass flags to see more verbose output, if the first line isn't clear enough, but in general, the convention for git as well is to have that short summary that explains your change, so if it's confusing, you probably need to do better on that.

                  > Specifically, there's a jira cli (well, multiple) as well as a gh cli for github as well as gitlab has one as well.

                  These systems do require branches in order to open a pull request. In these cases, I use `jj git push -c <change id>`, which will create a branch name for me, and push it up. This is configured to produce a branch name like steveklabnik/push-mrzwmwmvkowx for a change with the id mrzwmwmv, and ultimately, it's still easier to name locally with m or mr depending on if the prefix is ambiguous. That said, from there I do usually just click the button and then "open pull request" on GitHub, but like, all of these tools (gh is the only one I've used, but I can't imagine that the others do not work, since ultimately, it's a git repo) just work if you want to use them.

                  Other systems do not even require a branch to submit, and so you don't even need to do this. I would say "submit mr" and it would return me the URL for the created change request. Gerrit does this on top of plain old git.

                  > how does it get the ticket name to submit the code to the system under?

                  I haven't worked with Jira in a long time, but with GitHub, if I make a change that fixes issue 5, I put "Fixes #5" in my description, and when the PR is created, it updates ticket #5 to link the PR to that change automatically, no other process needed.

                • Zizizizz 23 hours ago

                  You can name branches in JJ too, they're just called bookmarks.

                  git checkout main git pull git switch -c jira-234 ... git commit git push -u origin main

                  jj git fetch jj new main ... jj commit jj b(ookmark) c(reate) jira-234 -r @- jj b(ookmark) t(rack) jira-234@origin jj git push

                  • steveklabnik 23 hours ago

                    Right, this is a good point: you can if you want to, or if you're working with a system that requires them.

                    Just in practice, anonymous branches end up feeling very natural, especially during development, and especially if your code review tooling doesn't require names.

            • tom_alexander 1 day ago

              > any competently set up shell PS1 will tell you that

              I certainly hope your shell is not running `git` commands automatically for you. If so, that is a RCE vulnerability since you could extract a tarball/zip that you don't expect to be a git repository but it contains a `.git` folder with a `fsmonitor` configured to execute a malicious script: https://github.com/califio/publications/blob/main/MADBugs/vi...

              • fragmede 1 day ago

                Might want to let git know. It's been a part of the git source code since 2006. If there were an RCE vulnerability from using __git_ps1, one would hope it would have been found by now!

                https://github.com/git/git/blob/master/contrib/completion/gi...

                • tom_alexander 23 hours ago

                  I was able to reproduce it using that script in my PS1 when `GIT_PS1_SHOWUNTRACKEDFILES=1` which triggers a call to `git ls-files`. Without that, it seems to be just calling `git rev-parse` which does not execute fsmonitor.

                  I was also able to reproduce it with `GIT_PS1_SHOWDIRTYSTATE=1` which invokes `git diff`.

                  As far as I am aware, this has been brought to the attention of the git maintainers years ago: https://github.com/justinsteven/advisories/blob/main/2022_gi...

            • MrJohz 1 day ago

              They look identical to people who don't know what to look for, and who don't realise that these two states are different, which is the key thing. You can also distinguish them by running `git status`, but that's kind of the point: there's some magic state living in .git/ that changes how a bunch of commands you run work, and you need to understand how that state works in order to correctly use git. Why not just remove that state entirely, and make all checkouts behave identically to each other, the only difference being which files are present in the filesystem, and what the parent commit was?

              What's wrong with unnamed branches? I mean, in git the main issue is that they're not surfaced very clearly (although they exist). But if you can design an interface where unnamed branches are the default, where they're always visible, and where you can clearly see what they're doing, what's wrong with avoiding naming your branches until you really need to?

              I think this is the key thing that makes jj so exciting to me: it's consistently a simpler mental model. You don't need to understand the different states a checkout can be in, because there aren't any - a checkout is a checkout is a checkout. You don't need to have a separate concept of a branch, because branches are just chains of commits, and the default jj log commands is very good at showing chains of commits.

              • fragmede 23 hours ago

                My command looks like either:

                    fragmede@laptop:(abranch)~/projects/project-foo$
                

                or fragmede@laptop:(abcdef)~/projects/project-foo$

                Depending on if abranch is checked out, or abcdef which may be HEAD of abranch is checked out.

                If you're having to run `git status` by hand to figure out which of the two states you're in, something's gone wrong. (That something being your PS1 config.) If people are having trouble with that, I can see why switching to a system that doesn't have that problem, it just that it doesn't seem like it should even be problem to begin with. (It's not that it's not useful to have unnamed branches and to commit to them, just that it's not a intro-to-git level skill. Throwing people into the deep end of the git pool and being surprised when some people sink, isn't a good recipe for getting people to like using git.)

                > What's wrong with unnamed branches? As you point out, those commits kinda just go into the ether, and must be dug out via reflog, so operationally, why would you do that to yourself. Separate from that though, do you "cd" into the project directory, and then just randomly start writing code, or is there some idea of what you're working on. Either a (Jira) ticket name/number, or at least some idea of the bug or feature you wanna work on. Or am I crazy (which I am open to the possibilty) and that people do just "cd" into some code and just start writing stuff?

                VCS aside, nothing worse than opening Google docs/a document folder and seeing a list of 50 "Untitled document" files an my habit of naming branches comes from that. Even though I'm capable of digging random commits out of reflog, if all of those commits are on unnamed branches, and have helpful commit messages like "wip" or "poop", figuring out the right commit is gonna be an exercise in frustration.

                As long as you've got something that works for you though, to each their own. I've been using too long for me to change.

                • MrJohz 21 hours ago

                  The only thing that changed in the two things you wrote was `ranch` -> `cdef`. Every other part of that PS1 output was the same.

                  Now put yourself in the shoes of a git novice and ask yourself if you'd always notice the difference. At least from my experience, they often don't, especially if they're concentrating on something else, it if they're using an IDE and the visual information about which branch/commit is checked out.

                  I don't think you're crazy, I think you're just too used to this sort of stuff to remember what it was like to still be learning git. When I say people make these sorts of mistakes, I'm thinking about real colleagues of mine who have made exactly these mistakes and then panicked that commits suddenly had disappeared.

                  Similarly, I think to you, unnamed branches feel like something complicated because in git that are. Git makes it very easy for commits to seemingly disappear into the ether, even though they are still there. But in jj, they don't disappear - they remain very visible, and the log UI shows them in a way that makes it clear where they come from. The default log UI is something like git's --graph output, which means you see how the different commits interact with each other. I really recommend having a look at the output of `jj log`, because I think then it'll be a lot clearer what I mean when I say that it's not hard to figure out what the right commit is.

                  • skydhash 20 hours ago

                    > Now put yourself in the shoes of a git novice

                    Sometimes it seems to me that's only in SWE we allow people to proceed in the workplace without any training. There's enough learning material that people should take a week or something to practice git and not be git novice anymore.

                    • MrJohz 19 hours ago

                      Or you make tools that are easier to use, so that you can spend that week learning something more useful than the finicky details of branch vs detached head checkouts.

                      • nightski 7 hours ago

                        Don't get me wrong, git has some accidental complexity (as will any tool introduced, including what I am seeing with jj). But a lot of it is just incidental complexity. It doesn't matter how much lipstick you put on it, at the end of the day some concepts need to be learned.

                        • MrJohz 2 hours ago

                          Did you mean inherent complexity instead of incidental complexity?

                          I think the inherently complex things in git are (1) the content-accessible object store, snapshots, plus the merkel tree approach to keeping track of commits and parenthood, (2) merges, rebases, and resolving conflicts between two different changes with a common ancestor, (3) possibly syncing commits between different remotes, although I think Git's approach adds accidental complexity to the problem as well.

                          Everything else is a question of the user interface: how you choose to show a commit, how you choose to update the project files, how you choose to let people create new commits, etc. And I think the Git CLI is a poor user interface in a lot of places. There are a lot of features which are really powerful but difficult to use, whereas actually they could be just as powerful but far more intuitive to use.

                          In fairness, this is no slight to the Git developers - they are improving a lot of that interface all of the time. And finding a simpler interface is often a lot more hard work than finding the complicated interface, and I don't think I would have figured out how to create something like jj until I'd seen it before.

                  • fragmede 4 hours ago

                    I missed the formatting on my original comment, but to use real commit names:

                        fragmede@laptop:(main)~/projects/project-foo$
                        fragmede@laptop:(cf95b5ac22fd...)~/projects/project-foo$
                        fragmede@laptop:(opengl_meters)~/projects/project-foo$
                    

                    Is the difference between these 3 really that subtle? How do you miss that the line lengths are different and that one of them is a pile of letters and the other is a word? I'm not trying to die on some "git is easy to use" hill because it isn't. Just that the difference between unnamed branches and named branches isn't this hidden undiscoverable thing. I don't have access to this dataset of Mr Klabnik's, but apparently it is.

                    I don't know how much it matters in AI-codepocalypse because I'll be honest, I haven't used git manually in days. AI writes my commit messages and deals with that shit now. (Claude ends a session with "Want me to push it?" and I just reply "yes".

                    > Similarly, I think to you, unnamed branches feel like something complicated because in git they are.

                    It's not that working with unnamed branches in git seems complicated, it's that it seems opposite to how I, and by extension, other people work. Now, obviously that assumption of mine doesn't hold true, otherwise we wouldn't be having this discussion, but going back to my Google docs example, staring at a page of documents called Untitled document isn't helpful, so in my mind, there's just a bit of digital hygiene that's necessary under any system.

                    > I really recommend having a look at the output of `jj log`, because I think then it'll be a lot clearer what I mean when I say that it's not hard to figure out what the right commit is.

                    You tell me which commit I want from this `jj log` output:

                        @  nlvqqyzv fragmede@example.com 2026-04-15 04:43:07 0d644b5d
                        │  (empty) (no description set)
                        │ ○  nkuswmkt fragmede@example.com 2026-04-15 04:43:07 e071e1a5
                        ├─╯  (no description set)
                        ○  ytyvtmpr fragmede@example.com 2026-04-15 04:42:51 bd5b4717
                        │  (no description set)
                        │ ○  srmtuszo fragmede@example.com 2026-04-15 04:42:35 aa64aea2
                        ├─╯  (no description set)
                        ○  nrqwxvzl fragmede@example.com 2026-04-15 04:40:42 c1dbbda3
                        │  (no description set)
                    

                    The effort to label those with `jj describe` what "nrqwxvzl" is vs "nkuswmkt" could just as well be put into using named branches.

                    Again, I'm not trying to die on a "git is easy to use" hill, because it isn't. My point was that "> these two states look completely identical" isn't true if you setup PS1 to tell you what state you're in.

                    jj gets a lot of things right, there's no panicking that work got lost. That is a big deal! The emotion toll that the possibility of that happening with git causes on new users is hard to understate. It's hard to not be scared of git after you've lost hours of work after running the wrong command.

                    I didn't raise $17m to build what comes after git, although I've thought a lot about that problem. Improvements in that area are welcome, and the easier it is for people to work with stacked change sets and branches, globally, can only be a good thing. I can't make everyone else get as good at git as I am (or better!), so I welcome better tooling.

                    • MrJohz 2 hours ago

                      I don't understand your example. Why haven't you added commit messages? Would you do that with git? In what situation are you creating different branches like that and not using either `jj commit` or `jj describe`?

    • nomel 1 day ago

      > preventing you from accidentally rebasing changes that have been shared.

      I think this ruins it for me then. I push my in-progress work, to my in-progress branches (then git-squash or whatever later, if needed). It makes switching between (lab) computers, dead or not, trivial.

      Is there some "live remote" feature that could work for me, that just constantly force pushes to enabled branches?

      • saghm 1 day ago

        Nothing stops you from doing the equivalent of `git push --force` in `jj`. The flag is just named differently: `--ignore-immutable`. This is a global flag though, so it's available to all commands, and `jj` requires it whenever you're making changes to immutable commits, even locally. I'd argue that this is one of the killer features of `jj`, since by comparison `git rebase` treats everything the same whether you're squashing your own local commits on a feature branch or messing with the history of `main` in a way that would break things for everyone.

      • aseipp 1 day ago

        Yes, almost all JJ users do this constantly. Just "track" the particular branch. JJ has an idea that only some commits are immutable, the set of "immutable heads", and the default logic is something like "The main branch is always immutable, remote branches are immutable, 'tracked' remote branches are mutable." In other words, tracking a remote branch removes it from the set of immutable heads.

        So just run:

            jj bookmark track myname/somecoolfeature --remote origin
        

        and the default settings will Do What You Want. This is intended as a kind of safeguard so that you do not accidentally update someone else's work.

        Some people configure the set of immutable heads to be the empty set so they can go wild.

  • Jenk 1 day ago

    This is literally jj's schtick and reason for existing, so I wouldn't be surprised if you decide it is not the tool for you.

    • tom_alexander 1 day ago

      Yeah, that's a very real possibility. On the bright side, jj is git-compatible so at least the two camps can live together in harmony.

  • saghm 1 day ago

    How are you "checking out" the old commit? It sounds like you're using `jj edit`, which I'd argue does what it says on the tin. Switch to using `jj new <branch>` and your problem goes away.

    • tom_alexander 1 day ago

      That avoids the problem for the specific workflow of checking out an old revision (and it was what I was describing with checking out a new branch off the old commit and adding a blank commit to that branch), but another way this design bites me: At work I am constantly jumping around numerous repos because I might be working on repo <foo> but then someone on my team will ask for help with repo <bar>. So I'll turn on screen sharing, open up repo <bar> and I'll type out psuedo-code into <bar> as I'm explaining things to them.

      So if the last thing I did on <bar> was finish some work by making a new commit, then writing some changes, and then giving it a commit message with `jj desc`, then I am now polluting that commit with the unrelated explanatory psuedo-code. So when switching to a repo I'm not actively working in, I need to defensively remember to check the current `jj status` before typing in any files to make sure I am on an empty commit. With git, I can jump around repos and make explanatory edits willy-nilly, confident that my changes are distinct from real meaningful commits.

      I guess one way to describe it is: we want to make it easy to make good commits and hard to make bad commits. jj seems to be prioritizing the former to the detriment of the latter. My personality prioritizes rigorous safety / lack of surprises.

      • icorbrey 1 day ago

        Fwiw I generally solve this by using `jj commit` instead of `jj desc` unless I'm specifically targeting something that isn't my working copy. Technically it violates the "we want commands to be orthogonal" guideline we use to write Jujutsu (otherwise this would indeed be `jj desc; jj new`) but as a habit it's never let me down

        • tom_alexander 1 day ago

          Ah, thanks! That's a command I haven't learned yet, so I'll have to check it out. I learned jj from the tutorial that was posted and I don't think it covered `jj commit` at all.

          • steveklabnik 1 day ago

            I didn't cover it for various reasons, but I think it's good to teach now that I've had more time to consider this sort of thing, so the next iteration will likely start by beginning with jj commit.

            • saghm 1 day ago

              In a pure `jj` model, commit might not even be necessary as it's own subcommand (since you could easily define an alias for `desc` followed by `new`). We're still living in a world where most people who would consider adopting `jj` are git users currently, so I wonder if starting with `commit` and then following it up with an explanation of "here's how you can change the commit message without needing to make a new commit" and "here's how you can make a new commit without changing the name of the current one" would end up fitting people's expectations better.

              • steveklabnik 1 day ago

                Yes, I do think that the latter is correct now.

                I tend to learn "bottom-up", so I like the new + describe as a way of learning, but people want to jump in and get going with tools, so commit fits that expectation better.

                • saghm 22 hours ago

                  I'm the same way. I've learned over the years that this ends up being somewhat uncommon though, and one of the harder but more rewarding parts of helping people learn is figuring out where they're coming from and meeting them there. (I'm positive this is something you've been well aware of for a while though, probably longer than me!)

      • ersatz-matty 1 day ago

        From your "polluted" snapshot, you can run `jj commit -i` and use the TUI to select only what you want.

        • hacker161 1 day ago

          Just like you can run `git add -p`

          • baq 1 day ago

            yes but no

            • hacker161 1 day ago

              Explain the difference.

              • baq 1 day ago

                git add -p doesn't create a commit.

                • em-bee 17 hours ago

                  then create one when you are done adding?

                  • baq 16 hours ago

                    why bother? jj does it automatically and gives tools to work with such commits, no point to force this on yourself with git

                    • em-bee 6 hours ago

                      sure, but i don't think it's that big of a deal either way. it's not the specific feature that draws me in. the overall concept of saving everything as a commit is interesting however. not needing to manually commit after adding changes is just a small positive side effect.

                      • baq 4 hours ago

                        yeah, jj is a different way of working with the same underlying data structure that git also operates on. I happen to like it more than git, but it took some using to because they're both used to arrive at the same end result withing mostly the same operations.

      • kps 1 day ago

        I think you have somehow picked up an overcomplicated workflow, and this is case is actually something that `jj` is much better at.

        If I'm in the middle of working on <foo> and someone asks about <bar>: `jj new <bar>`. When I'm done (and do whatever I want with those new changes in <bar>, including deferring deciding what to do), I just `jj edit <foo>` and I'm back exactly where I left off. It's a bit like `git stash` without having to remember to stash in advance, and using regular commit navigation rather than being bolted on the side.

      • MrJohz 1 day ago

        I think the right intuition to have with jj is that `jj st` should show an empty change unless you are actively working on something. `jj commit`, as mentioned below, is a good example of this - it automatically creates a new change and checks it out. The "squash flow" also does this well - you use the branch tip as a staging area and squash work into other changes on the branch as you go along. Either way, once the work is finished, there's an empty change at the tip of the branch.

        This is also supported by jj implicitly - whenever you check out a different commit, if the change you were on is empty, has no description, and is the tip of a branch, it's automatically deleted to clean things up for you.

  • et1337 1 day ago

    Jujutsu has a concept of mutable vs immutable commits to solve this. Usually everything in a remote branch is immutable. To work on a branch, I track it and that makes it mutable.

  • alunchbox 1 day ago

    if you loose an edit jj op log is incredible, I've saved a ton of work more-so now with AI's making mistakes. Also workspaces are super fast compared to git worktree's - same concept, different implementation.

    I agree, that was a bit of an interesting approach but more-so than not it's been better in DX even though you have to 'unlearn' long term it's been a benefit IMO, but a soft one, not something you can measure easily.

  • baq 1 day ago

    > edits to files are automatically committed

    this is a core feature and it makes jj possible - you're supposed to get used to jj new and jj squash into the previous bookmarked commit, which you map to the git branch head/PR.

    IOW you're supposed to work on a detached git head and jj makes this easy and pleasant.

  • rstuart4133 20 hours ago

    > so you need to defensively create empty new commits for your changes.

    I thought that for a long while too, and was equally pissed. Then I happened upon `jj evolog`. Turns out jj had a better solution than staging all along - I just didn't realise it existed.

    The move from using jj as an alternate porcelain to git to using it efficiently took me more months than I care to admit. I suspect being familiar with git cli is actually a handicap in learning jj. New users without pre-conceptions will have a much easier time of it.

    And oddly, I also suspect they will also end up knowing more about the underlying git storage engine than git users. It turns out it's capable of being used far more effectively than git uses it.

    Doubly oddly, I blame Linus's familiarity with CVS and SVN for that. He (correctly) bemoaned how unsuited to the job of distributed source code management they were and invented a storage engine that has proved to be remarkably good at the job. But he carried across many the concepts in their CLIs to gits porcelain. Jj (with a lot of help from hg), finally broke free of that legacy.

dgb23 1 day ago

The last paragraph might be the most important one:

> There's one other reason you should be interested in giving jj a try: it has a git compatible backend, and so you can use jj on your own, without requiring anyone else you're working with to convert too. This means that there's no real downside to giving it a shot; if it's not for you, you're not giving up all of the history you wrote with it, and can go right back to git with no issues.

  • eru 1 day ago

    Funnily enough, that's how I used git with CVS and Subversion, too.

  • jeremyjh 1 day ago

    But this is not true. They are interoperable but far from seamless. Those features mainly support migration use cases or things like git deployment from an repo managed in jj. Operations git does are not in jj’s log. You have to constantly import them. The project recommends a single primary interface.

    • miyoji 1 day ago

      But it is true. I (and many others) happily use jj on teams that use git without anyone else on the team using jj or knowing (or caring) what I'm using.

    • maleldil 1 day ago

      If you constantly switch between the two, you're going to have a hard time, but you can take a git repo, try jj for a while, and if you decide to go back, you don't lose anything.

      • jeremyjh 1 day ago

        Right, but that’s different from working in a team environment where everyone else continues using git.

        • saghm 1 day ago

          How so? I've used `jj` locally on teams where most (if not all) of the other team members were using git, and they only found out I was using `jj` when I told them.

        • pyreko 1 day ago

          fwiw I don't use it personally but some people on my team use it while the others use git, and nobody complains.

          • tonfa 1 day ago

            Yeah same here, have been using jj exclusively, the only reason people notice is because my branch names default to the changeid in my setup so I've had questions about the random looking strings.

        • stouset 1 day ago

          No?

          What problems, exactly, are you suggesting exist? I have used jj extensively on git teams and it has been seamless. The only people who have noticed or cared are the coworkers I’ve gotten to join me.

        • maleldil 1 day ago

          You're confusing mixing git and jj in your local copy of the repo vs what it looks like to other people. You can use jj locally, and it interoperates perfectly with any git remote, and no one has to know you're even using it. From the point of view of other people, it doesn't matter.

        • Balinares 7 hours ago

          I don't understand what you mean. The team I currently work with is 100% git while I only use jj anymore, and I've had zero issues?

    • ongy 1 day ago

      I think you are talking about colocation, which is slightly different than the `jj git push` `jj git fetch` type commands.

      Colocation has its uses bit is a bit finicky. The push/pull compatibility works perfectly fine (with some caveats of github being broken that can be worked around).

    • asdfasgasdgasdg 1 day ago

      Most importantly, submodules are not fully supported, which are used by almost every open source project at least in the space I work in (embedded). So you can't use jj to easily contribute back to those project. It can be done but you always have to be cognizant of whether a submodule has changed between two branches or when you sync, since they don't update automatically the way they do with git.

    • saghm 1 day ago

      It's been over a year since I last used git manually in the CLI, and I've exclusively worked with git remotes. The only time I had any friction was on a team where stale code-gen output was checked into the repo and for whatever reason no one was willing to either add it to the `.gitignore` or commit (pun intended) to keeping it up to date, meaning that I had to manually remove the changes from when I compiled before pushing. I would have argued in favor of adding to .gitignore or keeping it up to date even if I didn't use `jj` though because I think having stale output checked in is just silly.

      • nchagnet 1 day ago

        For what it's worth, you can have your own local gitignore by adding patterns to .git/info/exclude. It's quite useful in this exact situation.

        • saghm 22 hours ago

          I did try this, but for whatever reason it kept getting added back automatically. I forget the details of exactly why it was happening because it was close to a year ago, and in the compatibility guide it says this is supported, but I'm not sure if it was at the time or I was running into something different. This was a contract gig for me where I knew it would be ending within a month or so, which meant I didn't bother spending a ton of time trying to figure out a long-term solution.

  • IshKebab 1 day ago

    Big caveat: do not try to use Git and JJ in the same directory. It's probably fine if you only use JJ, but if you mix them you will horribly break things.

    • surajrmal 1 day ago

      This isn't true?

      • IshKebab 1 day ago

        It is when I tried it.

        • stouset 1 day ago

          Jujutsu uses git as its primary backing store and synthesizes anything else it needs on top on-the-fly. Any incompatibility here is considered a serious bug.

          Obviously I can’t argue against your lived experience, but it is neither typical nor common. This is quite literally an explicitly-supported use, and one that many people do daily.

          • rstuart4133 19 hours ago

            > Obviously I can’t argue against your lived experience, but it is neither typical nor common.

            I consider myself a proficient jj user, and it was my lived experience too. Eventually you get your head around what is going on, but even then it requires a combination of jj and git commands to bring them into sync, so `jj status` and `git status` say roughly the same things.

            The friction isn't that jj handles state differently, it's that `jj git export` doesn't export all of jj's state to git. Instead it leaves you in a detached HEAD state. When you are a newbie looking for reassurance this newfangled tool is doing what it claims by cross checking it with the one you are familiar with, this is a real problem that slows down adoption and learning.

            There are good reasons for `jj git export` leaving it in a detached head state of course: it's because jj can be in states it can't export to git. If we had a command, say `jj git sync` that enforced the same constraints as `jj git push` (requiring a tracked branch and no conflicts) but targeted the local .git directory, it would bridge the conceptual gap for Git users. Instead of wondering why git status looks wrong, the user would get an immediate, actionable error explaining why the export didn't align the two worlds.

    • chriswarbo 1 day ago

      I suppose it depends what you mean by "horribly break things".

      The only thing I've noticed is that `jj` will leave the git repo with either a detached HEAD, or with a funny `@` ref checked out.

      I don't think that would trouble someone who's experienced with git and knows its "DAG of commits" model.

      For someone who's less experienced, or only uses git for a set of branches with mostly linear history (like a sort of "fancy undo"), I could imagine getting a shock when trying to `git commit` and not seeing them on any of the branches!

      • nailer 23 hours ago

        > I don't think that would trouble someone who's experienced with git and knows its "DAG of commits" model.

        I think most people that have git experience don't know what a DAG is and have never used reflog.

  • verdverm 1 day ago

    Unless you use LFS, submodules, or hooks at your org.

    • igor47 1 day ago

      Submodules work fine but yeah, it's frustrating that lfs is taking so long. But there seems to be some momentum recently https://github.com/jj-vcs/jj/pull/9068

      • verdverm 1 day ago

        The git compatibility page states that submodules are not supported

        https://docs.jj-vcs.dev/latest/git-compatibility/

        • steveklabnik 1 day ago

          What "not supported" means with submodules specifically is that jj doesn't have commands to manage them. You can use git commands to manage them, and it does, in my understanding, work. There's just no native support yet.

          This is sort of similar to how you can create lightweight tags with jj tag, but you need to push them with git push --tags.

          • rstuart4133 20 hours ago

            > and it does, in my understanding, work.

            I use submodules with jj, and jj saves and restores submodule hashes perfectly. What it doesn't do is manipulate the sub-repository from its parent. You can do that yourself using jj or git of course, which is what I ended up doing using a few scripts. The result ended up being more reliable than using git's submodule commands directly.

            They can take all the time in the world to implement submodules as far as I'm concerned. jj's implementation of workspaces removes all of the hairs of git's worktrees. git submodule implementation has even more hairs than worktrees. If the jj developers need time to do as good a job on submodules as they did with workspaces, then I say give it to them.

            • steveklabnik 19 hours ago

              Gotcha, thank you for the context.

BeetleB 1 day ago

One of my favorite jj features is "jj absorb".

For each change you've made in the current revision, it finds the last commit where you made a change near there, and moves your changes to that commit.

Really handy when you forgot to make a change to some config file or .gitignore. You just "jj new", make the changes, and "jj absorb". No need to make a new commit or figure out where to rebase to.

Oh, and not having to deal with merge conflicts now is awesome. My repository still has merge conflicts from months ago. I'll probably go and delete those branches as I have no intention to resolve them.

  • dnmc 23 hours ago

    And If `jj absorb` gets it wrong, you can run `jj undo`.

    This is such a killer feature to me. I'm not scared to start potentially gnarly rebases anymore because I can painlessly undo.

    • BeetleB 23 hours ago

      Yes. With "jj undo", I'm not scared to do anything. The brief time I had to go back to using vanilla "git", I didn't enjoy being extra cautious.

      Using a version control tool shouldn't require much self discipline.

    • nchmy 22 hours ago

      yeah, i regularly try absorb then undo when it moves it to a commit from 7 years ago, then manually squash where appropriate

  • jhhh 19 hours ago

    After hearing about a workflow that used absorb I wrote a simple git version I called 'squash-index' in about 15 lines of bash in a few minutes. Since git allows subcommand extension I just made an executable 'git-squash-index' and everything worked as if it was a builtin subcommand. I get that initial ergonomics are important for new tools but if the only novel feature is reified merge conflicts it doesn't really seem worth making the switch. No one needed to evangelize git to people like myself who had worked with a variety of old version control systems (cvs, svn, etc). It was just obviously much, much better. jj seems like it streamlines certain things, and might be worth learning for new users who have no git experience, but doesn't really differentiate itself enough for people who've been using git for a long time.

    • BeetleB 5 hours ago

      How easy is it to version control the index and stash in git? Do I need to know any other commands?

      With jj, it's the same as any other commit. No new commands, no new options.

steveklabnik 1 day ago

Hey folks!

So, I haven't updated the tutorial in a long time. My intent is to upstream it, but I've been very very busy at the startup I'm at, ersc.io, and haven't had the chance. I'm still using jj every day, and loving it.

Happy to answer any questions!

  • klauserc 1 day ago

    jj automatically hides "uninteresting" changes. Most of the time, this is good.

    Occasionally, I need to see more changes. It is not obvious to me how I get jj to show me elided changes. I mean, sure, I can explicitly ask jj to show me the one ancestor of the last visible change, and then show me the ancestor of that one, etc. Is some flag to say: "just show me 15 more changes that you would otherwise elide"?

    • steveklabnik 1 day ago

      I use `jj log -r ..` for that, which is just an open ended range. It's not the "15 more" but it's what's worked for me. I suspect you could do it with some sort of revset stuff, but I like to keep it simple.

    • nickisnoble 1 day ago

      Easy: `jj log -n 25`

      (Default is 10 iirc, so if you want 15 more... 25)

      If you want everything, ever: `jj log -r ::`

      Or every ancestor of your current change: `jj log -r ..@`

      • steveklabnik 1 day ago

        IIRC -n only limits the output, not expands it. jj log and jj long -n 25 show the same results for me.

    • rstuart4133 19 hours ago

      I'm not sure what you are asking, but you want to look at how a particular jj change-id evolved over time, use `jj evolog`. By default that evolution is hidden. The `change-id/N` syntax has uses beyond conflicts.

  • opem 1 day ago

    I think one major difference between git and jj is how immutable their DAG is, due to the difference in how they refer to their unit of change (i.e. stable change ID with changing commit IDs vs. immutable commit ID). One implication of that is change history in a git repo feels much more immutable to the one in a jj repo. Consequently operations that involves changing the history like, undo/rebase feels much easier/flexible. Is my understanding correct?

    • steveklabnik 1 day ago

      Sorta! I think it can feel that way at times, but also the opposite. jj’s changes are immutable in the same way commits are, when you modify a change, it makes a new immutable commit and associates that with the change. So on the literal level, they’re the same.

      But it’s true that mutating history is easy and sometimes even automatic with jj, whereas it’s not with git. So that could make it feel more mutable. On the other hand, jj has the concept of mutable vs immutable commits; jj will present you from modifying certain changes unless you pass in a flag to override it. So in some ways, it’s more immutable than git.

      Just really depends on your perspective.

compiler-guy 1 day ago

One of the things that makes jj worth trying out is simply the fact that it is different than git, and having exposure to more than one way of doing things is a good thing.

Even if you don't adopt it (and I didn't), it's easy to think that "this way is the only way", and seeing how systems other than your own preferred one manage workflows and issues is very useful for perspective.

That doesn't mean you should try everything regardless (we all only have so much time), but part of being a good engineer is understanding the options and tradeoffs, even of well loved and totally functional workflows.

  • beanjuiceII 1 day ago

    being a good engineer is also understanding when something is a waste of time because the gain is insignificant 99% of the time

    • eddd-ddde 21 hours ago

      Using "good engineering" as an argument against learning is definitely an interesting approach.

    • dcre 20 hours ago

      It seems like all of your comments are like this. Consider stopping that!

    • dwattttt 20 hours ago

      I think siblings point needs to be made more sharply: this could've gone somewhere good, "I evaluated it and found the gain was not worth the cost to change", but instead went to "the gain from a change is insignificant 99% of the time, so it's not worth understanding it".

      The latter is poor engineering.

jiggunjer 1 day ago

I think the mental model is like C vs python. Git gives you a forensic trace back in time. jj gives you a story with chapters. Look under the hood you'll still see forensic map of state transitions, but this not what we want to navigate most of the time. Sometimes we need to rewrite an early chapter to make the latest chapter make more sense.

  • baq 1 day ago

    the fact that almost by definition stuff that jj does is possible in git makes it hard for some folks to let go of the baggage that git has; it's simply hard to imagine a world where you can't git add ('how do you commit what you need committed and not commit the rest?') or not having to resolve conflicts immediately ('why would I want not to?')

    ...and it turns out when you answer these questions differently ('working tree is a commit', 'conflicts can committed) but still want git compatibility, jj kinda falls out of the design space by necessity.

kevin_nisbet 1 day ago

I really wanted to like JJ, it was handy for a few months when I used it. But for me in the end I reverted back to regular git.

What triggered me to go back was I never got a really clean mental model for how to keep ontop of Github PRs, bring in changes from origin/main, and ended up really badly mangling a feature branch that multiple contributors were working on when we did want to pull it in. I'll probably try it again at some point, but working in a team through Github PRs that was my main barrier to entry.

  • steveklabnik 1 day ago

    This is good feedback, thanks. The next version of the tutorial will certainly focus on stuff like this, as I agree it's really important to teach people.

  • p_stuart82 1 day ago

    locally? sure. stacked changes in jj are great. but the moment you push to GitHub, the review UI still thinks in SHAs. a lot of the pain just moves from the author to the reviewer.

  • opem 1 day ago

    I see that is an issue with many people, but now with github adding support for stacked PRs, I guess that would change

zingar 1 day ago

"It's more powerful and easier" is a great claim, but I need examples in this opening page to convince me of the pain I could save myself or the awesome things I'm living without.

  • baq 1 day ago

    does trivially working on 3 PRs in a single checkout and pushing focused changes to each one independently without thinking twice count?

    if you don't need this, you might not see any value in jj and that's ok. you might use magit to get the same workflow (maybe? haven't used magit personally) and that's also ok.

    • alphabetag675 1 day ago

      Actually it is a anti-demo, because while software allows you to do it, I don't think many software engineers can work on this.

      • baq 1 day ago

        in large enough monorepos and teams and big enough changes you either do it like this or have a humongous giga-PR which eventually starts conflicting with everything.

    • VanTodi 1 day ago

      Guess he was talking about the presentation, not what the tool can achieve. It has no hard proof on the first page, which could easily just be a LinkedIn pitch, but not on hackernews

    • tinco 1 day ago

      It might count, but it is easy with git as well, what is the feature in jj that makes this easier? Switching branches and pushing changes to remotes is the core feature of git and in my opinion really easy so I'm curious how jj improves on it.

      • baq 1 day ago

        rebases don't lose branches and jj absorb trivially squashes changes to the correct head (or leaves changes alone if it can't find where to squash).

        is it possible in git? yeah, I've done it; there's a reason I haven't done it more than a few times with git, though. ergonomics matter.

    • VonGallifrey 1 day ago

      Can you show how you would do this in jj?

      I know how I would do this in git, but don't really see how this would be in jj. I currently don't use it in my workflow, but if it is super easy in jj then I could see myself switching.

      • baq 1 day ago

        the beauty of it is there's not much to show; I use a crude jjui approach where I have an octopus merge working tree commit (in command line terms, jj new PR_A PR_B PR_C) and either use native jj absorb (S-A in jjui) which guesses where to squash based on the path or, when I'm feeling fancy, rebase the octopus via jjui set parents (S-M) function (also handy to clean up parents when one of the PRs gets merged).

      • bilkow 1 day ago

        This is how I'd do it:

            jj new branch1 branch2 branch3
        

        This creates an empty commit that merges all 3 branches, you can think of this as your staging area.

        When you want to move specific changes to an existing commit, let's say a commit with an ID that starts with `zyx` (all jj commands highlights the starting characters that make the commit / change unambiguous):

            jj squash -i --to zyx
        

        Then select your changes in the TUI. `-i` stands for interactive.

        If you want to move changes to a new commit on one of the branches:

            jj split -i -A branch1
        

        Then select the changes you want moved. `-A` is the same as `--insert-after`, it inserts the commit between that commit and any children (including the merge commit you're on).

        There's one thing that's a bit annoying, the commit is there but the head of the branch hasn't been moved, you have to move it manually (I used + to get the child to be clearer, but I usually just type the first characters of the new change id):

            jj bookmark move branch1 --to branch1+
  • steveklabnik 1 day ago

    This is good feedback, for sure, thank you. It's sometimes hard to come up with truly concise examples, but that's also why they're so valuable.

  • ersatz-matty 1 day ago

    Consider using the table of contents on the left of the page to view "Real World Workflows", "Branching, Merging, and Conflicts", and then "Sharing Your Code with Others" and then evaluate how JJ does things against your current git workflow. This requires some minor effort on your part.

    The official JJ docs also have a "bird's eye view" introduction and tutorial available here: https://docs.jj-vcs.dev/latest/tutorial/.

    EDIT: Jujutsu for Git experts: <https://docs.jj-vcs.dev/latest/git-experts/>. This outlines some of the main advantages relatively succinctly.

  • latortuga 1 day ago

    Yeah we moved on from SVN to git because SVN branches were truly a pain in the ass to work with. I truly do not have any rough edges or big pains in my day to day git workflow.

  • qznc 1 day ago

    There is no index anymore. I guess that is the "easier" part.

    • steveklabnik 1 day ago

      That is, but not directly.

      The general idea here is that jj has fewer and more orthogonal concepts than git. This makes it more regular, which is what I mean by "easy."

      So for example, there is no index as a separate concept. But if you like to stage changes, you can accomplish this through a workflow, rather than a separate feature. This makes various things less complex: the equivalent of git reset doesn't need --hard, --soft, --mixed, because the index isn't a separate concept: it's just a commit. This also makes it more powerful: you can use any command that works on commits on your index.

      This is repeated across jj's design in general.

  • surajrmal 1 day ago

    Specific commands don't really showcase the appeal of jj. If anything they might scare someone at first glance. It's the fact that the workflows are intuitive and you never find yourself reaching for help to get something done. You really need to try it to understand it.

    • tomnipotent 1 day ago

      > that the workflows are intuitive

      It can't be both intuitive and yet too complicated to show examples at the same time.

    • WesolyKubeczek 1 day ago

      The only intuitive interface is the nipple. All other things are learned.

      I feel very comfortable using git. Maybe jj is better, but not seeing is not believing.

      • baq 1 day ago

        jj is better for some workflows, which, if you're a git expert as you claim, you conciously or subconciously avoid as 'too much work' or 'too brittle'.

        if you don't care about them after accepting this realization... it's fine. git is good enough.

        • ncphillips 1 day ago

          I’m not a fit expert by any means. The workflows being described do not appeal to me but not because of the way fit works. They sound confusing and I don’t understand what benefit I’m getting out of them. Like, it’s a solution to a problem I’m not sure exists (for me)

  • EliasWatson 1 day ago

    A couple things off the top of my head:

    - You aren't forced to resolve rebase/merge conflicts immediately. You can switch branches halfway through resolving conflicts and then come back later and pick up where you left off. You can also just ignore the conflicts and continue editing files on the conflicted branch and then resolve the conflicts later.

    - Manipulating commits is super easy (especially with jjui). I reorder commits all the time and move them between branches. Of course you can also squash and split commits, but that's already easy in git. Back when I was using git, I would rarely touch previous commits other than the occasional squash or rename. But now I frequently manipulate the commit history of my branch to make it more readable and organized.

    - jj acts as a VCS for your VCS. It has an operation log that is a history of the state of the git repository. So anything that would be destructive in git (e.g. rebase, pull, squash, etc) can be undone.

    - Unnamed branches is the feature that has changed my workflow the most. It's hard to explain, so I probably won't do it justice. Basically you stop thinking about things in terms of branches and instead just see it as a graph of commits. While I'm experimenting/exploring how to implement or refactor something, I can create "sub-branches" and switch between them. Similar to stashes, but each "stash" is just a normal branch that can have multiple commits. If I want to test something but I have current changes, I just `jj new`. And if I want to go back, I just make a new commit off of the previous one. And all these commits stick around, so I can go back to something I tried before. Hopefully this made some sense.

    Also note that jj is fully compatible with git. I use it at work and all my coworkers use git. So it feels more like a git client than a git replacement.

    • coldtea 1 day ago

      All of these features sound like the recipe for a confusing nightmare!

      "You can switch branches halfway through resolving conflicts and then come back later and pick up where you left off. You can also just ignore the conflicts and continue editing files on the conflicted branch and then resolve the conflicts later."

      "Similar to stashes, but each "stash" is just a normal branch that can have multiple commits. If I want to test something but I have current changes, I just `jj new`. And if I want to go back, I just make a new commit off of the previous one. And all these commits stick around, so I can go back to something I tried before."

      • ncphillips 1 day ago

        Yeah I legit do not understand the appeal. I’m willing to be wrong but it’s not clicking with me at all

        • baq 1 day ago

          typical for experienced git users who already 'just don't do' things which git punishes you for; after a decade it's hard to even imagine any other way, not to mention that it might be better. been there, done that, jj is legit after letting go of (some of) git.

        • tcoff91 1 day ago

          Anonymous branches are amazing for when you are trying out a bunch of different approaches to a problem. As I search the space of possible solutions for what I'm really looking for, I end up with a tree of various approaches.

          Then when you rebase, the entire tree of anonymous branches can be rebased onto main in 1 command. This is why the first class conflicts and not having to resolve conflicts immediately is so important: when i'm rebasing, an entire tree of branches is getting rebased and so if you had to resolve conflicts right away it would be incredibly cumbersome, because I'm rebasing like 30+ commits and a bunch of anonymous branches in a single operation.

          I work on top of an octopus merge of all my in-flight PRs. ON top of that merge commit i have a bunch of anonymous branches with various things going on. When I'm ready to submit a PR, I take one of those anonymous branches and rebase it onto main and make it an additional parent of my 'dev-base' merge commit. Then i give that branch a name and submit it as a PR.

          Every day when I start working, I rebase this entire subgraph of branches in a single command onto main. all my PRs are up to date, all my anonymous branches are up to date, etc... Takes like 2 seconds. If some of my anonymous branches are in a conflicted state, that's ok, i don't have to deal with it until I want to work on that change again.

          These anonymous branches aren't confusing because they all show up in the default revset that is shown when looking at the jj log. I can easily browse through them with jjui TUI and instantly see which ones are what. It's really not confusing at all.

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

      • steveklabnik 1 day ago

        The "you don't need to worry about resolving conflicts" thing is confusing when you hear it with words, so let me show you what it looks like in practice.

        Let's say I have two branches off of trunk. They each have one commit. That looks like this (it looks so much nicer with color, I'm going to cut some information out of the default log so that it's easier to read without the color):

            @  vvxvznow 
            │  (empty) (no description set)
            │ ○  uuowqquz 
            ├─╯  foo
            │ ○  uvlpytpm 
            ├─╯  bar
            ◆  lwywpyls trunk
            │  feat: blah blah blah
        

        So both `foo` and `bar` are on top of trunk, and I'm also working on a third branch on top of trunk (@). Those vvxv and such are the change ids, and you can also see the named trunk there as well.

        Now, I fetch from my remote, and want to rebase my work on top of them: a `jj git fetch`, and then let's rebase `foo` first: that's `jj rebase uu -o trunk` (you only need uu instead of uuowqquz because it's a non-ambiguous prefix, just like git). Uh oh! a conflict!

            @  vvxvznow
            │  (empty) (no description set)
            │ ×  uuowqquz (conflict)
            │ │  foo
            │ ◆  tswtuqmu
            ├─╯  chore: whatever
            ~  (elided revisions)
            │ ○  uvlpytpm
            ├─╯  bar
            

        Note that jj did not put us into a "hey there's a conflict, you need to resolve it" state. It just did what you asked: it rebased it, there's a conflict, it lets you know.

        So why is this better? Well, for a few reasons, but I think the simplest is that we now have choice: with git, I would be forced to deal with this conflict right now. But maybe I don't want to deal with this conflict right now: I'm trying to update my branches in general. Is this conflict going to be something easy to resolve? In this case, it's one commit. But what if each of these branches had ten commits, with five of them conflicted and five not? It might be a lot of work to fix this conflict. So the cool thing is: we don't actually have to. We could continue our "let's rebase all the branches" task and rebase bar as well. Maybe it doesn't have a conflict, and we'd rather go work on bar before we come back and deal with foo. Heck, sometimes, I've had a conflicted branch, and then a newer version of trunk makes the conflict go away! I only have to choose to address the conflict at the moment I want to return to work on foo.

        There's broader implications here, but in practice, it's just that it's simply nicer to have choice.

      • nickisnoble 1 day ago

        I thought the same until I started using it.

        Turns out, git sorta trains you to be very, very afraid of breaking something.

        jj answers this in a few ways:

        1. everything is easily reversible, across multiple axes.

        2. yes, everything is basically a stash, and it's a live stash — as in, I don't have to think about it because if it's in my editor, it's already safely stored as the current change. I can switch to a different one, create a new one, have an agent work on another one, etc, all without really caring about "what if I forgot to commit or stash something". Sounds like insanity from a git POV but it really is freeing.

        3. Because of 2, you can just leave conflicts alone and go work on something else (because they are, like you said, essentially stashed). It's fine and actually very convenient.

        The thing the article doesn't mention, that makes this all safe, is that trunk / "main" is strictly immutable. All this flexibility is *just* for unmerged WIP. (There are escape hatches though, naturally!)

      • ersatz-matty 1 day ago

        In practice, it isn't. What you're identifying as potentially nightmarish - and no doubt quite tedious in git - are things that JJ enables you to do with a small subset of commands that work exactly how you expect them to work _in every workflow context_ in which they are needed.

        Thinking specifically about conflicts: being able to defer conflicts until you're ready to deal with them is actually great. I might not be done with what I am actually working on and might want to finish that first. being forced into a possibly complicated conflict resolution when I'm in the middle of something is what I'd actually consider nightmarish.

        When you want to solve the conflict: `jj new <rev>`, solve the conflict, then `jj squash`, your conflict resolution is automatically propagated to the chain of child commits from the conflict.

      • BeetleB 1 day ago

        Remember when you used SVN or whatever before git, and you loved git because of how easy it is to make branches?

        With branches, jj is to git what git was to SVN. It's an order of magnitude less friction to do branching in jj than git.

        Not long ago, I pulled from main and rebased my branch onto it - merge conflicts. But I wanted to work on some other feature at the moment. Why should I have to fix this merge conflict to work on a feature on a totally different branch? With jj, I don't. I just switch to the other branch (that has no conflict), and code my new feature. Whenever I need to work on the conflicted branch, I'll go there and fix the conflict.

        Once I started using jj, I realized how silly it was for git to have separate concepts for stash and index. And it's annoying that stash/index is not version controlled in git. Or is it? I have no idea.

        In jj, a stash is simply yet another unnamed branch. Do whatever you want there. Add more commits. Then apply it to any branch(es) that you would like to. Or not.

        Why does git need a separate concept of a stash? And wouldn't you like a version controlled stash in git?

        Have you ever made a ton of changes, done a "git add", accidentally deleted some of the changes in one file, done a "git add", and thought "Oh crap!" I suppose that information can be recovered from the reflog. But wouldn't you wish "git add" was version controlled in the same way everything else is?

        That's the appeal of jj. You get a better stash. You get a better index. And all with fewer concepts. You just need to understand what a branch (or graph) is, and you get all of it. Why give it a name like "stash" or "index"?

        Why does git insist on giving branches names? Once you get used to unnamed branches, the git way just doesn't make sense. In jj you'll still give names wherever you need to.

    • capitainenemo 1 day ago

      I also like the powerful revision querying mechanisms that they pulled in from mercurial. They seem to work just like mercurial revset queries which can be used in various operations on sets of revisions.

      I would like them to have mercurial's awesome hg fa --deleted when it comes to history trawling, but apparently for it to work well, they also need to swap out git's diff format for mercurial's smarter one, so I'll be waiting on that for a while I suppose.

    • rasguanabana 1 day ago

      > So anything that would be destructive in git (e.g. rebase, pull, squash, etc) can be undone.

      It’s possible to recover from these with git reflog, though.

      • BeetleB 1 day ago

        I've used git for years, and used reflog once or twice.

        I've used jj for only a year, and have used "jj undo" countless times.

        There's a huge benefit to having a simpler mental model.

  • Rebelgecko 1 day ago

    "jj undo" is worth the price of admission by itself.

    See the current top thread on HN about backblaze not backing up .git repos. People are flaming OP like they're an idiot for putting a git repo in a bad state. With jj, it's REALLY HARD to break your repo in a way that can't be fixed by just running "jj undo" a couple times.

  • rstuart4133 19 hours ago

    What makes jj better requires a mindset change. When you start with jj, you use it as an alternate porcelain for git, meaning you just use the jj commands that map to the way you used git. You have to let go of that mindset. Until you do, you are still using those old git commands; they are just have prettier clothing. The prettier clothing is not worth the effort.

    I don't know how to explain a mindset to you, so I'll give one example of something that sounds so grand, it seems impossible. (There are so many unusual aspects to jj, but hopefully this is one you can immediately relate to.) Git famously makes it hard to lose work, but nonetheless there are commands like `git reset --hard` that make you break out in a sweat. There is no jj command that destroys information another jj command can't bring back. And before you ask - yes of course jj has the equivalent of `git reset --hard`.

rs545837 1 day ago

jj is genuinely great and I think it deserves way more adoption than it has right now. The mental model is so much cleaner than git, undo actually works the way you'd expect it to, and working with stacked changes feels natural instead of that constant low-grade anxiety of actually breaking something. It's probably the best frontend for version control that exists today.

For the last few months though I've been thinking a lot about what you said at the end there. What if version control actually understood the code it was tracking, not as lines of text but as the actual structures we write and think in, functions, classes, methods, the real building blocks? A rename happening on one branch and an unrelated function addition on another aren't a real conflict in any meaningful sense, they only look like one because every tool we have today treats source code as flat text files.

For enhancing this kind of structural intelligence I started working on https://github.com/ataraxy-labs/sem, which uses tree-sitter to parse code into semantic entities and operates at that level instead of lines. When you start thinking of code not as text there's another dimension where things can go, even a lot of logic at the comiler level with call graphs becomes useful.

jrockway 1 day ago

jj is great and while it was an adjustment at first, I've never looked back. I feel like when you're working with other people, things never get reviewed and merged as quickly as you'd like. With jj, it's pretty low-cost to have a bunch of PRs open at once, and you can do something like `jj new <pr1> <pr2> <pr3>` to build stuff that requires all 3. This lets me do things like... not do a big refactoring in the same PR as adding a feature. I can have them both self-contained, but still start on the next step before they're all merged. It's easy to add changes on top, switching between the individual PRs as comments come up, etc.

I always liked doing things like this. At Google where we used a custom fork of Perforce, I told myself "NEVER DO STACKED CLs HAVE YOU NOT LEARNED YOUR LESSON YET?" If one CL depended on another... don't do it. With git... I told myself the same thing, as I sat in endless interactive rebases and merge conflict commits ("git rebase abort" might have been my most-used command). With jj, it's not a problem. There are merge conflicts. You can resolve them with the peace of mind as a separate commit to track your resolution. `jj new -d 'resolve merge conflict` -A @` to add a new commit after the conflicted one. Hack on your resolution until you're happy. jj squash --into @-. Merge conflict resolved.

It is truly a beautiful model. Really a big mental health saver. It just makes it so easy to work with other people.

  • andrewdea 1 day ago

    I'm having trouble understanding the value of this and most other supposed advantages of jj I'm seeing. I'm trying to pinpoint if it's because 1) my workflow doesn't need jj's fancy stuff, 2) I've gotten so used to `git`'s "flaws" that I don't notice them, or 3) the git porcelain I use (magit) does a good enough job at managing the flaws.

    If you need to build on something that requires changes from 3 open PRs, can't you just start a new branch from main, merge all 3 PRs into it, and get to work? As changes are applied to the open PRs, you can rebase. Obviously that might cause some merge conflicts here and there, but as long as the PRs aren't super overlapping, they should still be manageable. If there's a ton of overlap between 3 open PRs, that to me sounds like a problem in the workflow/plan, which must be dealt with regardless of the VCS or porcelain.

    • steveklabnik 1 day ago

      For me, it wasn't so much that jj enabled things I couldn't do before, though there are some things. What it enabled was me doing the things I was doing, but in a more easy way. This also leads you to do things that you can do, but sometimes avoid because it's a lot of work.

      • andrewdea 1 day ago

        I guess that makes sense but also reinforces the confusion I have on whether jj is just another git "porcelain" (aka UI), or a replacement for git altogether.

        If it aims to mainly improve the UX (do the same things you were doing before but easier), then it's irrelevant to those of us who have been lucky to find and learn sensible UXs. If it aims to be a git replacement, I'm a little curious why the developers would decide to re-implement something from scratch only to end up with an "alternative" that is mostly compatible and doesn't radically change the internal model or add new features.

        I last used GitHub Desktop years ago and had a terrible time. The git CLI is powerful but not very intuitive. It really wasn't until I learned magit that things "clicked" for me. I know that many git UXs are pretty bad. But the way git works internally seems pretty great to me. Too often, criticism of git conflates the two.

        • naasking 1 day ago

          > then it's irrelevant to those of us who have been lucky to find and learn sensible UXs

          Only if you're a solo dev that doesn't work on a team or have to mentor new devs that haven't developed good intuitions for this.

          This strikes me a lot like the C vs. safer programming language debate all over again.

          • andrewdea 1 day ago

            When I mentor new devs, I explain to them how I use git. Sometimes I show them the workflow in magit, which makes it easier to visualize things. But mostly I just show them how their intended actions map onto the relevant CLI commands and I tell them to figure out how those map onto their porcelain of choice. I've developed this intuition thanks to magit, but I don't think magit is necessary. This approach seems preferable to me than onboarding new devs onto a new tool that is not the industry standard.

            > This strikes me a lot like the C vs. safer programming language debate all over again.

            I don't see how. Safer programming language address a clear problem in C, with trade-offs (sometimes arguably the trade-offs may not be worth it, and in my experience that's what the debate tends to be about). If jj is a replacement for git it should be clear what problem within git it aims at addressing. If the problem is in the UX, then to me and many others it's not worth the trouble.

            • naasking 1 day ago

              > When I mentor new devs, I explain to them how I use git.

              Now imagine not needing to do that.

              > I don't see how. Safer programming language address a clear problem in C

              Being productive in C means training and experience in avoiding the footguns of C. See above.

        • steveklabnik 1 day ago

          Fundamentally, jj is its own VCS. It's just that it has pluggable backends. So when you use it with the git backend, it functions as a nicer git UI, but it's also not just that, because you can use it without git entirely. (though the major alternative backend is not open source, so while people do, unless you work at Google (or the startup I'm at...) you may personally not be able to.)

          > then it's irrelevant to those of us who have been lucky to find and learn sensible UXs.

          I was never someone who was upset at git's UX. I never found the 'hg is so much nicer' thing compelling. But then, I found that jj is just so much nicer to use, for me, that I haven't used git itself in years at this point. But it's also true that if you like using git, and want to keep using it, that's fine! The wonderful thing about the interop here is that I can use jj, and you can use git, and we're all good.

          > I'm a little curious why the developers would decide to re-implement something from scratch only to end up with an "alternative" that is mostly compatible

          Realistically, with git's dominance, compatibility is the only way that you get people to actually try out your thing. I know I wouldn't have given it a shot unless I could use it with a git repo.

          > or add new features

          I mean, there's two things here: one of which is, jj does have new features. I described the ability for a jj repo to exist in a conflicted state upthread, for example. jj undo is a godsend. But at the same time, at the end of the day, when you're trying to manipulate a graph of changes, there's always some way to end up in the same end state with git, because, well, you're trying to interoperate. So you can sort of handwave away a lot of features with a kind of "well I can do that in git via <totally different means>", and sure, that's true in a sense, but tools affect the way you work. I'm much more effective with jj's model of the world than I was with git's, even though I didn't actively feel that pain until I tried jj.

          > It really wasn't until I learned magit

          Ah, you use magit! So yeah, like, jj is like magit in the sense that it lets you interact with a git repository in a different way than the standard tool. And that's useful. I never would have used magit because I don't use emacs. (and there are some folks trying to do "magit but for jj"...)

          > But the way git works internally seems pretty great to me. Too often, criticism of git conflates the two.

          I agree, in general. I do think that there are still good criticisms to be made, but a lot of it is uninformed. Just how things go.

          • skydhash 1 day ago

            > Ah, you use magit! So yeah, like, jj is like magit in the sense that it lets you interact with a git repository in a different way than the standard tool. And that's useful. I never would have used magit because I don't use emacs.

            I also use magit and I was confused by the "advantages" that jj has over git. The nice thing about magit is that it doesn't hide git. What it does add is easier typing of the flags (using transient), completions of arguments like branch names (using Emacs), DWIM behavior depending on cursor position and region selection (especially for commit hashes). Also it has nice presentation of the information which acts like hubs for all the above.

            I guest jj makes sense if you're using the cli directly. But with magit, every operation is only a few keystrokes. It is to git, what vim is to editing. And I could probably cobble something close with tig or lazygit if I switched from emacs.

acoustics 1 day ago

jj has made me much more comfortable using non-linear DAGs in my trunk-based development workflow. Several changes with the same parent, changes with several different parents, etc.

I used to have a habit of imposing an unnecessary ordering structure on my work product. My stack of changes would look like A -> B -> C -> D, even if the order of B and C was logically interchangeable.

jj makes DAGs easier to work with because of how it handles conflicts and merges. Now I feel empowered to be more expressive and accurate about what a change actually depends on. In turn, this makes review and submission more efficient.

  • tonfa 1 day ago

    Do you use a mega-merge + absorb workflow on top of the faned-out changes?

pheggs 1 day ago

I was already pretty happy with svn to be honest, I dont see myself switching away from the industry standard today for no substantial reason. in my opinion git was only able to change the standard thanks to github and a popular author (i love git and its branching, but I dont think it would have been enough if it was just for that). I personally believe its going to be very difficult for jj to replicate that.

  • newsoftheday 1 day ago

    I like the distributed nature of git but miss how svn worked to some degree. Generally I agree with you, git works, I read the post and didn't see anything that makes me want to change. The small number of jjites who have been inundating comment sections on this site lately feels like the devotees of Rust (a crap language), podman (which has too many operational inconsistencies) and JetBrains IntelliJ ballywhooers who used to preach IntelliJ's features on every programming related site.

  • GuB-42 1 day ago

    In my opinion, svn lost to git because git is just so much better in a distributed environment. To me the improvement between git and svn is much more than the improvement between svn and cvs. In fact, by many aspects, I preferred cvs, but now that we have git, they can both go to hell.

    Now, it is not the only DVCS, there is also Mercurial, which is built on similar principles. It could have become the standard, but I agree with you that the reason it didn't probably has to do with Github and Linux.

    jj isn't fundamentally different from git, it is actually compatible with git, which is a good and a bad thing. Good because it makes transition easy, bad because it is fundamentally the same thing and there is no pressing reason to change.

    But jj definitely look nicer than git, which is not hard, under the hood, git is great, but it is ugly. It started off as a hack and it still shows. I think the common sentiment of "I know git, I don't want to learn a new VCS" mostly tells about how painful the process of learning git was. If you had started off with Mercurial, like I did, I am sure it would feel much smoother, it is just git that is messy. For jj, it looks like they took the best parts of git and mercurial, so hopefully, the transition will be rather painless.

misnome 1 day ago

The problems with jj that led me to abandon are:

- All of everything good about it breaks down the instant you want to share work with the outside world. It's git on the backend! Except there isn't any concept of a remote jj so you have to go through the painful steps of manually naming commits, pushing, pulling, then manually advancing the current working point to match. And in doing so, you lose almost everything that gives it value in the first place - the elegant multi branch handling, anonymous commits, the evolog. Even if you want to work on the same project on two machines your only choice for this is without breaking everything via git is to rsync the folder. Yes, you can write alias to do all this like git. I might as well use git if I can't use the nice features.

- All files automatically committed is great until you accidentally put a secret in an unignored file in the repository folder. And then there is no way to ensure that it's purged (unlike in git) - the community response as far as I can tell is "Don't do this, never put a file that isn't in gitignore".

- And adding to .gitignore fails if you ever want to wind back in history - if you go back before a file was added to .gitignore, then whoops now it isn't ignored, is all part of your immutable history, and disappears if you ever switch off of that new commit state.

  • rstuart4133 19 hours ago

    > Except there isn't any concept of a remote jj so you have to go through the painful steps of manually naming commits, pushing, pulling, then manually advancing the current working point to match.

    All true. I ended up writing my own `jj push` and `jj pull` aliases that automated all this. They aren't simple aliases, but it worked. `jj push` for example "feels" very like `git push --force-with-lease`, except if you've stacked PR's it pushed all branches your on. It hasn't been a problem since.

    I ended up wondering if they deliberately left the `jj pull` and `jj push` commands unimplemented just so you could write something custom.

    > All files automatically committed is great until you accidentally put a secret in an unignored file in the repository folder.

        jj abandon COMMIT && jj git garbage-collect
    

    > And adding to .gitignore fails if you ever want to wind back in history - if you go back before a file was added to .gitignore, then whoops now it isn't ignored

    True, but how is this different to any other VCS?

    • misnome 9 hours ago

      > True, but how is this different to any other VCS?

      What other VCS behaves this way by standard? If it's not in e.g. .gitignore "git status" will show that it's aware of it, but won't automatically absorb them into the commit? https://github.com/jj-vcs/jj/issues/5596 doesn't seem to be the oldest instance of this problem, but does seem to be the current discussion, and there seems to be no consensus on how to fix.

      Oh, and ISTR submodules were a pain to work with.

      Edit: https://github.com/jj-vcs/jj/issues/323#issuecomment-1129016... seems likely to be the earliest mention of this problem

Aperocky 1 day ago

With agents, my go to is now have multiple of the same repository and each agents must work on a separate one.

Preventing dirty workspace by solving the co-work problem to start with. merges are much more trivial than trying to make agents remember which branch or which folder it is supposed to work on. Disk space is cheaper than mental anguish and token usage.

justinmayer 1 day ago

Many folks aren’t aware that there is also an open-source, cross-platform desktop GUI application for Jujutsu called GG: https://github.com/gulbanana/gg

I mention it because while the jj command line interface is excellent, there are certain tasks that I find easier to perform with a graphical user interface. For example, I often want to quickly tap on various revisions and see their diffs. GG makes that kind of repository browsing — and certain squash operations — much more efficient for me.

If you’re interested in more information about GG, my co-host and I talked about it in a recent episode of the Abstractions podcast at about the 8:17 mark: https://shows.arrowloop.com/@abstractions/episodes/052-they-...

  • tcoff91 1 day ago

    jjui TUI is incredible also

shuyang 1 day ago

Has anyone found a good code review workflow with `jj`? My problem is that GitHub doesn't remember the content of the last reviewed SHA, so every time I push a new change from jj, reviewers lose the delta from their last reviewed commit.

To work around this I stopped moving revs (squash/rebase) after review starts, which creates awkward local graphs if I have to merge from main for merge conflicts. Graphite works but it's $$$, and phabricator/reviewable/gerritt all have significant onboarding hurdles.

  • joshka 1 day ago

    The last reviewed sha is generally available on the PR page (not the changes page) when you force push. There should be a changes since review link somewhere near the push.

    When reviewing, you can also mark individual files as reviewed (useful in larger reviews where you're incrementally reviewing files). If you do this, only files that are changed will be expanded when you come back to the review.

    • shuyang 1 day ago

      yeah, this is where my complaint is - github shows a "compare" button when I force push, but it's not linked to the PR review. The "file changed" status is often not granular enough - if I change one line, force push, the entire file gets marked as unreviewed. the github "changes since your last review" is commit-based, not sha-based.

      what I want is something like graphite/gerritt/google's critique where after each force push, the review page shows only the true delta between the two shas (similar to the "compare" button in github, bu as a reviewable unit).

      poked around on github, doesn't look like the stacked PR feature has affected this "changes since your last review" selector yet :(

  • saghm 1 day ago

    I haven't noticed any significant change in my workflow needed to accommodate this, but it might be because I've always used rebase rather than merge. `jj rebase -d main` will put my current branch on top of the main branch, and fixing conflicts in `jj` is a breath of fresh air compared to git.

aubanel 12 hours ago

Does jj work well with parallel agents?

The current problem that I often have is that I want to work on several things in parallel through several agents, always forget to do worktrees, then the different branches of work tend to step on each other Does JJ make it simpler?

  • hi-im-buggy 7 hours ago

    jj supports workspaces, akin to git's worktrees. They've felt pretty natural for me to use.

    The only caveat is that if you're in jj repo that uses git colocation, only your main workspace will have a `.git/` directory. When you have other infra set up that assumes you're in a valid git repo, this can be a little annoying.

qezz 1 day ago

For those, who want to/need to keep some files uncommitted, the workaround I found is to put gitignore into some nested directory:

  mkdir junk
  echo '*' > junk/.gitignore

jj won't track those files under ./junk/

Also might be relevant for claude, since it wants to put its settings into the repo itself as `.claude/`:

  mkdir junk/.claude
  bwrap ... --bind "$(pwd)/junk/.claude" "$(pwd)/.claude" ...

For some more common files, I use global gitignore file as

  # ~/.gitconfig
  [core]
    excludesFile = ~/gitconf/gitignore_global

  # ~/gitconf/gitignore_global
  .envrc
  .direnv/*
  • tcoff91 1 day ago

    I run jj in colocated mode so I put stuff in .git/info/exclude if I want it ignored but not part of the main .gitignore

  • BeetleB 1 day ago

    You can also set snapshot.auto-track to tell it not to track certain files.

    Another option is to make a branch with the files that you want to keep around but not push (e.g. stuff specific to your own tooling/editor/IDE), and mark that branch as private. Private commits (and their descendants) can't be pushed.

    You then make a merge commit with this branch and main, make your changes, etc. You will have to rebase before pushing so that your branch isn't a descendant of the private commit.

    This will involve more work, but it has the benefit that you're actually version controlling your other files.

swoorup 1 day ago

I love jj, but mostly I use jjui.

I would like more uniformity in the way jjui handles commands when you are viewing changes vs when you are viewing files within a single change.

Often I just make my changes and leave it there without `new`, as I am not sure which file should go in a single commit. I just leave it there and I interactively commit later.

For me to use `new` more, I would like the ability to also simply be able to get a tree view of all changes, which contains file which contains file changes, so I could can have marks that span multiple changes and then either `split` or `commit` or `squash` the change, idk if there is a good word for it. Right now I can only mark within a single change, and I lose it once I navigate up.

  • joshka 1 day ago

    I use jj fairly regularly and I'm trying to understand what your approach means, but having difficulty following what you want to acheive here. Seems like you're using ambiguous language that isn't aligned - wdym by marks?

  • gcr 1 day ago

    In jjui, you can select multiple changes to squash/rebase by pressing space bar before pressing S or r. Is that what you mean?

    • swoorup 1 day ago

      yes exactly, pressing space bars to select them. I referred these selection as marks.

  • nchmy 22 hours ago

    yeah, i know almost nothing about jj because i only use it via jjui. Why would I waste brain power learning about and remembering new incantations when I can just browse around with arrow keys and mouse and then use like 9 hotkeys to do everything that I could possibly need?

waynecochran 1 day ago

Anything new or special in jj that allows me to work with large binary files simply? To me, this is still unsolved in terms of providing an elegant solution (e.g. things like Git Large File Storage (Git LFS) are awkward).

  • steveklabnik 1 day ago

    Not yet. It's desired to do something better here, but there's no active development that I'm aware of right now.

    (LFS support is in progress though)

  • brendoncarroll 1 day ago

    I've heard that jj has support for non-git backends? Can anyone comment on how difficult it would be to add support for another backend, any docs or examples?

    I have a project[0] that does the large file thing well, but is missing most of the version control porcelain. I've been looking for the path of least resistance to integrate it into something with a larger user base.

    [0] https://github.com/gotvc/got

    • steveklabnik 1 day ago

      To add a new backend, there's a trait that you implement for your backend: https://github.com/jj-vcs/jj/blob/713a0d0898448392d38fdcbaba...

      I suspect if you came by the jj discord, folks could help you with more detail than that.

      • brendoncarroll 1 day ago

        Thanks for the link.

        It looks like this treats files as blobs just like Git, and trees as single objects which fit in memory. Assuming that is a correct understanding, this core abstraction would need to change to handle large files and directories well.

        All the well known version control systems do this though, and it simplifies the system significantly. It's the right model for source code, but it doesn't translate well to arbitrary data.

        • steveklabnik 1 day ago

          Yes, it will require work to do large files well. But there is general interest in upstream in having that, there's just nobody driving the work at the moment.

TrysterosEmp 1 day ago

Why should you care about jj? Look, ethereal, balaeric Gothenburg indie from the 2010s may not be important in the grand scheme of things, but their strong hip hop influence was genuinely exciting at the time. When the great chill wave summer of 2009 crested, you definitely cared about jj’s otherworldly grooves and lil Wayne samples. Even if they never did reach the euphoric highs of Washed Out or even label mates The Tough Alliance.

tracker1 1 day ago

Seems interesting, but tbf, I only really use a small subset of git commands as it is... I've tended to practice a few workflows that just reduce the overhead and risk for conflicts.

hi-im-buggy 7 hours ago

jj's mental model maps really well to Gerrit's code review system. It's also really nice to have `jj undo` and automatic rebasing.

theappsecguy 1 day ago

I'm still struggling most with the fact that my day-to-day work involves a git first platform like GitHub.

Although jj as a vcs system, it does feel better, working with git through it still feels like a chore, but to be fair I only gave it a day before going back to git.

Does anyone have any good resources on how to augment a git flow through the lens of a git hosting platform to work smoothly and still reap the benefits of jj?

  • r5Khe 1 day ago

    I'm not sure what the hangup is, TBH. I use `jj` with GitHub and ADO every day, and it works great.

    What specific challenges are you running into that make it feel like a chore?

    • theappsecguy 1 day ago

      Hmm, it was a while back so now I'm struggling to recall, but I remember feeling like I'm going against the grain of easily using GitHub. I followed this exact tutorial at the time and it looks like there are now sections on how to work with GitHub.

      Perhaps I need to force myself to commit for longer...

rattray 1 day ago

Has anyone tried both jj and gitbutler extensively yet? Both seem to have some interesting new ideas on top of git.

  • ymir_e 23 hours ago

    I was hoping someone else had written about it here.

    From my knowledge there are three different takes on git being worked on which looked interesting. - JJ - GitButler - Zed

    Zed version system doesn't have that much public info yet, but they wanted to build a db for storing code versions for AI agents. Not sure if this is still the direction, and I'm a bit skeptical, but interested to see what they come up with.

    Even though git works well enough, I'm certain there will be another preferred way at some point in the future. There are aspects of git that are simply not intuitive, and the CLI itself is not up to standard of today's DX.

rob74 1 day ago

Still not finished unfortunately :( Guess Steve is currently busy writing the next big thing in programming languages (https://steveklabnik.com/writing/thirteen-years-of-rust-and-...) ?

  • BeetleB 1 day ago

    To be honest, while Steve's tutorial was what got me interested in jj, other tutorials were better in actually helping me understand it.

    • steveklabnik 1 day ago

      This is why I'm glad we have many of them! Not everyone is going to resonate with my writing style.

      • BeetleB 1 day ago

        Yes, but it's a sad state of affairs that the official jj docs point to your tutorial, which is incomplete (and IIRC, more incomplete than in the past - I think you took down some topics).

        • steveklabnik 1 day ago

          Nope, I haven't taken anything down. I have merged in some contributions from others though, it's actually grown (Manish, iirc, contributed the Gerrit section).

          • BeetleB 1 day ago

            Oh, OK. Must be a bad memory. I often go to your tutorial looking for something I could have sworn I read over a year ago and not find it. I must have read it elsewhere.

  • steveklabnik 1 day ago

    Nope, I have had zero time for personal projects lately, Rue is on the backburner until I do.

    I've been busy at https://ersc.io/ (and spending time with my family, and playing Marathon...)

ferfumarma 1 day ago

Can jj iterate through a list of repositories and clone them all to local storage?

It isn't very hard to make a bash script to do it, but I have about six github repos, all of which frequently need to be put on a new machine. that kind of functionality would be cool to have out of the box.

  • hosteur 1 day ago
        for url in url1 url2 ..; do git clone $url; done
    

    That’s not really a script but a basic one liner.

    • Filligree 1 day ago

      No, but to be honest, why would you want it to? That's... well, you already showed the trivial one-liner.

  • saghm 1 day ago

    Are you accessing these boxes via ssh or using them directly? If it's via ssh, I'd expect that you would already be using the clipboard for copying the names of them rather than typing them out manually, at which point copying `git clone <a> && git clone <b> && ...` would achieve the same thing.

enbugger 1 day ago

1) Are there any benefits in replacing a personal private project git repo with jj? I usually just commit straight to master in order to just not to lose the changes and be able to roll back. 2) Are there any pros in doing so in a project with many large binary files (3d models, images)?

  • Jenk 1 day ago

    You don't replace. jj is backed by git anyway.

    • gcr 1 day ago

      You can switch an existing git repo to jj by using:

      jj git init --git-repo my-repo

      I think (but CANNOT PROMISE) that just removing the .jj folder will bring you back, but definitely take a backup of .git before you try this in case I’m wrong.

      • Jenk 1 day ago

        No that is correct when in colocate mode (which is the default mode). Simply removing the .jj folder will "de-jj" the repo entirely, but will leave you in a headless state. Simple to fix with a `git switch` though.

        If you are _not_ in colocate mode, the .git folder is located _inside_ the .jj folder. So worth checking!

    • ncphillips 1 day ago

      Okay but why would they use jj when they do trunk-based dev

      • Jenk 1 day ago

        I do trunk based dev. My colleagues prefer git. I still prefer to use jj.

alunchbox 1 day ago

So glad to see this on HN, here to support it. JJ is amazing, the hardest hurdle was not the tool but the toolchain and ecosystem when I started ~ 2 years ago. It's grown rapidly and is incredible to see the community grow!

0xnadr 1 day ago

Interesting to see more Rust CLI tools gaining traction. The undo-everything model is compelling. I've lost work to bad git rebases more times than I'd like to admit.

mi_lk 1 day ago

Please update the "Stacked PRs" workflow article Steve...

PUSH_AX 1 day ago

If it ain't broke...

  • surajrmal 1 day ago

    I think there are a lot of things that work well but still get improved versions. For example, grep works well but there are plenty of better versions these days.

    • newsoftheday 1 day ago

      Really? I use grep multiple times per day, works great, I've seen no need to change. Same with git.

  • BeetleB 1 day ago

    As a Mercurial user, git was broken from day 1 :-)

QuiDortDine 1 day ago

> $ cargo install jj-cli@0.23.0 --locked

I won't install Rust just to test your software. Make a debian package like everyone else.

  • harg 1 day ago

    I believe the full docs page does indicate that there are binaries to install via popular package managers [1]

    [1]: https://docs.jj-vcs.dev/latest/install-and-setup/

    • QuiDortDine 1 day ago

      I did check that page, as far as I can tell you still need to run Cargo which I don't want to do because I don't care about Rust.

      I'm not complaining for the sake of complaining, I'm saying if they want to play in the Big Boy leagues, they need to do things right.

      • steveklabnik 1 day ago

        You do need Cargo to build from source.

        If you're on Arch, gentoo, or openSUSE, you can use the package. It is true that Debian has not packaged jj yet.

        It'll get there, and it's fine if you'd rather wait until things are more mature.

        • QuiDortDine 23 hours ago

          Thanks! I hope I didn't come off as too dismissive, I'm hearing a lot of good things about Jujutsu. As a developer though, I've never wanted to build from source (probably in the minority on that front).

          • steveklabnik 23 hours ago

            Nah, you're right that installing a compiler toolchain to build a project is a pain in the butt if you don't already have it. It's a legitimate thing, but it does mean that you won't be adopting more cutting edge tools, which is also just fine! I've done the same with projects built with tools I don't have installed too.

  • Philpax 1 day ago

    Immediately after that line:

    > If you're not a Rust developer, please read the documentation to figure out how to install things on your platform

    Rather selective reading we have here, don't we?

    • QuiDortDine 1 day ago

      I did! No apt install jujutsu. I also did 'apt search jujutsu'.

      Don't ask me to care about yet another language's package manager, I already know way more than I wish to.

octocop 1 day ago

Nobody is asking for a git replacement? I keep seeing these posts and I don't know who wants them.

  • SatvikBeri 1 day ago

    jj has almost 30,000 stars on github. You might not be looking for a different git ux, but plenty of people are!

  • vaylian 1 day ago

    You could have said the same thing about subversion.

    Subversion is a fine VCS. But git offers a better approach with being offline-first and decentralized. It also makes merging branches a lot easier.

    I don't know enough about jj to praise it, but I don't think git will be the last VCS that will become widely popular.

  • steveklabnik 1 day ago

    I wasn't asking. I loved git.

    But then after trying jj, I wrote this tutorial because I love it even more.

  • __turbobrew__ 1 day ago

    The feeling I get with jj is it is almost like people are trying to convince others why jj is superior instead of just sharing that jj exists and let others decide themselves. It seems like every few months there is a jj post on HN which devolves into the narratives of “git works for me” and “but jj is better at X”.

    • ncphillips 1 day ago

      Where X = something I’ve never wanted to do

    • chriswarbo 1 day ago

      Exactly the same thing happened when git showed up (alongside the same things for bzr, darcs, hg, etc. too!)

  • klauserc 1 day ago

    I definitely am. Haven't touched git in over a year. If there was just a single feature to point at where jj meaningfully improves on git, I think it's: `jj undo`.

    It is a universal undo command. It works for every change in your repository. You don't need to memorize/google/ask claude how to revert each individual kind of operation (commit, rebase, delete branch, etc.). You try a jj command, look at your repo, and if you don't like what you see, you `jj undo`.

    The biggest downside for me is that no longer have the necessary expertise to help coworkers who get themselves into trouble with git.

  • newsoftheday 1 day ago

    Agreed and not only that but for any comment on this page which raises a valid exception; there is a cadre of jj devotees ready to counter them if and where they think they can. It feels like someone had a some financial state in brainwashing the world to leave git for jj.

  • baq 1 day ago

    I'm asking for a github replacement, alas it might come from github itself with stacked PRs.

    git is good, but jj is good, too. nobody asked for a better CVS either, until someone did.

    • andrewdea 1 day ago

      are you conflating github and git? I'm just confused as to how jj is a replacement to github

      • baq 1 day ago

        I'm not, github sucks, git is ok. Stacked PRs are what jj makes easy but is completely broken in github and as of today they're releasing a 'stacked prs' product which only makes jj better value when working with github vs git.

  • crazygringo 21 hours ago

    Yes they are. I am. Many other people are too.

    git was a great step forwards, but its conceptual model just doesn't map well to a lot of workflows, and some very simple things are very difficult or impossible with it. It was designed using a certain set of assumptions and primitives, and other assumptions and primitives turn out to be much more suitable for certain workflows.

    I don't know if jj is the perfect answer, but it's a huge step forwards in many ways.

eranation 1 day ago

For those who switched, what is the main selling point that made you stay with Jujutsu?

  • tonfa 1 day ago

    For me, can do anything complex in terms of history rewriting without ever shooting myself in the foot (worse case just `jj undo`).

    And the UI is pretty intuitive so don't really have to search for a solution, usually the command to use is obvious (tho I guess now you could delegate the UI to an LLM to ask it to give you the right incantations).

wolttam 1 day ago

What happens when jj introduces this native repository format - break compatibility with all the popular git hosts?

If jj is so great now and works with git as a backend, it’s tough to imagine why it’s worth pursuing a native and presumably incompatible backend.

  • steveklabnik 1 day ago

    Nothing will break. You just keep using the git backend if you want to keep being compatible with git.

    > it’s tough to imagine why it’s worth pursuing a native and presumably incompatible backend.

    Well, there's no active work on a "native" backend. There are basically three backends right now:

    1. the git backend

    2. A simple backend used for tests, you can think of it almost like a mock backend, you wouldn't use it for real work, but it's still useful as part of the test suite

    3. the piper backend at google

    There's not a lot of reason for anyone to produce another open source "native" backend, because 99% of open source projects use git.

    • wolttam 1 day ago

      That makes sense, thanks for the reply. For some reason I was under the impression that there was an active drive towards a backend that was not git.

      • steveklabnik 1 day ago

        You're welcome. I think it's natural to assume that.

andrepd 1 day ago

Is there a concise introduction / overview of jj? I've read 8 pages of this link and the author is still in preambles and considerations... Not my favourite style of article!

  • SAI_Peregrinus 1 day ago

    You have a DAG of "changes" to the repo state. Each change has a stable ID, you can modify its contents or description without changing the ID. There's always a "current" change checked out, JJ automatically snapshots your edits to files into this change. JJ provides tools to edit changes, describe them, group them into named branches (bookmarks), reorder them, split them apart, etc.

    JJ defaults to being backed by git. Each change has a corresponding git commit. When you edit the contents of a change, jj makes a new git commit & points the change at that new git commit. JJ is never actually amending or editing git commits, it's using git as a content-addressed data store.

    That's the mental model. It's like git with a lot of accidental complexity (staging area, stashes, commit ID instability) removed.

    There are a few ways you can work with this model. I like the following:

    When I want to start new work, I fetch any changes from upstream `jj git fetch`, then make a new change after the current `main` or `master` bookmark: `jj new main`. Then I start writing code. When I want to commit it, I type `jj commit` and write a description. If I find I want to make an edit to a previous change, I edit my working copy and interactively squash to that change ID with `jj squash -i -r <change_id>`. When I'm ready to push those changes, I name the branch HEAD with `jj bookmark create`, then push it with `jj git push -b <bookmark_name>`. If there are review comments I squash edits into the appropriate changes or add new changes, and move the bookmark to the new head with `jj bookmark move`. If I want to merge two (or more) branches, I use `jj new <branch_1_name> <branch_2_name> <...>` to make a new commit with those branch names as parents. If I want to rebase some changes I use `jj rebase`. JJ doesn't care about conflicts, I fix them after a rebase has completed, instead of in the middle.

    • jampekka 1 day ago

      That sounds a bit faffy. In solo or small team work git is often git pull, edit code, git commit -a, git push.

      With jj you have to fetch, start new space off a bookmark, edit code, commit it, update the bookmark and finally push?

      • SAI_Peregrinus 19 hours ago

        Git pull equivalent is `jj git fetch`

        Git `checkout -b` equivalent is `jj bookmark create`

        `git commit -a` equivalent is `jj commit`

        Git push equivalent is `jj git push`

        No more faff than git, I just prefer to do things in a slightly different order. I don't usually name the branch (`git checkout -b` equivalent) until after I'm done making changes & ready for a PR. I prefer to make changes, try things out, get it working, and then rearrange the commits to have a clean history to make my reviewer's lives easier. With `git` that's an interactive rebase, which tends to require a lot of faffing about.

        JJ doesn't require branches to have names. By default it just leaves bookmarks (names of branches, roughly) where they are when new changes are made, but that's easily configured. You can even have it automatically create names when you push.

  • steveklabnik 1 day ago

    I can be a bit verbose, it's true :)

    You've got some decent replies, but if you give me some background, like how comfortable you are with git, how much you care about certain details, I'd be happy to respond here with something more concise.

jancsika 1 day ago

I was going to write a big long comment, but honestly it boils down to this:

Whatever git's practical benefits over SVN and CVS back in the day (and I can go into the weeds as a user if someone wants that), git was the DVCS that took over from the centralized VCS's of that era.

There is nothing in jj, pijul, or Bram Cohen's thing that is anywhere near as dramatic a quality of life improvement as going from VCS to any DVCS. And dramatic improvement is what is needed to unseat git as the standard DVCS.

I mean, if you're not doing something so important[1] that it adds a letter to the acronym, it's probably not the next new thing in version control.

1: I originally wrote the word "novel" here. But it has to be big-- something like guaranteeing supply chain integrity. (No clue if a DVCS can even do that, but that's the level of capability that's needed for people to even consider switching from git to something else.)

  • rstuart4133 18 hours ago

    There is some truth to that, but to give a different perspective ...

    A long, long time ago, back when VCS's were novel enough to be of academic interest, I read numerous papers describing what these VCS's could be. They talked in terms of change sets, and applying them like a calculus to source code. In the meantime those of us writing code people actually used were using sccs and rcs, or if you were really unlucky Visual SourceSafe. To us in the trenches those academics were dreamers in ivory towers.

    With the passage of time we got new VCS that gradually knocked the rough edges off thoase early ones. svn gave us atomic commits, hg gave us a distributed VCS, git gave us that plus speed. But none came close to realising the dreams of those early academics. It seemed like it was an impossible dream. But then along came jj ... and those dreams were realised.

bikelang 1 day ago

Reading threads like this and the GitHub stacked PRs just makes me feel like an alien. Am I the only one that thinks that commits are a pointless unit of change?

To me - the PR is the product of output I care about. The discussion in the review is infinitely more important than a description of a single change in a whole series of changes. At no point are we going to ship a partial piece of my work - we’re going to ship the result of the PR once accepted.

I just squash merge everything now. When I do git archeology - I get a nice link to the PR and I can see the entire set of changes it introduced with the full context. A commit - at best - lets me undo some change while I’m actively developing. But even then it’s often easier to just change the code back and commit that.

  • steveklabnik 1 day ago

    You're not an alien: this is the workflow that GitHub encourages.

    It's just that not every tool is GitHub. Other systems, like Gerrit, don't use the PR as the unit of change: they use the commit itself. And you do regularly ship individual commits. Instead of squashing at the end, you squash during development.

    • bikelang 1 day ago

      Thanks for explaining that. Having a bit of a (dim) lightbulb moment now. I’ve never used Gerrit - just GitHub and GitLab and Forgejo. So I assumed the PR/MR model was more or less universal. But if smaller development commits are being squashed into the shippable/reviewable unit - then the focus on commits makes a lot more sense.

      • steveklabnik 1 day ago

        You're welcome! It is one of those "do fish realize they're wet" kind of things, tools can shape our perception so strongly that you don't even realize that they're doing it!

  • rs545837 1 day ago

    You could agree that the PR is the meaningful unit for shipping, but push back gently that for agents working in parallel, the commit/changeset level matters more than it used to because agents don't coordinate the way humans do. Multiple agents touching the same repo need finer-grained units of change than "the whole PR."

    • bikelang 1 day ago

      Could you elaborate a bit more on this? Curious what your workflow looks like. Is this multiple agents running on the same feature/refactor/whatever unit of work? For concurrent but divergent work I just use a git worktree per feature. And I think I only ever have a single agent (with whatever subagents it spins up) per unit of work.

      • rs545837 15 hours ago

        Think two agents working on the same codebase at the same time. Agent A is refactoring the auth module, Agent B is adding a new API endpoint that imports from auth. Separate worktrees, separate branches, but they're touching overlapping code.

        ingle agent per feature works great today. But as agents get faster and cheaper, the bottleneck shifts to, how many agents can work on one repo simultaneously without stepping on each other.

randyrand 1 day ago

jj sounds awesome. I think I’ll give it a shot.

But I found this article a bit long winded and ended up asking an LLM about it instead.

LoganDark 12 hours ago

I tried jj and holy wow it's so perfect for me. There's a learning curve but I picked a bunch of it up within a couple hours and I am going to be pretty much using only jj from now on.

palata 1 day ago

I tried jj for a few months. It was fun to learn a new thing, but I haven't had a single case of "wow, this would have been a pain with git". Then I went back to git (it's been 6 months now) and I haven't had a single case of "this is so painful, I wish something better existed".

So it felt like the XKCD on "standards": I now have one versioning system, if I learn jj I will have two. What for?

Don't get me wrong: it's nice that jj exists and some people seem to love it. But I don't see a need for myself. Just like people seem to love Meson, but the consequence for me is that instead of dealing with CMake and Autotools, I now have to deal with CMake, Autotools and Meson.

EDIT: no need to downvote me: I tried jj and it is nice. I am just saying that from my point of view, it is not worth switching for me. I am not saying that you should not switch, though you probably should not try to force me to switch, that's all.

  • naasking 1 day ago

    > Then I went back to git (it's been 6 months now) and I haven't had a single case of "this is so painful, I wish something better existed".

    The core issues are: how long did it take you to get there, how many lucky decisions did you have to make to not run into git footguns, and how many other people accidentally made different choices and so have very different experiences from you?

    • palata 1 day ago

      What you're saying is that other people may find jj easier for them, right?

      I am fine with that. I am just saying that the "you should use jj, you will finally stop shooting yourself in the foot regularly" doesn't work so well for me, because I don't remember shooting myself in the foot with git.

  • BeetleB 1 day ago

    For me, it was kind of the same. I used jj. Really liked it, but did not find it all that much better than git.

    Then, for various reasons, I switched back to git.

    By day 2, I was missing jj.

    Stuff like "jj undo" really is nice.

nailer 1 day ago

JJ might be good (this article couldn't convey why in the "What is jj and why should I care?" page) but it's not 10x better than git, so it will likely die. Sorry, nothing personal, Mercurial/hg was a little bit better than git and died too. Network effects.

What has a change is ast-based version control.

You adding a feature to a function that uses a struct I renamed shouldn't be a conflict. Those actions don't confliuct with each other, unless you treat code as text - rather than a representation of the logic.

Ending merge conflicts might make a new version control 10x better than git, and therefore actually replace it.

  • saghm 1 day ago

    > JJ might be good (this article couldn't convey why in the "What is jj and why should I care?" page) but it's not 10x better than git, so it will likely die. Sorry, nothing personal, Mercurial/hg was a little bit better than git and died too. Network effects.

    The difference is that I can (and do) use `jj` with existing git repos today without needing anyone else using the repo to change what they're doing. There's no need to replace something when it can exist alongside it indefinitely.

    • cageface 1 day ago

      Unless you need to work with a repo that uses submodules or lfs.

  • Nuzzerino 1 day ago

    Did you confirm that the network effects are applicable here before posting that?

    • nailer 1 day ago

      Nope. As mentioned "What is jj and why should I care?" didn't cover it and I timeboxed jj advocacy to the page that says it will get me to care.

lofaszvanitt 16 hours ago

Someone add attribute and rights handling to git. Plus empty dirs. And thats all.

carverauto 1 day ago

am a big fan, just started using it a few days ago

jtrueb 1 day ago

Call me crazy, but jj is more confusing than git.

sam_goody 1 day ago

I use the CLI often enough, but still most of my time is in a GUI. It just makes the diffs easier, the flow simpler, etc.

As such, I wanted to break into jj via the GUI, and only adopt the command line after I could visualize the concepts and differences in my head.

Alas, the GUI I tried - a VSCode plugin - did more to confuse me than to help, and it made it very easy to catastrophically rewrite the history by accident. I tried another UI and it kept failing and leaving me with cleanup work. I couldn't find a third UI that looked promising.

So, I gave up. One less jj user on the planet - no biggie. But I really wonder if it would be worth the effort for some of the jj pushers to try to get a proper UI in place - I bet I am not the only one that likes to learn visually.

ai_slop_hater 1 day ago

Unfortunately OpenAI's models really suck at JJ, while, on the other hand, Opus 4.6 is much better at it, probably due to a later training cut off.

kristo 1 day ago

I followed steve's excellent tutorial about two months ago, and haven't looked back. I have never felt so competent at git (vcs rather) as I do now. jj is so much simpler and easier for me to reason about.

dhruv3006 1 day ago

this looks pretty interesting.

scotty79 1 day ago

Does jj have partial clones of remote repo?

  • steveklabnik 1 day ago

    jj does not have partial clones, but it does have sparse checkouts.

    • scotty79 1 day ago

      Thank you for the info.

      Sparse checkouts from remote repo directly or do I need to clone it in whole locally and only then can do a sparse checkouts into some work directory?

      • steveklabnik 1 day ago

        I'm 99% sure it's "clone the whole locally and then sparse checkout from there".

tucnak 1 day ago

16 year-old me would have been very impressed by this!

  • auggierose 1 day ago

    Is that a compliment, or the opposite?

    • tucnak 1 day ago

      A bit of both. I guess it's nice, but nothing I actually care about.

      • xtracto 1 day ago

        I had a similar thought: there surely are lots of young folks who will be all excited with this (I was back in the CVS/SVN days when git appeared).

        But nowadays I'm extremely lazy to attempt to learn this new thing. Git works, I kind of know it and I understand its flow.

        • nithril 1 day ago

          Same here, I’m not experiencing so much friction to justify looking for an alternative

        • cestith 1 day ago

          We still have some repos in Subversion and most things in git. It’s still exciting for every repo we get migrated out of svn. That’s a high bar to cross if we’re talking further improvements compared to git though.

        • steveklabnik 1 day ago

          I was 38 or 39 when I found jj.

          • hacker161 1 day ago

            See? You weren’t even over 50! Young whippersnappers…

SV_BubbleTime 1 day ago

>You can request to not use the pager by using jj st --no-pager, or if you hate the pager and want to turn it off, you can configure that with

    $ jj config set --user ui.paginate never

In one feature they can’t help themselves from calling it two different things already.

Why do this? Why can’t the very clearly smart people making things step 1/2 step outside themselves and think about it like they are the users they want?

Earlier they talk about the native format and how it isn’t ready… so that to start you need

    jj git init

… but… if they’re planning a native format that makes no sense as a command. It would be ‘jj native init’ later?

Early planning keys/plans imo but those rarely change so as to not accept your early adopters.

These seem like small things but to me it’s a warning.

  • Philpax 1 day ago

    1. Pagination with a pager is a reasonable default. See `git log`.

    2. The native format would be `jj init`. For precedent, see how uv dealt with its pip compatibility: `uv pip install` was obsoleted by `uv add`.

    • SV_BubbleTime 1 day ago

      1. No one with good vision would give a single feature two names. It’s dumb. Here is our pager feature. Cool, how do I access it? Oh you set the ui.paginate options of course!!

      2. It’s almost like we have some established ways to denote arguments that are pretty popular… ‘jj init —-git’ for example? By using ‘jj git init’ I would expect all of the git compatible commands to be be ‘jj git xxx’ because that is a reasonable expectation.

      This is a problem with the voodoo. These obscure nonsense commands only makes sense when you are accustomed to them. If there’s no reasonable expectation that you could just figure it out on your own. Go on vacation and come back and be surprised when you forget the voodoo. Not to mention that every tool has to have its own unique voodoo.

      Almost like the professional world has figured out that made by software engineers for software engineers will never be popular. And then engineers don’t understand the effects of why you might want tool to be intuitive and popular.

      • steveklabnik 1 day ago

        You're right that, looking solely at `init`, a flag could make sense to choose the backend.

        The bigger picture here though: `jj git` is the subcommand that prefixes all commands that are git specific, rather than being backend agnostic. There is also `jj git clone`, `jj git fetch`, `jj git push`, etc.

        For a different backend, say Google's piper backend, there's `jj piper <whatever>`.

        This means that backend specific features aren't polluting the interface of more general features.

        • SV_BubbleTime 1 day ago

          >There is also `jj git clone`, `jj git fetch`, `jj git push`, etc.

          If the compatibility isn’t automatic… why would I bother with jj commands here at all? “Git with extra steps”

          • steveklabnik 1 day ago

            The on-disk repository compatibility is automatic. But if you're trying to fetch something via a specific protocol, you use the command for the protocol you want to use.

            There is no extra step between `git push` and `jj git push`, they're both one step.

            • SV_BubbleTime 1 day ago

              I meant the extra step being why would I bother with jj if I’m having to specific gut inside of jj?

              The issue is pretty obvious to me. GIT is the standard and that likely won’t change for some time. So if jj makes my git life better, awesome, but it’s just a wrapper and I need to know all the git voodoo now with jj voodoo on top, I don’t quite get it.

              • steveklabnik 1 day ago

                If you're happy with git, you should keep using it.

fallat 1 day ago

Now that Steve is part of a GitHub competitor to push jj, I see all these posts as just sales pitches.

  • Philpax 1 day ago

    This tutorial predates his involvement with ERSC.

  • steveklabnik 1 day ago

    I am quite happy for anyone to use whatever tools they find to be good. I'm also happy for anyone to use jj with whatever server they want to.

    It is right to be skeptical of me, but I hope to keep that integrity by continuing to talk about things that I believe are legitimately good, regardless of anything else.

    • fallat 1 day ago

      Thanks for the reply Steve - I think it's only natural, and charitably unintentional :) But almost everything I've seen upvoted around the Internet from you has been about jj and being directly tied to east river source control... I think that's a reasonable framing. I can only hope me signalling this maybe changes something. While I'm not a fan of jj (I'd much rather Pijul were to eat the world), I think you as a person is really nice and always have been "for the community", but I can't shake this current framing!

      • steveklabnik 1 day ago

        I mean, most people seem to think I solely post about AI these days, so it’s kinda funny to run into someone that feels otherwise!

        I’m not sure why I’d stop posting about a project I’ve been passionate about for years, just because my job is adjacent to it.

        • fallat 22 hours ago

          You can't say it's adjacent to it, when your job directly involves the technology. You'd stop posting because anything you say about jj could be interpreted as a sales pitch for jj, and a lot of people can be turned off by that. That's one reason. Our lack of creativity is not proof of no more.

          But I don't think "stop writing" is the only strategy to jump on...

          Or maybe I'm just extremely unlucky to have only caught these kinds of posts and gained this framing! Totally possible.

newsoftheday 1 day ago

OK I read it, I'm not interested, git does exactly what I want.

  • tombert 1 day ago

    That's sort of where I've been stuck with with jj.

    Maybe someone can convince me otherwise, but to me it hasn't felt sufficiently better than git to justify bothering re-learning this stuff, even if it's relatively easy.

  • plutokras 1 day ago

    Git wins by not needing to be replaced badly enough. Latley you can just ask an agent to "amend the last commit" so even that is being abstracted away.

jansan 1 day ago

We all need to give ourselves a push and finally make the next step in version control. Github, Google, Microsoft, Meta (did I forget anyone relevant? Probably) should just join forces and finally make it happen, which should not be a problem with a new system that is backend compatible to Git. Sure, Github may lose some appeal to their brand name, but hey, this is actually for making the world a better place.

  • ramblerman 1 day ago

    The new solution is better. It’s so good we must get all the big players to mandate its usage.

    If ur making an appeal on a forum like this u could have gone with ur favorite feature, or anything else really.

    • jansan 1 day ago

      It is not about starting over, like moving from CVS or Subversion to Git. jj is backend compatible to Git, so nothing really had to change on the backend.

      It's just that although Git was created by Linus Torvalds it is not perfect and could be more beginner friendly. But efforts to improve this should be concerted, not individual efforts.

      And it does not have to be jj. I just think there is room for improvement, and not to alienate old farts it could be called GitNext, GitStep, GitFlow or similar to emphasize that is still is just Git, only with an improved front end.

      Maybe Linus Torvalds himself should start the initiative.

      • childintime 1 day ago

        Linus? Too tired of the open source community to risk having to deal with it more. Hasn't released anything since 2005, he just drifts on the waves. So sad he doesn't see the human energy wasted on his projects, and doesn't move them into the modern era, where compatibility with the past can be dropped in favor of a much tighter feature set, while also coming free of C/C++. In short: don't count on Linus, he's been a coward, he's too comfortable leading from the back.

  • SOLAR_FIELDS 1 day ago

    Every time I see a statement like this I wonder what specific features of git that people feel like are terrible enough that it’s time to completely start over. Besides “the UX is kinda shit and it’s confusing to learn”, which there are many solutions for already that don’t involve reinventing a pretty good wheel.

    • seanhunter 1 day ago

      Right.

      How we got git was cvs was totally terrible[1], so Linus refused to use it. Larry McEvoy persuaded Linus to use Bitkeeper for the Linux kernel development effort. After trying Bitkeeper for a while, Linus did the thing of writing v0 of git in a weekend in a response to what he saw as the shortcomings of Bitkeeper for his workflow.[2]

      But the point is there had already been vcs that saw wide adoption, serious attempts to address shortcomings in those (perforce and bitkeeper in particular) and then git was created to address specific shortcomings in those systems.

      It wasn't born out of just a general "I wish there was something easier than rebase" whine or a desire to create the next thing. I haven't seen anything that comes close to being compelling in that respect. jj comes into that bucket for me. It looks "fine". Like if I was forced to use it I wouldn't complain. It doesn't look materially better than git in any way whatsoever though, and articles like this which say "it has no index" make me respond with "Like ok whatever bro". It really makes no practical difference to me whether the VCS has an index.

      [1] I speak as someone who maintained a CVS repo with nearly 700 active developers and >20mm lines of code. When someone made a mistake and you had to go in and edit the repo files in binary format it was genuinely terrifying.

      [2] In a cave. From a box of scraps. You get the idea.

      • bombcar 1 day ago

        To be fair the "shortcomings" that spurred it on mainly were the Samba guys (or just one) reverse-engineering Bitkeeper causing the kernel free license getting pulled, which caused Linus to say "I can build my own with blackjack and pre-commit hooks" and then he did, addressing it toward his exact use case.

        It gained tons of popularity mainly because of Linus being behind it; similar projects already existed when it was released.

        • xtracto 1 day ago

          Mercurial was there, was better and more complete.

          Too sad it didnt win the VCS wars.

          • seanhunter 1 day ago

            When I tried both at that time hg was just really slow so I just adopted git for all my personal projects because it was fast and a lot better than cvs. I imagine others were the same.

            • bombcar 1 day ago

              I went with bzr mainly because it had an easy way to plugin "revision" into my documents in a way I could understand and monotonously increment.

              hg was slow though I don't know how bzr compared as I was using it pretty light-weight.

    • mhh__ 1 day ago

      Git is basically fine even though the verbs are backwards - e.g. you shouldn't need to name branches, commits should be far more automatic, but the basic mechanisms are fine.

      GitHub is an abomination.

      • dieortin 1 day ago

        You might already be aware, but jj fixes exactly those complaints you have with git

    • bluGill 1 day ago

      Coming from mercurial (which is older than git), git doesn't understand a branch. Instead of a branch you get a tag that moves, which is very different. Too often I'm trying to figure out where something came in, and but there is just a series of commits with no information of which commits are related. Git then developed the squash+rebase workflow which softof gets around this, but it makes commit longer (bad), and loses the real history of what happened.

      Git was not the first DVCS, there were better ones even when it was made. But Linus pushed git and people followed like sheep.

      (I'm using git, both because everyone else is, and also because github exists - turns out nobody even wants a DVCS, they want a central version control system with the warts of SVN fixed).

      • jcranmer 1 day ago

        > Coming from mercurial (which is older than git)

        Git is older than mercurial by 12 days. Bazaar has git beat by about the same amount of time. The major DVCSes all came out within a month of each other.

        > But Linus pushed git and people followed like sheep.

        I don't think this is true. Until around 2010-2011 or so, projects moving to DVCS seemed to pick up not git but mercurial. The main impetus I think was not Linux choosing git but the collapse of alternate code hosting places other than GitHub, which essentially forced git.

        • sevenseacat 1 day ago

          way way back in the day I did some digging into all three - and picked bazaar for my personal projects. that didn't last long lol

      • em-bee 1 day ago

        the lack of a proper branch history is also the main pain point for me. but i disagree that noone wants a DCVS. having a full copy of the history locally, and being able to clone from any repo to anywhere else and even merge repos (without merging branches) is a major win for me.

    • l72 1 day ago

      If git would change two defaults, that would make me really happy:

        1. git merge ONLY does merges (no fast forward/rebase). git pull ONLY does a fast forward
        2. git log by default is git log --first-parent. Just show commits where the parent is the current branch. This makes the merge workflow really easy to understand an linear, because in the end, you only care about commits on the trunk.
ux266478 1 day ago

Clicked on this hoping it would be the irc client, very disappointed!

For those in the know, how does jujutsu stack up to something like Darcs?

7e 1 day ago

Is it better for AIs? That’s the only reason I would care.

  • VMG 1 day ago

    I've had mixed results.

    Most models don't have a 100% correct CLI usage and either hallucinate or use some deprecated patterns.

    However `jj undo` and the jj architecture generally make it difficult for agents to screw something up in a way that cannot be recovered.

    • glasner 1 day ago

      I've gone all in on jj with a OSS framework I'm building. With just a little extra context, the agents have been amazingly adapt at slicing and dicing with jj. Gives them a place to play without stomping on normal git processes.

  • joshka 1 day ago

    The cli and a few concepts have evolved with time past the model's knowledge cutoff dates, so you have to steer things a bit with skills and telling it to use --help a bit more regularly.

    I find it reasonably good with lots of tweaking over time. (With any agent - ask it to do a retrospective on the tool use and find ways to avoid pain points when you hit problems and add that to your skill/local agents.md).

    I expect git has a lot more historical information about how to fix random problems with source control errors. JJ is better at the actual tasks, but the models don't have as much in their training data.

aftbit 1 day ago

Nope, git is good enough, and is the global standard. We don't need more new VCS.

  • cryptos 1 day ago

    Might be true, but Subversion was also good enough and a de-facto standard.

  • porksoda 1 day ago

    Great opinion. Have you tried it? It takes just 30 minutes to wash all the Stockholm syndrome off of you.

    • homebrewer 1 day ago

      Not necessarily. I used jj for a couple of weeks and found it to be a complete waste of time.

      For an advanced user, it did not offer anything I cannot quickly solve in git. Which is probably the wrong thing to optimize in the first place, because even though I frequently rewrite history and split commits during normal worklfow, it takes so little time that improving something else would yield greater returns.

      We (not royal we) don't usually go out of our way repeating negative experiences with these tools, so you build a very skewed view of their adoption.

    • newsoftheday 1 day ago

      Their opinion is great, why do you feel like you need to counter it with [but, but jj is for the clean masses, not the unclean users].

butlike 1 day ago

FWIW, it's a pretty decent fried fish chain in Chicago as well.

  • cestith 1 day ago

    And chicken. And they have a few other things like Italian beef.

    They’re branching out, too. We had one in our neighborhood in Houston before moving back here to Illinois.

demorro 1 day ago

This doesn't seem different enough to be worth the transitional cost, even if you don't need to actually move away from a git backend.

  • maleldil 1 day ago

    It is definitely worth a try. Just being able to squash changes to earlier commits without having to fiddle with fixups and interactive rebases is worth it for me. jj absorb is great too.

systems 1 day ago

its almost impossible for me to tell if this better or worst than git i read few things about jj, and my conclusion

   1. its different
   2. very few user would really care about this difference 

i think git is good (not good enough, good just good, or really good) and unlike shells, i cant think of a reason to have mass migration to it

people use zsh because apple choose it, and pwsh because microsoft settled on it, on linux i am sure we can do better than bash, but it good enough and nothing justified replacing it (that being said, all 3 OSes should have settled non nushell)

in summary, if we couldnt replace bash on linux, i dont think anyone can replace git, git as an scm tool if far better than bash as a shell

  • porksoda 1 day ago

    I may be reading too deeply but it sounds like you haven't even tried it. You should! Its really hard to live without it, once you feel it in your fingers.

  • kccqzy 1 day ago

    > very few user would really care about this difference

    Oh the user absolutely does if that user creates lots of branches and the branches are stacked on top of each other.

    I get your feeling though; sometimes in my own private repositories I don’t bother creating branches at all. Then in this case jj doesn’t really make much of a difference.

  • em-bee 21 hours ago

    we may have not been able to replace bash yet, but the popularity of alternative shells that are not bash compatible is growing. besides nushell there are fish, elvish, murex, oils (also includes a bash compatible mode to help with the transition) and probably some others that i missed. i see more and more tooling support for some of these, which shows that usage of these shells is growing.