The actual project website is here: https://martinvonz.github.io/jj/latest/
The repo is https://github.com/martinvonz/jj
Note: the project author is a Googler, and it appears to be under the umbrella of Google/Alphabet, as a CLA is required: https://martinvonz.github.io/jj/latest/contributing/
Anonymous branches? How do I get back to my work?
Auto commit of saved files? How do I commit the things I want without leaving all sorts of junk I don’t wanna share in my history?
I have no impression of how this makes anything easier.
It’s expected that you look at the commits before you push, the same as you do with staging. If you’d just "commit -am" the results would be the same.
And since a commit can branch you can have a multiple (conceptual) stages.
Jujutsu respects gitignore but cannot be told to not track in-file changes. Annoying when your peers commit customization files into the repository.
Any experiences of anyone trying it out and switching over to it?
I would like to know more about the first class conflict handling. That could potentially be useful for an application I have in mind but I need to understand the algorithms better. How does it compare to CRDTs or other merge strategies?
With the lack of staging area it really seems like this encourages the exact opposite. Seems like a good way to get secrets as well as just general junk and clutter committed to your repo history.
If I am working on a big project, I will start to commit change sets as parts of the code solidify without committing other less solid changes. That seems pretty basic. I don't want half finished changes forever committed to history.
I know, gitignore could help, but it eliminates the convenience of being flexible/quick&messy.
At this point, I wonder if maybe we should not even mention the staging area. It seems to confuse a lot of people, so maybe it hurts more than it helps to mention it.
[1]: https://reasonablypolymorphic.com/blog/jj-strategy/index.htm...
I still had a few things I didn’t know how to do optimally, but it was close enough to be productive. Within a week I’d closed basically all of the gaps.
It’s been three or so months now and I’m never going back. It’s been so transformative I can barely remember all the innumerable frustrations and papercuts I used to put up with daily. Rebase conflicts. Juggling the stash. Ugh. I say this as someone who considered themselves extremely proficient with git. I mean, I wrote a compatible Ruby implementation of it over a decade and ago.
If it gains momentum, jj has a better chance than anything I’ve seen at finally dethroning git.
To put it another way: the staging area is great because it's really easy to manipulate and "chop it up" as you like. If you make commits themselves really easy to manipulate, just as easy as the staging area, then you can just get rid of the staging area and use commits instead i.e. split a commit in two, or squash two commits together, or parallelize them or reorder them, etc. This actually means that many workflows in Jujutsu are more open ended and flexible than the Git equivalents, while simultaneously having less nouns (which means that, for example, there are fewer UX irregularities because there's less surface area.)
The README that Martin mentions first says there's "no staging area", but then a few paragraphs later mentions that the working copy concept subsumes it and so it provides a superset of functionality. That should probably be rewritten.
Hacks/workarounds/etc are actually pretty easy to handle and there are a couple strategies. The one I use is to use a merge commit. Let's say you have a patch P that you want to keep private (it hardcodes some build settings you like to use or whatever) and a patch A you want to work on. The base these changes are based off is B which is the upstream branch "main".
Then, you create a merge between A and P, creating commit M, and your working copy (called @ or "at") sits on top:
--> P -->
/ \
B (main) M --> @
\ /
--> A -->
And now, both `P` and `A` exist in your repository at the same time. Now, you can just use the `jj squash` command to take changes from `@` and put them inside either A or P, as you do your work, thus moving them "through" the merge commit M. These kinds of techniques where you move changes between commits are very common in Jujutsu so this is pretty natural once you get used to it.Note that you can also have `M = @`, i.e. the merge commit is the working copy itself. I prefer to keep the commit M separate and work "on top" of it like in the above graph, because you may solve a conflict between P and A, and M would be the correct place to put it, and you would want to keep it separate from everything else.
A neat feature of this trick is that you can create a branch for change `A` and if you are sitting on top of `M`, and you run `jj git push`, then only `A` will be pushed and `P` will be ignored and left alone. In fact `P` will never be pushed unless you explicitly ask for it by creating the branch pointing to it. Nor is the merge commit ever pushed anywhere; it only exists so that you can have both changes available locally.
I do this all the time these days in various forms, and in practice, the day-to-day stuff isn't terribly different than it is when you just leave things out of the staging area inside your working copy.
It's a relatively easy thing to adopt something you don't need, when you are the only person involved. But VCS is pretty much in the same category as instant messaging and social network platforms are. In a sense, maybe it is a social network platform. I don't see it happening and I highly doubt I would want it to happed, even if I knew this jj stuff better.
I think it's me not understanding the difference between "jj new" and "jj commit", but I'm not sure.
I'm a big fan of the staging area for crafting clean commits, so this would be super useful for me to understand.
You can quickly see any commits that have merge conflicts. If some operation introduced them, you can go edit that commit and fix them. When you do so, the fix propagates forward automagically to all descendant commits. The benefit here is that the operation you were trying to do fully completes and you're never ejected into an intermediate state where changes are dropped into your working copy and you're expected to fix it right here and now before anything else happens. You don't have to unwind everything because you screwed up an earlier conflict resolution since everything is stored in history; you just go back to the commit you want to fix, make a change, and then jump back to the later commit that still needed some work.
Git is the native backend for jj. It is extremely reliable. There is no reason for "things to go wrong" uniquely to jj, particularly if you take basic precautions like forbidding force-pushes to master/main. For that matter plenty of people make mistakes with git itself, and having used both (and being a power user of git) I can confidently claim that there is significantly less surface area for mistakes with jj than there is with git.
I have never, in 25 years of software development, needed to "communicate my workflow" with another engineer. It's understood that if I use a different tool than the rest of my team, I'm on the hook for knowing any differences in the workflow. But it's not even like this is unique to jj; my team uses a variety of editors/IDEs, operating systems, and shells. I use macOS, fish, VS Code, and jj. Others use nix, zsh, vim, and git. Somehow we all manage.
The worst part is that your argument is trivially rewritten to discourage trying anything new. This is frankly a terrible mindset to have.
If you don't want to learn a new tool, by all means don't. But not learning anything that might be different than what other people on your curent team use is a great way to completely stagnate your growth.
That said, the argument I was responding to was that this can never take hold because of git's dominance. My point was that use of jj can grow organically since one person converting doesn't actually require the entire team to do so. Whether or not you're particularly hesitant to adopt new things is irrelevant; individuals can switch whenever they feel appropriate and it doesn't impact others.
When you're ready to "commit", you give a description to your current set of changes (`jj describe -m`) and then split out any of the pieces you don't want into the next commit (`jj split`). You get to pick the parts you want pulled out, and those get pulled out onto a new, fresh commit.
The nice (and quite unique) thing about jj is that it takes all the good cues from systems which kept improving (and in particular, from mercurial), and slaps them onto git without requiring a storage model change, practically offering the social "perks" of GitHub (by being fully compatible with it), with no-compromise. And to be honest, at this point, there isn't much reason for a new comer to learn git rather than jj, and that's a wonderful news in my book.
Also, Rebasing is cause for Dev Riots because of nightmare merge conflicts that can result.
Since the author is from Google, my guess is they are seeing issues that hit the level of "WE MUST FIX THIS" that only arise at Google Monorepo size.
This is exactly why I hew to squash+rebase. As I like to put it "I don't care about every little sneeze a developer had." Git has spoiled me with this, where I have the power to commit to my private repo anything I damn well please, but in the end I can clean things up and keep the central repo clean and bisectable[0].
Any VCS that doesn't offer these (squash, rebase, bisect) is a complete non-starter for me.
[0] - https://blog.carbonfive.com/always-squash-and-rebase-your-gi...
More generally: I am more knowledgeable about version control than 99% of people (having worked on it full time for many years). The Git UI is well beyond papercut bad. Many advanced users use the rebase-interactive workflow, which is broken in numerous ways.
See my testimonial (top one on the page) for an example where Jujutsu's rethink of VCS basics leads to an incredibly coherent interface. Jujutsu is built by world-class developers who have spent many years working on other systems and drawing lessons from them, and it shows! https://martinvonz.github.io/jj/latest/testimonials/
If I were introducing version control to someone new, I would not be proud of introducing Git and would constantly apologize for it. I'd be proud of introducing Jujutsu.
The reason would be that jj uses git as a backend, and all your coworkers use git, and the whole world uses git, so using jj while not knowing git is like being "html-programmer" that doesn't know what RAM is. Surely one can live like that, but I wouldn't want to work with him.
On other hand, what is the reason to learn jj rather than git? What actual problem it solves?
"git is the underlying protocol that jj is compatible with, and sites like GitHub speak git's protocol", there now you know what git is.
> I will start to commit change sets as parts of the code solidify without committing other less solid changes
I feel like you are pushing your idiosyncratic way of working on others. It's great that you have a way that works for you, but it seems rather odd to me that you would ever be working on more than one thing in the same staging area.
I always git add -a . because I'm working on one specific thing, and my tests just passed.
You are saying that rebase-interactive workflow is broken. I do rebase-interactive a lot. Well, maybe it is broken, maybe I would get annoyed about it beyond belief if I spent as much time messing with its internals as you (supposedly) did. But I don't think I often have problems with it as a user. I guess conflict-handling could have been much smarter, but if I keep "temporary" commits very small and atomic it somehow manages to do the job better than I sometimes expect, and in the end I don't think I have any problems with it whatsoever. So, once again, what is the problem jj solves? Can you give some practical, not overly-contrived example when using jj over git is not "nice", but significantly useful, when it saves me somehow?
Let's say you have a stack of three commits, A <- B <- C. Let's say you do a git rebase -i with edit commands on A and B. You make some changes to A, then you go and edit B.
But then you realize that some changes you wanted to make to B, you want to make to A instead -- you want to go back to A.
git rebase -i doesn't let you go back to A. (In other words, the modal nature of git rebase -i means that you can't do a rebase -i inside of a rebase -i.) Instead, you must complete the rebase -i and go to C, and start a new rebase -i.
Now that doesn't sound like a problem, but let's say C is not just 1 commit, it's 10 or even 20. And maybe there are branches with experimental work that you're grabbing bits and pieces of.
A <- B <- C1 <- C2 <- C3 <- C4 ... C9 <- C10
^ ^
| |
E1 E2
And those commits have significant merge conflicts that you must resolve immediately before you can go back to A.In contrast, Jujutsu isn't modal. You can freely move up and down a stack, and merge conflict resolution can be deferred until later.
(This isn't a contrived example. I literally just finished up a few weeks' work on a stack of 14 rather substantial commits. At its peak, 12 commits were outstanding, totaling around 15k lines of code. Jujutsu handled this extremely well.)
edit: apologies, monospace formatting isn't working well on HN. But I hope you get the gist.
As an example of something that's much easier in Jujutsu and very difficult with Git is to work on all branches at the same time. With Jujutsu you can rebase all branches simultaneously too.
See this description of the workflow: https://steveklabnik.github.io/jujutsu-tutorial/advanced/sim...
Note that the solution probably involve a local daemon process for caching reads and optimistically accepting writes and then sending them to the server in the background. That's what we do for latency at Google. But that's just an optimization and can come after the server is done.
If you're interested in working on it, it would help if you joined our Discord server.
This is a major pain for people learning Git for the first time and basically everyone have run into these issues, while a more user-friendly tool would've saved so much pain and suffering. There's a reason why most people cling to a Git GUI like their life depends on it.
Being easier to learn and reason about together with better conflict resolution and rebasing are substantial improvements I'd say.
I remember CVS during my early Linux days. At work it was SourceSafe and around the 2010 era, worked for companies using TFS or SVN, etc.
Each one of these had there issues. Some had a better relationship with SVN than others. I did not mind it. However, I HATED SourceSafe! Glad to never touch that again!
When distributed version controls started to become a thing, I played with git, mercurial and bazaar before they were common. I liked them all better than SVN. Eventually I worked for a company using Mercurial but it wasn't long before git was the breadwinner and replaced practically everything else.
Personally, the only reason I ended up on git is because is solved the problem of doing local commits. Many times I was not on the internet or local network, but I can commit whenever I like and push when available to do so. This, for me, was a HUGE win!
Now, I dont have a reason to switch to something else. Its not comparable like moving from SVN to git. Could there be some cool or simpler features? Maybe. Regardless I hardly have issues with git overall to cause me to look about.
The only "issue" I have came across with git is its not great for large binary files, or for storing a collection of images, etc. For coding - its awesome.
I guess the only way I will starting considering alternatives is if the git team decide to make terrible choices.
We previously saw that git is in fact two very distinct things: a repo format and a user interface; and we saw that git is too critical a mass to be dislodged as a repository format. The next best thing we can do is then to address its disastrous UX, and this is precisely what jj is about. And it tackles that without even incurring "contagious" changes in established organizations: neither you or I have to know/care whether others are using git-cli/vscode gui/jj/whatever.
This is progress, because teaching git to this day is in equal parts teaching "DVCS theory", git "the useful parts", git "the very many pointy bits not to get too close to", and git "I messed it up, please help me rescue my files". At least jj offers to simplify the learning while improving on the experience greatly, by offering a very cohesive, straightforward, discoverable safe and mostly trouble-free implementation whose tenets that can be intuited from the theory.
I loved git. I now love jj more.
To be honest, JJ makes it way easier for me to craft clean commits. The design philosophy of JJ is commit-oriented, not branch-oriented. Since it's a frontend to git, everything it does is fundamentally git. But it allows commit-oriented workflows to flow so much easier than git does.
I fully appreciate this question, as I was there too until I started working with anonymous branches.
Here's some sample output from jj log:
$ jj log --limit 5
@ pzoqtwuv steve@steveklabnik.com 2024-03-01 15:06:59.000 -06:00 9353442b
│ added some cool new feature
│ ◉ xrslwzvq steve@steveklabnik.com 2024-02-29 23:06:23.000 -06:00 a70d464c
├─╯ create hello and goodbye functions
│ ◉ yykpmnuq steve@steveklabnik.com 2024-02-29 23:03:22.000 -06:00 210283e8
├─╯ add better documentation
◉ ootnlvpt steve@steveklabnik.com 2024-02-28 23:26:44.000 -06:00 b5db7940
│ only print hello world
◉ nmptruqn steve@steveklabnik.com 2024-02-28 23:09:11.000 -06:00 90a2e97f
│ refactor printing
Here, we are working on change 'pzoqtwuv'. (@ means the working copy.) There are colors in the real CLI to make the differences more obvious, and to show you unique prefixes, so for example, you probably only need 'p' or 'pz' instead of 'pzoqtwuv' to uniquely identify the change. I'll use the full IDs since there's no syntax highlighting here.We have two anonymous branches here. They have the change IDs of 'xrslwzvq' and 'yykpmnuq'. The log output shows the summary line of their messages, so we can see "create hello and goodbye functions" on one branch, and "add better documentation" on the other.
You don't need an additional branch name: the change ID is already there. If you want to add even more better documentation, 'jj new yykpmnuq' (or again, likely 'jj new yy' in practice) and you're off to the races. (jj new makes a new change off of the parent you specify.)
That's all there is to it. We already have the commit messages and IDs, giving an additional identifier doesn't help that much.
(And if you're in a larger repo with more outstanding branches, you can ask 'jj log' to show specific subsets of commits. It has a powerful DSL that lets you do so. For example, say you only want to see your commits, 'jj log -r 'mine()'' can do that for you.)
> Auto commit of saved files? How do I commit the things I want without leaving all sorts of junk I don’t wanna share in my history?
The simplest answer is "you put that stuff in your .gitignore and it never gets committed." That said, it is recognized that sometimes that is not possible or easy. See https://github.com/martinvonz/jj/issues/323 for the current discussion about how to maybe support alternatives here.
See also https://news.ycombinator.com/item?id=40917149
> I have no impression of how this makes anything easier.
My take on this is that jj has fewer but also more orthogonal concepts. Naming things is hard. I don't like to do it. Not needing to name branches is really nice, as I don't really lose anything by not naming them, and I no longer have to name them.
With regards to some other stuff, the "auto commit of saved files" really means that jj turns two of git's concepts, commits and the index, into one concept: commits. How is that easier? Well, I don't need to learn two sets of tools for dealing with the index vs a commit. For example, git has two kinds of resets: hard and soft. This is because git needs to have two sets of behaviors to deal with both concepts here, as git reset is about changing the index but maybe or maybe not the working tree. All of these distinctions don't matter in jj, because it's all just commits, so you use regular old tools on commits for them.
1. jj new (create new change)
2. <make typo fix>
3. jj edit @- (please go back to editing the previous change: @ is the working directory, - means previous. kinda like HEAD^ in git)
You're done in a few seconds, and can get back to your regular work, while that change is split out into its own thing. If you're not as comfortable with the anonymous stuff, you could also `jj new -m 'typo fix'` to set a message then, but for small stuff like this, I don't personally bother until I am done with what I'm actually working on and am ready to submit that too.
If you’re treating @ like the git index (sometimes called the “squash workflow”) then you’d just do the same thing you do in git: squash each part of @ into the proper commit.
Of course, you could also just make it in the current change, and split it out later, but that's easier to forget imho.
This is because I pretty much never use "edit". Well, not "never", but I avoid it, and it is perfectly avoidable in the vast majority of cases. Also, I don't like to deal with conflicts, as they are error prone, so I try to avoid them as well. Every time I want to change something in an old commit (on the same branch) I'll just add a new commit with barely comprehensible commit message, and I commit every little change. Also, if there is an atomic change in 2 files (function renamed), I usually commit them separately, including file name in the message. Then, after finishing some significant part of work, I'll rebase -i master, reorder commits and most of these "new" fixes will end up with "f" modifier after an olde commit, some I'll just drop. I'll do it in multiple iterations, to see which (reordering) operation results in a conflict, to keep conflict resolution operation as small and straight-forward as possible. So being modal is a good thing, as I'll run rebase a half-dozen times, before changes made up to the moment are all cleared up and I can move forward with committing new stuff. I never want to stay inside of one "rebase" operation for long, if I do, I'll probably --abort, and stop to reconsider what I'm doing here.
Even if "editing" past commit would be completely effortless, I just don't want to do that, because I don't want to think about how "later" changes might affect this place. When I do that, there is a whole new class of potential human mistakes one could make, that just doesn't exist when you always work on the "latest" version of the code. In worst case scenario, I might break some intermediate commit when carelessly reordering stuff, but the final code result must remain the same as before I did rebase. If this isn't the case, there is this uncomfortable scenario that I make some edit which is logically incompatible with a "later" conflict, which I won't notice if it doesn't cause a conflict: and it totally might not cause a conflict. I don't want to worry about that.
But also, tools shape our behaviors. Your current workflows are influenced by how Git works, and it's quite possible with Jujutsu you'll change your workflows. Maybe in a direction that you'll like more in the end.
Disclaimer: I work at XetHub.