• Hacker News
  • new|
  • comments|
  • show|
  • ask|
  • jobs|
  • tracker1 6 hours

    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.

  • misnome 6 hours

    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.

  • jancsika 5 hours

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

  • 0xnadr 4 hours

    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.

  • randyrand 7 hours

    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.

  • kristo 6 hours

    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.

  • sam_goody 6 hours

    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 6 hours

    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.

  • jimmypk 7 hours

    [dead]

  • jtrueb 5 hours

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

  • 9 hours

  • dhruv3006 9 hours

    this looks pretty interesting.

  • Openpic 7 hours

    動画などでわかりやすいものが見たい

  • carverauto 6 hours

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

  • alunchbox 7 hours

    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!

  • tobinfricke 6 hours

    Here are some notes on Jujutsu from Evan Martin that I found interesting/useful:

    https://neugierig.org/software/blog/2025/08/jj-bookmarks.htm...

  • eranation 5 hours

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

    tonfa 4 hours

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

  • compiler-guy 6 hours

    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 2 hours

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

  • PUSH_AX 9 hours

    If it ain't broke...

    BeetleB 8 hours

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

    surajrmal 8 hours

    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 6 hours

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

  • blacknig 9 hours

    [flagged]

    whattheheckheck 9 hours

    The tower continues to build higher and higher, I wonder when it will collapse under its own weight

  • aftbit 8 hours

    Nope, git is good enough, and is the global standard. We don't need more new VCS.

    cryptos 8 hours

    Might be true, but Subversion was also good enough and a de-facto standard.

    porksoda 8 hours

    Great opinion. Have you tried it? It takes just 30 minutes to wash all the Stockholm syndrome off of you.

    homebrewer 6 hours

    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 6 hours

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

  • justinmayer 8 hours

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

    jjui TUI is incredible also

  • jiggunjer 6 hours

    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 6 hours

    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.

  • acoustics 5 hours

    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 4 hours

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

  • rs545837 7 hours

    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.

    sieabahlpark 7 hours

    [dead]

  • nailer 8 hours

    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.

    Nuzzerino 7 hours

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

    nailer 6 hours

    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.

    saghm 7 hours

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

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

  • andrepd 9 hours

    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!

    steveklabnik 4 hours

    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.

    marcuskaz 9 hours

    I wrote a brief intro to Jujutsu here: https://mkaz.blog/code/jujutsu-vcs

    SAI_Peregrinus 5 hours

    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 4 hours

    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?

  • bikelang 6 hours

    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.

    rs545837 5 hours

    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 3 hours

    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.

    steveklabnik 6 hours

    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 5 hours

    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 4 hours

    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!

  • wolttam 7 hours

    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 6 hours

    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 3 hours

    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 3 hours

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

  • enbugger 9 hours

    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 9 hours

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

    gcr 9 hours

    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 9 hours

    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 6 hours

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

    Jenk 3 hours

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

  • mi_lk 8 hours

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

    steveklabnik 7 hours

    I'll get there... someday...

  • zhaoyongjie 8 hours

    To be honest, JJ is dick in Chinese, literally.

    verdverm 8 hours

    https://chinese.stackexchange.com/questions/31646/is-jj-slan...

  • butlike 9 hours

    FWIW, it's a pretty decent fried fish chain in Chicago as well.

    cestith 8 hours

    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.

  • kevin_nisbet 7 hours

    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.

    p_stuart82 6 hours

    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.

    steveklabnik 7 hours

    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.

    opem 1 hours

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

  • demorro 9 hours

    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 8 hours

    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.

  • ux266478 8 hours

    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?

    steveklabnik 7 hours

    jj is still snapshot based.

  • BeetleB 8 hours

    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.

    xixixao 8 hours

    git absorb exists too fyi

    bitdivision 6 hours

    Not by default: https://github.com/tummychow/git-absorb

    dnmc 53 minutes

    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 48 minutes

    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.

  • dmitshur 3 hours

    What is the current story for serving jj repositories over HTTPS? Git has https://git-scm.com/book/ms/v2/Git-on-the-Server-The-Protoco... . Does jj have something better, equivalent, or nothing (at this time) at all?

    steveklabnik 2 hours

    Unless you're at Google (or writing your own backend), jj talks to a git server, and so you'd just do exactly that: host a git repo over https.

  • cynicalsecurity 9 hours

    [flagged]

    ramon156 9 hours

    [flagged]

    tomhow 6 hours

    Please don't post shallow dismissals, especially of other people's work. A good critical comment teaches us something.

    https://news.ycombinator.com/newsguidelines.html

  • newsoftheday 6 hours

    OK I read it, I'm not interested, git does exactly what I want.

    tombert 6 hours

    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 6 hours

    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.

    6 hours

  • rattray 6 hours

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

    ymir_e 58 minutes

    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.

  • TrysterosEmp 3 hours

    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.

    erdaniels 3 hours

    Preach... thank you

  • swoorup 9 hours

    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 9 hours

    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 9 hours

    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 8 hours

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

  • Aperocky 6 hours

    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.

    chriswarbo 5 hours

    https://git-scm.com/docs/git-worktree

    rattray 6 hours

    What's your naming scheme?

    Aperocky 6 hours

    REPO_NAME_0

    REPO_NAME_1

    REPO_NAME_2

    REPO_NAME_3

  • shuyang 9 hours

    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.

    saghm 7 hours

    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.

    bananapub 9 hours

    github added support for this in the last six hours - https://github.github.com/gh-stack/faq/#will-this-work-with-...

    nerdypepper 8 hours

    https://tangled.org does exactly what you want :)

    have a longer write up here: https://blog.tangled.org/stacking but we have "interdiffs", to view a delta from previous review. pull-requests advance in the form of immutable rounds much like the patch workflow on email.

    we have been interdiffing and stacking for a while on to dogfood, sample PR: https://tangled.org/tangled.org/core/pulls/1265/round/1?diff...

    roblabla 9 hours

    I wonder if the recent github stack system could help with that (https://github.github.com/gh-stack/guides/ui/)

    shuyang 9 hours

    waitlist only :/ waiting to get in so i can test it out!

    joshka 9 hours

    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 9 hours

    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 :(

  • pheggs 6 hours

    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.

    GuB-42 4 hours

    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.

    newsoftheday 6 hours

    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.

  • systems 8 hours

    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

    kccqzy 7 hours

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

    porksoda 7 hours

    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.

  • palata 9 hours

    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.

    BeetleB 8 hours

    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.

    newsoftheday 5 hours

    [dead]

    naasking 8 hours

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

    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.

  • ferfumarma 8 hours

    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.

    saghm 7 hours

    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.

    hosteur 8 hours

        for url in url1 url2 ..; do git clone $url; done
    
    That’s not really a script but a basic one liner.

    Filligree 7 hours

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

  • tiborsaas 9 hours

    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.

    saghm 8 hours

    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.

    gcr 3 hours

    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 hours

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

    baq 7 hours

    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.

    jezzamon 8 hours

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

    EliasWatson 8 hours

    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.

    chriswarbo 6 hours

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

    miyoji 8 hours

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

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

    miyoji 5 hours

    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.

    minraws 8 hours

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

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

    cornstalks 7 hours

    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 3 hours

    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.

    fmckdkxkc 8 hours

    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 31 minutes

    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.

    Jenk 9 hours

    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 8 hours

    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.

    shermantanktop 7 hours

    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 6 hours

    > 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 6 hours

    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.

    joshka 9 hours

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

    frio 29 minutes

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

    greenicon 5 hours

    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()'

    saghm 8 hours

    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!

    smweber 9 hours

    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 9 hours

    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 8 hours

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

  • steveklabnik 7 hours

    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!

    opem 1 hours

    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 hours

    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.

    klauserc 6 hours

    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 6 hours

    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 6 hours

    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 4 hours

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

  • qezz 8 hours

    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/*

    BeetleB 6 hours

    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.

    tcoff91 7 hours

    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

  • 7e 10 hours

    Is it better for AIs? That’s the only reason I would care.

    joshka 9 hours

    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.

    VMG 10 hours

    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 9 hours

    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.

    nvader 40 minutes

    Try using https://github.com/danverbraganza/jujutsu-skill

    This is enough of a command reference that with it, agents are able to work with jj pretty well.

  • theappsecguy 7 hours

    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?

    BeetleB 6 hours

    What problems are you running into?

    The jujutsu docs have a page for this - it has everything I needed.

    https://docs.jj-vcs.dev/latest/github/

    r5Khe 7 hours

    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 6 hours

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

  • scotty79 8 hours

    Does jj have partial clones of remote repo?

    steveklabnik 6 hours

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

    scotty79 4 hours

    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 3 hours

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

  • zingar 8 hours

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

    latortuga 7 hours

    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.

    ersatz-matty 7 hours

    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.

    Rebelgecko 5 hours

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

    steveklabnik 7 hours

    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.

    qznc 7 hours

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

    steveklabnik 7 hours

    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.

    baq 7 hours

    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.

    VanTodi 7 hours

    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

    VonGallifrey 7 hours

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

    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 6 hours

    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+

    alphabetag675 7 hours

    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 6 hours

    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.

    tinco 7 hours

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

    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.

    surajrmal 7 hours

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

    > that the workflows are intuitive

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

    WesolyKubeczek 6 hours

    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.

    BeetleB 6 hours

    > but not seeing is not believing.

    Classic denying the antecedent :-)

    https://en.wikipedia.org/wiki/Denying_the_antecedent

    coldtea 6 hours

    It's not supposed to be a modus ponens deduction.

    Just an expression of what the Missouri state's nickname says.

    baq 6 hours

    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 6 hours

    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 6 hours

    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.

    capitainenemo 6 hours

    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.

    k4rnaj1k 6 hours

    [dead]

    rasguanabana 6 hours

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

    ersatz-matty 6 hours

    `jj undo` compared to what exactly?

    BeetleB 4 hours

    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.

    coldtea 6 hours

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

    steveklabnik 6 hours

    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.

    BeetleB 2 hours

    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.

    ersatz-matty 6 hours

    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.

    nickisnoble 6 hours

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

    ncphillips 6 hours

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

    tcoff91 5 hours

    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

    baq 5 hours

    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.

  • octocop 7 hours

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

    vaylian 7 hours

    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.

    newsoftheday 6 hours

    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.

    klauserc 6 hours

    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.

    steveklabnik 7 hours

    I wasn't asking. I loved git.

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

    SatvikBeri 7 hours

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

    __turbobrew__ 6 hours

    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 6 hours

    Where X = something I’ve never wanted to do

    chriswarbo 5 hours

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

    baq 5 hours

    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 5 hours

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

    baq 5 hours

    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.

  • fallat 7 hours

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

    Philpax 7 hours

    This tutorial predates his involvement with ERSC.

    steveklabnik 6 hours

    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 hours

    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 hours

    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 24 minutes

    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.

  • waynecochran 6 hours

    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 6 hours

    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 6 hours

    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 6 hours

    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 6 hours

    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 4 hours

    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.

  • tucnak 10 hours

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

    auggierose 10 hours

    Is that a compliment, or the opposite?

    tucnak 9 hours

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

    xtracto 9 hours

    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 9 hours

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

    cestith 8 hours

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

    I was 38 or 39 when I found jj.

    hacker161 6 hours

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

  • dgb23 10 hours

    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 9 hours

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

    IshKebab 8 hours

    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.

    chriswarbo 5 hours

    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 1 hours

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

    surajrmal 7 hours

    This isn't true?

    IshKebab 6 hours

    It is when I tried it.

    stouset 6 hours

    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.

    verdverm 8 hours

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

    igor47 5 hours

    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 4 hours

    The git compatibility page states that submodules are not supported

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

    steveklabnik 4 hours

    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.

    jeremyjh 9 hours

    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 8 hours

    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.

    asdfasgasdgasdg 8 hours

    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.

    ongy 8 hours

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

    saghm 8 hours

    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 hours

    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 3 minutes

    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.

    maleldil 8 hours

    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 8 hours

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

    maleldil 2 hours

    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.

    saghm 8 hours

    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.

    stouset 6 hours

    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.

    pyreko 6 hours

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

    tonfa 4 hours

    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.

  • QuiDortDine 7 hours

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

    Philpax 7 hours

    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 5 hours

    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.

    harg 7 hours

    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 5 hours

    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 4 hours

    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 55 minutes

    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 47 minutes

    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.

    saint_yossarian 2 hours

    It's available in Debian sid, although a few versions behind: https://packages.debian.org/search?searchon=names&suite=all&...

    steveklabnik 1 hours

    You know, I went and searched before I posted. I wonder why it didn’t come up! Thanks.

  • rob74 9 hours

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

    surajrmal 8 hours

    It hasn't been touched in 3 months: https://github.com/rue-language/rue .

    steveklabnik 7 hours

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

    BeetleB 8 hours

    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 6 hours

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

    BeetleB 6 hours

    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 6 hours

    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 6 hours

    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.

  • jansan 10 hours

    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 10 hours

    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 9 hours

    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 5 hours

    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.

    1 hours

    SOLAR_FIELDS 10 hours

    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.

    l72 8 hours

    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.

    mhh__ 9 hours

    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 8 hours

    You might already be aware, but jj fixes exactly those complaints you have with git

    bluGill 9 hours

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

    em-bee 8 hours

    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.

    jcranmer 8 hours

    > 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 6 hours

    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

    seanhunter 10 hours

    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 9 hours

    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 9 hours

    Mercurial was there, was better and more complete.

    Too sad it didnt win the VCS wars.

    seanhunter 9 hours

    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 8 hours

    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.

  • jrockway 7 hours

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

    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 6 hours

    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 5 hours

    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 4 hours

    > 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 3 hours

    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 3 hours

    > 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 3 hours

    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 2 hours

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

  • SV_BubbleTime 8 hours

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

    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 6 hours

    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 6 hours

    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 6 hours

    >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 6 hours

    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 4 hours

    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 3 hours

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

  • tom_alexander 10 hours

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

    alunchbox 7 hours

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

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

    et1337 7 hours

    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.

    csmantle 9 hours

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

    Jenk 9 hours

    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 8 hours

    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.

    jdkoeck 9 hours

    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.

    9 hours

    saghm 8 hours

    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 6 hours

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

    Diggsey 9 hours

    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 1 hours

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

    smackmybishop 10 hours

    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.

    embedding-shape 9 hours

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

    dzaima 8 hours

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

    surajrmal 8 hours

    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.

    saghm 8 hours

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

    joshuamorton 7 hours

    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.

    embedding-shape 7 hours

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

    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 4 hours

    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.

    VMG 10 hours

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

    BeetleB 8 hours

    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.

    incognito124 9 hours

    then you `new` & `squash` :)

    arccy 9 hours

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

    throawayonthe 9 hours

    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 9 hours

    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.

    hacker161 7 hours

    [flagged]

    mh- 5 hours

    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 4 hours

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

    arianvanp 9 hours

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

    tom_alexander 3 hours

    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.

    kps 3 hours

        [snapshot]
        auto-track = '~glob:**/*'

    steveklabnik 3 hours

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

    tom_alexander 3 hours

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

    saghm 8 hours

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

    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.

    kps 6 hours

    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 3 hours

    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.

    icorbrey 7 hours

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

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

    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 6 hours

    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 5 hours

    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.

    ersatz-matty 7 hours

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

    hacker161 7 hours

    Just like you can run `git add -p`

    baq 6 hours

    yes but no

    hacker161 6 hours

    Explain the difference.

    baq 6 hours

    git add -p doesn't create a commit.

    smweber 9 hours

    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.

    nomel 8 hours

    > 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 8 hours

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

    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.

    nomel 6 hours

    This is all incredible. I even see a great looking GUI [1]!

    [1] https://jj-gui.com/

    saghm 8 hours

    > 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 8 hours

    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.

    MrJohz 3 hours

    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 3 hours

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

    MrJohz 2 hours

    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 1 hours

    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.

    tom_alexander 2 hours

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

    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 1 hours

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

    steveklabnik 3 hours

    > 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 1 hours

    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 49 minutes

    > 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 48 minutes

    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 45 minutes

    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.

    saghm 7 hours

    > 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 6 hours

    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 6 hours

    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 5 hours

    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.

    tom_alexander 3 hours

    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 2 hours

    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.

    sswatson 4 hours

    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 3 hours

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

    dwattttt 20 minutes

    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.

    baq 1 hours

    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)