Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I have colleagues who are big fans of it. I gave it a try, but I really don't get what is the problem it is trying to solve. Like, what is it that I will be able to do that I couldn't do with git, and why bother learning it at all


I find JJ to be what you get when you take a step back, evaluate everything that works in Git and try to find a design that accomplishes all those things with the fewest amount of concepts possible. Like a Git 2.0, if you will.

You can do most of the same things, but it's easier to understand it intuitively so you don't need to google or give up on the more advanced usages. And the things you do everyday you can do with fewer steps.

Couple that with colocated repos, meaning I can use JJ while everyone keeps on using Git, there wasn't really anything holding me back.


> You can do most of the same things, but it's easier to understand it intuitively so you don't need to google or give up on the more advanced usages. And the things you do everyday you can do with fewer steps.

Care to point out any concrete example?


The entire chapter 2.4 "Undoing Things" in the Git book can be replaced with a single `jj undo`.

----

Anything to do with IDs is simpler, because a jj change ID is fixed at creation, even as the underlying current commit ID changes. This makes IDs useful even in a rebase-heavy environment.

----

Likewise, jj replaces the need for staging and stashes as completely separate concepts with their attendant extra commands. Anything you used them for is replaced with uniform commit/change commands: split/rebase/squash/etc.

Staging is just the current change; when you like the code you have, you make a new change, and the old one is just the parent commit. If you want to freeze part of it, you `jj split -i` the commit into a parent/child, and move the parts you're still working on to the child.

Stashes are WIP commits you leave lying around. If you want to move on to work on something later, you just `jj new some-other-commit`, and you're now on a blank commit whose parent is `some-other-commit`. When you want to resume work on that WIP, you `jj edit some-wip-commit` and/or `jj rebase` it first.

It's truly much simpler to have a bunch of pervasive change/commit operations, instead of superfluous ad hoc concepts with their own mini-commands and behaviors.


> The entire chapter 2.4 "Undoing Things" in the Git book can be replaced with a single `jj undo`.

Are you sure about that? Git reset doesn't need s chapter to explain.

Also, you should really pay attention to what you're writing. Your only tangible case in favor of JJ is user experience, and you even struggle to even explain what jj supposedly does, let alone how it's a preferable alternative. Even more baffling, this completely ignores the fact that the bulk of Git users use it through a GUI frontend, which completely negates any hypothetical selling point.

So, why bother with a solution for a problem you can't even clearly formulate?


> Are you sure about that? Git reset doesn't need s chapter to explain.

