Hacker News new | past | comments | ask | show | jobs | submit login
Jujutsu: A Git-compatible DVCS that is both simple and powerful (github.com/martinvonz)
673 points by lemper 10 months ago | hide | past | favorite | 261 comments



Nice to see this posted here. I switched over to it about 2-3 weeks ago, and I haven't looked back. It took a lot of mental rewiring, but I really enjoy the workflow `jj` provides. There's no longer a time to think about what's being committed, because all file changes automatically amend the working copy commit. Of course, sometimes you don't want this, so you have things like the ability to split a commit into two, moving commits, etc. But having _everything_ operate on commits is really nice! Other niceities that I like:

- `jj log` is awesome for getting an overview of all your branches. If, like me, you have a lot of work in progress at once, this provides a great map

- Conflict resolution is really cool, as you can partially resolve conflicts, and then switch branches. Conflicts are also tracked specially, but I haven't done too much with this yet.

- The abbreviated changeset ids are really handy. I often will just `jj log` (or in my case just `jj` as that's the default), notice a changeset I want to rebase, then run `jj rebase -s qr -d master`. `qr` here is an abbreviated changeset id for a branch/commit, and usually much quicker than typing the branch name out! This will probably change when clap gets updated to support dynamic tab-completion though.


What happens if you accidentally save a file with some sort of secret that gets sucked in?


Isn't the idea that you continue editing the working copy commit until you actually commit it?

Also from the documentation:

https://github.com/martinvonz/jj/blob/main/docs/git-comparis...

"As a Git power-user, you may think that you need the power of the index to commit only part of the working copy. However, Jujutsu provides commands for more directly achieving most use cases you're used to using Git's index for. For example, to create a commit from part of the changes in the working copy, you might be used to using git add -p; git commit. With Jujutsu, you'd instead use jj split to split the working-copy commit into two commits. To add more changes into the parent commit, which you might normally use git add -p; git commit --amend for, you can instead use jj squash -i to choose which changes to move into the parent commit."


Sometimes you have changes that are permanent to your repo (ie local workflow), that you always want to keep locally, but never push to the remote.

In git you would always leave the changes unstage, does that mean with jj you would always have to remove them before pushing? I haven’t found an answer on the linked page.

Side note: I really wish git had a way to mark commit has ‘no-push’ so they never leave your local copy, as an upgrade of the unstaged workflow.


> Sometimes you have changes that are permanent to your repo (ie local workflow), that you always want to keep locally, but never push to the remote.

I appreciate that there are times when this has to be in the middle of an otherwise committed file, but it's worth avoiding that if at all possible and putting the locally-changed bit in a different file because, as others have pointed out, this is error prone. It feels like the equivalent of keeping your important files in the recycle bin because it's easily accessible.

For 90% of git users 90% of the time, the staging area is an inconvenience that adds an extra step.


> In git you would always leave the changes unstaged

I do this too, but I quite frequently forget that I've done it and "git commit -am" and end up pushing my private changes anyway.


tell git to treat it as if it's unchanged

i have these 2 aliases assume = update-index --skip-worktree unassume = update-index --no-skip-worktree

"assume" as in "assume it's unchanged / not wanted"

which lets me say "git assume path/to/file" and then "unassume" it when/if i want to commit it.


Someone else's warning to not do this:

https://news.ycombinator.com/item?id=36954723


Ooh, that's me! As in the comment, please don't do this.

It's been a while since I worked at a place doing this, and I'm on my phone, so some details may be fuzzy or wrong, but when I was figuring all this out, I remember this SO comment being really helpful:

https://stackoverflow.com/a/23806990

Basically, there's two different APIs, and neither of them are designed for ignoring config files (but both of them happen to do things that look like ignoring config files, therefore get misused).

`assume-unchanged` is an optimisation tool that tells git that it shouldn't both checking files and folders that are expensive to check but never changed. If changes do ever happen, and git realises this, then git will remove the assume-unchanged flag - because clearly it wasn't true!

`skip-worktree` is a more aggressive version of this that tells git never to touch certain files or folders at all. But now it changes do happen to those files, it's not clear what git should do. Overwriting the files would cause data loss, but ignoring the files completely means that you miss out on upstream changes. And because you're telling git that these files should never be checked, there's no good way to handle merging and conflicts.

What typically happens with both of these flags is that they work well about 80% of the time, and then cause a lot of confusion that last 20% of the time.

The alternative is almost always some sort of configuration file that is directly referenced in .gitignore, and that therefore never gets checked in. (In addition, it's often useful to have a (e.g.) config.json.default file for getting new developers up and running quickly.) Any system that needs to be configured (database connections, API URLs, IP addresses, etc) should use this configuration file. Alternatively, I find environment variables, along with .env files for local development, to be really effective, because most things can already be configured that way.

This typically takes sightly longer to set up the first time, but will work very reliably from then on.

See also these changes in git's official documentation, and the commit message that explains why it was necessary:

https://github.com/git/git/commit/1b13e9032f039c8cdb1994dd09...


For a long time I have been wishing that Git had a capability of "track this file locally, but don't transfer it when pushing or cloning". Such files would be listed using the same syntax as .gitignore, but in a different file, say .gitlocal or something.

Git kind of has some "local tracking" already: if I am not mistaken, lightweight tags behave like this. It would be cool if it could track files in the same way.

Under the hood it could be done via a separate database, stored not in .git but in .gitl directory or some such. The database in .git would behave as if both .gitignore and .gitlocal contribute to ignoring, and .gitl as if .gitlocal was .gitignore in reverse: it would ignore anything not covered in .gitlocal (and also ignore what's in .gitignore). Or something along these lines.


this may not help you, but JetBrains IDEs have "local history" which addresses many of those concerns, including applying tags to meaningful points in time: https://www.jetbrains.com/help/idea/local-history.html


That has saved me so much time over the years. Makes quick experimentation totally painless.


A safer way to keep temporary changes is to create a new local-only branch with a commit of those temporary changes. Then, whenever you need them, you rebase the branch on top of your working branch.

Usually these temporary changes are to config files, so rebasing shouldn’t create conflicts if you don’t otherwise modify the config file.

An alternative is to stash only those temporary changes but I find branches to be easier to work with.


Isn’t this just .gitignore? I feel like I’m missing something.


That won't work for local changes to files that are legitimately committed in the repo. Like if you need to set your local database, or your own dev hostname in config, for example.


In my experience, there is almost always a simpler way to handle these cases that involves being able to put a file in gitignore. This is one of the big advantages of environment variables, for example - they can typically be stored in a .env file (with a .env.default file checked in for new developers to copy from), and loaded dynamically in different configuration systems.

If my local setup requires me to ignore changes in checked-in files, I usually find that I need to handle configuration more cleanly.

(I did work on a project that made use of git update-index - this was a terrible mistake and caused pain every time we needed to update the config file in the repository. Please never go down this route!)


dotenv and direnv are more than enough for this task, I think, but you can make it as complicated as you care to with similar tooling that pulls secrets/config from the cloud (Doppler, parameter store, etc.)

Most places I've worked at have created tooling that more or less merges the two - sane defaults and non-sensitive values go into a `.env` file of some kind (.env, .env.development, whatever), and then a tool merges a subset of that config from a remote store which contains the stuff you don't want committed in the repo.

Usually used for connecting to a remote dev or staging instance if you can't or don't want to run the entire stack locally.


This is a smell that you should refactor how your configuration is done. What happens when there's a legit change to the config? If it's in git with `update-index`, you're going to have a hard time actually changing that file and getting the changes to the team. There are other reasons, but this is why things like 12 Factor recommend putting config in environment variables. It's made my life much easier. https://12factor.net/config


Why wouldn't it work? You gitignore that file, and your modification is ignored, even if it is committed? Or will git delete that file from everyone?


gitignores are usually committed (they're treated like a normal file), so yes, in this context that would delete it from everyone.

There's .git/info/exclude, but that has some kinda large surprises if it excludes a tracked file and I don't recommend anyone use it unless they know what to look for and can always remember what they've excluded.


I have a $HOME/.gitignore that I use for this. (You can configure git to use that globally.) It's not a panacea, and I think other commenters are right that you should instead endeavor to organize things so that the project's own gitignore results in a sane workflow. But I think having permanently unstaged changes is worse.


It's the perfect location to ignore *.log.

Git will fight you if you ignore .config files which are actually used.


For this, you can use .git/info/exclude.


That only works for nonexistent files right? Not files that exist but whose modifications you want to ignore.


Git has a feature for this, called clean/smudge.


So in your workflow you never "git commit -a"? So you have to always manually mark what you stage. Which is probably more work than always manually removing the changes you don't want to commit.

The ability to rewrite older commits easily in jj also looks like it would help with this usecase if you get it wrong once.

Concretely I think you would do is: Instead of staging part of your changes and then committing as in git, you would call jj split and split the commit into the part you want to keep local and the part you want to push. This way the local changes always stay in your working copy commit.

Even better, just commit the local changes once when you start. Work locally and before you push you call jj diffedit on your initial commit of the local changes and remove them. Now all the work you did since then will be automatically rebased on the edited initial commit and you can now push up. Instead of excluding your local edits every single time you just have to do it once before pushing.


> So in your workflow you never "git commit -a"?

I like seeing what I'm about to commit, so I always do `git commit -p` or `git add -i`. Most people where I work do the same, so I don't think this workflow is uncommon.


I never `git commit -a`, because I'm paranoid that I've done something like named a variable "foo" as I'm just working through the logic and not caring about naming.

I'll then go back through, tidy and add all my changes.


if I don't do this (actually I use magit to select each hunk to stage, but that's just add -p with a fancy interface) then I'll accidentally commit testing log lines and that sort of experimental code

I never, ever commit -a. That flag horrifies me. I want to choose, specifically, each line of code that I am going to publish.


I almost always do "git add -p", "git commit".

"add -p" is great for showing you all the chunks of code you've written one-by-one and then you do "y" or "n" for whether you're adding them. Doing it like this means that you are reviewing what you've changed as a final check that you haven't left a debug line in, or forgotten part of what you meant to do. It's also a natural way of splitting up what you've done into two separate commits.


The analogous command here is `jj split -i`, which interactively splits the current commit (which is your working copy).


`jj split -i` gives:

> error: unexpected argument '-i' found

Actually, maybe I'm just a complete git, but I couldn't figure out how to `git reset HEAD~` my accidental commits, `git rebase -i HEAD~6`, format `jj log` more like `git log --color --oneline --graph --full-history` (which shows one-line-per-commit), `git checkout -p` (and obviously, `git add -p`), `git show HEAD~`, refer to N-th parents, e.g. `master~5`, and a bunch of other things...

It also feels a bit weird that new files are automatically committed without being manually approved, but I suppose this might theoretically help with some of git's annoyances.


Thank you, I have yet to see a workflow mentioned here that is not just as easy in jj (just conceptually different).

Also learning about progress git workflows though, so that's cool.


I never do `git commit -a` precisely because I don’t want to randomly add files that I have in the repository. If I’m in a hurry, I’ll do `git add -u && git commit` to add changed files that were already in the repository. But, more typically, I use magit to stage exactly what I want to commit.


Git should really steal the only thing I like about perforce, many uncommited change lists. It should be an easy workflow change to add 'named stages' along side the default stage. That way you can just leave changes in a different stage, tracked but uncommitted.


Unless I have misunderstood your feature request, that feature exists, and it's called "worktrees"

Worktrees allow you to have multiple branches open potentially with dirty state on any or all of them

https://git-scm.com/docs/git-worktree


No I don't think that is a similar feature. It looks like worktrees are put on different paths and by default do not operate on the same commit.

My feature request is about having n stages instead of one so uncommitted work can be organized better while staying in the usual working copy.

How would I accomplish this with worktrees?


Is that different to git stash?


Yeah, they stay in the working set but files are marked for different commits. Ideally git could do it at the line level instead of by file.


In git, you have the .git/info/exclude file to track these down. Given that jj is compatible, maybe it works?

Disclaimer : I'm not on my computer, I know a file like that exists, it may not be at this exact location


I think you want git's skip-worktree?


Ugh, I was baited by myself here. I’m he scripts I use at the day git (which are the only way I tolerate hit, honestly. It does this, and also recursively merges from parent branches in commit.

We’re on an old school feature branch/master is production topology though.


Assuming in git you would keep it as untracked file in the working copy, I find jj’s automatic anonymous working copy commit better.

For once, it avoids the downsides of carrying untracked files around. Ever accidentally committed an untracked file somewhere deep in history during a long rebase?

Also I find it clearer what files are committed: every file - except if it is in .gitignore. Meanwhile in git you scourge through your untracked files before each commit to decide which ones you don’t want to add. Ever accidentally committed all files and forgot that there were untracked files you didn’t want?


Are you simply using it with GitHub repos?

It mentions that it can be used with backends like Dropbox, but it would be wonderful if we finally had a system that could easily be used with IPFS. This is especially important for large data, since you can't store 1TB on github (and no, I don't count lfs, since you have to pay for it).

IPFS is the natural solution here, since everyone that wants to use the dataset has it locally anyway, and having thousands of sources to download from is better than just one.

So if this uses IPFS for the data repo, I'm switching immediately. If it doesn't, it's not worth looking into.


If you're storing 1TB of binary files in git, you're just doing it wrong anyways. You have a bunch of other tools and capabilities for doing this in a way that doesn't make your repository nightmarishly stupid to deal with because of its size.


I didn't exactly intend it to operate precisely the same way that git does, but rather to have extensions of git that unify the system into one easy to use version control for data and code.

In most projects today, the code is (or generates, anyway) the data. This is true for materials science in physics, neural networks, and creation of databases via ETL. So, it would make sense to remove the requirement of making users of some software to regenerate this data, which may take 2 months on a supercomputer. Downloading that would be much faster. You can put it on a university server, or AWS, but now the data is in some system that is not guaranteed to be there. In fact, it's almost guaranteed to *not* be there in a very short period of time (people move positions and lose their access to these servers constantly).

So the very obvious best solution is IPFS for distribution of the data, but it does need to be linked to the git repo somehow. Of course, the data may not be simple or textual and play well with simple text based diffs for version control, so using something like borg can solve the issue of both data privacy, if needed, and block based diffs.

So this isn't to suggest "just git everything", but rather to say, 'if there's a new version control system for data and code, it's probably added some improvements to fit, and this could be a direction that makes sense'.

So I was checking to see if it had gone that direction yet.


One can still use Subversion to store binary files in VCS...


Nexus, Artifactory, Packages (deb, rpm, nix), Cache, GitHub Releases... There are so many places you can grab a signed binary from that are just outright better for the health of your repo, and will respect your developers time.


The issue is not limited to archives, artifacts, and packaging. Game projects, for example, have large directories with many binary assets which need to be change-controlled. Artifact repositories address distribution, in a way, but don't generally support change control much if at all.

(And yeah, git's historically a poor choice for this – so you may see companies sticking with Perforce or other non-distributed solutions.)


The `Backend` interface is not that wide: https://github.com/martinvonz/jj/blob/48b1a1c533f16fc5df5269.... Mostly it just handles reading/writing various objects. You could very plausibly add IPFS support yourself!


In the author’s presentation [0], the Google roadmap includes “custom working copy implementation for our internal distributed VFS”. The related graphic shows a “working copy” block connected to a “distributed file system block”.

This work might be extensible to include IPFS and other distributed virtual file systems.

[0] https://docs.google.com/presentation/d/1F8j9_UOOSGUN9MvHxPZX...


There are two questions in play here:

1. When we ingest files or make new commits, how are these additions to the object store persisted?

2. When operations modify the working copy, how should these changes be reflected in the user's view of that working copy?

Ordinary git handles (2) by directly modifying the files on the filesystem. If you `git checkout` a branch, git will `rm` nonexistent files, `open()` and `write()` new ones, and adjust modification timestamps etc as needed. As you make changes to these files, some commands will occasionally "notice" that the file changed after the fact, and some may choose to modify the index to match.

the jj on github also does this, but inside Google, our concept of "working copy" needs to be disconnected from local files. Developers don't have their own local "working copy" backed by files on the ordinary filesystem; instead, we do all development inside a FUSE-mounted virtual FS called "client in the cloud" (CitC), so working anywhere inside our giant monorepo doesn't take any disk space (except caching). I think that's what the "Distributed file system" refers to - instead of modifying the local filesystem, jj would need to talk to whatever remote service provides the user's FUSE-backed view whenever the user uses `jj checkout` or some other jj operation that modifies the working copy.

When you speak of implementing IPFS storage, I think instead you want to keep the object store and operation log on IPFS while keeping the local working copy right on the ordinary file system, similar to how git-LFS keeps local files untouched while modifying the way they're persisted to the git object store.

Alternatively, perhaps we could imagine an IPFS backend similar to `jj git` and `jj native`, perhaps `jj ipfs push/pull`. Then, a completely local repository could push/pull to and from IPFS, completely agnostic of how the user's repository is stored on disk.

In any case, jujitsu's API surface is flexible enough to support any of these use cases since the author designed it from the ground up to smoothly support very different needs for internal and external users. Most users outside Google just want a familiar working copy containing ordinary files, and the fact that the repository structure happens to be backed by a git-like object store (linus' "git is just a merkle tree of files" philosophy) is incidental under the hood. That's just fine, even though most internal users will be interacting with a very different way of using jj when everything's said and done. Ideally, nobody needs to notice or care about the difference.

[1]: More about Google's internal VCS needs: https://cacm.acm.org/magazines/2016/7/204032-why-google-stor...

[2]: Linus Torvalds on git: “In many ways you can just see git as a filesystem — it’s content addressable, and it has a notion of versioning, but I really designed it coming at the problem from the viewpoint of a filesystem person (hey, kernels is what I do), and I actually have absolutely zero interest in creating a traditional SCM system.”


IPFS didn't always seem to have pinning services at BitTorrent prices($3-$5 a month with 1TB bandwidth, no crypto wallet needed) and the client usew tens to hundreds of kb per second idle.

Unfortunately, the model of putting every block in the DHT instead of having roots mode be the default, and then spamming your wantlist to tons of peers seems to still be at least partly around.

Right now IPFS looks pretty good thanks to the gateways and services, so I would imagine well see more of it in the future, but I can see why it took so long.


Yea, just GitHub repos. In fact just a single repo (work's mono repo) for now, but that's where I spend the majority of my day.


Why do you want such big files in a git repo?


The point is to have an easy way to distribute code as data. This is important for many areas, such as training neural networks (code with proper seeds can ensure the weights output by training), various applications in basic physics, database creation via ETL, etc.

If the choice is "run this code in the repo, wait 10 weeks while it's running, and retrieve the 50GB file", vs "download this file", of course, the latter is better. But many of these processes exist in academia, wherein you are essentially guaranteed to lose access to the server and maintenance of that file for download, it can get pretty annoying. Additionally, there's no seamless way of distributing it (it's in the docs, point somewhere else that may or may not exist, etc).

Since essentially all big data is really just code, it would make much more sense to tie these directly at the hip. So, a git/repo commit hash that is a key directly to the IPFS data hash would fix this problem directly.

So it's not "wanting big files in a git repo" (an obvious no-no, since central servers shouldn't be used for storing large data, and github centralized repos only should store single digit MB or so), it's wanting to relieve the cost of running processes that may require supercomputers weeks of processing for QM calculations, etc by providing a guaranteed hash pairing of the output of the code.


How about why not? The only reason it's not done is because git doesn't support it.


Maybe I came across as accusative, but I'm genuinely curious. Do you have 1Tb text files or this is some kind of media management for video production, something like that?


Because it's a source control system, which means it's intended to store source code, not the artifacts generated from the source code. It seems far-fetched that anyone would manage to author 1 TB of source code.


This isn't true at all. We were storing binary files separately via Maven for Java projects for almost 20 years now.

This was done with SVN projects. Keeping the blobs out of your source repos has been the preferred way for a long time.

[Edit] The only folks who seem to want to do this are game developers, and they are generally not people you would want to emulate.


Then how come git-lfs even exists at all? There's clearly a demand for it. Whether it's good practice is up for debate.

> Keeping the blobs out of your source repos has been the preferred way for a long time.

This is just appeal to tradition.


> This is just appeal to tradition.

It might be, but the arguement was that we don't do it because of git.

We haven't been doing it for a long time, but that's not because of git.


I'd be curious to know if someone is successfully using this in a team. How is it when two people are working in the same branch?


It's not really different than using Git to work on the same branch. If you and the teammate commit to the same branch, then you'll need to resolve the divergence somehow, usually a merge or rebase, or choose to forcibly overwrite the other's changes.


I can theorize about how it might work in a team also, but I was curious how this has played out in practice for anyone


To be clear, I am describing my actual usage of jj. It’s worth noting that there’s currently not a `jj pull` command, so divergences typically involve manually setting a branch pointer for me.


Jujutsu started as author's personal project and now author's full-time project at Google. It's presented at Git Merge 2022:

Jujutsu: A Git-Compatible VCS - Git Merge 2022:

Video:

https://youtu.be/bx_LGilOuE4

Slides:

https://docs.google.com/presentation/d/1F8j9_UOOSGUN9MvHxPZX...


> started as author's personal project and now author's full-time project at Google

That's got to feel good!


Absolutely, good for the author. However, given the numerous examples of Google pulling the plug on nonessential projects, I don’t see this as strictly positive development.


Well, it is open source, so the author or someone else could always pick it up again as a personal project if that happened.


Based on this https://devboard.gitsense.com/martinvonz/jj it looks like there are two people working on it full-time, with 7 people contributing at least 500+ lines of code churn and 14 contributing code changes. Overall the project is fairly healthy and there does appear to be enough people engaged with the project that it should continue, should Google pull the plug.

Full disclosure: This is my tool


Google should go back to slapping a huge "Beta" label on every single product.


I've been using jj daily for close to a year and without a doubt, it blows the Git UX out of the water. I'm very particular about breaking up my changes into atomic commits, so a killer feature for me is automatic rebasing. Since conflicts can be recorded in commits, you never have to stop a rebase to fix conflicts like you do in Git and Mercurial - history operations work exactly the way a human would expect. Modify a commit? Delete it altogether? jj just automatically rebases all of the descendants for you (and yes, it handles multiple children like Mercurial evolve and unlike Git rebase).

On top of this is a great set of primitives for manipulating commit history - `jj move|restore` to move|copy changes from one commit to another, `jj split` to break up commits, `jj rebase` to move commits around. _Everything_ is inside commit history (no "worktree", no "index"), so one set of commands and flags will do everything you want.

The operation log is also incredible. Most operations can be undone with zero problems. Compare that to Git, where your best hope to fix a bad rebase/commit --amend is to scrounge around the reflog for the right commits and manually fixing up your refs.

The biggest pain points for me are:

- Automatic working copy commit sometimes tries to commit things I don't want. Usually .gitignore has things covered, but sometimes moving files around and rebasing commits breaks things. Fixing up broken commits with `jj split|restore` is pretty easy though.

- No rename detection. jj doesn't handle merges as elegantly as Git. jj devs are thinking about this, but haven't quite cracked it. I'm not sure how long that will take.


> If an operation results in conflicts, information about those conflicts will be recorded in the commit(s). The operation will succeed. You can then resolve the conflicts later.

I’m really glad people are trying this out. I’ve spent the last decade or so playing with collaborative editing algorithms. Ideally I’d like tools like git to eventually be replaced by CRDT based approaches. CRDTs would let us use the same tools to do pair programming. CRDTs also handle complex merges better (no self-conflicts like you can get with git). And they’re generally a more powerful model.

One problem with all modern text CRDTs (that I know of) is that they do automatic conflict-free resolution of concurrent edits. But when we collaborate offline on code, we usually want conflicts to show up and be resolved by hand. CRDTs should be able to handle that no problem - they have more information about the edit history than git, but doing this properly will (I think) require that we put the conflicts themselves into the data model for what a text file is. And I’m not sure how that should all work with modern text editors!

Anyway, it sounds like jj has figured out the same trick. I’m excited to see how well it works in practice. With this we’re one step closer to my dream of having a crdt based code repository!


You should check out Pijul, as it essentially implements everything you mentioned here. Pijul works on patches which are CRDTs, it makes conflicts a first-class concept, etc.


Pijul is interesting, but my understanding is that it still operates on lines, and does diffs when you push the commit button. And that makes it not work well for real-time collaborative editing. Ideally I’d like a tool that can span both the real-time collaborative editing and offline collaboration use cases. But it’s a very interesting tool, and I’d like to have another read through the docs at some point. I remember being very impressed with it when I took a look a few years ago.


Last time I looked at it they had experimental support for binary diffs (not just lines). That was quite a while ago though, they have probably gotten further now.


I wonder: Is there a git backend for Pijul?


I will be surprised if it's possible at all. And I think right now such a back-end would not be high on Pijul priority list. But if I am wrong, I would be very interested to learn about it!


Long ago I suggested it as a way to get market share and it seemed not very high priority.


Have you not ever found any value in `git bisect`?

If you have a bug which is reproducible, but whose cause is complex, do you not think it's useful to be able to find the commit that introduced the bug in order to see which change caused it? If only to get a good first idea of what might need to be fixed?

Currently, `git bisect` works best if every commit is buildable and runnable, in order that any commit can be automatically tested for the presence of the bug, to narrow down the problem commit as quickly as possible. If some commits don't build or run because they contain conflict markers, this make `git bisect` need a lot more manual intervention.

Can you think of a way in which an equivalent of `git bisect` might be adapted to work in this scenario?

Note that just scanning for conflict markers might not be appropriate, in case a file legitimately contains text equivalent to conflict markers - e.g. in documentation talking about conflict markers, or something like `=======` being usable as an underline in some markup languages.


Yeah git bisect is great. And CRDTs on their own probably won’t preserve enough information to allow bisecting to happen - since we don’t know which points in time have working builds.

One approach to preserve the ability to bisect would be to allow users to periodically mark points in time with “commits” if they want. The commits wouldn’t be used for synchronisation or merging (since we have the crdt information for that). Instead, they could act semantically much more like anonymous git tags. But they would still be useful as landmarks to mark working builds and milestones. And for git bisect. We could give them associated commit logs. (“Got feature X working”, “Release 1.0.5”, etc).

Commits might also be a good way to manage pruning. If users type then delete something, many CRDTs will keep a copy of the deleted characters indefinitely in a log. But we could design it so it only durably persists inserted characters which still exist at at least one commit. Yjs already does something like this.


It seems CRDTs in the limit approach a commit for every keypress. Meanwhile a commit in git is an atomic set of keypresses, to transition from one working state to another. So, in a CRDT world with a commit for every keystroke we would need to annotate sets of commits. Like “all of these commits change the title ‘hello’ to ‘world’”. Would be interesting.


> Ideally I’d like tools like git to eventually be replaced by CRDT based approaches. CRDTs would let us use the same tools to do pair programming. CRDTs also handle complex merges better (no self-conflicts like you can get with git). And they’re generally a more powerful model.

I'd be interested to see how this plays out in practice.

It seems to be in conflict with the idea that scm history is a meaningful deliverable that should be arranged as series of incremental atomic changes before a patch series leaves your development machine.

However, most developers I interact with already treat git history as an infinite editor undo history, this approach seems like it would crystalize that fact.

How do you envision the (long-term) history working? Do you think it would provide more/less utility?


I’ve thought a lot about that. Personally, I think it’s silly that people try to use git’s history for two different purposes:

1. An audit log of what actually happened, and when

2. A curated story of features added and changed

I don’t think git can be both of those things at the same time already. And we see this tension play out when people argue about squishing commits and rebasing before merging.

Personally I think both pieces of information have value. And they should be managed separately - as different features. Eg Git could (and perhaps should) have a second semantic layer which marks a set of commits (atomic history of changes) as semantically associated with a particular feature or issue. This is how I imagine a crdt based scm working: the history of all changes is stored immutably, and synchronised. And change sets can be grouped and marked as “this set of changes is associated with feature XXX”, to make it easier to understand the intent behind code, and roll back specific change sets.


> Eg Git could (and perhaps should) have a second semantic layer which marks a set of commits (atomic history of changes) as semantically associated with a particular feature or issue.

It does (sort of)! They're called branches and tags.


"working copy is automatically committed" seems like a good idea at first glance, but there are many situations where this is not a good idea:

- when new artefact files are added and you have not yet added them to .gitignore, they'll be automatically committed

- when you have added ignored files in one branch and switch to another branch, the files will still be in your working copy but not listed in your .gitignore file, and would then be automatically committed

- staging only some files and comitting is much easier than splitting a commit after the fact


> - staging only some files and comitting is much easier than splitting a commit after the fact

I see this project as a challenge to that conventional wisdom. This view is certainly the one I have embedded in my mind. But is it right? I end up fixing up the index and amending commits post facto quite often. I can also do it pre facto. But in a world where you can't fully avoid editing after the fact, mightn't it be better to have a single workflow for this kind of editing? That is, if you can't totally get rid of post facto commit editing (which I think is reality), can you actually get rid of pre facto editing, and be left with just one editing workflow? If so, maybe that's good!

I haven't used this yet, but this strikes me as a very plausible attack on a conventional wisdom that we take for granted but may not actually be doing us any favors.


I agree. We've gotten used to pick what should go into the commit after we've done the work.

There's a very serious and glaring error in doing that if you don't commit everything. When someone checks out your commit in a clean repo, his codebase is in a state that has never existed before, not even on your computer.

Some people (I think we've all done it at least once) split their work into multiple commits after a few days of hacking. Like "Adding data services for X", "UI features for X". This is a bad example, but you get the point.

Normally this doesn't cause a problem since all commits get pushed at the same time. But it can easily break things like git-bisect (which is just an amazing tool for finding astonishing bugs).


> But is it right?

Obviously it depends on your workflow. If you're working on one feature at a time and only saving things out of your editor that you want to go to the project you cloned, it probably is.

That's not my world at all. I have logging and instrumentation all over the place when debugging. I have multiple features in flight. I have tweak patches that I maintain externally I don't want committed. I'm trying things for someone else, etc...

The index is the tool that allows you to manage all that without hating the process. That's why it was invented, in fact. Now, if you don't need it it seems needless, like someone is making you type "git add" for no reason. And jj seems to be aimed at that demographic, and that's fine.

But jj then needs to jump through some odd hoops to get back the "partial commit" workflow that is natural in git. Meh. Not a win for me personally.

There's also the problem of "patch hygiene", which flows like this tend to do pretty badly with. It's routine in some projects (Linux is the flagship here, obviously) to demand clean commit messages and perfect bisectability. Pull requests in that world routinely need to be split/squashed/reworked during review and when moving across branches. You need tooling to do that.


This, to me, reads like you're incredibly used to one workflow so any different one seems "odd". But I don't think it's odd, I think it's just different. If we'd all been doing it the jj way for a decade and a half, I think the "index" and "staging" would be the thing that seems odd to us.

So for instance, to work with the index with all your logging and instrumentation and debugging that you describe, you must be doing a lot of `git add -i` over the course of your workflow to partially stage things. Maybe you're also stashing the unstaged changes to make sure what you have in the index works on its own.

Well, I don't think that interactive add workflow actually seems any different fundamentally from copious use of `split`ing changes out of the working copy commit with jj. And I don't think stashing seems fundamentally different from rebasing the changes you split onto the branch you want to apply them to, leaving behind all the logging and instrumentation.

Yes, it seems like a lot of commit editing. But this is downstream of your workflow that requires some step to differentiate between what to check in and what not to, at a fine-grained level. You would have to split things up manually with jj, but you're already splitting things up manually with git, it's just a different workflow with a different command, but I'm not convinced it's any harder. Just different.

> Pull requests in that world routinely need to be split/squashed/reworked during review and when moving across branches. You need tooling to do that.

Yes, but jj seems to come with all that tooling! And I think its model of implementing all that on top of commits, rather than a combination of the concepts of unstaged+staged+stashed+commits, may actually be a better fundamental model.

But again, I still haven't used jj. My workflow is a lot like yours, at least oftentimes. I split stuff up ahead of time, I use the index a lot, I stash stuff a lot, I edit commits, I rebase interactively, I squash and split. But by my reading of jj, I think I can accomplish all that stuff, and just maybe in a better way.


I'm of the same opinion as you, here. I generally have 10+ "extra" files in my project directory (output files, notes, one-off scripts for certain things, etc). When I add files to a commit, I do it by filename, never "everything that's new/changed". I don't have a use case for "everything I've created/changed goes into the commit, always".

> switch to another branch, the files will still be in your working copy but not listed in your .gitignore file

This is a failing of git, imo. There should be a .local.gitignore to somesuch, that is "added to" .gitignore. It's VERY common for me to have files that I want ignore, but are specific to me; they don't belong in the project's .gitignore. I know there are ways to do this, but all of them are clunky. There should be a simple, out of the box way to do it.


> There should be a .local.gitignore to somesuch

I think .git/info/exclude is what you're looking for? (and also the global ~/.config/git/ignore)


I've used that file before but... it always felt sort of hacky. And every time I want to use it, I need to figure out where it is. It really feels like there should be a more obvious, straight forward way to do it. Admittedly, "user friendly" is not git's strong suit.


The .git directory seems like the obvious location for it; I agree that info/exclude isn't the most obvious path (I had to look up what the exact path was again for my previous comment), but all things considered that seems like a minor issue. I suppose you could symlink .git/ignore to .git/info/exclude or something in the git template.


> The .git directory seems like the obvious location for it

The same location as .gitignore seems like the obvious location for it, to me.


There actually a local .gitignore, it's called or located at .git/info/exclude

You can even ignoring changes in files that are already tracked with `git update-index --assume-unchanged <file>`


These might be the clunky way your talking about but you can have “private” git ignored either in ‘.git/info/exclude’ in the repo or in the ‘core.excludesfile’ set in your git config. The later is quite nice because you could actually version control that elsewhere if you version control your system config.


i always have a large pile of temp files and such that should never be committed but it's never been a problem because i never use `git add .` i use `git add -u` which is "only add things that are already being tracked"

there's a newer flag for the same thing but my brain is hardcoded to the old "-u" option from "back in the day". It feels like a "simple, out of the box way to do it" to me.


I do the same, but in all the mess that git status outputs, I occasionally forget to git add some required source code - which leaves entire chains of commits in my repository in a broken, impossible to compile or run state. Worse, I don’t notice until I try to run the code on another computer - and find I’m missing data.

Having a “local gitignore” makes a lot more sense. I’d like to be more explicit about which local files should be ignored.


Add those files to `.git/info/exclude` and they won't show up in `git status`


For point 1, they could easily diff the already tracked to the new ones right ?

Interesting nonetheless, I never liked staging in git but I never hit the case you mention so now I realize there's a need for a phase between a file system and a commit


> - when new artefact files are added and you have not yet added them to .gitignore, they'll be automatically committed

From only reading the README, my understanding is that you would just add the files to .gitignore and at the next auto commit they are removed again from the anonymous working copy commit. I agree it feels unintuitive that files are added and removed from the anonymous working copy commit, instead of not added in the first place. But since the anonymous working copy commit is largely invisible to you it shouldn’t make a difference.


I don't see how these things are an issue in jjs design, nor do I see how staging some files is easier than splitting a commit after the fact...

Check out the documentation, many of the cases you are concerned about are explicitly mentioned:

https://github.com/martinvonz/jj/blob/main/docs/git-comparis...


Constantly having to split after the auto-commits feels like I have to keep fighting this automated commit system. It's a bit like the autosave feature of text editors, which some people find useful, but I never felt like it's needed.

A big downside I can see is that sensitive information might end up in the repo forever because I forgot to revert the auto commit.


I think you are misunderstanding the autocommit, but maybe I am.

I think you don't need to do this after every autocommit but only before the manual ones? Isn't that how amending commits in git works? The unamended commit is no longer around, right?

Also it seems to me that you can rewrite old commits in jj to get rid of accidentally committed information a lot more easily than you can remove it from when you accidentally commit it to git...

Meta comment: Bit frustrating to see so many downvotes in these threads when people are just trying to grok what the jj model means concretely for important workflows.


I always end up reading through my commit diffs while committing and then maybe like a third of the time I edit something about them. So for me this seems like it could be a natural acknowledgement of that; just make that editing after the fact be the primary / only workflow.

Haven't used this yet so no idea if it's actually great or terrible, but I like to see ingrained conventional wisdom challenged like this. We're always in some local maximum, so I think it's often interesting to be pushed in some new direction on the gradient.


> staging only some files and comitting is much easier than splitting a commit after the fact

Re this point, how is it any different? "Staging" the files is essentially the same as splitting the commit, anyways — it's just that the newly-split contents go into a "staging area" vs a commit. Do you mean that the tooling to accomplish this is not good?


Staging is additive, while splitting is subtractive


I am working on a TUI tool for Git/jj to handle staging/splitting. Could you tell me more about what workflow would be problematic for you? For example, if you had to select lines that go in the first commit/staging area, does that work? Would it be better or worse if, for each line, you had to decide whether it would go into the first commit/staged vs second commit/unstaged area? Do you ever need to invert the first commit/staging area vs second commit/unstaged area?


I most definitely agree. To be honest I know I go against the current here, but so far there is nothing I really like from what I’ve seen in jj. I should try it for real, see how it feels when using it to get a better sense of it.


I haven't really used git on the command line for years now, except for some special cases. In my daily usage, I rely on the built-in IDE integration (IntelliJ, FWIW), and I don't understand why anyone would put up with doing it manually. I can do partial commits by selecting individual lines right in my editor. I can view all branches, merge them, cherry-pick from them, commit stuff or amend it, pull updates, edit tags - everything at once, with keyboard shortcuts.

Apparently, I'm in the minority here (also considering all the talk about git being such an essential skill that real programmers can issue commands blindfold). Why is that?


Because I have always and will always prefer to interact with my VCS on the command-line.

The skillset is portable across environments (I can remote into a box and look at a repo as easily as I can interact with one locally), across editors (I don't have to learn and re-learn how each editor interacts with the VCS), and I can use all my familiar tools to work with it.

As for those workflow examples, I can just as easily do all those things via the command-line. The editor integration isn't anything special. And when I need to something weird and advanced (e.g. interacting with the reflog), odds are I'm gonna have to bust out those command-line skills, anyway.

Why would that be so hard to believe?

Edit: BTW, to be clear, I have no issues with people using GUIs. If you're productive with your tooling, who am I to judge? But you asked why, so I answered why. I don't claim my way is any better than your way.


I, on the other hand, have always and will always prefer to interact with my VCS via the editor-integrated plugin with GUI.

The skillset is portable across environments (I can remote into a box and look at a repo as easily as I can interact with one locally), and I can use all my familiar plugins from the extension marketplace to work with it. Yes, when I switch my favourite editor I'll have to relearn most of the particulars but that happens only about every 5 years or so, so it's fine.

As for those workflow examples, I can just as easily do all those things via the GUI. The shell integration isn't anything special. And when I need to something weird and advanced (e.g. interacting with the reflog), odds are I'm sure as hell NOT gonna have to bust out my command-line skills: from experience, 50% of the time I use some advanced commands, I mangle my repo into a broken mess that I am not exactly sure how to fix; not to mention that for routine tasks I keep re-doing "git status/diff" after every change because, again from experience, 10% of the time I issue slightly wrong commands.

No thanks, I'd stick with GUI which shows me what exactly I am going to modify and how. Would that be so hard to believe?

Now on a less facetious note: I prefer vi to ed, mc to naked shell, and gdb in dual mode (even though it routinely mangles my xterm's geometry into something non-Euclidean) to plain gdb for the same reasons — I can clearly see the state of the system that I am about to change, and the preview of the changes I am about to make, too. I don't have to second guess myself, or review the output of complicated shell pipelines with "echo" or "--dry-run" appended to the actual worker commands before actually committing to them.


> No thanks, I'd stick with GUI which shows me what exactly I am going to modify and how. Would that be so hard to believe?

It's not. That's, you know, why I specifically said "I don't claim my way is any better than your way."


> gdb in dual mode

The TUI feature?


It's not that it's hard to believe, I've seen enough discussion on this very topic to know many people (at least on HN) seem to prefer the CLI for VCS interaction.

I'm merely wondering why people seem to prefer a lower-level, separate tool to a higher-level, integrated one. To me it seems similar to writing your Makefile by hand vs. using automake.

The same as you, I don't want to dismiss anyone's tooling. Just curious :-)


I think there are two dynamics at play:

* Low-level tools come out and become robust sooner than high-level ones. It makes sense to learn them when no alternatives exist. Once alternatives are mature they're more effort to learn. I'm just much less fluent with an IDE than I am with the commandline, and the ubiquity of the commandline has meant I haven't been forced to learn to use the IDE well.

* There's some value to shaving off levels of dependencies. An IDE sends commands to git. There's just more moving parts and more to go wrong compared to just using git. When things go wrong, you don't have to understand two levels of error messages. Even when things are right, things can be slower. git is designed with obsession for large repos. Many UI screens might not scale as well as the underlying command providing them data.

In my experience most low-level people as you put it focus on the second reasoning. You don't use git in an IDE because you prefer to use Vim over an IDE, etc. But the first seems valid as well.


Scripting and automation are the goal ultimately. Working at the CLI supports that, GUI often doesn't. While I do like to see things in a GUI at times, ultimately it all gets encoded in scripts for automation and CI purposes. So terminals continue to be the focus.


Except you can't do that just as easily because the interface is worse

And you don't have to learn about each editor, just learn about one

And those command line skills can just be used in those advanced cases, that doesn't mean the 90% of the time you have to have worse experience

It's not hard to believe, it's just the arguments don't square


> It's not hard to believe, it's just the arguments don't square

Have you ever had a conversation like this?

Person A: Hey, what's your favourite food?

Person B: Pizza.

Person A: Really! Why?

Person B: I dunno, I just like the taste and the whole experience of eating it. But obviously that's just my opinion, and I totally get that some people might prefer something else.

Person A: Your argument doesn't square. Here, let me explain why your preference for pizza is wrong...


Instead of this irrelevant pizza example you could've tried to address the real issues with your arguments (hint: the one where you liked/prefered wasn't on the list)


I see I was too subtle, so I'll be direct: you seem to be confused because I'm not having an argument.

The question you might consider asking yourself is: why do you insist on trying to create an argument where none exists?

This is an incredibly bad habit. Arguing with people about their personal, subjective preferences when it's clear that person isn't inviting in an argument in the first place is incredibly annoying and it pisses people off. Don't do it.


You're repeating your mistake: I didn't argue about your subjective experience, but about your more objective justification of a particular workflow.

And you've turned a constructive conversation into off-topic pizza moralizing. Don't do it

(and of course you're having an argument, just a using poor arguments)


My experience with IDE integration is that it never implements the full feature set of the git CLI. It's probably improved, but I've generally found that anything besides basic clone/branch/commit/merge with very few developers and branches eventually leads to having to resort to the git CLI to resolve issues.


This is true of most IDEs, but you should really give IntelliJ's plugin a shot. It's far better at merge conflict resolution than using the CLI, which in turn makes even very complicated rebases easy. And it's more than capable of performing the most complicated git workflows I can imagine actually using in a real project (squash, cherry pick, rename, reorder, drop, apply patch, etc.).

The only time I ever reach for the CLI when I'm using IntelliJ is for `git bisect`.


I don't know about 'real' programmers, but I have an, admittedly, irrational fear of git GUIs doing the wrong thing. Even in Intellij, I open the built in CLI to interact with git. Old habits die hard :)


As they say, fear is the mind killer ;) if it helps: there is a Git tool window (not the VCS one) that has a Git Log tab, which shows the exact git command line IntelliJ executes. Gives a little peace of mind.


A lot of people who are starting out using git don’t understand how git works (what a commit is, what a branch is, what you can do to them). And they start to blame the CLI tool and start to hope that using the GitHub Desktop app will make everything make sense. This is the most common context where people say “you have to learn the git CLI.”

(Here’s an example from a few days ago from someone who proposes using GitHub Desktop in order to avoid learning git commands: https://www.reddit.com/r/learnprogramming/comments/15b7pra/s...)


Because I have two types of people who understand git on my team. People who use the CLI and people who don't understand git and just start clicking buttons


I was annoyed by a lack of support for git worktrees in my current employer's dependency system (think virtualenv from python, but for everything). I fixed some scripts to be worktree dependent and tested it out thoroughly on the CLI, but as soon as I got the PR through with the change, I got a bazillion messages on Teams from people yelling at me that I broke their workflow.

Their workflow was basically "blindly point and click in Visual Studio because I don't understand even the basics of git. I miss the old custom in-house VCS and am very grumpy".


You're confusing "git" (which is relatively easy to understand and use) with "the git CLI" (which is a UX nightmare).

I learnt how to use git years before I learnt to use the git CLI.


You forgot the people that only know: git commit -a -m "foobar" and think that's better than using a GUI to easily make clean commits.


If I could enforce one thing, it would be to REQUIRE partial commits for everything from an UI. I can't tell you the amount of commits I have seen that need to be cleaned up from spare debugging statements to vendor files to things that need to be added into the git ignore.

The other thing would be to make sure people understand what things like 'squash' and 'rebase' to and how they effect the tree.

Also to explain if you have multiple features, please don't work on it all on the same branch unless you have too. (This is a internal workflow, not really related to git but how we handle merges)...

Yeah, a UI could be just fine for an experienced git user. In my experience though, experienced git users don't use it though, so it's a moot point.


> from an UI

Just a really quick aside...

Do you... say "oooey" instead of "you eye" when reading "UI"?


UI


It's because most Git GUIs are not especially good. There are a lot of them, the best ones aren't cross platform and some of the most popular are some of the worst.

I 100% agree with you that a GUI is by far the sanest way to do most git operations (I have yet to find an interactive rebase GUI that is better than a text editor). But if you just pick a random GUI it's probably going to be not very good.

Good GUIs I have discovered:

* GitX (Mac only) * GitExtensions (Windows only I think) * The VSCode Git Graph extension is basic but decent. Unfortunately it seems to have been abandoned.

I have tried many many others and they all sort of work but don't present things in an easy to follow way.


Give the IntelliJ integration a spin. It's really something else. Check out the merge conflict resolution manual for some impressions:

https://www.jetbrains.com/help/idea/resolve-conflicts.html


How well does your integration handle stacked PRs (if at all)? I find that the majority of my interaction with git is super basic that I get no value add replacing `git add -p` (esp. since I'm always in my terminal with Vim/tmux).

What _is_ annoying and something I could probably automate if I thought about it for more than a few minutes is when:

a) I have stacked PRs for isolation/workflow purposes in the form of: A <- B <- C

b) we use squash and merge

c) now when A gets merged I need to fix B and C because while the change set is the same the history is not (because of the squash & merge)

d) when B gets merged I have to fix C for the same reasons


> I rely on the built-in IDE integration (IntelliJ, FWIW)

That you're using IntelliJ makes a huge difference. VSCode's git integration is okay, but I honestly just reach for the command line if I'm using VSCode for a project. IntelliJ's, though, is hands down the best git UI out there. Even standalone apps can't compete with the convenience of having all the features bundled directly into your editor.

From what I've seen, a lot of people have tried git integrations in other IDEs and found that they are missing functionality and the features they do have aren't well done, so they assume that all git integrations will be the same. But as I've been reading through all the jj testimonials here, I can't help thinking that I already have all of this through the IntelliJ git plugin.


I have realized I have 15 years of git experience (incredible if true) and just got really, really used to it. Still excited if jj is a good follow-up since it sounds like it's not too far away from git's model.


Either it's employers who got tired of people not even knowing the basics of git (which would be common to any dvcs) and weren't productive as a result.

Or it's folks that think the base set of linux tools are the be-all-end-all of programming. ("Why use Dropbox when I can rsync", "Use the ext4 filesystem as a database and store metadata in inodes and use git for MVCC", "I will do sed | awk | cut | xargs find | tr instead of a 10 line python script").

Or it's folks that cult-follow one of the two groups above.


It's faster for me to interact with IntelliJ's git porcelain using key bindings than it is for me to switch to a terminal and write a command.

It makes fewer errors than I do. Besides forgetting syntax, fat fingering is a thing.

Conflict resolution is faster, easier, and less error-prone.

Pretty typical sorts of efficiency gains that IDEs can give.


This looks promising. One question I had after reading about its git compatibility is that they seem mostly focused on the use case where a Jujutsu user accesses a git repository (hosted by e.g. GitHub) with jj. But does it support the converse way of working, i.e. accessing a native Jujutsu repository with git?

I ask this because most developers are already quite familiar with the git CLI so in production use one would probably see developers co-working with jj and git in the same codebase. Or would the realistic production scenario be always using git (as opposed to native Jujutsu database) as the backing storage to allow accessing both with git and jj CLIs?


The README's footnote:

At this time, there's practically no reason to use the native backend. The backend exists mainly to make sure that it's possible to eventually add functionality that cannot easily be added to the Git backend.


That footnote has a “won’t be big and professional like GNU” feeling to it.


I would assume there would always be the expectation that you either use Jujitsu as a frontend to a git repo, or have a complete Jujitsu based remotes.

If you're going to work on and contribute to a project that is already using Jujitsu, it is reasonable to expect that you'd adapt your workflow to the project itself and not the other way around.


Thanks, it makes sense. I don't understand why this was downvoted.


This Git-compatibility-first approach makes Jujutsu seem like a stronger contender to replace Git than I've seen so far.

I'm curious about its management of conflicts. I know that pmeunier has taken a lot of care to formally work out a theory of patches to drive Pijul, and that unsound or problematic notions of patches/conflicts can lead to serious problems— they say that's what led to all the performance problems with Darcs, right? I'd love if the comparison page on the repo wiki gave a little more detail than that Pijul's handling of conflicts seems 'similar'.


There is a little more detail here: https://github.com/martinvonz/jj/blob/main/docs/technical/co...

Storing the conflicts symbolically in this way lets you reproduce the conflicts later and even auto-resolve certain conflicts, but it doesn't address resolving the actual contents of conflicts. You could probably use Pijul as a jj backend and get the best of both worlds (if someone were to implement it).


>safe replication via rsync, Dropbox, or distributed file system

Neat. I've been leaning more and more towards systems that are dumb-sync-friendly because I can throw them anywhere with anything and they just work. Always glad to see new things doing this!


Can someone say more about this?

I've heard of this issue before but haven't experienced it myself. How does it arise, I.e. what's the race condition between git and rsync/Dropbox/... that causes problems?


Git mutates files to do many of its normal operations. Dumb-syncing without being perfectly up to date before you disconnect and switch to another machine and start making more changes means you risk conflicts (new conflict-named files in Dropbox, silently clobbered changes in many, etc), and git has essentially no way to notice this and warn you about what happened or recover what you lost. So you do your stuff, later discover the sync error when you reconnect, and now you have to choose losing data or understanding git's plumbing and raw data well enough to save both by hand.

---

For example, branches. From what I can tell (I'm moderately confident here, but definitely not 100%):

Git stores branches as a file at .git/refs/heads/branch-name which contains the current commit SHA. When you make a new commit in that branch, it modifies the file's contents to contain the new SHA. Syncing a conflicting change here means... well lots of possibilities. Maybe one write silently wins and the other SHA gets silently garbage collected eventually, maybe there's now a new branch with a conflicty name, maybe you have a broken file because it contains a `diff` conflict and it has <<<===>>> markers, maybe demons erupt from your nostrils.

Jujutsu stores branches as a folder at some/path/branch-name/. The current head of the branch is a file in that folder with the SHA as its filename (the file is empty), giving you some/path/branch/c0ffee . If you sync changes from somewhere else due to concurrent changes to your clone, it either agrees and there's no harm, or it becomes a new file in that folder, some/path/branch/00f .

Your local tool doesn't know which is correct, but it can tell that a conflict occurred, and both actions' results are still available.

---

That's a thing you have to design carefully around, so it's completely understandable that it's not the default. But one of the benefits is that it means you don't need special protocols to exchange data, so tons of things simply work correctly without further effort. Git has to use lock files to protect its internals, and they have numerous issues and are completely useless in some situations (e.g. NFS, Dropbox). Jujutsu simply doesn't need them, and works safely regardless of your system or usage patterns (for this at least, dunno about everything).

All this^ is why I pay to store my git repos in a git host rather than simply using my own backup systems (and why I don't have two remote backups). And it's why I avoid SQLite for much of my heavy-use personal backed-up stuff - it makes one large file that mutates, so fully syncing is a MUST before making any changes. Same with keepass - one large mutating database (but it has pretty good database merging built in, so the dozen or so conflict files I've gained are easy to resolve). There are pros and cons to splitting things up into a million files of course, but I run into sync issues pretty regularly so that's the pain I feel most keenly.


> The working copy is automatically committed.

Thank goodness. This has been needed for a long time.

Props to the author Martin for investing himself in creating elegance!


I've just started looking into this, and since this seems to be doing a few automatic rebases under the hood, I wonder how this behave if commits get randomly pushed to origin. For git is always obvious when you are about to amend/overwrite a pushed HEAD and you can push forcefully only explicitly.

Edit: anonymous branches are destined to be pushed remotely (to be reviewed and merged) and there is no local merge as far as I can tell, you can name these branches but no "merge back to development branch once done". Completely different workflow, having the ability to merge or "collapse" the anonymous branch to its parent would be nice, when you don't really need to push your feature branches anywhere.


> I wonder how this behave if commits get randomly pushed to origin

You would expect the push to fail in the normal way, as if you had manually done the rebase, because your commit history may have diverged. That being said, I don't think this happens much in practice: the automatic rebases are typically for explicit history-rewriting operations that users tend to only do on their local work. If a user prefers to use a "no-rewriting" workflow, then they can certainly do so by simply not issuing the history-rewriting commands.

> anonymous branches are destined to be pushed remotely (to be reviewed and merged) and there is no local merge as far as I can tell, you can name these branches but no "merge back to development branch once done".

I'm not sure what you mean by this. You can do `jj merge` in a similar way to `git merge`, or you can do a rebase workflow.


> You would expect the push to fail in the normal way, as if you had manually done the rebase, because your commit history may have diverged. That being said, I don't think this happens much in practice: the automatic rebases are typically for explicit history-rewriting operations that users tend to only do on their local work. If a user prefers to use a "no-rewriting" workflow, then they can certainly do so by simply not issuing the history-rewriting commands.

Yeah, most of those rebases happen in the working copy or inside the anonymous branch.

> I'm not sure what you mean by this. You can do `jj merge` in a similar way to `git merge`, or you can do a rebase workflow.

I just meant merging a feature branch back to its parent, jj merge exists but it's not obvious which revisions you are supposed to pass to it and jj log doesn't even seem to be able to update the HEAD for the parent branch. It should be an easy operation but it's not, clearly not the suggested workflow (or at least, not documented from what I've read).


> jj merge exists but it's not obvious which revisions you are supposed to pass to it

I don't think I understand your confusion. `jj merge` is essentially like Git merge, except that you also normally pass the current commit as one of the revisions. For example, you could write `jj merge @ main` to make a merge commit with the working copy[1] and the `main` branch. Feel free to open a GitHub discussion or drop by the Discord channel to discuss more.

> jj log doesn't even seem to be able to update the HEAD for the parent branch

`git log`/`jj log` are non-mutating operations, so why would you expect that they can update `HEAD`/branches?

[1]: In my workflows, I typically use `@-` instead of `@` in this situation because I consider the working copy commit to contain "uncommitted" changes, but there is a significant complement of users who primarily use `@`, so it depends on your workflow.


Thanks for the detailed answer.

> For example, you could write `jj merge @ main` to make a merge commit with the working copy[1] and the `main` branch.

Since it's my first time seeing this, the syntax is a bit confusing, the man says "Unlike most other VCSs, `jj merge` does not implicitly include the working copy revision's parent as one of the parents of the merge; you need to explicitly list all revisions that should become parents of the merge.", it's not clear to me if the order matters and how many can be listed (and why for more than 2). What I wanted to replicate was just doing a "git co main; git merge feature-branch", thanks for the explanation.

> git log`/`jj log` are non-mutating operations, so why would you expect that they can update `HEAD`/branches?

What I meant was that with git status/log the differences between your local clone and the remote are clearly shown, you know if they are misaligned and see the last commit for each branch without using additional commands, jj log/st provide way less info. I see where "main" is in jj log but have no idea if that label will update after a push or a fetch of the remote. Just a bit confusing for newcomers, I have to play with it.


> it's not clear to me if the order matters and how many can be listed

It's worth noting that in Git, 1) the order is recorded and tracked in the commit (but almost always doesn't matter), and 2) you can have any number of parents for a commit. A >2-parent commit is called an "octopus merge" in Git, which you can search further to learn about. Thus, the only real difference is that Git defaults to including the current commit as one of the merge parents (and this can't really be disabled via `git merge` itself; you'd have to use one of the plumbing commands and construct the commit manually).

In Git, you can have 0+ parent commits; in Mercurial, you can have 0-2 parent commits; in Jujutsu, you can have 1+ parent commits (except for the special root commit with the zero hash, which is the ancestor of all commits that would otherwise have no parents).

To explicitly answer about the merge parent order, you can see a question like this: https://stackoverflow.com/q/49715421/344643. I suspect that under jj the difference is not nearly as complicated because of how it records conflicts, but I don't know for certain.

> What I meant was that with git status/log the differences between your local clone and the remote are clearly shown

In general, the multi-remote visualization could be improved in jj to handle some common workflows. You can customize the default `log` by setting `revset.log` https://github.com/martinvonz/jj/blob/7751cea47cfe6dd9654275... to show the commits you care about.

If the same branch exists on multiple remotes pointing to different commits, then I believe they're rendered with the remote to disambiguate as e.g. `main@origin`. This originates from Mercurial.


This looks incredibly good to me. The working copy as "commit being worked on right now" is a much simpler data structure than the "working copy + stash + staging area" of git.

I really hope this succeeds... VSC support would be a great start...


Related DVCS: https://pijul.org/

I need to try it out at $WORK since constant rebasing on a busy repo with a hundred or so committers is not fun.


Pijul needs a 1.0 release if it wants wide adoption. I don't understand why they wait.

Meanwhile, if rebasing on git is an issue, you should probably try stacked-git (https://stacked-git.github.io/). It manages commits as a stack of patches - like quilt, but on top of git.


git-branchless also fits in the same vein as stacked-git with everyone’s favorite commands like `record`, `unrecord`, & `smartlog`.

That said, I’ve been using darcs this past month & the performance issue seem overblown (/ largely fixed, tho not fully addressed) while the stability & usability are there. Email support is a bonus.


I'm semi-sold on the idea of everything always being a commit, and lighter weight editing of those commits & structure, it sounds good. Except:

1. Not until I run some jj command? It's kind of begging for a 'jjd' isn't it? Or if you use an IDE you'd want/need it to be not just saving but doing some kind of 'jj nop'.

2. I haven't looked more into it than the readme, but that at least doesn't discuss (and I think it's important) withholding commits from the remote(s)? If everything's always(ish) committed, I've lost some control of untracked files or unstaged or locally stashed changes that I now need at the point of pushing; to mark those commits 'private' or something. I assume it does exist, and I'll look for it when I make time to play with it, but I find it slightly concerning (for how good it will be, or how important it's considered to be) that it's not more prominently discussed.


> Not until I run some jj command? It's kind of begging for a 'jjd' isn't it? Or if you use an IDE you'd want/need it to be not just saving but doing some kind of 'jj nop'.

In practice, I find that it doesn't matter much. Some people do run `jj` in a loop incidentally (usually they have a live graph-log open on some screen). I suppose that you could get a "local history" feature like in some editors of more fine-grained changes to the codebase in this way. Folks have discussed adding a jj daemon, but so far it's not a priority.

> I haven't looked more into it than the readme, but that at least doesn't discuss (and I think it's important) withholding commits from the remote(s)? If everything's always(ish) committed, I've lost some control of untracked files or unstaged or locally stashed changes that I now need at the point of pushing; to mark those commits 'private' or something. I assume it does exist, and I'll look for it when I make time to play with it, but I find it slightly concerning (for how good it will be, or how important it's considered to be) that it's not more prominently discussed.

Usually it's pretty obvious to me which of my commits are public or private. When interfacing with GitHub, commits that are not reachable by a branch are definitely private . Additionally, commits without a description are private, and `jj git push` will warn you before allowing you to push them.

There has been some discussion about adopting Mercurial-style "phases" (https://wiki.mercurial-scm.org/Phases), which would explicitly accomplish the goal of marking commits public or private.


The jj daemon thing is something I've had on my mind to maybe hack up, but in practice it's not that huge of a deal I've found.

It is worth noting that jj is designed as a CLI and a library. So, for the hypothetical IDE integration, it could use its own custom written daemon or just integrate all this as part of its autosave functionality via the Rust crates. That's the long-term goal, anyway.


Fascinating.

How does it handle very large repos and binary files? I want petabyte scale history and terabyte scale sparse clones.

Alternatively, why use this over Git? I read the differences but feel like I’m missing a higher level difference. What problems does this solve that Git/Hg don’t solve already? What was the inspiration for starting the project?


The tool looks great; I have a hurdle to overcome with the name.

I'm long accustomed to spelling it, in English, as Jujitsu. I've also seen Jiu-jitsu. "jutsu" is much less common, IME.

Is there such thing as canonical Romanisation of Nipponese? I can deal with a project being "wrong" better than not knowing which of us is wrong.


There are multiple romanisation systems for Japanese, but the most common one is Hepburn. In Japan, Kunrei-shiki is sometimes used (especially by the government), which is designed with Japanese speakers in mind (vs Hepburn which was designed with English speakers in mind).

It's jujutsu in both, but there are varying ways of representing the long vowel on the first "u" -- either omitting it (jujutsu), using a macron or circumflex (jūjutsu), or repeating the vowel (juujutsu).

Ju-jitsu and jiu-jitsu are not correct in any romanisation system that I know of, I'm not really sure how they came about. Probably historical accident.


So is it pronounced like it looks here? I've never seen this spelling before, and IME (and I assume GP's) everyone says 'jew-jit-sue'. But it's more like 'jew-jut-sue' (as in to jut out)?


To use your phonetic spelling juujutsu would be more like jew-jew-tsue, with the first jew being stretched out a little. That is, both ju syllables would be pronounced the same way. The tsu on the end is its own syllable.

Unless we're talking about the English word, which I'm not sure how to pronounce. If we're talking about the American word then it's really just like a lot of other murkin words: butcher it any way you like.


The base assumption, though, is that we're talking about 柔術 (with a long u), not 呪術 (without a long u). Without a clarification from the author, it's hard to tell which one it's supposed to be.


Just to add clarification: 柔術 is the martial art, while 呪術 means sorcery.

The first kanji in 呪術 means curse, so while "sorcery" could be a cool name for a software project in English, a Japanese speaker would probably have apprehensions about using 呪術.


I cringe whenever I see it spelt with an "i." It would "jujutsu" or similar in either of the typical romanization schemes (Hepburn, etc). "Ji" would be pronounced "jee" in any of the standard romanizations.

Pronunciation is more like joo-juh-tsu. "Tsu" is its own syllable.


I think your question is getting into the field of martial arts lineage. People might have their own narratives/ mythologies around this, but here's the most neutral way I can explain it: As techniques and styles evolve over the years, people come up with new names to describe those styles. Name similarities will often imply closer ties in lineage.

As a Brazilian Jiu Jitsu practitioner, I cringe when I see it spelled any other way, but also I have to recognize that I only feel that way because I have more exposure to that specific martial art/spelling.


The spelling ‘mistake’ makes it easier to web search.


Explain how does it handle large binary files?

(the UX around this is the shortcoming of all current DVCS..)


README says that the git backend is the recommended backend, as the "native" one has no additional features, so I imagine: it handles them the same as git (ie. they are just objects in the .git repo data, and each time you change them you add a new one, and they are poorly compressible and optimizable) -- which is, I imagine, the problem you're referring to.


It won't be completely the same as git, because the client fetches stuff more lazily (according to the linked presentation). So the backend will still fill up with stuff and need lots of storage, but the clients then won't necessarily slow down in the same way.

That said, any git users will still get slowed down - only jj users would see the benefit. Git does have better features these days for shallow clones though and even git is on the way to killing off the need for LFS.


Might want to look at purpose built tools for that such as lakeFS (https://github.com/treeverse/lakeFS/)

* Disclaimer: I'm one of the creators/maintainers of the project.


Now I'm curious: how would you want a DVCS to handle large binaries?


Partial checkout/Shallow first checkout.

JJ doesn't seem to do that though, right?


Probably something block based, such as borg


Oh wow, this looks really promising.

I've been playing with a homegrown centralized VCS client and server project, aimed at basically being the VCS host for monorepos, and this looks like a much better foundation for the VCS layer.


Looks really cool! One thing I'm not clear on from the docs: does it support ignoring changes to some files for "real" commits? For example, a repo at work has a file used for mocking up feature flags. The file is tracked but it's encouraged to edit it when testing things, just don't commit your changes. If I'm not mistaken, I'd have to remember to undo changes to that file before "describing" the commit. Is that right?


I know the nuisance of having to tiptoe around files you don't want to add to history.

In case it helps your use case:

    git update-index --assume-unchanged <file>
    git update-index --no-assume-unchanged <file>
This would ignore changes while you're testing - but you have to remember to turn it off or, iiuc, you won't pull intentional changes either.

You might find hooks useful too. Not to assume your knowledge, these are shell scripts placed in .git/hooks that are invoked, e.g., before commit or before push. You could have it parse git status, detect changes to <file>, prompt for confirmation if changed and remove from working set if the change is unintentional.


I never knew about the `update-index` command. That's going to save me a lot of time. Thanks!


The commit will indeed be created immediately, there's no way to prevent that except for .gitignore I'm aware of. Until you run `jj describe`, it won't have a description.

However, if you don't manually put a branch on it, it'll never get pushed and will stay on your machine only.

You can sit on this personal commit and rebase it on top of any other commit to move around the repo, again and again if you like.


> I (Martin von Zweigbergk) have almost exclusively used `jj` to develop the project itself since early January 2021. I haven't had to re-clone from source (I don't think I've even had to restore from backup).

WTF?? Me neither on none of my git project. Ever. So what? I’m a git genius?? The truth is I don’t even understand how one can reach a point where re-cloning a (git) repo is needed: git can always reset --hard and it has never ever failed for me, even during complicated interactive rebase and co.


Looks interesting. Unfortunately doesn't support signing commits - apparently it's possible via "jj export" and using classical git:

https://github.com/martinvonz/jj/issues/58#issuecomment-1247...


The plan for how the add signed commits is there, and the work isn't that hard (especially as gitoxide continues to add functionality), it just has to be pushed over the line and I've been a bit slack on getting that going.

There's definitely nothing foundational blocking it though and it will happen one day if you'd like to give it a go in the meantime.


I had to choose a VCS for a project a year ago. At that point jujutsu looked extremely promising but immature. It's great to see that so much progress has been made. I can't wait to give it another go, the next time I get to choose a VCS for something. Innovation in this space is desperately needed.


I am certainly no expert in version control systems, but I've gotta say that it's really wonderful to see a project that builds on the algorithmic and cultural successes of Git but with a simplified and modernized approach. The reason that Git took over the open-source world is two-fold: first, it was adopted by Linux, which ended up being the most influential OSS project of all time. Second, the Git model, which is distributed and egalitarian at its core, is a natural model for a fast-paced, globally distributed community of developers. These are both reasons to appreciate Git, but they do not imply that Git is the final word in version control. I'm excited to see innovation in this space!


> which is distributed and egalitarian at its core, is a better model for a fast-paced, globally distributed community of developers than previous monorepo systems like Mercurial

I'm a bit confused by this. I don't think that's what monorepo means, is it? Monorepo is what you choose to put in a repo? And I thought Mercurial was extremely similar to Git as it's also a DVCS?


Git and Mercurial are fairly similar, at least compared to what OP is apparently hinting at. Different tech and different internal architectures, but both of them are DVCS offering roughly equivalent workflows.


Mercurial is indeed extremely similar to Git. Also, it's two weeks younger, contrary to OP's (apparently now-deleted) assertion that git was replacing hg. (I find it amusing that bzr, git, and hg all came out within a month of each other.)


It is. The OP is wrong, probably because he has never used Mercurial, which wouldn’t be that surprising given it doesn’t have a lot of users outside of the few remaining companies still using it.


I've never used it, but I have read hg.init[0]!

[0] https://hginit.com


I am too; it's hard, now, to imagine anything dethroning git, but presumably something will do so one day, and this could be that thing.

SQLite uses a custom vcs called Fossil, but doesn't make much effort to push broader adoption (afaics) so it remains academic at this point.

Jujutsu keeping git compatibility looks like a differentiator that reduces cost of adoption. I'm excited!


Extremely cool looking project which I would love to try, alas I am blocked by the lack of git-lfs support, for now

https://github.com/martinvonz/jj/issues/80


Some more discussion a year ago: https://news.ycombinator.com/item?id=30398662

Anything new in the project?


The automatic rebase and jj diffedit sound very nice addition to some of my most-used git workflows.

Looks very interesting, will definitely have to give this a deeper try.


It's nice to have alternatives and maybe I have Stockholm Syndrome about this topic, but isn't git's complexity inherent to the area?


Git fails to make the common paths simple. There's no need for most of the complexity to be so prevalent in day to day use


Has anyone tried both this and Sapling? https://engineering.fb.com/2022/11/15/open-source/sapling-so...

Both of these are on my TODO lists but haven't had time to try them yet.


Check out https://github.com/martinvonz/jj/blob/main/docs/sapling-comp...

In my opinion:

- Sapling is much more mature/full-featured at this point. - Jujutsu improves source control workflows in a more principled way. - Jujutsu currently supports colocation with a Git repo, while Sapling requires that the Git repo be kept separately.


Does anyone have experience with large repo's of say, 100 GB? Does jj incur performance penalty's compared to native git?


It depends on whether you're talking about 100 GB repository size or working copy size.

- Currently no partial/shallow clones, so you need to materialize the entire repo on disk.

- Working copy status can take a while (see https://github.com/martinvonz/jj/issues/1841 for tracking issue). This can be ameliorated at present by configuring Watchman as a filesystem monitor, and work is underway to improve status further.

- No support for Git LFS at present (even in colocated repos). When using the Git backend with jj, you would expect the same problems with regards to large file management.

- I haven't noticed any particular performance issues when interacting with the object store a large repository. It should be approximately the same as for Git since it uses libgit2 for the Git backend.

- Faster for operations that can be done in-memory in jj but only on-disk with Git, such as various rebase operations.


This seems like a really unnecessary amount of overhead. With build times often taking multiple minutes, being able to multi-task is essential. I often work on two things at once. That seems difficult / impossible with this.


What does the version control system have to do with build times and multi-tasking? I use jj in repositories with long builds, but I don't see what it has to do with that.


Every jj command creates a commit. So if you have two unrelated pieces of work you need to split all the commits created.


I see that the README is unclear or even misleading on this point. How it works is that, upon each command, the commit corresponding to the working copy is updated in a similar way as `git commit --amend`. You will typically only have one "working copy commit", which corresponds to "unstaged changes" in Git. The difference is that instead of using special-purpose commands like `git add` to interact with the staging area, you instead use the same set of commands that you would use to manipulate other commits. (There is a `jj commit` convenience command to model the common case of adding a commit message to the working copy commit and then creating a new working copy commit on top of it.)


I'm thrilled to see the comments here. Jujube is a gem, an absolute gem. Ive never felt so empowered or fearless to make changes in a git repo, and never more confident that it would be really hard to lose work, unlike with git.


Very impressive! Special congrats for not bloating the title with "in Rust"


This project is a great example of subliminal marketing.

It is less apparent now but still, the repeated flex of “Google”, 20% project etc when no typical reader would assume them is classic corporate charlatanry.

Shame because I like the project otherwise


It may be a calculated move. But, more charitably, perhaps it is simply unpolished communication.


What sourcery is that? It highlights the first character(s) of the hash!

Does [git log] have that? I think too much hash looks noisy.

All I could find was:

  $ git rev-parse --short=4 HEAD
  8f60


Do I understand this correctly?

This is some kind of background process that automatically commits any changes you make.

You can use the CLI to check what it did and if you want to modify the auto commits.


No daemon, it happens 'whenever you run a command'.

> Commands snapshot the working copy before they start, then the update the repo, and then the working copy is updated (if the working-copy commit was modified). Almost all commands (even checkout!) operate on the commits in the repo, leaving the common functionality of snapshotting and updating of the working copy to centralized code.


so commit is a side effect, not an explicit action

weird choice, isnt explicit always better than implicit


... so a Jujutsu Kaisen begins if getting a commit conflict?


Really sad jj wasn't short for "JoJo's Bazaar Gitventure".


I never understand such projects... How is git hard, especially compared to the complexity of most things that you would even commit to your git repository?


Working on large complex projects the main branch will have dozens or hundreds of commits per day pushed continuously. The value is a consistent and linear commit history moving from known-state to known state.

Facebook version control system built on mercurial is the best I've used. One of the huge benefits of the rebase based workflow is commits can be reordered and land as they pass tests rather than a single linear commit history.


You all work on one shared main branch, without any feature branches? Isn't it horrible to have other people half-finished code in the code that you are trying to work with?


Work in whatever branch you want, but if you want to merge to main and deploy it better be a stack of PRs that merge cleanly and don't expect others to wait or serialize on your change.


This looks really cool, I'll try it out on some of my repos to get a feel for it.

Too bad support/discussion happens entirely on Discord.


Realistically, Jujutsu is a pretty new project and there are many features to add and lots and lots of code that needs to be written. There is a lot of talking that needs to happen, in other words. So, most of the developers and committers actively hang out and discuss things in Discord, yes, because it's convenient, and fast, and allows all the interested parties to be alerted. I say this as someone who started hacking/working on Jujutsu recently; having Discord has been nice for such a new project.

The reality is that the project is in a part of its life where active discussion and feedback with the userbase is pretty valuable. So, you just go where the users are, and Discord is a big place for that.

GitHub Issues and GitHub Discussions are also actively used by a lot of people, and you can just post things there. Every major committer watches those venues as well, AFAIK, I know my notifications are set up for it.

Over time, assuming things are wildly successful, you'd probably have a lot of different venues to discuss things. I would view Discord mostly in the context of a new project that wants feedback, in that regard.


Support/discussion happens a fair amount on GitHub Discussions and Issues as well.


I was expecting to be meh, but by halfway through the readme I was thinking "this actually sounds great!"


I was (and still am) meh. YMMV I guess.


my initial reaction, half OT:

Ooof, random "ASCII" (actually: Unicode) art & dev-chosen colors, my bane of the "modern" CLI applications. That drawing you like? Doesn't work for me, give me the raw output please. Those colors you love? Aside of red-green weakness being the most dominant factor, what you're really doing is trying to set things apart, connotating color with semantics as well. It's nice this works fine on your white-on-black terminal. Have you tried this on a white-on-firebrick terminal? Yellow-on-green? Or anything else than _your_ "normative" setup? Man ...

Also not sure the information presented is adequate. E.g. consider commit 76(2941318ee1) - jj makes it look like that was committed to that repository, while it was done to another. The git presentation looks more spot-on (for that particular commit, while the rest of the display is just a mess - ASCII art that does not add semantics, random colors); also where is 1e7d displayed in jj's output? Why is jj's order different? I remain unimpressed by both UIs.

" Create a file at ~/.jjconfig.toml" ... $XDG_CONFIG_HOME ?

When is that working copy committed? When I run jj? Why bother, when it's not working asynchronously and automatically? And if you commit working copies, do you sync under the hood with stuff the other folks you collaborate with? If not, why bother?

Oh nice, a command to fix "stale" workspaces.. how about you don't let workspaces go stale?

This may all seem to make sense to git-minded people, given the comments here. To me, neither jj nor git make sense (as fossil-minded person who has to work with git), so shrug enjoy....

..but please fix that ASCII Art and Color Stuff, thank you very much.


All the colours can be adjusted or turned off entirely in the config. [1] A number of different graph styles are supported [2], and failing that, you can completely customise the output template [3]

$XDG_CONFIG_HOME/jj/config.toml is supported, that's where I keep mine.

The working copy is updated whenever you run jj by default, but watchman is also supported (recently added). [4]

In my experience, the command to fix the stale workspaces only needs to be run in exceptional cases where a bug got triggered and a command failed to complete or if you're doing manual poking around.

It's a mindset shift, but it's well worth it in my opinion.

[1] https://github.com/martinvonz/jj/blob/main/docs/config.md#ui... [2] https://github.com/martinvonz/jj/blob/main/docs/config.md#gr... [3] https://github.com/martinvonz/jj/blob/main/docs/templates.md [4] https://github.com/martinvonz/jj/blob/main/docs/config.md#fi...


> It's nice this works fine on your white-on-black terminal

I was curious about this as well, as I found the images in the README a bit hard to read. In fact the program itself seems to use quite sensible colours in my white-background terminal, and it also respects the NO_COLOR environment variable.


I often use 5%-black-on-90%-white (and vice versa) and various colored setups that bear semantics to me (like work context, for example, authenticated user, preferred REPL, ...) and as soon as you leave a monochrome world, those colors tend to break down completely.

But, TIL about NO_COLOR!


I don’t mind color, but pushing beyond the 16 colors is often a stretch without a very specific use case & bound to lead to a lack of legibility for some unless both foreground & background are defined—which has a tendency to look just as bad in a terminal. Similar issues happen with CSS when folks define color but not background color.

But the one CLI trend that annoys me is using Emoji terminal. I often find their colors and shapes to be too distracting, commanding too much of the visual hierarchy of output. They also have a tendency to kind of fall apart when some characters or combinations of characters are missing or they no longer line up with the monospace output. A big part of CLI output is being able to scroll through the logged output, but the Emoji actually make visual scanning more difficult.


Does it honor NO_COLOR?

If not, then I have https://github.com/alrs/nofun


Wow, you really just looked at a screenshot, got mad, and then went to write this shallow hate message.

> my bane of "modern" CLI applications

Ok boomer, I guess you're also a Rust hater too - because I love that every last CLI written in Rust is not garbage because clap is so good and so ubiquitous

All the colors and unicode graph can be configured and disabled through config and CLI flags, and the default pager is less which supports NO_COLOR and piping and everything else that less does.

It literally has an extensive template system as one of the features to make the log output ANYTHING you would want:

adjust some colors that you hate? sure (also there's a config for color names that are used in templates, for colorblind or contrast adjustments etc)

add/remove/adjust some info (that you hate)? sure

just list full commit ids to be machine-readable that match some revset? - absolutely

Man, why do I feel so second-hand embarassed from reading this dumb ignorant reply, lol


I just find another alternative to Git called Grace. It's made by a Microsoft employee with F#.


I saw the presentation, its about having cloud ready or cloud native scm, i dont think this is a great idea

git is about working locally github (or similar solutions) is the cloud part

cloud native scm sound like a bad idea


Is there any good GUI client supporting this?


One of our Discord members had a prototype implementing support in IntelliJ if you want to try that. Other than that, I'm not aware of any jj GUI clients. However, you can use jj directly in Git repositories with the colocated mode and then continue using your preferred Git GUI client. (Obviously, you miss out on the jj-only features.)


Thanks, maybe will try the co-located mode since I only use cli git for some operations not covered by GUI, and git cli UI is atrocious


Why/how did Git's UX become so bad?


Torvalds is not a UX person. If the creator is indifferent, you get a slew of commands and flags that map to the data structures and idiosyncrasies of the creator, rather than anything presenting a cohesive API to the outside world.


As I see, it is both a DVCS and a meta-DVCS.


I thought it was about UFC for a sec :)


what's the advantage of native backend compared to git repo backend?


> At this time, there's practically no reason to use the native backend. The backend exists mainly to make sure that it's possible to eventually add functionality that cannot easily be added to the Git backend.

From the README, no advantage for now.


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

Search: