←back to thread

Jujutsu for everyone

(jj-for-everyone.github.io)
434 points Bogdanp | 2 comments | | HN request time: 0.456s | source
Show context
idoubtit ◴[] No.45085587[source]
I've now seen a dozen of articles that explain that jj is wonderful and better than Git for everything. This tutorial is of the same kind. Now that I've read extensively about the good part, I'd be more interested by the bad and the ugly. Because my experience with jj was more balanced.

When I tried jj, I found a few pain points that made me return to Git. For instance, I was sharing a branch with a co-worker where we were just piling commits as soon as they were ready (after `pull --rebase` if necessary). Since jj doesn't have names branches, that workflow was easy with git and tedious with jj – even with the `tug` alias. The process in the "Tracking remote bookmarks" chapter of this tutorial still doesn't look nice to me.

Another pain point was that jj could not colocate with light clones, like `git clone --filter=blob:none`. Maybe that's fixed now.

replies(7): >>45085702 #>>45085823 #>>45085842 #>>45085897 #>>45086038 #>>45089721 #>>45095158 #
stouset ◴[] No.45085823[source]
I’m slightly confused. jj has named branches. They’re just called “bookmarks”.

Once you track the remote bookmark, `jj git fetch` will update your local one to match the remote.

replies(2): >>45087124 #>>45089727 #
MrJohz ◴[] No.45089727[source]
Bookmarks are similar to branches (and I believe implemented as branches?) but aren't quite the same thing. In particular, you can't check out a bookmark, you can only check out commits. If you do something like `jj edit <bookmark>`, it'll resolve <bookmark> to figure out which commit it points to, and then check out that commit. If you create a new commit now, the bookmark won't automatically be moved along, because jj doesn't know if you want to add a new commit to the <bookmark> branch, or if you're trying to create a new branch that forks off <bookmark>.

Whereas `git checkout <bookmark>` puts you in a state where you've checked out the _branch_ and not the commit, which means if you create more commits, they'll automatically be added to <bookmark>. To create a branch forking off from <bookmark>, you need to first create a new named branch, then start adding commits to it. (There are other ways as well, but this is the most standard approach.)

The tradeoff with Jujutsu's approach is that it's very easy to create lots of lightweight spin-off branches (just `jj new <fork point>`, no need to come up with names first). But it's slightly harder to do the traditional linear approach where you're consistently committing in a straight line on a single branch, and then syncing that branch with another remote. This is because for each new commit you create, you also need to update the bookmark manually.

In the parent poster's case, it's probably complicated further by them working on the same branch as someone else. Assuming there's no rebasing going on, this shouldn't be too complicated, but it's another case where in git you would check out a branch, and `git pull` will automatically forward you to the head of the branch, whereas `jj git fetch` will just move the bookmark without moving you.

It sounds like the workflow that this person is using doesn't fit that well with how jj "wants" to be used. I believe there's been some talk in jj of a way to automatically update branches when creating a new commit, which would help somewhat, but I think it's also a natural effect of having built their workflow around git, and that flow just not quite working so well in jj.

replies(1): >>45091477 #
epolanski ◴[] No.45091477[source]
> In particular, you can't check out a bookmark, you can only check out commits.

That's because "branches" in git are an imaginary concept, they are simply a label you give to a certain commit, nothing else, nothing more.

Moreover, in git this is further complicated by the fact that you may have branches with the same identical name on different environments (your local and remotes). So you can have, by any means, 4 `feat/foo` branches all being the "right" `feat/foo`, and you need to decide which one is the right one (we tend to default to some origin, but what if you have local work that is ahead of origin? maybe on two different computers? Meanwhile your branch is on a fork of a different origin. Git branches do nothing but complicate a simpler model where what matters is the commit, history and diff, yet we fixate on label names which are just conveniences).

At the end of the day branches do nothing but add complexity, I much prefer the jj model, bookmarks are just _local_ names you give to some edit, you move the bookmark manually, and it _may_ point to a git branch just to preserve the git backend.

replies(2): >>45091827 #>>45091842 #
tsimionescu ◴[] No.45091842[source]
> That's because "branches" in git are an imaginary concept, they are simply a label you give to a certain commit, nothing else, nothing more.

They're really not. The whole Git ecosystem makes a very clear distinction between branches and tags, even though they are virtually the same thing in the underlying data model.

For example, if you checked out a branch and then do a git commit, the branch name tag will be automatically moved to the new commit. Conversely, if you checked out a tag (or a random commit that is not the head of a branch) and then run git commit, the new commit will not get any tag.

Another example is `git rebase`, whose semantics only make sense if you consider that branch names refer to branches, not just to their latest commits, since they often modify many children of those branches.

> Moreover, in git this is further complicated by the fact that you may have branches with the same identical name on different environments (your local and remotes).

This is presented as more complicated than it is. If you are working with multiple remotes, you may end up in these complex situations, true. But Git is pretty clear about the names and identities of each of these. You have the local branch, `feat/foo`, which typically has as a default upstream the remote branch named `feat/foo` on a remote called `origin`; the local copy of that branch is the tracking branch called `origin/feat/foo`. If you have other remotes `remote1` and `remote2`, the tracking branches of those are called `remote1/feat/foo` and `remote2/feat/foo`. You will never want to commit directly to any of these tracking branches - instead, you'll create either separate local branches with each as an upstream(`feat/foo-remote1`, `feat/foo-remote2` - if you want different commits for each remote), or directly push from `feat/foo` to all of them if the intent is to keep them in sync (`git push feat/foo:remote1/feat/foo` or similar).

replies(1): >>45093434 #
1. epolanski ◴[] No.45093434[source]
> the branch name tag will be automatically moved to the new commit

On your specific repository.

replies(1): >>45095364 #
2. tsimionescu ◴[] No.45095364[source]
Of course, where else? All git operations only affect local branches, except git push.