Live data from Hacker News

jj – the CLI for Jujutsu

steveklabnik.github.io

491–500 of 517 posts

Re: jj – the CLI for Jujutsu

#491
post #486
post #456

Earlier quoted context omitted.

# I've finished something significant! Carve it out from the working "change" as its own commit. git add -p git commit # Oops, missed a piece. git add -p git commit --amend # Let me look at what's left. git diff # Oh right, I had started working on something else. I could just leave it in the working change, but let me separate it out into its own commit even though it's unfinished, since I can always add pieces to i…

Yes, and? I wasn't trying to demonstrate jj superiority. I was responding to a post about a normal messy workflow and showing how to handle it in jj. If I'm writing a description of how to use jj, I could take several different approaches. Am I writing for a git novice? A git expert? An expert in a different VCS? A novice in any VCS? And even within those, there's a big difference in whether you're a solo dev working…

i apologize if my comment came across as accusing you of claiming superiority and failing your intention. it was not meant to do that. if there is any accusation then it is the general assumption that jj is better that can be felt in the overall tone of this discussion thread.

your examples show a particular aspect of jj, and to me they demonstrate that jj isn't better across the board for a certain group of users at least.

i'd be interested to know if i got the impression right that jj provides more commands for specific actions that don't require you to understand how the system works underneath. that is significant because i like to understand how things work underneath. more commands then means that i have to learn more to understand each one of them.

in a sense it is like high level languages vs low level languages. don't get me wrong, i like high level languages. i prefer them actually, but i also like minimalistic syntax, so i prefer smalltalk and lisp over, say scala which has a reputation for being complex. but scala is starting to make more sense once i learn what lower level primitives the syntax translates to.

same goes for jj. i want to understand the primitives that the commands translate to. git puts me closer to those primitives, which makes it harder to learn but possibly easier to use or understand once you get it. since jj is built on top of the same primitives it should be possible to reach the same understanding. jj uses those primitives differently, and that's the part that i find interesting. the more i understand how jj works with git primitives the more i like it. but it also shows that git primitives are not bad if they enable such different approaches. (i wonder how different git primitives are from other approaches such as pijul which is based on a theory of patches).

i guess what would help me would be a guide to jj for those who are familiar with how git works underneath.

the examples in your second to last paragraph sound very interesting. please show. of link to references if you know any.

Re: jj – the CLI for Jujutsu

#492
post #421

Earlier quoted context omitted.

The only thing that changed in the two things you wrote was `ranch` -> `cdef`. Every other part of that PS1 output was the same. Now put yourself in the shoes of a git novice and ask yourself if you'd always notice the difference. At least from my experience, they often don't, especially if they're concentrating on something else, it if they're using an IDE and the visual information about which branch/commit is chec…

I missed the formatting on my original comment, but to use real commit names: fragmede@laptop:(main)~/projects/project-foo$ fragmede@laptop:(cf95b5ac22fd...)~/projects/project-foo$ fragmede@laptop:(opengl_meters)~/projects/project-foo$ Is the difference between these 3 really that subtle? How do you miss that the line lengths are different and that one of them is a pile of letters and the other is a word? I'm not try…

I don't understand your example. Why haven't you added commit messages? Would you do that with git? In what situation are you creating different branches like that and not using either `jj commit` or `jj describe`?

Re: jj – the CLI for Jujutsu

#493
post #447

Earlier quoted context omitted.

Or you make tools that are easier to use, so that you can spend that week learning something more useful than the finicky details of branch vs detached head checkouts.

Don't get me wrong, git has some accidental complexity (as will any tool introduced, including what I am seeing with jj). But a lot of it is just incidental complexity. It doesn't matter how much lipstick you put on it, at the end of the day some concepts need to be learned.

Did you mean inherent complexity instead of incidental complexity?

I think the inherently complex things in git are (1) the content-accessible object store, snapshots, plus the merkel tree approach to keeping track of commits and parenthood, (2) merges, rebases, and resolving conflicts between two different changes with a common ancestor, (3) possibly syncing commits between different remotes, although I think Git's approach adds accidental complexity to the problem as well.

