I remember plenty of people who used SVN saying why did they need to use git. In fact, I remember companies complaining about distributed version control generally, saying they preferred centralized version control.
If it works for you, you can keep using it. No one will stop you, but you may find that just as SVN users have largely shrunk down, maybe the same will happen with git.
2. jj today
I use jj today and the biggest advantages for me are not needing to worry as much about the order of operations.
I write some code and then I come across a bug. I can pull out the bug and fix it while still working on my existing code. I don't have to go back in time to fix it.
I also appreciate not needing to stop just because of a conflict. This is something I miss from SVN, which was similar-ish in that conflicts didn't need to be addressed immediately, only jj's implementation is better.
Generally I find the jj workflow to be something that takes a few days of adjustment, then it just makes more sense.
3. The way things will be
jj is still relatively early. The commands to interact with git aren't all very intuitive or easy to use. I had to make some aliases to make this easier. More importantly, there's no jj backend yet. Once that happens and it's adopted by forgjo or another forge (Gitlab, Github), I think we'll see a big shift towards it.
I think git is going to be around a long time, but the day to day use of jj is so much nicer than git that I hope it will gain fast adoption.
> I remember plenty of people who used SVN saying why did they need to use git
I remember everyone saying “SVN is a piece of shit, I hate it, why can’t I easily create branches.” Also mixed revisions which was the worst part of it.
The jj people should really write a guide to show how it’s better than git because, so far, I’m still very confused and we’ve had that conversation on HN at least 10 times without anyone giving concrete examples.
For me, jj is better than git because it has achieved something of a holy grail: it is both simpler and more powerful, at the same time. It achieves this by having more cohesive primitives.
Take the index. I love(d) git’s index. It is a powerful way to build up focused commits. Jj does away with the index, yet gives you equivalent power. How? Because instead of a separate index concept, jj has a special name for a commit representing the index: @. Files are (by default) automatically tracked and included in @. Just like how you’d git add -p, you create a new commit for your work, then create a commit on top of it. As you work, @ gets built up with your code, and when you’re ready, jj squash (with various flags or paths to control exactly what gets added) to move them into your parent, “real” work commit.
So if it’s the same, why is it better? Because we’ve reduced the number of states: instead of dirty, staged, and committed, you just have committed. And with that, commands are simpler: no distinction between —hard or —soft for something like git reset, everything just operates on committed state. This means you can bring the full set of tools to manipulate commits to bear on your index!
The same goes with stashing: because heads are allowed to be detached, to use the git terminology, we don’t need a special stash concept: create a new commit that’s forward of where you’re working to “stash” your changes in there until you need them.
These are two simple examples, but it’s really about how everything fits together into an extremely powerful and cohesive tool.
And I remember everyone saying that git is confusing, that they get into states that they don’t understand and have to blow their local changes away, and that they have completely fucked their local copy more than once.
I’ll give you some concrete advantages:
1. Every time you run a `jj` command a snapshot of your local changes is made. I don’t know about you but often while I’m working on something experimental I have an idea for a slightly different approach, try it out, it doesn’t pan out, and I’m like fuck I wish I’d saved where I was an hour ago. git won’t do that for you but jj will.
2. The rebase workflow is dramatically better. Rebasing multiple commits with conflicts is excruciating. You have to fix it right here and right now but you’re in a weird repo state where you can’t easily to another commit and tweak it or look at the state of things at different times. It’s a pain in the ass and easy to get wrong. With jj there are no weird intermediate rebase states. Rebasing always succeeds and if there are conflicts you’re able to address them just by editing your changes as you would in any other situation.
3. I don’t have to make WIP commits or stash when switching between lines of work and I don’t have to remember to undo them when I come back. Your work is always committed. Stashing doesn’t need to exist any more and as a result I never have to worry about conflicts when unstashing. I never have to realize I’ve forgotten work that was stashed.
4. Named branches don’t follow you automatically. This is huge if you ever have to do some linear work that involves multiple PRs that have to be merged and deployed one by one by one. With git you have to make branch after branch after branch. And god help you if one of the earlier lines of work needs to be changed, now you have to rebase all the descendants. With jj all this work is linear. If you have to edit an earlier commit or insert a new commit inbetween two, it’s trivial.
5. And the real killer feature? I get to have all this and nobody on my team needed to change a thing for themselves.
I was a git expert. I’ve used it since before GitHub. I’ve written a barebones implementation of git from scratch. I was the guy everyone came to when they got stuck. I gave talks on git to my local language user meetup. And I will never need to touch it again.
> Every time you run a `jj` command a snapshot of your local changes is made.
I think that's going to be pretty divisive. For projects where your input is here, your output is there, and that's that I imagine it's fine. Getting people to implement one of the workarounds to avoid junking up the repo feels like a huge ask.
I’ve found most modern codebase have good gitignore discipline, but it’s true that there are codebases that are not. There are also some tools that by default write files to the current directory.
The auto add feature is configurable these days though, so if you don’t like it, you can turn it off.
Auto-adding your changes to existing files sounds like a great feature, auto-adding any random files it finds in the project is a terrible one. I can't imagine why the author decided that was a good idea. Can someone explain the reasoning behind it? I can understand someone wanting it as some point so it makes a certain amount of sense to add as an option, but not as the default. Makes me wonder about what other bad decisions have gone into the project.
I'm a bit more than skeptical. Right now, for a project at work, looking at files in the repo (for testing, etc) I have 294 files untracked and 10 ignored. Using jj all but the ignored files would be committed to the repo and I'd have to something akin to git's filter-branch to clean them out again to avoid that bloat.
I guess I should ask... is that final step made super easy? That is does jj make it easy to completely remove all traces from the repo of those auto-added files (like filter-branch)?
You live with those files showing up as untracked because it was easy and convenient to do so with git.
If git auto-added them from the get go, you would have used a pattern in your personal or project gitignore to skip them, or you would have put them into an ignored tmpdir, or you would keep them on a separate branch if you intend to make them part of the repo. None of these steps would have felt painful or out of place. You see files you don’t intend to include in `jj status` and you’d do whatever is appropriate to exclude them.
I’m with Steve here. I too was skeptical, and it’s turned out to be a complete non-issue.
> and I'd have to something akin to git's filter-branch to clean them out again to avoid that bloat
It’s even more trivial than you might imagine. If you have a commit that added files you don’t want to include you `jj edit` that commit, remove them however is appropriate (rm, mv, add to gitignore, whatever), and… there is no step 3. Later commits are instantaneously and automatically rebased so as to not include those files. Tools like filter-branch are completely unnecessary.
Cool. You've convinced me. A small change in workflow + easy cleanup sounds like it addresses most of my concerns. Though if I were to adopt jj I'd still probably just turn off the feature at first until I saw where I could have used it in practice. I'm going to wait and see for a bit myself. I'm pretty comfortable with git.
The way jj tracks changes isn't a feature you can turn off AFAIK. Maybe you can, but if you can, you shouldn't. This is fundamental to the jj internals and workflow.
As someone who also kept a lot of files in their repo that went untracked, it was a small adjustment. Add to my .gitignore, untrack the files, and done.
You can disable automatically adding untracked files to the repo, but you can't disable it from automatically incorporating changes to tracked files. I believe GP was talking about the former.
You don’t have to convert your team to use jj. So there’s nobody to resist but yourself.
For me, writing temporary output to a gitignored tmp directory has been trivial. And I do exploratory coding in a separate branch. Just because it’s part of the repo doesn’t mean it needs to get pushed.
I get what you are saying about the nicer workflows for those cases.
However, why would the contributors to jj not just try to make git better by addressing these weaknesses?
I’m not being glib, just that it puzzzles me when a new oss comes out that does the same thing as another tool but a a bit different. I would have though that there is a way to have a git plug-in or even a way to contribute to git to enhance the issues outlined.
Yes, I know that some code bases are too far gone for major enhancements…
> However, why would the contributors to jj not just try to make git better by addressing these weaknesses?
Because the git workflow causes a lot of the difficulty and fixing it involves making a new concept: mutable changesets with a unique id which are built on top of immutable commits.
jj isn’t just some bugfixes or a few porcelain improvements, it deeply changes the way you approach things. And there’s a pretty decent amount of unlearning of subtly-broken concepts you need to do.
In some sense they already are making git better since that’s the data format that the tool supports.
Also, some of the UI improvements or even protocol improvements might make it into git eventually.
I’m skeptical about it being worthwhile to switch to a new backend. Maybe it would work if it’s just local, but a lot of IDE plugins and other tools would need to be written, and git has lots of inertia.
> why would the contributors to jj not just try to make git better by addressing these weaknesses?
Because jj isn't being built to improve git, but to allow using git at Google to work on Piper-backed code instead of hg/fig, and adding support for the abstractions that are needed to support git and piper was probably seen as free complexity on git's side.
This is slightly incorrect. I started it because I believed in the idea of modeling the working copy as a commit, but it's true that I made the storage pluggable from the beginning because I wanted to be able to convince my team at Google that we should use it internally too.
I thought it was simply a response to git5/git-multi getting deprecated once fig became popular.
I'm a bit surprised then, that's one of the things that has kept me from trying it as it feels weird to not have that much control over what gets into the commit. Using magit doesn't help as it's just way too good once you get used to it.
Think of it this way: they are trying to make git better by addressing fundamental weaknesses in git, but some of those weaknesses require a fundamentally different UI paradigm and as such aren’t practical to upstream, hence a new project.
> I’m not being glib, just that it puzzzles me when a new oss comes out that does the same thing as another tool but a a bit different. I would have though that there is a way to have a git plug-in or even a way to contribute to git to enhance the issues outlined.
You would have thought? Plugins? Git doesn’t have plugins to my knowledge. Hooks are not plugins for security reasons. Meaning you can’t distribute hooks, have people install them and call it an extension.
Then, assuming that they could just make Git better. Are they interested in supporting all of Git plus their new work? Why would they be?
If anything, lazygit has shown it is possible to have more natural workflows on top of git as demonstrated by https://www.youtube.com/watch?v=CPLdltN7wgE, so it is indeed possible
Merges were so, so painful in SVN, though. Maybe SVN improved towards the end of its life? I remember everyone went well out of their way to avoid needing to merge until git, because it could take hours to resolve a heavily-conflicted tree.
People complain constantly about how painful git is when their simple workflows break down. It is legendarily complicated and difficult to use.
But when an alternative comes along and long-term git users try to tell people how much better it is? Everyone immediately forgets all their issues and frustrations. It’s honestly kind of bizarre.
Anecdata: I have no issues with my simple git workflow that I’ve used at the previous 3 companies I’ve worked for. As long as communication is solid across teams/people, merge conflicts and rebasing isn’t that big of a pain to introduce an un proven tool to an org.
My thought would be that most people are happy and it’s the <5% of people who complain the loudest that are heard.
Your org doesn’t have to adopt the tool. Nobody on my team needs to know or care that I use jj instead of git. The only people who do know or care have themselves switched when I showed it off to them.
I’ve worked too many places where I’ve helped fix too many coworkers’ broken git repos to believe in a simple git workflow. Basically everyone uses the same fetch/branch/commit/merge-to-main approach and people still constantly run into problems. None of the people with some claimed simple workflow are doing anything meaningfully different than what everyone else is doing.
It’s just astonishingly easy to internalize all the fixes and band aids we’ve adopted to smooth the sharp edges and forget how often we have to work around them.
But I don’t see the complaints that were claimed. I help people as well. They learn, and we move on. If they don’t learn then we have another problem to solve.
I will admit there is occasional call where I have to get someone out of a twisted pretzel, but that is few and far between.
You don’t deal with rebase conflicts? You don’t ever unstash to the wrong branch? Or to the right branch, but it was changed and now it’s applied uncleanly? You don’t ever want to go fix a previous commit? You don’t ever need to do linear work that gets merged one piece at a time into the trunk?
All of these things (and others) can be worked around with varying levels of annoyance ranging from just living with it to new commands that help out to changing your workflow to completely avoiding some workflows. But in my experience nearly everyone deals with those annoyances on a semi-regular basis.
With a proper workflow and communication, only the first one (rebase conflicts) is a semi-regular occurrence (once a month). The others I have never had to do while working on a team. We establish a clear way of working and everyone abides by it.
And before you say well this doesn’t work with a bigger team, my team is 8 and the org is 50 on the same codebase. At “google scale”, I understand this might not be the same case.
If someone has to go out of that workflow to fix a previous commit on main, they submit a pr on top of latest.
Again, I don’t see the major complications here. It seems to me to be fixing a communication issue in an org more than anything.
jj has no evidence today to support the idea that it will be a main tool in the future.
An even heavier burden of proof comes from the idea that it could be adopted by mainstream forges, so it's certainly not an argument on how jj is better than git.
Author here: The biggest benefit for me is the ability to work on change N+1 (or sometimes even N+2 also) while change N is being reviewed without subjecting myself to constant merge resolution if I need to update change N due to e.g. reviewer feedback, issues in pipelines, etc. `rerere` helps somewhat, but the flow is much nicer when I can just rewrite one commit and everything downstream updates itself.
The others are also listed in "the good" section of the article, but that's the biggest one. How much of a benefit that is for you will really depend on your usage patterns - maybe it's solving a problem you don't have. But the nice thing is since it's git compatible, people for whom this solves a problem can use jj, without having to migrate their whole team/company from git, unlike other solutions like fossil, darcs or pijul.
I commented on exactly this elsewhere in the thread. It’s hard to understate how insanely useful it is to be able to painlessly edit earlier history even when there are later threads of work based on it.
Also in my experience everyone says “my default workflow is easy” when you talk about an alternative VCS but in every other context they complain about how often that workflow starts getting exceptional: rebase conflicts, stash conflicts, WIP commits, uncommitted work accidentally blown away, etc.
And what are the steps when you work on 3 things at the same time (you are working on PR #2 + fixing issues in PR#1 + a hotfix needs to be deployed for an issue)? Or when you need to continue fixing things on the PR?
On the absolute happy path what you do above is the same I do, where jj shines is when you het out of the happy path.
Isn't git-worktree designed for this kind of situation, where you could open each PR or branch in its own directory and switch your editor / other tooling between workspaces without losing state between them?
yeah, you could do this, but it gets kind of annoying in my experience. However it is another solution, most of what you do in jj you can do in git, its just more convenient
that's fine, what Im saying is that the above workflow is idealized and you have to do a lot more things overtime. If you look at the happy path it looks similar. With the unhappy path instead of keeping stashes and commiting trash then amending. You describe your change beforehand, and any change you do goes there. Thats it.
Hotfix? jj new main
go back to what you were working on? jj edit refA
need to solve some problems in another pr of youre? jj edit refB.
Theres no overhead at all in thinking these, you can never lose work by mistake, you can never mess it up. It just works
Personally I usually stash if it's partial work I'm going to return to soon. But you can create a commit if you want. Just name it 'WIP' or 'temp' or something. When you come back and finish it you can write the proper commit message.
And then you have to remember which branch the each entry of the stash was on, deal with unstashing conflicts, remember to drop the latest stash after fixing those, etc.
Tools like the stash are band aids that come with their own set of “fun” failure modes.
So when I say there a problems with stashing, you suggest wip commits. When I point out problems with wip commits, we go back to stashing?
jj needs neither because everything in your working copy is already part of a commit. You can’t accidentally push unfinished changes because jj will complain if it hasn’t been described (given a commit message). If you need to move changes between commits you don’t need to stash because you can easily move the contents of a commit around.
Stashing vs WIP commits have pros and cons. I've never hit any of these problems you're pointing out, so I don't have any particular strong opinion about which one you should use.
I can see that being nice I guess.
If jj's stashes are just regular commits, don't you have the problem where you accidentally push a stash just like you push a WIP commit?
> don't you have the problem where you accidentally push a stash just like you push a WIP commit?
Because jj doesn’t require commits to be on a branch, and the equivalent feature (bookmarks) don’t automatically advance, if you created a new commit to stash some stuff, the name wouldn’t move to it, and so when you push nothing would happen.
I don't think so. Like the OP, my everyday git usage is pretty straightforward. So "maybe this thing you do every once in a while could be a little nicer" doesn't feel very compelling.
Now let’s talk rebase/merge conflicts, stash conflicts, forgetting to un-wip, and on and on and on and then maybe acknowledge that all of these “but my git workflow is simple” comments are a convenient fiction that doesn’t actually exist in practice.
that's what I used to do, now I just use jj and I dont need to either keep a set of stashes nor create superfluous commits based on when I decided to change branches
One advantage is that jujutsu tracks merge conflicts so you don't end up in a situation where you'll have to resolve the same conflict more than once, which may sometimes happen with git.
I'm not a heavy jj user. But as I understand it, git-rerere is bolted on top and not well integrated. It just stores resolutions locally on each machine in .git/rr-cache. You need extra steps to share resolutions between contributors. https://stackoverflow.com/questions/12566023/sharing-rerere-...
In jj, resolutions are first-class objects—treated the same as any normal commit, pushed/pulled like normal, etc.
Linus doesn’t want people who he pulls from to back-merge, i.e. to merge at “random points” from him into their trees to keep up to date. But what about merge conflicts? Then he has to deal with them. So then he might begrudgingly ask them to back-merge right before making a pull: a real zig-zag pattern.
First-class conflicts would solve all that I think.
In some cases, yes, but I think the way jj handles conflicts is easier to follow. You can see the conflict resolution in `jj diff` and you can rebase it like a regular commit. rerere's state is harder to understand, I think. See https://github.com/martinvonz/jj/issues/175#issuecomment-107... for some more discussion.
But why build a whole VCS around that one use case? I looked through the docs and honestly it looks like it complicates life instead of making it easier.
edit: everyone is so focused on the "single use case" part and not the fact that it looks more complicated and verbose than regular git. Unless everyone using it is some git god that runs into edge cases left and right... maybe my workflow is just not that complicated to need a solution to problems I don't have?
Because every person in here who’s actually used jj is telling you straight up that it’s less complicated.
Using jj is less complicated in—as far as I can tell with six months of conversion—actually 100% of the time. I spent one day adapting and maybe two week of having to look something up every other day. And now all of git’s infamous pain points are gone.
There is no index. There is no stash. Rebase conflicts are (and rebasing in general is) no longer painful. Jumping between work is a no-op. Fixing up earlier work is a no-op. There are no modal “interim” states like during conflicts where you have to abort or continue to get back to normal.
You don’t have to be a git god to run into the many sharp edges of git, and all of the “but my workflow is simple” comments break down upon even cursory inspection. Everyone’s workflow is simple until a rebase goes wrong, or they unstash in the wrong branch, or they need to unfuck an earlier commit, and that list goes on and on. And when you’re in these states you have no practical way to save or persist incomplete work.
Jujutsu is both more powerful and simpler, and I say that as someone who knows git inside and out and who’s used it pre-GitHub.
The point for me is to build better and remove the need to know so much about vcs. Git is powerful but requires too much knowledge about git itself to get things done or to tackle possible conflicts in collaboration. Jujutsu tried to remove some of the concepts or make a better ui, to remove some complications. It's like providing an abstraction like python instead of C. Yet it's not perfect.
I also struggle to transition to jujutsu yet I admit some benefits. But honestly both are yet too complicated and error prone for something as basic as communicate changes and align versions with each other. I'm happy to see initiatives such as jj but it's still an alpha.
You’re like the perfect audience for jj. Give it a try with some of your git repos. If you hate it, easy to go back to git afterwards. But I think you’ll like it.
But as I’ve read (according to Martin) doing it this way simplifies the implementation a lot. You track everything directly in the DB. In turn you don’t have to care about all sorts of states like being in conflicted git-am state, conflicted git-merge, etc.—these states cannot happen, it’s all in the DB (not some dirty working tree state where some flags or files somewhere in `.git` or `.jj` tell you “there are some conflict markers thereabouts, enjoy”).
And that frames it in terms of being easier for the people implementing jj. But I think it will also benefit us users by making it easier for them to create streamlined tools.
git-rebase(1) and all that is great and all. But definitely messy. And we only recently got `--update-refs` to update dependent branches, which is kind of late considering how important branches are in Git.
Moreover, even if it was say 15% better than git (and I doubt it is), the overhead of moving a whole industry across VCs is billions of dollars of loss.
Every single engineer has to start over in their mastery experience by learning all the new quirks and issues, and all the tools, libraries, pipelines need to be rewritten.
IMO better to support the dominant tooling than invest in fracturing one of the few areas where engineers can agree on something for once (to everyone's benefit).
The upside with jujutsu though is that it's completely compatible with Git and you can work on the same repository with Jujutsu while your coworkers use Git.
This allows the people who want the 15% gain to have it, while not forcing anyone to do a costly migration.
I know people who really hate git because the UI is so awful, and it’s (theoretically) way more than a 15% gain for them. They’ll get to stop rubbing broken glass against their legs every day.
Git feels like solving puzzles everytime there are branches or collaboration made on a repo.
So every day. Most productive days are when I dont need to use git.
JJ is a workaround for git pains but the UX is not mature enough for replacing git yet.
Having distinct local working copies of same repo per git branch still works great, not stash needed, no conflicts or pain with local wip and drafts changes overwritten by pull.
No blocker for switching branches. No reset hard/soft whatever. And makes files comparisons easier and makes my git workflows easier.
I still feel vcs could be simpler and solved problem if more engineers care about the topic of vcs and generally speaking about the ux of their main tools instead of creating new TUI and plugins to workaround broken ones.
> Every single engineer has to start over in their mastery experience by learning all the new quirks and issues, and all the tools, libraries, pipelines need to be rewritten.
I was a git power user. I became comfortable enough with jj to replace git entirely in one day.
Funny thing, I'm becoming an evangelist of Jujutsu but somehow I found myself stuck for 30 minutes with Jujutsu not being able to push a desired branch/commit, and Jujutsu had made my local git copy in detached HEAD. In the end I created a few useless branches through jj, and was still not able to articulate or understand through the docs how to sort my situation and tell jj that I want to move my latest work on main, nor to find a way to rebase/merge my accidentally created bookmarks/branches on top of main. Then ended up switching back to git, redo all the coding again and push.
Having to understand either jj or git models through tons of tutorials and videos, while I just want a way to dialog easily with my changes in my own vocabulary and my own mental model, make me believe I'm a lost cause or I need my own VCS
Anyway Jujutsu stil feels great, I just hope now to not need to suffer like I did with git.
I find the most benefit from `jj` when I'm preparing some commits for review and I want to make them tell a coherent story. I often want to then rearrange commits, split or merge them, move things from one to another.
For people who work on repos by themselves or with a small number of others, the advantages of `jj` would be smaller in most cases, I would think.
I've so far failed to see why jj needs to reimplement git to add some UI pleasantness.
To me, the real value I saw when playing with jj was the drag-and-drop change history manipulation in the `gg` GUI app -- and as far as I can tell, that could just as well live directly on top of git.
Is there a sales pitch for git users who are of the history preservation school of thought, does the changeset id stability help tracking what happened in the automatic merge heuristicss applied during rebases?
I have used git since pre-GitHub. I have written an implementation of git. I have given talks on git at local users’ groups. I am awed by the underlying data model of git and deeply appreciate it as an incredible innovation. I still think that it’s an amazing foundation to build a great VCS on.
I learned jj in one morning and will never go back to git.
My ideal git workflow is pretty simple:
1. `git switch -C new-branch` 2. Make changes, time passes 3. `git add .` 4. `git commit -m "Description of changes made"` 5. `git fetch && git rebase origin/main` 6. `git push` 7. Make pr to main
If main is out of date I rebase again to update.
This to me feels pretty light weight and is not a hinderence in any way on my day to day tasks.
What exactly makes `jj` better? Is there a specific usecase i'm not understanding?