I'm giving jj a try but one aspect of it I dislike is edits to files are automatically committed, so you need to defensively create empty new commits for your changes. As in, want to browse the repo from a commit 2 weeks ago? Well if you just checkout that commit and then edit a file, you've automatically changed that commit in your repo and rebased everything after it on top of your new changes. So instead you create a new branch off of the old commit and add an empty commit to that branch so any file changes don't end up rewriting the past 2 weeks of history. git is much nicer in that I can do whatever I want to the files and it won't change the repo until _I tell it to_.
Just don't ever use `edit`, use `new` instead; then your changes are tracked without making a mess. I think that's much nicer than juggling stashes in git.
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.
The idiom here is use `edit` if you want to edit a commit, and use `new` if you want to make a new commit. This works identically whether you specify the commit via branch name or commit id. I'm not sure why people are saying not to use `edit` ever. It's basically just a shorthand for staging and amending changes in an existing commit, and there's still a use case for that; it's just not "I want to see the changes on this old branch".
> 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.
I disagree with the people saying "never use edit". There are plenty of people saying conflicting things about git too, and I'd argue that understanding edit versus new isn't anywhere close to the level of wart that having to get people to agree on merging versus rebasing. Like you said though, have fun with that!
No system is perfect, but there's nothing wrong with `jj edit` and `jj new`. Both commands are completely reasonable and do what you think they would do.
jj has far fewer warts than git. You don’t have to learn every jj idiom, you just have to find a workflow you like, which you will, quickly, because it’s so easy to use. Personally I don’t know why anyone uses `edit` but if they like it then I’m happy for them.
But I have a workflow I like with git and I can’t see how jj would be better. I’m genuinely curious as to whether it would be or not, but the behaviours people are describing are not things that interest me.
I think it's because it's easy to make annoying mistakes (still easy to fix with undo) with edit. And it gains relatively little over new+squash. Edit is a useful power-feature, but I think for a novice, "never use it, only use the more well understood workflow of new+squash" is a good heuristic.
`edit` is still useful; just, for ..editing (!) something, instead of viewing it.
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)
"jj new" is like "I'm going to make some changes", then you do "jj squash" to say "OK these look good enough to commit".
If you work this way you're "always" in a WIP state. And if you switch to another spot you won't lose your work, cuz it's persisted.
The end result if you work like this is you don't need stashing, since you get "free" stashing in your commit tree, which is more likely what people want (and if it's not... rebasing is easy so just move the node/`jj duplicate` it!)
`jj edit` exists but I think it's just not what people want in the default case at all. In exchange: rebasing "just works", stashing is not a thing to think about, and you don't lose your work
jj edit has good use cases, but it's not the default command you need. For instance, say you were working on some changes but had to change branches for a few minutes to do something. If you didn't manage to create a commit and want to go back to the previous staging area, you would use the jj edit command rather than jj new. It's very intuitive in my experience, something I can't say is true for managing git commits (unless you've spent years forcing it into muscle memory). I never need to run jj help. I run help commands with git all the time.
Once you get your head around it a bit, doing a new in this circumstance will be second nature, since you will have realized that a `new XYZ` in jj leads to the same underlying git state as a `git checkout XYZ` in git
If I'm understanding the thread correctly, I have a git alias to `git commit --amend --no-edit`, for exactly this workflow. When I'm hacking on something locally and want to just keep amending a commit. I only ever do this if it's HEAD though.
I go back and forth between the two approaches, but because of the whole "accidentally made some temporary changes and now it's a pain to separate/undo them because not all changes were temporary", I also usually do a jj new and then jj squash.
jj edit is the biggest jj footgun I can think of, as other comments said just use jj new. But also if you do accidentally edit or change something jj undo works surprisingly well.
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.
> jj edit is the biggest jj footgun I can think of
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.
It's not "wildly" different behavior based on the thing it's pointing to. In all 3 cases, the command is pointed at a commit and the behavior is the same. Once you know that branches/HEAD are just named pointers to commits, then it becomes obvious you are always just working on commits and branches/ids/HEAD etc are just ways of referencing them.
But branches are not just named pointers to a commit. If they were, then checking out the pointer would be the same as checking out the commit itself. But I can check out a commit and I can check out a branch and depending on which I've done, I'm in two different states.
Either I'm in branch state, where making a commit bumps the branch pointer and means the commit will be visible in the default log output, or I'm in "detached head" mode, and making a commit will just create a new commit somewhere that by default is hidden into I learn what a reflog is. And the kicker is: these two states look completely identical - I can have exactly the same files in my repository, and exactly the same parent commit checked out, but the hidden mode changes how git will respond to my commands.
In fairness, none of this is so difficult that you can't eventually figure it out and learn it. But it's not intuitive. This is the sort of weirdness that junior developers stumble over regularly where they accidentally do the wrong kind of checkout, make a bunch of changes, and then suddenly seem to have lost all their work.
This is one of the ways that I think the JJ model is so much clearer. You always checkout a commit. Any argument you pass to `jj new` will get resolved to a commit and that commit will be checked out. The disadvantage is that you need to manually bump the branch pointer, but the advantage is that you don't necessarily need branch pointers unless you want to share a particular branch with other people, or give it a certain name. Creating new commits on anonymous branches is perfectly normal and you'll never struggle to find commits by accidentally checking out the wrong thing.
No they don't. As you noted, one state is "detached head" and any competently set up shell PS1 will tell you that, or that you're on a branch by displaying the name of the branch vs the commit.
> Creating new commits on anonymous branches is perfectly normal
Sorry, that that's an example of more intuitive behavior on jj's partc, you've lost me. I've done that intentionally with git, but I know what I'm doing in that case. For someone new to version control, committing to an unnamed branch doesn't seem like a desired operation no matter which system you're using. What's wrong with requiring branches to be named?
> For someone new to version control, committing to an unnamed branch doesn't seem like a desired operation no matter which system you're using.
We have data on this! I can't cite anything public, but companies like Meta have to train people who are used to git to use tools like sapling, which does not require named branches. In my understanding, at first, people tend to name their branches, but because they don't have to, they quickly end up moving towards not naming.
> What's wrong with requiring branches to be named?
Because it's not necessary. It's an extra step that doesn't bring any real benefits, so why bother?
Now, in some cases, a name is useful. For example, knowing which branch is trunk. But for normal development and submitting changes? It's just extra work to name the branch, and it's going to go away anyway.
Fascinating. The benefit it brings is you can map the branch to its name. Of the, say, 10 branches you've got checked out, how do you know which branch maps to jira-123 and which one maps to jira-234, or if you're using names, which anonymous branch maps to addFeatureA or fixBugB?
More to the point though, what tooling is there on top of raw jj/git? Specifically, there's a jira cli (well, multiple) as well as a gh cli for github as well as gitlab has one as well. When you call the script that submits the branch to jira/github/gitlab, how does it get the ticket name to submit the code to the system under? Hopefully no one's actually opening up jira/github/gitlab by hand and having to click a bunch of buttons! So I'll be totally transparent about my bias here in that my tooling relies on the branch being named jira-123 so it submits it to jira and github from the command line and uses the branch name as part of the automated PR creation and jira ticket modification.
> Of the, say, 10 branches you've got checked out, how do you know which branch maps to jira-123 and which one maps to jira-234, or if you're using names, which anonymous branch maps to addFeatureA or fixBugB?
The descriptions of the changes. I shared some jj log output in another comment, here it is with more realistic messages, taken from a project of mine:
@ vvxvznow
│ (empty) (no description set)
│ ○ uuowqquz
├─╯ Fix compiler panic in error rendering for anonymous struct methods (rue-fwi9)
│ ○ uvlpytpm
├─╯ Stabilize anonymous struct methods feature
◆ lwywpyls trunk
│ Fix array return type unification in type inference
That (rue-fwi9) is the equivalent of jira-123, if I super care about it being obvious, I might put it in the message. But also, I might not, as you can see with the other two. You could also pass flags to see more verbose output, if the first line isn't clear enough, but in general, the convention for git as well is to have that short summary that explains your change, so if it's confusing, you probably need to do better on that.
> Specifically, there's a jira cli (well, multiple) as well as a gh cli for github as well as gitlab has one as well.
These systems do require branches in order to open a pull request. In these cases, I use `jj git push -c <change id>`, which will create a branch name for me, and push it up. This is configured to produce a branch name like steveklabnik/push-mrzwmwmvkowx for a change with the id mrzwmwmv, and ultimately, it's still easier to name locally with m or mr depending on if the prefix is ambiguous. That said, from there I do usually just click the button and then "open pull request" on GitHub, but like, all of these tools (gh is the only one I've used, but I can't imagine that the others do not work, since ultimately, it's a git repo) just work if you want to use them.
Other systems do not even require a branch to submit, and so you don't even need to do this. I would say "submit mr" and it would return me the URL for the created change request. Gerrit does this on top of plain old git.
> how does it get the ticket name to submit the code to the system under?
I haven't worked with Jira in a long time, but with GitHub, if I make a change that fixes issue 5, I put "Fixes #5" in my description, and when the PR is created, it updates ticket #5 to link the PR to that change automatically, no other process needed.
Right, this is a good point: you can if you want to, or if you're working with a system that requires them.
Just in practice, anonymous branches end up feeling very natural, especially during development, and especially if your code review tooling doesn't require names.
They look identical to people who don't know what to look for, and who don't realise that these two states are different, which is the key thing. You can also distinguish them by running `git status`, but that's kind of the point: there's some magic state living in .git/ that changes how a bunch of commands you run work, and you need to understand how that state works in order to correctly use git. Why not just remove that state entirely, and make all checkouts behave identically to each other, the only difference being which files are present in the filesystem, and what the parent commit was?
What's wrong with unnamed branches? I mean, in git the main issue is that they're not surfaced very clearly (although they exist). But if you can design an interface where unnamed branches are the default, where they're always visible, and where you can clearly see what they're doing, what's wrong with avoiding naming your branches until you really need to?
I think this is the key thing that makes jj so exciting to me: it's consistently a simpler mental model. You don't need to understand the different states a checkout can be in, because there aren't any - a checkout is a checkout is a checkout. You don't need to have a separate concept of a branch, because branches are just chains of commits, and the default jj log commands is very good at showing chains of commits.
or
fragmede@laptop:(abcdef)~/projects/project-foo$
Depending on if abranch is checked out, or abcdef which may be HEAD of abranch is checked out.
If you're having to run `git status` by hand to figure out which of the two states you're in, something's gone wrong. (That something being your PS1 config.) If people are having trouble with that, I can see why switching to a system that doesn't have that problem, it just that it doesn't seem like it should even be problem to begin with. (It's not that it's not useful to have unnamed branches and to commit to them, just that it's not a intro-to-git level skill. Throwing people into the deep end of the git pool and being surprised when some people sink, isn't a good recipe for getting people to like using git.)
> What's wrong with unnamed branches?
As you point out, those commits kinda just go into the ether, and must be dug out via reflog, so operationally, why would you do that to yourself. Separate from that though, do you "cd" into the project directory, and then just randomly start writing code, or is there some idea of what you're working on. Either a (Jira) ticket name/number, or at least some idea of the bug or feature you wanna work on. Or am I crazy (which I am open to the possibilty) and that people do just "cd" into some code and just start writing stuff?
VCS aside, nothing worse than opening Google docs/a document folder and seeing a list of 50 "Untitled document" files an my habit of naming branches comes from that. Even though I'm capable of digging random commits out of reflog, if all of those commits are on unnamed branches, and have helpful commit messages like "wip" or "poop", figuring out the right commit is gonna be an exercise in frustration.
As long as you've got something that works for you though, to each their own. I've been using too long for me to change.
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 checked out.
I don't think you're crazy, I think you're just too used to this sort of stuff to remember what it was like to still be learning git. When I say people make these sorts of mistakes, I'm thinking about real colleagues of mine who have made exactly these mistakes and then panicked that commits suddenly had disappeared.
Similarly, I think to you, unnamed branches feel like something complicated because in git that are. Git makes it very easy for commits to seemingly disappear into the ether, even though they are still there. But in jj, they don't disappear - they remain very visible, and the log UI shows them in a way that makes it clear where they come from. The default log UI is something like git's --graph output, which means you see how the different commits interact with each other. I really recommend having a look at the output of `jj log`, because I think then it'll be a lot clearer what I mean when I say that it's not hard to figure out what the right commit is.
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 trying to die on some "git is easy to use" hill because it isn't. Just that the difference between unnamed branches and named branches isn't this hidden undiscoverable thing. I don't have access to this dataset of Mr Klabnik's, but apparently it is.
I don't know how much it matters in AI-codepocalypse because I'll be honest, I haven't used git manually in days. AI writes my commit messages and deals with that shit now. (Claude ends a session with "Want me to push it?" and I just reply "yes".
> Similarly, I think to you, unnamed branches feel like something complicated because in git they are.
It's not that working with unnamed branches in git seems complicated, it's that it seems opposite to how I, and by extension, other people work. Now, obviously that assumption of mine doesn't hold true, otherwise we wouldn't be having this discussion, but going back to my Google docs example, staring at a page of documents called Untitled document isn't helpful, so in my mind, there's just a bit of digital hygiene that's necessary under any system.
> I really recommend having a look at the output of `jj log`, because I think then it'll be a lot clearer what I mean when I say that it's not hard to figure out what the right commit is.
You tell me which commit I want from this `jj log` output:
The effort to label those with `jj describe` what "nrqwxvzl" is vs "nkuswmkt" could just as well be put into using named branches.
Again, I'm not trying to die on a "git is easy to use" hill, because it isn't. My point was that "> these two states look completely identical" isn't true if you setup PS1 to tell you what state you're in.
jj gets a lot of things right, there's no panicking that work got lost. That is a big deal! The emotion toll that the possibility of that happening with git causes on new users is hard to understate. It's hard to not be scared of git after you've lost hours of work after running the wrong command.
I didn't raise $17m to build what comes after git, although I've thought a lot about that problem. Improvements in that area are welcome, and the easier it is for people to work with stacked change sets and branches, globally, can only be a good thing. I can't make everyone else get as good at git as I am (or better!), so I welcome better tooling.
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`?
Sometimes it seems to me that's only in SWE we allow people to proceed in the workplace without any training. There's enough learning material that people should take a week or something to practice git and not be git novice anymore.
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.
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.
> any competently set up shell PS1 will tell you that
I certainly hope your shell is not running `git` commands automatically for you. If so, that is a RCE vulnerability since you could extract a tarball/zip that you don't expect to be a git repository but it contains a `.git` folder with a `fsmonitor` configured to execute a malicious script: https://github.com/califio/publications/blob/main/MADBugs/vi...
Might want to let git know. It's been a part of the git source code since 2006. If there were an RCE vulnerability from using __git_ps1, one would hope it would have been found by now!
I was able to reproduce it using that script in my PS1 when `GIT_PS1_SHOWUNTRACKEDFILES=1` which triggers a call to `git ls-files`. Without that, it seems to be just calling `git rev-parse` which does not execute fsmonitor.
I was also able to reproduce it with `GIT_PS1_SHOWDIRTYSTATE=1` which invokes `git diff`.
If you don't provide it a <tree-ish> it reads from the index (staged files). So you're right its not really pointed anywhere, since the index isn't a ref.
That's my overall point: the argument itself (with respect to the current state of the repo) is what determines the behavior. I don't think this is anywhere close to as intuitive as commands that only ever accept one "type" of argument (and erroring if it's different).
I stand corrected by this one scenario, but I’ve been using git for over a decade and never found that useful. Just don’t use checkout on a file path, there is no need.
I find this kind of advice to be a more scathing indictment of an interface than a critic could ever muster: asking users to forego available functionality so that some sense of order can be imposed.
That goes in the same bucket as rebase. Until you know what it does, you'll be fine avoiding it.
Since people are sharing their experiences and my recent one is relevant to edit, I'll go:
Working on a feature recently, I ended up making 3 changes ("commits") on top of each other and hopping between them via jj edit.
The first change wasn't feature specific, it was extending the base project in preparation.
The second change just added a doc describing all the changes needed for the feature.
The third change removed the doc as parts were implemented, bit by bit.
As I progressed on the third change & found stuff I'd missed at the start of this process, I jumped back to edit the first change (maybe I had a bug in that base project extension) and the second change (oh hey, I found something else that needed to be done for the feature).
It sounds crazy compared to a git workflow, but at the end of the process I have 3 changes, all tested & working. If I was doing this with git, I'd have to rebase/squash to get the final changes into a neat clear history.
I suggested that since you seemed really concerned about editing the commit that you just told it to edit. Use 'edit' all you want if your goal is to edit commits, otherwise 'new' does what it seems like you're expecting...
edit is useful and there are good reasons to use it, 'never use edit' is like 'never use goto' i.e. false - but if you're just starting out, jj new/jj squash is the way to go indeed.
(my particular favorite reasons to use jj edit are git-native tools which expect to work with uncommitted files e.g. autoformatters, linters, etc. which have been scripted in CI/dev workflows such that they cannot accept a list of files as params)
"Just don't accidentally do things wrong" is also the way to avoid null pointer errors, type mismatches in dynamically typed languages, UB in C/C++. It works, until it doesn't, and in practice that happens pretty quickly. Personally, I like things that have proper safety checks.
Except it's not an unrecoverable error. If you do it all it does is add that file to the working index. No commits are made and no harm is done. So no, I do not feel it's the same as a null pointer exception or type mismatch.
> If you do it all it does is add that file to the working index.
No, that's not at all what it's doing!
git init
touch foo.txt
git commit -m 'empty foo.txt'
echo something >> foo.txt
git diff --stat # Shows one line added to foo.txt
git checkout foo.xt
git diff --stat # empty output; no changes!
It removes changes that are not yet checked in. You can only get them back by going into the reflog (which is pretty much identical to the situation described with `jj edit` at the beginning of this thread; `jj op log` will let you get them back fine).
edit: Actually, I was wrong; the changes will NOT be in the reflog because they were never checked in. It's fully destructive; you've lost the changes and don't have any way of getting them back through git. This is strictly worse than anything possible with `jj edit`, and even when making this point I didn't realize how bad it was, and clearly neither did anyone defending it.
The fact that there have already been two instances of defending git's behavior without even understanding what it's doing with this command is exactly my point. It's not your fault; the command is unintuitive, and that's my entire point. How many times should it take for people to try to explain what's happening incorrectly before we accept this?
It's a useful thing to be able to do! It just fundamentally shouldn't be under one command. To its credit, git did add `switch` (with `-c` for creating a new branch and `-d` for specifying detached HEAD), but after two decades I can't imagine they'll ever get rid of checkout entirely because it was so fundamental for so long, and as long as its there, it's a loaded footgun with the safety off.
If you don't run checkout on file paths, how do you undo changes to specific files that you haven't committed yet? Like you've edited but not committed <foo>, <bar>, and <baz>. You realize your edits to <bar> are a mistake. I'd just run `git checkout <bar>` to revert those changes, what do you do?
It is also really useful when you realize you want <bar> to be the version from a commit two weeks ago. I guess you could always switch to the branch 2 weeks ago, copy the file to /tmp/, switch back, and copy the file into place, but `git checkout c23a99b -- <bar>` is so quick and easy. Or does this example not fall under the "dont run checkout on a path" since it is taking a treeish first before the path?
> preventing you from accidentally rebasing changes that have been shared.
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?
Yes, almost all JJ users do this constantly. Just "track" the particular branch. JJ has an idea that only some commits are immutable, the set of "immutable heads", and the default logic is something like "The main branch is always immutable, remote branches are immutable, 'tracked' remote branches are mutable." In other words, tracking a remote branch removes it from the set of immutable heads.
Nothing stops you from doing the equivalent of `git push --force` in `jj`. The flag is just named differently: `--ignore-immutable`. This is a global flag though, so it's available to all commands, and `jj` requires it whenever you're making changes to immutable commits, even locally. I'd argue that this is one of the killer features of `jj`, since by comparison `git rebase` treats everything the same whether you're squashing your own local commits on a feature branch or messing with the history of `main` in a way that would break things for everyone.
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.
> so you need to defensively create empty new commits for your changes.
I thought that for a long while too, and was equally pissed. Then I happened upon `jj evolog`. Turns out jj had a better solution than staging all along - I just didn't realise it existed.
The move from using jj as an alternate porcelain to git to using it efficiently took me more months than I care to admit. I suspect being familiar with git cli is actually a handicap in learning jj. New users without pre-conceptions will have a much easier time of it.
And oddly, I also suspect they will also end up knowing more about the underlying git storage engine than git users. It turns out it's capable of being used far more effectively than git uses it.
Doubly oddly, I blame Linus's familiarity with CVS and SVN for that. He (correctly) bemoaned how unsuited to the job of distributed source code management they were and invented a storage engine that has proved to be remarkably good at the job. But he carried across many the concepts in their CLIs to gits porcelain. Jj (with a lot of help from hg), finally broke free of that legacy.
How are you "checking out" the old commit? It sounds like you're using `jj edit`, which I'd argue does what it says on the tin. Switch to using `jj new <branch>` and your problem goes away.
That avoids the problem for the specific workflow of checking out an old revision (and it was what I was describing with checking out a new branch off the old commit and adding a blank commit to that branch), but another way this design bites me: At work I am constantly jumping around numerous repos because I might be working on repo <foo> but then someone on my team will ask for help with repo <bar>. So I'll turn on screen sharing, open up repo <bar> and I'll type out psuedo-code into <bar> as I'm explaining things to them.
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 think you have somehow picked up an overcomplicated workflow, and this is case is actually something that `jj` is much better at.
If I'm in the middle of working on <foo> and someone asks about <bar>: `jj new <bar>`. When I'm done (and do whatever I want with those new changes in <bar>, including deferring deciding what to do), I just `jj edit <foo>` and I'm back exactly where I left off. It's a bit like `git stash` without having to remember to stash in advance, and using regular commit navigation rather than being bolted on the side.
Fwiw I generally solve this by using `jj commit` instead of `jj desc` unless I'm specifically targeting something that isn't my working copy. Technically it violates the "we want commands to be orthogonal" guideline we use to write Jujutsu (otherwise this would indeed be `jj desc; jj new`) but as a habit it's never let me down
Ah, thanks! That's a command I haven't learned yet, so I'll have to check it out. I learned jj from the tutorial that was posted and I don't think it covered `jj commit` at all.
I didn't cover it for various reasons, but I think it's good to teach now that I've had more time to consider this sort of thing, so the next iteration will likely start by beginning with jj commit.
In a pure `jj` model, commit might not even be necessary as it's own subcommand (since you could easily define an alias for `desc` followed by `new`). We're still living in a world where most people who would consider adopting `jj` are git users currently, so I wonder if starting with `commit` and then following it up with an explanation of "here's how you can change the commit message without needing to make a new commit" and "here's how you can make a new commit without changing the name of the current one" would end up fitting people's expectations better.
I tend to learn "bottom-up", so I like the new + describe as a way of learning, but people want to jump in and get going with tools, so commit fits that expectation better.
I'm the same way. I've learned over the years that this ends up being somewhat uncommon though, and one of the harder but more rewarding parts of helping people learn is figuring out where they're coming from and meeting them there. (I'm positive this is something you've been well aware of for a while though, probably longer than me!)
I think the right intuition to have with jj is that `jj st` should show an empty change unless you are actively working on something. `jj commit`, as mentioned below, is a good example of this - it automatically creates a new change and checks it out. The "squash flow" also does this well - you use the branch tip as a staging area and squash work into other changes on the branch as you go along. Either way, once the work is finished, there's an empty change at the tip of the branch.
This is also supported by jj implicitly - whenever you check out a different commit, if the change you were on is empty, has no description, and is the tip of a branch, it's automatically deleted to clean things up for you.
sure, but i don't think it's that big of a deal either way. it's not the specific feature that draws me in. the overall concept of saving everything as a commit is interesting however. not needing to manually commit after adding changes is just a small positive side effect.
yeah, jj is a different way of working with the same underlying data structure that git also operates on. I happen to like it more than git, but it took some using to because they're both used to arrive at the same end result withing mostly the same operations.
if you loose an edit jj op log is incredible, I've saved a ton of work more-so now with AI's making mistakes. Also workspaces are super fast compared to git worktree's - same concept, different implementation.
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.
`jj new` works like `git checkout` most by creating an empty revision on the top. `jj edit` on the other hand resembles `git checkout; [edits...]; git add -A; git commit --amend --no-edit`.
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 <file> /tmp/ && jj status && mv /tmp/<file> .` to get the file out of the repo.
You would have had to run `jj edit` in order for this to happen, so I think it's a stretch to say you didn't ask for the edit?
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.
Using `jj edit` will edit a commit you specify, and `jj new` will make a new empty commit after the one you specify. These work exactly the same whether you specify a commit by branch or by the hash. I'd argue that you're getting exactly what you ask for with these commands, and by comparison, what "checkout" is asking for is much less obvious (and depends on context). We've just internalized the bad behavior of git for so long that it's become normalized.
Jujutsu has a concept of mutable vs immutable commits to solve this. Usually everything in a remote branch is immutable. To work on a branch, I track it and that makes it mutable.
Induction is also faster to boil water, easier to clean since it's just flat glass, and safer since an induction stove without a pot/pan stays room temperature (in fact, they usually can detect if a pot/pan is present and automatically turn themselves off)
Induction is also particularly nice for certain types of cooking because many induction stoves can be set to a specific temperature instead of just to a power level.
Agreed that folks should look more favorably on induction. I did not mean my comment as a defense of gas. Just pointing out that most of the pollution from cooking is one where you want a good range hood that vents to the outside. And you need to clean it.
Our last house did not have a vent to the outside and it was eye opening to realize how much grease throughout the entire house was from that.
As an alternative, you could get a stand-alone USB-C power meter which can be used with any cable. That way, when the cable breaks, you don't have to buy a new power meter. Here is an example of one such product (though I've never used this model): https://www.amazon.com/Adapter-Voltage-Current-Extension-Con...
These two statements aren't mutually exclusive. The link is looking at the analog signal through an oscilloscope. The person you replied to is pointing out that after decoding and applying error correction, you can still end up with the same digital signal output. So the eye diagram charts are useful for detecting the quality of the cable, but as long as the quality is past a certain threshold, it does not matter.
And that threshold is "baked in" to the eye mask pattern you load into the tester. If the eye stays out of the masked areas, it passes, if it goes into the masked areas it fails. Oscilloscopes capable of eye diagram testing can trigger on failure, so if it passes an eye test it'll reconstruct correctly with proper timing.
> Probably fun for those who already bought DDR5 memory
Nah, those of us who already bought DDR5 memory also already bought decent CPUs. Dropping another $1k for these incremental gains would be silly. It'd make a lot more sense if DDR5 had been around longer so that people had the option to make generational upgrades to this CPU but DDR5 on AMD has only been around for Zen4 and Zen5.
I’ve only ever used Outlook when forced to by an employer and I find it a dreadful application to use. I would guess that most people prefer something else. I would imagine that most people tend to stick with the default email app on their computer (no idea what that is on Windows as I’ve managed to avoid having to use Windows for 7 years now).
The default mail app on Windows is now called Outlook for Windows, no relation to the Outlook in Office (sorry, Microsoft 365 Copilot), and it's a significantly worse barely functional webview. It also replaced the entire Calendar app, which was decent.
They've really shifted how Outlook works... as well as how the backend is more tuned to the way M365 mail works far more than how it used to work with Exchange, or independently. It's been a slow downslide imo since around 2007 or so.
I know the why, but it's really worse as an experience for most people than the older integrations... but the use of horizontally scalable backends makes for a saner platform at the expense of better UX.
In the US, social security is based on the 35 highest paying years. If that system is good enough for social security, I don't see why we don't do the same for government pensions.
Only tangentially related: Is there some joke/meme I'm not aware of? The github comment thread is flooded with identical comments like "Thanks, that helped!", "Thanks for the tip!", and "This was the answer I was looking for."
Since they all seem positive, it doesn't seem like an attack but I thought the general etiquette for github issues was to use the emoji reactions to show support so the comment thread only contains substantive comments.
> It also seems that attacker is trying to stifle the discussion by spamming this with hundreds of comments. I recommend talking on hackernews if that might be the case.
Totally agree with the author of rg here. Config names should be unambiguous. Anyway, must have been something else, then. As I've said, I cannot remember what was the specific problem, only that it wasn't quite compatible with the workflow I was used to, and now it'd take another full-in attempt to switch to figure out what was so annoying to me back then.
reply