Everything else is a question of the user interface: how you choose to show a commit, how you choose to update the project files, how you choose to let people create new commits, etc. And I think the Git CLI is a poor user interface in a lot of places. There are a lot of features which are really powerful but difficult to use, whereas actually they could be just as powerful but far more intuitive to use.

In fairness, this is no slight to the Git developers - they are improving a lot of that interface all of the time. And finding a simpler interface is often a lot more hard work than finding the complicated interface, and I don't think I would have figured out how to create something like jj until I'd seen it before.

Re: jj – the CLI for Jujutsu

#494

Earlier quoted context omitted.

Some of jj's users are "I find git hard and like jj more" but a lot of us are/were git experts before switching.

Yes, I understand that but what I'm saying is that the problem definition isn't completely clear to me. I'm not saying that there is none, it's just that it may not be obvious at the first read.

Ah, no worries.

Re: jj – the CLI for Jujutsu

#495
post #491
post #486

Earlier quoted context omitted.

Yes, and? I wasn't trying to demonstrate jj superiority. I was responding to a post about a normal messy workflow and showing how to handle it in jj. If I'm writing a description of how to use jj, I could take several different approaches. Am I writing for a git novice? A git expert? An expert in a different VCS? A novice in any VCS? And even within those, there's a big difference in whether you're a solo dev working…

i apologize if my comment came across as accusing you of claiming superiority and failing your intention. it was not meant to do that. if there is any accusation then it is the general assumption that jj is better that can be felt in the overall tone of this discussion thread. your examples show a particular aspect of jj, and to me they demonstrate that jj isn't better across the board for a certain group of users at…

Heh, sorry, I guess you triggered my defensive "book-length response" reaction. Here's the next book:

As you say, jj uses git primives so the core data model is the same. I say "core" because jj subtracts out the staging area and stash and adds in "changes" and the operations log. But otherwise, your understanding of the git data model translates seamlessly, with one exception: git-style branches aren't really a thing, they can only be emulated. What I mean is that in git, the topological head that is associated with a branch ref (apologies if I'm getting terminology wrong), when accessed through a branch name, is taken to represent not just that commit but all ancestors of it as well. So "merging a branch" or "checking out a branch" are natural things to do.

jj prefers to be grounded in the commit DAG. Since you're always "updating" a commit (really replacing it with new ones), the only other state is the location of that commit in the DAG. You specify that before that change exists, with the default being the same as in git: the single descendant of the last thing you were working on (the branch tip in git, the @ change in jj). `jj new` (or `jj commit`) creates a single child change of your current change, unless you pass flags putting it elsewhere in the graph -- and it is very common to put it elsewhere if that's the thing you want to do.

An example: say you have a sequence of changes (and thus commits) where you're working on a feature: A->B->C, with C being what you're editing now (the @ change). You realize that your latest change, C, might not be the best approach and decide to try another. You do `jj new B` to create an alternate descendant and implement a C2 approach. Now you have A->B->{C,C2}. Which of C or C2 is the tip of your "feature branch"? Answer: who cares? At least until you want to push it somewhere. My default log command will show the whole DAG rooted at A, the first change off of "trunk" commits. I might do further work by creating descendants of C2 and thus have A->B->C and B->C2->D->E, and I might rebase or duplicate ->D->E on top of C if I want to try it out with that approach. Or I might inject or modify things earlier in the graph like A->X->B'->Y->{C,C2} (where B' is an updated B). If I push part of that DAG up to a server that I have declared to be shared with other people, then jj will know what parts of the graph should no longer be mutated and prevent me from doing that (by default).

All of this follows the core git model, it's just that mutating the graph structure is expected and common with jj and only restricted modifications seem to be common with git. You can do any and all of it with git, but rebase, undo, and conflicts are better-supported and feel less exceptional with jj. And you can jump to anywhere in the graph, or move around pieces of the graph, anytime and without necessarily changing the @ (working change) you're looking at.

