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.
If ur making an appeal on a forum like this u could have gone with ur favorite feature, or anything else really.
It's just that although Git was created by Linus Torvalds it is not perfect and could be more beginner friendly. But efforts to improve this should be concerted, not individual efforts.
And it does not have to be jj. I just think there is room for improvement, and not to alienate old farts it could be called GitNext, GitStep, GitFlow or similar to emphasize that is still is just Git, only with an improved front end.
Maybe Linus Torvalds himself should start the initiative.
How we got git was cvs was totally terrible[1], so Linus refused to use it. Larry McEvoy persuaded Linus to use Bitkeeper for the Linux kernel development effort. After trying Bitkeeper for a while, Linus did the thing of writing v0 of git in a weekend in a response to what he saw as the shortcomings of Bitkeeper for his workflow.[2]
But the point is there had already been vcs that saw wide adoption, serious attempts to address shortcomings in those (perforce and bitkeeper in particular) and then git was created to address specific shortcomings in those systems.
It wasn't born out of just a general "I wish there was something easier than rebase" whine or a desire to create the next thing. I haven't seen anything that comes close to being compelling in that respect. jj comes into that bucket for me. It looks "fine". Like if I was forced to use it I wouldn't complain. It doesn't look materially better than git in any way whatsoever though, and articles like this which say "it has no index" make me respond with "Like ok whatever bro". It really makes no practical difference to me whether the VCS has an index.
[1] I speak as someone who maintained a CVS repo with nearly 700 active developers and >20mm lines of code. When someone made a mistake and you had to go in and edit the repo files in binary format it was genuinely terrifying.
[2] In a cave. From a box of scraps. You get the idea.
It gained tons of popularity mainly because of Linus being behind it; similar projects already existed when it was released.
Too sad it didnt win the VCS wars.
hg was slow though I don't know how bzr compared as I was using it pretty light-weight.
GitHub is an abomination.
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).
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.
1. git merge ONLY does merges (no fast forward/rebase). git pull ONLY does a fast forward
2. git log by default is git log --first-parent. Just show commits where the parent is the current branch. This makes the merge workflow really easy to understand an linear, because in the end, you only care about commits on the trunk.i do this for example when i want to see a specific edit highlighted in my editor, it's a nice workflow i think
As a git-ist (?), if I'd ever move away from git, it would be to avoid tooling that has idioms like this (like git too has), if `jj` just gonna surface a bunch of new "bad ideas" (together with what seems like really good ideas), kind of makes it feel like it isn't worth picking up unless you don't already know git.
If you have some unfinished changes at the tip and want to temporarily checkout something 2 weeks ago, you `jj new` to there (similar to `git stash; git switch whatever`), and then later `jj edit your-old-tip` to go back (equivalent to `git switch main; git stash pop`; I think `jj edit` being an extended replacement for stash-popping things is a reasonable way to think about it). (and if you don't have any uncommitted changes, you always `jj new`)
jj also has a concept of immutable commits (defaulting to include tagged commits, and trunk at origin, which it'll disallow editing as a layer of defense)
> The idiom here is use `edit` if you want to edit a commit
You know, you guys have fun with that, I'll continue using git which (probably) has the same amount of warts, but I already know them. I'll continue to refer new VCS users to jj, seems a lot easier to learn, but really don't have the interest to re-learn a bunch of ever-changing idioms.
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.
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.
Honestly, this is only because `git checkout` is so convoluted that we've collectively changed our expectations around the UX. "checkout" can mean switching to another branch (and creating it if you specify a flag but erroring if you don't), looking at a commit (in which case you have "detached HEAD" and can't actually make changes until you make a branch) or resetting a file to the current state of HEAD (and mercy on your soul if you happen to name a branch the same as one of your files). Instead of having potentially wildly different behavior based on the "type" of the thing you pass to it, `jj edit` only accepts one type: the commit you want to edit. A branch (or "bookmark", as jj seems to call it now) is another way of specifying the commit you want to edit, but it's still saying "edit the commit" and not "edit the bookmark". Unfortunately, the expectation for a lot of people seems to be that "edit" should have the same convoluted behavior as git, and I'm not sure how to bridge that gap without giving up part of what makes jj nice in the first place.
echo "something" >> foo.txt
git checkout foo.txt
What's the name of the branch this is pointed at? If I have to run another git command to find out, then it's not "pointed" at it.I think this ruins it for me then. I push my in-progress work, to my in-progress branches (then git-squash or whatever later, if needed). It makes switching between (lab) computers, dead or not, trivial.
Is there some "live remote" feature that could work for me, that just constantly force pushes to enabled branches?
So just run:
jj bookmark track myname/somecoolfeature --remote origin
and the default settings will Do What You Want. This is intended as a kind of safeguard so that you do not accidentally update someone else's work.Some people configure the set of immutable heads to be the empty set so they can go wild.
So if the last thing I did on <bar> was finish some work by making a new commit, then writing some changes, and then giving it a commit message with `jj desc`, then I am now polluting that commit with the unrelated explanatory psuedo-code. So when switching to a repo I'm not actively working in, I need to defensively remember to check the current `jj status` before typing in any files to make sure I am on an empty commit. With git, I can jump around repos and make explanatory edits willy-nilly, confident that my changes are distinct from real meaningful commits.
I guess one way to describe it is: we want to make it easy to make good commits and hard to make bad commits. jj seems to be prioritizing the former to the detriment of the latter. My personality prioritizes rigorous safety / lack of surprises.
I agree, that was a bit of an interesting approach but more-so than not it's been better in DX even though you have to 'unlearn' long term it's been a benefit IMO, but a soft one, not something you can measure easily.
this is a core feature and it makes jj possible - you're supposed to get used to jj new and jj squash into the previous bookmarked commit, which you map to the git branch head/PR.
IOW you're supposed to work on a detached git head and jj makes this easy and pleasant.
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.
I find it reasonably good with lots of tweaking over time. (With any agent - ask it to do a retrospective on the tool use and find ways to avoid pain points when you hit problems and add that to your skill/local agents.md).
I expect git has a lot more historical information about how to fix random problems with source control errors. JJ is better at the actual tasks, but the models don't have as much in their training data.
I've been busy at https://ersc.io/ (and spending time with my family, and playing Marathon...)
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.
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.
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 :(
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...
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.
They’re branching out, too. We had one in our neighborhood in Houston before moving back here to Illinois.
jj git init --git-repo my-repo
I think (but CANNOT PROMISE) that just removing the .jj folder will bring you back, but definitely take a backup of .git before you try this in case I’m wrong.
If you are _not_ in colocate mode, the .git folder is located _inside_ the .jj folder. So worth checking!
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.
`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.
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.
Like `jj commit -m 'Feature A' file1 file2` then `jj commit -m 'Feature B' file3 file 4`
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.
Yes, but this is not backwards, the way you do it in git is backwards. =)
The autosync feature is really nice too, and you can store backup repos in cloud storage folders and auto sync to those as well.
I want to build xyz,
```
jj desc -m "feat: x y & z"
```
do the work.
```
jj split
```
Split up the parts and files that you want to be separate and name them.
This will also allow you to rename stuff.
```
jj bookmark create worklabel-1 -r rev1
jj bookmark create worklabel-2 -r rev2
# Push both commits
# since we just split them they are likely not inter-dependent
# so you can rebase them both to base
# assuming rev1 is already on top of base
jj rebase -s rev2 -d base
jj git push
```
That is it.
You also don't have to follow what the GP said. I never say `jj describe` before writing code. I write the code then just say `jj commit -m "Foo stuff"`, just like I would in git.
The bigger difference I've noticed is:
1. Switching between changesets just feels more natural than git ever did. If I just run `jj` it shows me my tree of commits (think of it like showing you git's branches + their commits), and if I want to edit the code in one of them I just say `jj edit xyz`, or if I want to create a new commit on top of another one and branch it off in a new direction, I just say `jj new xyz`. It took a little bit for my brain to "get" jj and how it works because I was so used to git's branches, but I'm really enjoying the mental model.
2. `jj undo`. This alone is enough to convert me. I screwed something up when trying to sync something and had a bunch of conflicts I really didn't want to resolve and I knew could have been avoided if I did things differently, but my screwup was several operations ago! So I ran `jj undo`. And ran it again. And again. And again. And then I was back to my clean state several stages ago before I screwed up, despite having made several changes and operations since then. With git? Yeah I could have gotten it fixed and gone back. But every time I've had to do something like that in git, I'm only 25% confident I'm doing it right and I'm not screwing things up further.
3. Rebasing. When I would try to sync git to an upstream GitHub repo that used rebasing for PRs, I would always get merge conflicts. This was because I stack my changes on top of each other, but only merge in one at a time. Resyncing means my PR got a new commit hash, even though none of the code changed, and now git couldn't figure out how to merge this new unknown commit with my tree, even though it was the same commit I had locally, just a different hash. With jj? I never get merge conflicts anymore from that.
Overall the developer experience is just more enjoyable for me. I can't say jj's flow is fundamentally and objectively better than git's flow with branches, but personally and subjectively, I like it better.
the working tree being a commit has wide ranging implications as all the commands that work with commits start working with the working tree by default.
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.
So it felt like the XKCD on "standards": I now have one versioning system, if I learn jj I will have two. What for?
Don't get me wrong: it's nice that jj exists and some people seem to love it. But I don't see a need for myself. Just like people seem to love Meson, but the consequence for me is that instead of dealing with CMake and Autotools, I now have to deal with CMake, Autotools and Meson.
EDIT: no need to downvote me: I tried jj and it is nice. I am just saying that from my point of view, it is not worth switching for me. I am not saying that you should not switch, though you probably should not try to force me to switch, that's all.
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?
I am fine with that. I am just saying that the "you should use jj, you will finally stop shooting yourself in the foot regularly" doesn't work so well for me, because I don't remember shooting myself in the foot with git.
Then, for various reasons, I switched back to git.
By day 2, I was missing jj.
Stuff like "jj undo" really is nice.
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
Oh the user absolutely does if that user creates lots of branches and the branches are stacked on top of each other.
I get your feeling though; sometimes in my own private repositories I don’t bother creating branches at all. Then in this case jj doesn’t really make much of a difference.
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/*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.
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.
I mention it because while the jj command line interface is excellent, there are certain tasks that I find easier to perform with a graphical user interface. For example, I often want to quickly tap on various revisions and see their diffs. GG makes that kind of repository browsing — and certain squash operations — much more efficient for me.
If you’re interested in more information about GG, my co-host and I talked about it in a recent episode of the Abstractions podcast at about the 8:17 mark: https://shows.arrowloop.com/@abstractions/episodes/052-they-...
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.
for url in url1 url2 ..; do git clone $url; done
That’s not really a script but a basic one liner.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.
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.
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.
The official JJ docs also have a "bird's eye view" introduction and tutorial available here: https://docs.jj-vcs.dev/latest/tutorial/.
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.
It can't be both intuitive and yet too complicated to show examples at the same time.
For those in the know, how does jujutsu stack up to something like Darcs?
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.
$ 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.
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`.
I won't install Rust just to test your software. Make a debian package like everyone else.
> If you're not a Rust developer, please read the documentation to figure out how to install things on your platform
Rather selective reading we have here, don't we?
But I found this article a bit long winded and ended up asking an LLM about it instead.
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.
If you need to build on something that requires changes from 3 open PRs, can't you just start a new branch from main, merge all 3 PRs into it, and get to work? As changes are applied to the open PRs, you can rebase. Obviously that might cause some merge conflicts here and there, but as long as the PRs aren't super overlapping, they should still be manageable. If there's a ton of overlap between 3 open PRs, that to me sounds like a problem in the workflow/plan, which must be dealt with regardless of the VCS or porcelain.
If jj is so great now and works with git as a backend, it’s tough to imagine why it’s worth pursuing a native and presumably incompatible backend.
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.
Although jj as a vcs system, it does feel better, working with git through it still feels like a chore, but to be fair I only gave it a day before going back to git.
Does anyone have any good resources on how to augment a git flow through the lens of a git hosting platform to work smoothly and still reap the benefits of jj?
What specific challenges are you running into that make it feel like a chore?
Subversion is a fine VCS. But git offers a better approach with being offline-first and decentralized. It also makes merging branches a lot easier.
I don't know enough about jj to praise it, but I don't think git will be the last VCS that will become widely popular.
But then after trying jj, I wrote this tutorial because I love it even more.
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!
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. When you start thinking of code not as text there's another dimension where things can go, even a lot of logic at the comiler level with call graphs becomes useful.
dgb23•3h ago
> There's one other reason you should be interested in giving jj a try: it has a git compatible backend, and so you can use jj on your own, without requiring anyone else you're working with to convert too. This means that there's no real downside to giving it a shot; if it's not for you, you're not giving up all of the history you wrote with it, and can go right back to git with no issues.
eru•2h ago
jeremyjh•2h ago
miyoji•1h ago
maleldil•1h ago
jeremyjh•1h ago
saghm•1h ago
ongy•1h ago
Colocation has its uses bit is a bit finicky. The push/pull compatibility works perfectly fine (with some caveats of github being broken that can be worked around).
asdfasgasdgasdg•1h ago
saghm•1h ago
IshKebab•1h ago
surajrmal•9m ago
verdverm•1h ago