315 karma · joined November 15, 2021
See https://jj-vcs.github.io/jj/latest/conflicts/.
> If I rebase and then fix a conflict later, are those conflict markers going to appear in some of the commits on github when I push?
We error out if you try to push conflicts because the Git remote would probably not know how to interpret them. We will probably add an option to allow it later because it can be useful to be able to share conflicts with others if you know that they're also using jj.
> Maybe I'm in the minority, but I like fixing conflicts as I go. What am I missing?
You can still do that. Hopefully the above answers your question.
> Everything is what?
Not interactions affecting remotes (you can't unpush a commit). Configuration is also not tracked. Neither are ignored files.
You can undo changes in the working copy, rebases, splits, squashes, etc.
> The things git cannot undo is removing untracked files (well they're untracked) on git-clean, or files added to the index for the first time and then reset.
Files added to the index can be recovered at least, but it's not easy to find them. The bigger problem is if you have not added them to the index and run e.g. `git reset --hard`. You can't undo that (via Git, but maybe via your IDE or your file system).
It's also about ease of use. I know you can undo a lot of things by using Git's reflogs, but it's not easy to e.g. undo a `git rebase --update-refs` that update many branches.
> I use git-worktree for multiple branches I work on, so my working copies may remain dirty even in conflict stage.
Yes, Jujutsu also supports that (`jj workspace`), but IMO, it's nice to not have to use them just because your working copy is dirty.
> What does make rebase hard? It's just re-applying a bunch of patches or possibly merges on top of the new base, and it doesn't even require interactive mode.
I've worked on the `git rebase` itself, so it's not like I don't know how to use it. Some things that I think are wrong with it:
* Without `--update-refs`, it only rebases a single branch. That's basically never what I want when I have branches pointing to other rebased commits.
* It's not able to rebase more than one head (no trees of commits)
* Doesn't rebase merge commits properly. Instead, it redoes the merge, discarding any conflict resolutions. (Yes, I know about rerere.)
> Interactive mode makes magic I'm happy with. Seriously, what's wrong with it?
Mostly that it's so stateful. While doing an interactive rebase (e.g. editing a commit), you're in a state where you can't easily check out another commit, for example. If you run into conflicts, you have to finish resolving all conflicts in the stack of commits, or you have to abort the whole rebase (throwing away any previous conflict resolutions).
Again, it will be easier to understand if you just try using Jujutsu for a few days.
The other issue you ran into with ignored files becoming tracked is a known limitation. I agree that it's really annoying. We have talked about it many times before but we didn't have a proper issue for tracking it, so I created one now: https://github.com/jj-vcs/jj/issues/5596
https://drewdevault.com/2024/12/10/2024-12-10-Daily-driving-...
The advantages people first think of when they hear about jj's conflict handling are usually that you can collaborate on conflicts and that you can leave conflicts for later. What's less obvious [1] is that being able to store conflicts in commits means that we can always rebase descendants, so there are states like what Mercurial (and Sapling, I think) call "obsolete" and "orphan". There is also no "interrupted rebase" state when you're resolving conflicts.
These things simplify for the user. They also simplify a lot for developers. An example is how I spent about 2 weeks on a `hg amend --into` command for amending the changes in the working copy into an ancestor. I then implemented that in under an hour in jj. Much of the complexity in hg stemmed from dealing with the interrupted states while dealing with conflicts. (Other complexity in hg that jj doesn't have is dealing with a dirty working copy, dealing with concurrent operations, and simply complexity in the APIs for creating new commits in memory.)
[1] IIRC, it took me about a year after I added support for "first-class conflicts" until I figured out that it meant that we should simply always rebase descendants. Jujutsu had a orphans and a `jj evolve` command before then.
Note that the solution probably involve a local daemon process for caching reads and optimistically accepting writes and then sending them to the server in the background. That's what we do for latency at Google. But that's just an optimization and can come after the server is done.
If you're interested in working on it, it would help if you joined our Discord server.
At this point, I wonder if maybe we should not even mention the staging area. It seems to confuse a lot of people, so maybe it hurts more than it helps to mention it.
For example, our "Piper" backend at Google is a native backend in the sense that it stores all data in its own database. I think the most exciting thing about that backend is that it's cloud-based so users will be able to access each others' commits (e.g. `jj show <commit id from chat>`) without requiring a push.
There's no native forge yet.
It can be used with regular Git repos.
Sure. I presented about that at Git Merge 2022. https://github.com/martinvonz/jj#disclaimer has links to the slides and the recording from there. I'll summarize the problems we have with Mercurial here:
1. Performance/scalability. That's partly because Python is slow. Both the Mercurial project and Meta have rewritten many parts of it in C and Rust as a result. Maybe more importantly, there are many assumptions in Mercurial's design that don't scale well. We have extensions for downloading only a slice of the repo. We slice it in both file space and in version space. However, it can get very expensive to change afterwards. For example, checking out an old revision that they user hasn't previously downloaded is very slow (it requires rewriting all local revisions after that point).
2. Consistency. Mercurial was designed for local file systems, so when we store repos in our distributed file system, we run into write races that can corrupt repos.
3. Integrations. We integrate with Mercurial by running the `hg` binary and parsing the output. That's unnecessarily complicated and slow.
We also see several opportunities by switching to jj (in addition to hopefully fixing the problems above):
1. Simpler workflows. Things like: working-copy commit (no "dirty working copy" errors, for example), undo, first-class conflicts (no interrupted rebases, for example). See the GitHub project for details.
2. Cloud-based repos. The repos will be stored in a database instead of being stored in files on top of a distributed file system. That makes them much easier for our server to work with, and it opens up for many kinds of integrations that were not feasible before.
3. Simpler architecture. We designed jj from the beginning to be easy to integrate with our internal systems, so there should be much fewer workarounds.
4. Simpler code base. You can typically add a command without worrying about concurrent commands, a dirty working copy, or conflicts. An example I like to mention is how I spent about two weeks trying to implement a command for amending into an ancestor commit in Mercurial. Then I implemented a more powerful version of that (can move changes from any commit to any other commit) in an hour in jj.