This also means that jj doesn't need a `git switch` or `git checkout` command. That notion is replaced by making your current change (there's always a current change, it's the one that will be associated with any edits you make) be the child of wherever in the DAG you want, probably with `jj new `. As with git, there's a snapshot associated with it, so the other purpose of `git checkout` (to make your on-disk copy of a file match the contents of a given commit) is `jj restore filename`. In `git help checkout`, it says "Switch branches or restore working tree files". The "switch branches" part is `jj new`, the "restore working tree files" part is `jj restore`. `jj new` is equivalent to `git checkout --detach` or `git checkout ` (or plain `git checkout `). If you only consider the "make (part or all of) the working tree match that of this commit", then all of the different variants `git checkout` being in one command makes sense. But jj distinguishes things that operate on the DAG vs trees (file contents). And it doesn't need different flags or modes for the index/stage vs a commit; everything is a commit. `git stash` is unnecessary, the equivalent is `jj new ` (you're just moving your working directory to be based on a different commit; the previous patches aren't lost or anything, they stay where they were in the DAG. Grab them from there if you want them and put them anywhere else in the graph, with or without changing your working directory as you wish.) `git reset` is redundant with `jj restore` -- well, mostly; the other part of what it does is handled by `jj abandon`. (Again, `restore` for tree contents, `abandon` for the graph node.) `git merge` is `jj new` with multiple parents instead of one so a command isn't needed.

One way to say it: with jj, `new` + `describe` + `rebase` + `abandon`, you can do all the graph manipulation. Add in `squash` + `restore` and you can do all content manipulation too. That's it for local operation. Well, maybe `jj file track/untrack`. The flags are pretty consistent across commands and don't fundamentally change what they do. Everything else is either for working with servers and other repositories, convenience utilities for specific situations, details like configuration, or jj-specific functionality like the operation log.