But it gets one — as they pointed out, Chapter 2.4 of the Git book (https://git-scm.com/book/id/v2/Git-Basics-Undoing-Things) is about undoing things. And to be clear, that's not just `git reset` — depending on what exactly you need to undo, you should use different tools, including amending existing commits and using `git checkout`. All of which will have different effects, and some of which can be dangerous if you misunderstand them (such as `git checkout` which can destroy data if you've not properly committed it yet).

Meanwhile, whatever the last thing you did with `jj` was, `jj undo` will undo it. On top of that `jj op log` will provide you a list of all the things you've done, and `jj op restore` can reset the entire repository to a previous state.

This isn't about UX: Git doesn't have the capability to do what JJ is doing here, because it doesn't track the evolution of the repository as a whole in the way that JJ does.


I use git through a GUI frontend and it does the same things git does in the same way, like being unable to do most things without checkout, because cli needs this implicit context.


Sure.

I think what comes up most often for me is when I want to fix something on a different branch than the one I'm at now, or when I want to fix something that feels like it should be separate commit.

In git you can `git stash`, commit the unrelated change and then run `git stash pop` to get your in-progress changes back.

In jj your work is already added to the current commit whenever you run a jj command, so you can simply switch to the other branch and do the work, and when you switch back your work is as you left it. It saves you the `git stash` commands. I also like that anonymous branches are completely normal in jj, so I don't need to create branches just to find my way back to a change.

Similarly, jj removes most usages of `git rebase --interactive` by allowing you to checkout a previous commit, and as you're making changes to it all descendant commits are being rebased automatically. This makes it easy to insert changes between two commits, simply create a new commit where you'd like it to go and then descendant commits will automatically be rebased on this new commit as you make changes.

To me this is more intuitive, and it removes `stash` and `rebase --interactive` as concepts.


> In git you can `git stash`, commit the unrelated change and then run `git stash pop` to get your in-progress changes back.

Not necessarily. You can just commit your changes, switch to whatever branch you want to work on, and then when you switch back to the old branch you can resume working as always.

This is not a Git thing. Even Mercurial advocated for this approach, and sold it as the only and true way of stashing changes. Mercurial only received support for a stash-like feature much later and in the form of an extension.

Also, even though git stash can be used in workflows that involve switching branches, it is suitable primarily for very short-lived tasks that prevent you from committing changes to a branch. Things like pulling changes from a remote branch or switching brsnched when you have random local changes in place (I.e., changed to config files for troubleshooting purposes)


> Not necessarily. You can just commit your changes...

Saves me from doing that extra commit, then.

> [...] it is suitable primarily for very short-lived tasks that prevent you from committing changes to a branch.

That's also a thing I like about jj. Nothing prevents me from committing changes. Even merge conflicts are commited, so I know I can always come back to it later.

I find I think less about when to commit my work in jj. In Git I find that I like to commit to avoid loosing work, in addition to commiting when I feel "done" with part of a change.

It's a small thing, really, but it feels quite big (for me).


Ultimately, jj is trying to make the "branches are a mutable series of patches" workflow easier.

The problem is that that workflow is still ultimately harmful nonsense[0] that is based on a misunderstanding of version control.

[0]: https://fossil-scm.org/home/doc/trunk/www/rebaseharm.md


Rebase is not harmful, but thanks for your link. There are quite some people that do not understand rebase, and this link helps to get a grasp of how people can be confused.

- in section 2.2, they think that in a feature branch only the latest commit counts. You should not just be interested in how the tip of your feature branch compares to the latest main, but also into the logical sequence of steps your feat branch builds up a feature. In a rebase model, every step of your feature can be compared to main.

- in section 2.2, they conveniently omit what happens if you keep merging main into your feat branch. Your feat branch will show all useless commits telling you that "at this time, my feature diffed such and such from how main looked at at that time". "Oh, and here again a useless diff" "Oh and here again." "By the way, have a nice ball of diffs again". I don't fucking care. Why don't you just commit some info about the local weather at that time too or that your back was hurting on Wednesday 23th?

- "Rebase encourages siloed development". That claim is just ridiculous. If you don't want people to take ownership of a feature, by all means, forego branches. If someone wants to work on my feature, then we just agree that the public feat branch is to be considered public history. When the work finishes, someone can take ownership, (possibly clean up the mess with rebasing) and get it into mainline by rebasing on top of main.


> You should not just be interested in how the tip of your feature branch compares to the latest main, but also into the logical sequence of steps your feat branch builds up a feature.

Absolutely. And we should work to preserve that history.

> In a rebase model, every step of your feature can be compared to main.

No it can't (unless you just rebased again before running the diff). What it can be compared to is the closest common ancestor.

    git diff main...<commit>
Which is exactly the same operation that you'd run in a merge workflow.

> I don't fucking care. Why don't you just commit some info about the local weather at that time too or that your back was hurting on Wednesday 23th?

But those events do go wrong, and having the context to understand what happened matters. Have you never had a mismerge or a merge conflict?


> But those events do go wrong, and having the context to understand what happened matters. Have you never had a mismerge or a merge conflict?

Interestingly, despite being designed for a very rebase-heavy workflow, this is one of the things that Jujutsu does really well. You work with mutable changes rather than immutable commits, and each change references one or more commits that represent (roughly) the history of that change. So locally, you can see how a particular change has evolved, and you can easily undo some action or reference an older state while working on that change itself. Then when you push to a remote, the other people will only see the end-result of the change.

Which is typically what I want: I want to retain a good sense of the history of the changes I'm working on at the moment, so I can go back and forth, try out different things, and not worry about losing any data or handling conflicts badly or whatever. But, when I've finished with my change, and it's been pushed, reviewed, etc, that history is now superfluous. I don't need to know that, while I was developing a feature, I accidentally committed a typo and then needed to make another commit to remove that typo. (And if there is information I do think is important, then I can create additional changes, or add the information to the commit message or the code itself as a comment.)


> Interestingly, despite being designed for a very rebase-heavy workflow, this is one of the things that Jujutsu does really well. [snip] Then when you push to a remote, the other people will only see the end-result of the change.

Yes, jujutsu is designed to hide (some of) the problems with rebase from yourself, while still inflicting them on everyone else . History for me, but not for thee!

While also complicating the interaction model because you now have two sorts of history to contend with.

Which actually sucks, because jj could have had been a much better alternative to Quilt for maintaining patch series over third-party software (á la Linux distributions) if it supported collaboration over the metahistory/oplog.

> But, when I've finished with my change, and it's been pushed, reviewed, etc, that history is now superfluous. I don't need to know that, while I was developing a feature, I accidentally committed a typo and then needed to make another commit to remove that typo.

This assumes that you'll actually find all the issues during the review. Do you also erase your history after every release?


Do you have a case where seeing all the small changes I made during development is helpful for fixing bugs and issues in the future? I would have thought it is rarely useful, and often actively unhelpful.

Let's say I make a change in three steps: I rename all uses of X in the codebase, I remove the Y component, and I fix bug Z. To me, this should be three commits: X, Y, and Z, one for each of the changes.

But to get to the final point, I probably haven't done that in only three steps. What I've probably done is I've tried out change W, but that didn't help at all so I abandoned it, then I started doing Y, realised that I needed to do X first for Y to make sense, so I switched and did X, then I finished Y, then I could do Z. Then I realised that while doing X I'd forgotten some stuff so I finished that off, then I realised that the tests weren't passing because part of Y wasn't finished. Then I pushed everything and the reviewer pointed out that while fixing Z, I'd left some code commented out and I needed to remove it properly.

Long-term, which of these sets of changes would be most useful? Surely the first one: it shows my aim while making changes, it is clearly bisectable, and it is divided into clean units of work without overlap or having those units intermingled. As a future developer, I can run `git blame` or equivalent and clearly see why each line of code was changed, and for what reason.

Whereas with the latter case, bisecting the codebase will stumble on all the intermediate situations where a test failed or the code didn't compile or whatever, because bad states during the development process are now part of the permanent history. And it'll produce spurious results when I try to annotate a file: the last time this file was changed was because I temporarily commented out this line and then immediately commented it back in again, how do I see only the changes that made a meaningful impact to this line?

In my experience, recording these sorts of "fixup" changes permanently in the history has never been useful. Having clear, precise commits that each do one thing: definitely, yes, this is a fantastic tool. But being able to go back in time with such granularity as to see when you broke a test and then fixed it in review? Or when you started out doing one change and then did something else instead in a different order? I have never found that level of detail useful, and it is often makes things harder when I'm trying to focus on the significant commits and changes and just want to filter all of those out.


When you rebase your feat branch on top of latest main, all commits in your feat branch are YOUR commits. Each of those commits change something relative to main. Unless your are committing against yourself. So if you are confusing yourself (which should be rare because --I repeat-- all commits in your feat branch are your own commits) you have a good opportunity to clean up your own mess with git rebase -i.


> The problem is that that workflow is still ultimately harmful nonsense[0] that is based on a misunderstanding of version control.

The problem with that article is that it's utter nonsense and completely misses the whole point of rebase.

No one cares if under the hood a rebase is technically a merge that forgets where it came from. I mean, isn't that the whole point of a rebase?

The whole point of a rebase is to move your local branch onto the tip of another branch while preserving the local history. The goal is to simplify the repository history by eliminating irrelevant noise. That's not a problem, it's a solution. Is this too hard to understand? How can they possibly miss the extra forks and joins in their commit graph? Do they not see that? Do they spend any time looking at commit graphs?

It's ok if Fossil's devs want to force their opinionated take on VCS onto Fossil's users, but this does not mean they are right or have a point. In this case they are not only missing the whole point but also taking a completely wrong approach to mitigate it. The fact that they felt compelled to put up a page trying to present their case (and failing) is already telling.

If there is criticism to throw at Git, rebases clearly ain't it.


> No one cares if under the hood a rebase is technically a merge that forgets where it came from. I mean, isn't that the whole point of a rebase?

Hurting people is the point of a gun, that doesn't mean guns are suddenly excused from doing so.

> The whole point of a rebase is to move your local branch onto the tip of another branch while preserving the local history.

But you aren't; history is as much about where you came from as where you are now.

> The goal is to simplify the repository history by eliminating irrelevant noise.

Because merges aren't noise, they are fallible events just like manual changes. They also help to group changes together and provide helpful context.

> That's not a problem, it's a solution. How can they possibly miss the extra forks and joins in their commit graph? Do they not see that? Do they spend any time looking at commit graphs?

If you don't want to see the merges, hide them. `git log --first-parent` shows you the commit history as if all merges were squashes instead.

Meanwhile, the rest of us can suddenly appreciate having the extra context available when it's actually needed.

> It's ok if Fossil's devs want to force their opinionated take on VCS onto Fossil's users, but this does not mean they are right or have a point.

Deleting data is opinionated, preserving it leaves it up to the reader to judge.

But my point isn't that you should use Fossil, but that Git is a far better VCS if you use it as if it was Fossil.

> If there is criticism to throw at Git, rebases clearly ain't it.

In my, uh, decade or so of using Git, every single avoidable problem that I have seen people have with Git turns out to originally come from rebasing.


> In my, uh, decade or so of using Git, every single avoidable problem that I have seen people have with Git turns out to originally come from rebasing.

That means that both you and those people do not fully grasp the concept. The problem I have is that those misunderstandings are spread such that I have to deal with it. Especially those that do not understand Git at all keep merging balls of merges of merges. Then they lose track, and decide to just merge main again because something broke and they hope that Git will solve it with just one more merge.

I have no problem with incompetence, I myself am incompetent in a lot of areas, and I know that. When I have to work with people, I have a problem with those that are not able or willing to acknowledge they lack some overview.


> That means that both you and those people do not fully grasp the concept. The problem I have is that those misunderstandings are spread such that I have to deal with it. Especially those that do not understand Git at all keep merging balls of merges of merges. Then they lose track, and decide to just merge main again because something broke and they hope that Git will solve it with just one more merge.

I would take a step further and say the root cause is not users who do not fully grasp a concept of Git, but who fail to grasp one of the most fundamental aspects of version control systems: branching and merging.

Git just happens to take a bad rap because it's ubiquitous and for some it's the only VCS they ever used.

Of course a bad workman blames his tools. If the tool they are using is Git then of course that's the one being thrown under the bus.

What is perplexing is how this problem is then approached by people who are expected to know better by framing it as something only a bad tool would do, and thus they roll out yet another tool that does the same thing and poses the same problems.

In the case of jj, they even claim to fix git by running git as the backend. Think about that for a second.


Merges go wrong sometimes. That's life.

That mismerge would have been just as bad if they had rebased instead, except you wouldn't have had the context to go back and fix it. How would that be better?


If your merge goes wrong, you should rip it out of your feature branch. You use git rebase -i for that, or git reset even.

But you shouldn't merge into your feature branch at all. We are interested in how your commits change what becomes before it in mainline. We are not interested in how main differed from your feature branch at various points in time.

Git rebase is wonderful for people that create mess when working. Your feature branch can contain lots of WIPs for all the times you hit five 'o clock. With interactive rebase you can clean up your mess: squash, split, delete and reorder commits. You can do this all graphically even, like drag-drop reordering of commits. Also read up about git fixup and friends for quick fixes. There is also git rebase abort if you think you are doing something wrong.


> But you aren't; history is as much about where you came from as where you are now.

No. That's clearly something you are failing to understand. Commit history is not chronological. Commit history is ordinal. I make my commits, and I say where they should go and in which order they should go. Try to understand that.

> Because merges aren't noise, they are fallible events just like manual changes.

You're not even paying attention to what you are saying. If you interpret merges are "fallible events" then what's the point of pushing a "fallible event" to a shared repository when they are irrelevant to describe your changeset? The only purpose they have is to make sure your feature branch can be merged back to mainline with the latest changes in mainline. Why introduce a "fallible event" for that?

Isn't it far simpler if you just reapply your commits on top of the branch, and leave those "fallible events" out of the commit history?

Moreover, look at the silliness of it all. What prevents anyone from manually replaying a commit history on top of another branch? Should that be prevented too? Should support for squashed merges be removed too because they lose a reference to the origin commit?

What's exactly the point you're trying to argue?

> Deleting data is opinionated, preserving it leaves it up to the reader to judge.

Try to understand this: you read what I write. No more, no less.

> In my, uh, decade or so of using Git, every single avoidable problem that I have seen people have with Git turns out to originally come from rebasing.

So what? Are you also going to argue to ban furniture because unsupervised toddlers can bang their heads on it?

Do you know who doesn't waste time posting remarks on rebase? People who use it all the time and don't have a problem with it.


The author of that article clearly doesn't understand rebasing. They state that "the golden rule of rebasing" in the "Git rebase documentation" (actually just an Atlassian git tutorial) says that you should "never rebase on a public branch", when the tutorial they reference unambiguously states you shouldn't rebase a public branch onto your own local changes. Rebasing on public branches is the raison d'être of rebasing. The entire article seems like cope, the author deciding to bash rebase because they don't understand it.


Have you tried asking your colleagues? What did they say?


I considered it, but it would inevitably involve talking to people, and it is a little too far for me


If you ever did stacked prs and then had to rebase them all on top of each other, this is why.

Otherwise I also find it not really an improvement and a downgrade in terms of other tooling (editor etc.)


I do this regularly with magit, but mby I am missing something?

I.e. the typical Gerrit workflow




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: