Discussion
Steve's Jujutsu Tutorial
dgb23: 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.
jansan: 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.
SOLAR_FIELDS: 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.
tom_alexander: I'm giving jj a try but one aspect of it I dislike is edits to files are automatically committed, so you need to defensively create empty new commits for your changes. As in, want to browse the repo from a commit 2 weeks ago? Well if you just checkout that commit and then edit a file, you've automatically changed that commit in your repo and rebased everything after it on top of your new changes. So instead you create a new branch off of the old commit and add an empty commit to that branch so any file changes don't end up rewriting the past 2 weeks of history. git is much nicer in that I can do whatever I want to the files and it won't change the repo until _I tell it to_.
smackmybishop: 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.
ramblerman: 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.
VMG: ... unless you actually want to edit a change!
arccy: still use new, and then squash your changes in. that way you can actually see what changes you made
throawayonthe: well, you can do jj new <revision>, make your edit, and then do jj squash which will add the changes to the prev revisioni do this for example when i want to see a specific edit highlighted in my editor, it's a nice workflow i think
tucnak: 16 year-old me would have been very impressed by this!
auggierose: Is that a compliment, or the opposite?
tucnak: A bit of both. I guess it's nice, but nothing I actually care about.
mhh__: 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.
7e: Is it better for AIs? That’s the only reason I would care.
VMG: 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.
seanhunter: 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 I don't care whether it has an index or not". 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.
xtracto: 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.
Aeolun: 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.
PUSH_AX: If it ain't broke...
swoorup: 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.
jdkoeck: 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.
andrepd: 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!
glasner: 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.
shuyang: Has anyone found a good code review workflow with `jj`? My problem is that GitHub doesn't remember the content of the last reviewed SHA, so every time I push a new change from jj, reviewers lose the delta from their last reviewed commit.To work around this I stopped moving revs (squash/rebase) after review starts, which creates awkward local graphs if I have to merge from main for merge conflicts. Graphite works but it's $$$, and phabricator/reviewable/gerritt all have significant onboarding hurdles.
joshka: 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.
bombcar: 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: Mercurial was there, was better and more complete.Too sad it didnt win the VCS wars.
seanhunter: 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.
Diggsey: 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.
smweber: 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.
enbugger: 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 loose 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: You don't replace. jj is backed by git anyway.
shuyang: 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 :(
tiborsaas: 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.
smweber: 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.
marcuskaz: I wrote a brief intro to Jujutsu here: https://mkaz.blog/code/jujutsu-vcs
Jenk: 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.
pythonaut_16: 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`
demorro: 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.
saghm: 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.
jeremyjh: 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.
maleldil: 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.
joshka: > Does JJ really prefer for me to think backwards? It wants me to start with the new and describe command, but with git I first make the changes and name the changeset at the end of the workflow.A good way to think of it is that jj new is an empty git staging area. There's still a `jj commit` command that allows you to desc then jj new.> I also often end up with in a dirty repo state with multiple changes belonging to separate features or abstractions. I usually just pick the changes I want to group into a commit and clean up the state.jj split allows you do to this pretty well.> Since it's git compatible, it feels like it must work to add files and keep files uncommitted, but just by reading this tutorial I'm unsure.In jj you always have a commit - it's just sometimes empty, sometimes full, has a stable changeid regardless. jj treats the commit as a calculated value based on the contents of your folder etc, rather than the unit of change.
saghm: 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!
scotty79: Does jj have partial clones of remote repo?
fmckdkxkc: 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.
nerdypepper: 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...
saghm: 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.
cestith: 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.
asdfasgasdgasdg: Most importantly, submodules are not 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.
IshKebab: 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.
jeremyjh: Right, but that’s different from working in a team environment where everyone else continues using git.
BeetleB: 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.
palata: 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.
naasking: > 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?
qezz: 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/*
surajrmal: 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.
bombcar: 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.
systems: 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 itpeople 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
mi_lk: Please update the "Stacked PRs" workflow article...
nomel: > 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. 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: 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.
zhaoyongjie: To be honest, JJ is dick in Chinese, literally.
minraws: 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 rev1jj 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 basejj rebase -s rev2 -d basejj git push```That is it.
saghm: > jj edit is the biggest jj footgun I can think ofHonestly, 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: 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.
zingar: "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.
ferfumarma: Can jj iterate through a list of repositories and clone them all to local storage?It isn't very hard to make a bash script to do it, but I have about six github repos, all of which frequently need to be put on a new machine. that kind of functionality would be cool to have out of the box.
hosteur: for url in url1 url2 ..; do git clone $url; done That’s not really a script but a basic one liner.
Jenk: 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: 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.
EliasWatson: 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.
kccqzy: > very few user would really care about this differenceOh 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.
tom_alexander: 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.
miyoji: > 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: git promises "version control", this clearly implies that the versions predate the control: in this picture the git workflow is not backwards.
jrockway: 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.
saghm: 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.
randyrand: 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.
motbus3: I am dumb. why is that better than a git branch or a git worktree ?
QuiDortDine: > $ cargo install jj-cli@0.23.0 --lockedI won't install Rust just to test your software. Make a debian package like everyone else.
harg: 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/
porksoda: 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.
tcoff91: 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
justinmayer: 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/ggI 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: jjui TUI is incredible also
kevin_nisbet: 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.
alunchbox: 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!
baq: 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.
tinco: 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.
nailer: 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: Did you confirm that the network effects are applicable here before posting that?
icorbrey: 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: 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.
VonGallifrey: 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.
rob74: 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-...) ?
steveklabnik: 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...)
steveklabnik: I'll get there... someday...
octocop: Nobody is asking for a git replacement? I keep seeing these posts and I don't know who wants them.
steveklabnik: I wasn't asking. I loved git.But then after trying jj, I wrote this tutorial because I love it even more.
shermantanktop: 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.
qznc: There is no index anymore. I guess that is the "easier" part.
rs545837: 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 github.com/ataraxy-labs/sem, which uses tree-sitter to parse code into semantic entities and operates at that level instead of lines.
latortuga: 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.
baq: > edits to files are automatically committedthis 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.
embedding-shape: > Just don't ever use `edit`, use `new` insteadAs 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.
saghm: The idiom here is use `edit` if you want to edit a commit, and use `new` if you want to make a new commit. This works identically whether you specify the commit via branch name or commit id. I'm not sure why people are saying not to use `edit` ever. It's basically just a shorthand for staging and amending changes in an existing commit, and there's still a use case for that; it's just not "I want to see the changes on this old branch".
embedding-shape: > Just don't ever use `edit`,> The idiom here is use `edit` if you want to edit a commitYou 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: 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!
surajrmal: This isn't true?
hacker161: It took you 12 years to grok git??
steveklabnik: 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: 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.
saghm: > 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: Unless you need to work with a repo that uses submodules or lfs.
bluGill: Coming from mercurial (which is older than git), git doesn't understand a branch. Instead of a branch you get a tag that moves, which is very different. Too often I'm trying to figure out where something came in, and but there is just a series of commits with no information of which commits are related. Git then developed the squash+rebase workflow which softof gets around this, but it makes commit longer (bad), and loses the real history of what happened.Git was not the first DVCS, there were better ones even when it was made. But Linus pushed git and people followed like sheep.(I'm using git, both because everyone else is, and also because github exists - turns out nobody even wants a DVCS, they want a central version control system with the warts of SVN fixed).
jcranmer: > 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: 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
aftbit: Nope, git is good enough, and is the global standard. We don't need more new VCS.
porksoda: Great opinion. Have you tried it? It takes just 30 minutes to wash all the Stockholm syndrome off of you.
homebrewer: 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.
IshKebab: It is when I tried it.
BeetleB: 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.
xixixao: git absorb exists too fyi
bitdivision: Not by default: https://github.com/tummychow/git-absorb
EliasWatson: 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.
p_stuart82: 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.
ersatz-matty: From your "polluted" snapshot, you can run `jj commit -i` and use the TUI to select only what you want.
hacker161: Just like you can run `git add -p`
baq: yes but no
hacker161: Explain the difference.
BeetleB: 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.
stouset: 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.
SV_BubbleTime: >You can request to not use the pager by using jj st --no-pager, or if you hate the pager and want to turn it off, you can configure that with $ jj config set --user ui.paginate never In one feature they can’t help themselves from calling it two different things already.Why do this? Why can’t the very clearly smart people making things step 1/2 step outside themselves and think about it like they are the users they want?Earlier they talk about the native format and how it isn’t ready… so that to start you need jj git init … but… if they’re planning a native format that makes no sense as a command. It would be ‘jj native init’ later?Early planning keys/plans imo but those rarely change so as to not accept your early adopters.These seem like small things but to me it’s a warning.
Philpax: 1. Pagination with a pager is a reasonable default. See `git log`.2. The native format would be `jj init`. For precedent, see how uv dealt with its pip compatibility: `uv pip install` was obsoleted by `uv add`.
SV_BubbleTime: 1. 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: 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: >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”
pheggs: 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.
misnome: 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.
bilkow: 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+
capitainenemo: 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.
steveklabnik: Hey folks!So, I haven't updated the tutorial in a long time. My intent is to upstream it, but I've been very very busy at the startup I'm at, ersc.io, and haven't had the chance. I'm still using jj every day, and loving it.Happy to answer any questions!
klauserc: 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"?