The other major difference is that jj adds in the notion of persistent "changes" (I wish we'd come up with a better name) that in normal operation correspond to a single "visible" commit, but which commit that is changes over time. A change has a single semantic purpose and doesn't lose its identity when rebased or merged or the contents of its files are changed. This difference is actually starting to diminish, because git (or rather, the git forges and tooling) has started preserving the footers that identify these change IDs over various operations. So if you're using git in connection with gitbutler or gerrit or whatever, you'll be getting the benefit of these persistent identifiers even if you continue using the git cli.

Sorry, that's a lot, but it'd be my attempt at "jj fundamentals for someone who understands git primitives well".

Re: jj – the CLI for Jujutsu

#496
post #32
post #26

Earlier quoted context omitted.

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…

Mercurial was there, was better and more complete. Too sad it didnt win the VCS wars.

Mercurial and Git started around the same time. Linus worried BitMover could threaten Mercurial developers because Mercurial and BitKeeper were more similar.

Re: jj – the CLI for Jujutsu

#497

Earlier quoted context omitted.

> 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. All true. I ended up writing my own `jj push` and `jj pull` aliases that automated all this. They aren't simple aliases, but it worked. `jj push` for example "feels" very like `git push --force-with-lease`, except if you'v…

> True, but how is this different to any other VCS? What other VCS behaves this way by standard? If it's not in e.g. .gitignore "git status" will show that it's aware of it, but won't automatically absorb them into the commit? https://github.com/jj-vcs/jj/issues/5596 doesn't seem to be the oldest instance of this problem, but does seem to be the current discussion, and there seems to be no consensus on how to fix. Oh…

> https://github.com/jj-vcs/jj/issues/5596 doesn't seem to be the oldest instance of this problem, but does seem to be the current discussion, and there seems to be no consensus on how to fix.

Ahh, sorry - I didn't understand the issue. Yes, that is going to catch you unawares.

I can't see a better solution than a warning. It looks to be an unavoidable negative side effect of jj's auto-commit behaviour, but auto-commits bring so many positives I'll wear the occasional stray add. Usually they are harmless and trivial to reverse - but I've made a mental note to revise my jj workflow. When I change .gitignore, do it in the oldest mutable commit.

> Oh, and ISTR submodules were a pain to work with.

Yes, they are, although that's no different to git. JJ's tracking of submodule hashes in the parent repository behaves exactly as you would expect. What jj doesn't have is a command to bring the submodule into sync with the parent repository, like git's submodule command.

However, I find git's submodule command behaviour borders on inexplicable at times. I wrote my own `jj submodule` alias that does one simple job - it checks out every submodule the parent repository owns, at the hash recorded by the parent. I find that far better than the git command, which follows the usual git pattern of being a complex mess of sub-sub-commands with a plethora of options.

If jj automatically recursively updated submodules when you moved to a different change-id, it would be most of the way there. If it added commands to add and remove submodule directories, it would be there, and the reasons people dislike git's submodules would be gone.

Re: jj – the CLI for Jujutsu

#498
post #493

Earlier quoted context omitted.

Don't get me wrong, git has some accidental complexity (as will any tool introduced, including what I am seeing with jj). But a lot of it is just incidental complexity. It doesn't matter how much lipstick you put on it, at the end of the day some concepts need to be learned.

Did you mean inherent complexity instead of incidental complexity? I think the inherently complex things in git are (1) the content-accessible object store, snapshots, plus the merkel tree approach to keeping track of commits and parenthood, (2) merges, rebases, and resolving conflicts between two different changes with a common ancestor, (3) possibly syncing commits between different remotes, although I think Git's…

I think you need to setup something like magit, tig, and maybe lazy git, to truly see the power of git. Most people don't do version control other than snapshotting things every once in a while. They might as well use git inside cron.

A patch is an idea to take the code from a state to another state. It contains both the mechanical work, the intent, and extra metadata. It's essential when doing distributed development work over a single code base. A git repo is a node and it lets you accept and produce new message to the other nodes.

If you care about the state of the canonical repo, you want every step to be able to compile/build/be verified. So that means accepting good patches from everyone else.

But the work of producing new patches can be messy. But you still need to track each steps, branch off to do experiments, catchup with new updates to the foundational model of the code,...

The design of how git store information makes all those operations simple while giving you the maximum control over them. But there's one mechanism that is kinda the bane of every novice: diffing and patching. Because git doesn't store the file deltas. It stores the changed files. Diffing is the interface for inspecting the changes and patching is how those changes are propagated to files.

The default diff mechanism relies heavily on lines. Which suits most programming languages as a line is often the unit of intent. Conflict occurs when git detect that two diffs is trying to alter the same section of the files. It's actually a solution mechanism instead of a problem as most novices see it.

But I don't blame them as a lot of novice don't have experience with reading diff files or use the patch tool to apply such changes. The tree of commits and object store is more easily explained (although it rarely get explained). But a lot of fellows I met are genuinely terrified of resolving conflicts, because they can't read diff files.

I genuinely think that git is an awesome tool. But you need to get familiar with some concepts like: What a commit is, what a branch is actually, why HEAD is important,... The operations like fetch, pull, push, rebase, cherry-pick, commit, checkout,... become way more obvious.

Re: jj – the CLI for Jujutsu

#499
post #296

Earlier quoted context omitted.

Please don't post shallow dismissals, especially of other people's work. A good critical comment teaches us something. https://news.ycombinator.com/newsguidelines.html

I'm sorry, it's hard to say anything good about a project that brings nothing new to the table while desperately trying to replace a well-established industry standard. Such attempts look annoying at best and irritating at most. At the very least the person behind the project could have been more humble in pushing it, and instead of presenting it as a "git killer" causing everyone only headache he could have had 1) p…

I'm the moderator who changed the title, which is a routine action. That doesn't mean the post itself is inappropriate for HN.

> allowing this kind of posting only works against the HN

HN is for curious conversation between good hackers. We can't and won't ever disallow a post about a software project that someone has built and that others find interesting. That would be way too interventionist and completely against the ethos of HN.

If you have a substantive critique of the project, you're most welcome to share it in the spirit of curious conversation that this site strives to cultivate. Your original comment and your reply to me reek of just the kind of curmudgeonliness and snarkiness we're trying avoid here.

Re: jj – the CLI for Jujutsu

#500
post #364

Earlier quoted context omitted.

ooo that will be a nice improvement. So many times I've run `jj status`, then saw a file I wanted gitignored, so I'll edit my gitignore, but the file has already been added to the repo so I have to `mv /tmp/ && jj status && mv /tmp/ .` to get the file out of the repo.

[snapshot] auto-track = '~glob:**/*'

[dead]
Post reply on HN