1. 88
    1. 22

      Yet another Jujutsu post :)

      The intended audience for this is mainly my colleagues at Mozilla, but I haven’t yet seen posts comparing Jujutsu to Mercurial, so figured this might be useful more generally.

      1. 2

        I started using Jujitsu recently, and looks very promising, especially for splitting up a big PR into smaller conflict-free PRs. I never used Mercurial, but I like the idea of tracking change sets.

      2. 11

        Git is so dominant, that this capability should now be considered table stakes for any budding DVCS projects out there. Without it, those projects will be doomed to obscurity.

        With it, those projects won’t care if they’re doomed to obscurity. Which is perhaps even more important: to each their own front end, as long as we have a common protocol.

        1. 19

          FWIW JJ is ontrack to be used internally at Google, it’s not a pet project anymore.

          1. 3

            Is it a small personal project where nobody said “no” to JJ’s inclusion or is it being absorbed into real projects as well?

            1. 14

              Hehe, the real litmus test: Are people using it against their will?

              1. 3

                It’s a test JJ is practically designed to fail, at least if they’re using the Git backend: anyone participating in a project tracked by JJ can just keep using Git.

                1. 6

                  Google’s jj usage isn’t backed by git.

                  1. 1

                    You’re telling me they’re using a different back-end? One that does not comply with Git communication protocols?

                      1. 2

                        Oooh, so, Googlers are using a JJ frontend over the company monorepo am I correct?

                        Because if that’s true, then anyone can adopt JJ without forcing anyone else to do so, and my central point remain: JJ as adopted by Google is still explicitly designed to not force anyone, and thus fail the “real litmus test” @qznc was referring to.

                        Which is a good thing.

                        Ping @steveklabnik

                        1. 3

                          Sure, I didn’t disagree. I think jj being able to be incrementally adopted by an organization is a very strong point for it.

                      2. 3

                        That’s correct. I forget what the name is. It’s not even a DVCS.

                2. 12

                  It’s the named successor to our mercurial frontend to the monorepo. It’s in limited alpha.

              2. 13

                Most notably on the missing front, are an equivalent to hg histedit (or git rebase -i) and hg absorb

                Conveniently, absorb has just been merged, literally yesterday \o/

                1. 16

                  And authored by a long time Mercurial maintainer by the looks of it.

                  Mercurial’s DNA is all over Jujutsu and I’m loving watching Jujitsu’s evolution.

                  1. 2

                    Yeah, I saw that! I’ll update my post once it makes it to a release.

                    1. 1

                      Thanks for this heads up, was a nice ping to update my jj; I could really make use of this feature atm!

                    2. 5

                      Jujutsu is and will remain a completely unusable project for me until it has support for pre-commit hooks, unfortunately. I enjoyed what I saw when I demoed it a bit to learn, but every repo I’ve worked with in my 12-year career has had at least one pre-commit hook in it (including personal projects which generally have 2-3!), and running them manually completely defeats the entire purpose of them especially in a professional setting.

                      I keep checking in on the issue and the tracker for it, but still no luck.

                      I think given that the original issue starts with

                      I don’t think I had seen https://pre-commit.com/ until @chandlerc linked to it

                      shows the completely different universe the devs of jj live in, and it leads me to believe that this feature just won’t get much priority because obviously they never use it, and all projects like this are (rightfully, usually) targeting fixing problems for the people making it.

                      I have my hopes still, but until then…. back to git.

                      1. 15

                        I’m curious why people like pre-commit hooks. I run my makefile far more frequently than I commit, and it does the basic testing and linting. And the heavyweight checks done on the server after pushing. So there doesn’t seem much point to me in adding friction to a commit when the code has already been checked, and will be checked and reviewed again.

                        1. 8

                          To take an example from my jj-style patch-manipulation-heavy workflows:

                          • I often I abort (“stash”) work and switch to a new speculative approach, which means committing it before it’s been checked. Indeed, at this stage, it’s not expected to pass checks, so triggering a pre-commit hook would be a waste of time.
                          • I often significantly modify the graph structure (combine and split various commits, discard some, etc.), and then need to re-run checks on the whole patch stack/tree. Therefore, I rely primarily on post-hoc checks that run across all the commits in the stack/tree, rather than pre-commit checks.
                          • I often want to re-run checks for each commit in my stack after rebasing on top of the latest main commit (in a trunk-based development workflow).

                          One should definitely distinguish between “checks that should run on each commit” and “pre-commit checks”.

                          • Most pre-commit checks represent checks that should run on each commit, but it doesn’t mean that before committing is the optimal time to run the check. That depends on one’s specific local workflows and how they use commits.
                          • In your case, you are already running your checks before committing, and presumably you don’t modify the commits afterwards, so an actual pre-commit hook seems unlikely to help your workflows.
                          1. 5

                            I use pre-commit hooks extensively, to ensure that the code I’m pushing meets all kinds of project requirements. I use formatters, and linters, and check everything that can be checked. For one thing, it does away with the endless battles over meaningless nonsense, like where commas belong in SQL statements, or how many spaces to use for indenting. Another is it just reduces the load on the CI/CD systems, trading a small fraction of my time locally for expensive time we pay for by the cycle.

                            I’ll never go without them again.

                            ETA: but, based on sibling comments, it seems that the jj folks are On It, and yeah, it won’t be a “pre-commit” hook, the same way, but as long as it can be automagically run … ok, I’m in.

                            1. 4

                              As others here have stated, I think the fundamental issue is that commits in jj are essentially automatic every time you save. There are a few consequences to this such as:

                              1. no predefined “significant event” to sensibly attach a pre-hook to (I’d make them pre-push hooks, maybe?)
                              2. inability to save any secrets to disk as those immediately get committed, meaning they’re in the history, meaning you have to rewrite history locally to remove them before pushing, or Lord help you
                              1. 3

                                I care about these things too, but they’re tested in CI, once I am ready to integrate them, rather than locally.

                                1. 2

                                  Horses for courses. I’d rather use my compute than The Cloud but I am notoriously a cranky greybeard.

                                  1. 3

                                    For sure, I’m just saying it is possible to care about those things and not use hooks. You should do what’s best for you, though.

                                2. 2

                                  Aren’t those all already available in the ide? I get my red squiggles as I write, instead of waiting for either the precommit or the ci.

                                  1. 2

                                    Not everyone uses an IDE, or the same IDE, or the same settings in the IDE. I think that computers should automatically do what they can, and using pre-commit hooks (or whatever the jj equivalent will be) is a way to guarantee invariants.

                                3. 4

                                  Pre-commit hooks are really easy to enforce across a large team, while any sort of IDE settings are not. Some I’ve used before:

                                  • Remove all graphical output from Jupyter notebooks, so that any files added to git remain small
                                  • Similarly, check that there are no large files being added, unless someone uses a option
                                  • Run the formatter on any changed code

                                  You can do all of these in other ways, but pre-commit makes it easy to do exactly the same thing across the entire team

                                  1. 8

                                    I totally agree with you all that stuff is super important to run before changes make it to the repo (or even a PR). The problem is that pre-commit hooks (with a lower case “p”) fundamentally don’t mesh with Jujutsu’s “everything is committed all the time model”. There’s no index or staging area or anything. As soon as you save a file, it’s already committed as far as Jujutsu is concerned. That means there’s no opportunity for a tool to insert itself and say “no, this commit shouldn’t go through”.

                                    The good news is that anything you can check in a pre-commit hook, works just as well in a pre-push hook, and that will work once the issues 3digitdev linked are fixed. In the meantime, I’ve made myself a shell alias that runs pre-commit -a && jj git push and that works well enough for me shrug

                                    1. 2

                                      Not everything runs that easily. Eg https://github.com/cycodehq/cycode-cli has a pre_commit command that is designed specifically to run the security scan before commit. It doesn’t work the same before push because at that point the index doesn’t contain the stuff you need to scan.

                                      1. 1

                                        Hm, I’m not sure if cycode-cli would work with Jujutsu in that case. I’m sure if there’s enough demand someone would figure out a way to get it working. Even now, I’ve seen people MacGyvering their own pre-commit hooks by abusing their $EDITOR variable.. E,g export EDITOR="cycode-cli && vim" ><

                                        1. 1

                                          Many people never use an editor to write a commit message, they use the -m flag directly on the command line 🙂

                                          But yeah, at that point we could mandate that we have to use a shell script wrapper for git commit that does any required checks.

                                    2. 2

                                      In my years I’ve had exactly one repo that used makefiles.

                                      The issue with makefiles has nothing to do with makefiles – the issue without pre-commit is deliberate action.

                                      If I’m on a team of 12 developers, and we have a linter and a formatter which must be run so we can maintain code styles/correctness, I am NOT going to hope that all 12 developers remembered to run the linter before they made their PR. Why? because I FORGET to do it all the time too. Nothing irritates me more and wastes more money than pushing to a repo, making a PR, and having it fail over and over until I remember to lint/format. Why bother with all of that? Setup pre-commit hooks once, and then nobody ever has to think about it ever again. Commit, push, PR, etc, add new developers, add new things to run – it’s all just handled.

                                      Can you solve this with a giant makefile that makes it so you have just one command to run before a PR? Yes. But that’s still a point of failure. A point of failure that could be avoided with existing tools that are genuinely trivial to setup (most linters/etc have pre-commit support and hooks pre-built for you!). Why let that point of failure stand?

                                      Edit: Also, keep in mind the two arent mutually exclusive. You like your makefile, fine keep it. Run it however many times you want before committing. But if everyone must follow this step at least once before they commit…..why wait for them to make a mistake? Just do it for them.

                                      1. 16

                                        Generally, I believe jj developers and users are in favor of the idea of defining and standardizing “checks” for each commit to a project, but the jj model doesn’t naturally lend itself to running them specifically at pre-commit time. The main problems with running hooks at literally pre-commit time:

                                        • Tends to pessimize and preclude the patch-manipulation workflows that jj is designed to facilitate.
                                          • In particular, an arbitrary commit is likely not to be in a working state, so the pre-commit hook would fail.
                                          • Running pre-commit hooks for each commit serially is often slower. There are ways to parallelize many formatting and linting tasks (for multiple simultaneous commits), but you can only do that once you’ve actually written all the commits that need to be validated.
                                        • Unclear how it would work with all the jj commands that do in-memory operations.
                                          • It’s not necessarily possible to run pre-commit hooks on those commits since they’ve never been materialized in a working copy.
                                          • For example, if you rebase commits from one place to another, those commits may no longer pass pre-commit checks. A lot of the technical and UX advantages of jj rely on “commits” always succeeding (including rebases and operations that may introduce merge conflicts); adding a way to make committing fail due to pre-commit hooks would sacrifice a lot of the benefits.
                                          • The current jj fix command can run on in-memory commits, I believe, but this also means that it’s limited in capability and doesn’t support arbitrary commands.

                                        The hook situation for jj is not fully decided, but I believe the most popular proposal is:

                                        • Support “pre-push” hooks that run immediately before pushing.
                                          • These can run the checks on each commit about to be pushed, and either fix them or reject the push as appropriate.
                                        • Rather than support “pre-commit” hooks, delegate the checks to commands like the following, which run post-commit, even on commits that aren’t the ones currently checked-out. Then the user can run these commands manually if they want, and the pre-push hook can invoke them automatically as well.
                                          • (present) jj fix: primarily for single-file formatters and linters
                                          • (future) jj run: can run arbitrary commands by provisioning a full working copy

                                        For the workflows you’ve specified, I think the above design would still work. You can standardize that your developers run certain checks and fixes before submitting code for review, but in a way that works in the jj model better, and might also have better throughput and latency considerations for critical workflows like making local commits.

                                        1. 1

                                          This does sound promising.

                                          1. 4

                                            @arxanas is speaking from experience and you can see this in action today if you want: it’s built into git-branchless (which he built) as git test: https://github.com/arxanas/git-branchless/wiki/Command:-git-test

                                            git-branchless is a lovely, git-compatible ui like jj, inspired by mercurial but with lots of cool things done even better (like git test)

                                        2. 2

                                          Setup pre-commit hooks once, and then nobody ever has to think about it ever again.

                                          Once on every clone, across the entire team if a hook changes or is added?

                                          How do you manage hooks? Just document them in the top level readme?

                                          1. 5

                                            We had at least one conversation at a Mercurial developer event discussing how to make hg configs (settings, extensions, hooks, etc) distributed by the server so clones would get reasonable, opinionated behavior by default. (It’s something companies care about.)

                                            We could never figure out how to solve the trust/security issues. This feature is effectively a reverse RCE vulnerability. We thought we could allowlist certain settings. But the real value was in installing extensions. Without that, there was little interest. Plus if you care this much about controlling the client endpoint, you are likely a company already running software to manage client machines. So “just” hook into that.

                                            1. 3

                                              I’m not entirely sure I follow what you’re asking. My guess here is that you’re unfamiliar with pre-commit, so I’ll answer from that perspective. Sorry if I’m assuming wrong

                                              Pre-commit hooks aren’t some individualized separate thing. They are managed/installed inside the repo, and defined by a YAML file at the root of your repo. If you add a new one, it will get installed (once), then run the next time you run git commit by default.

                                              As long as you have pre-commit installed on your system, you can wipe away the repo folder and re-clone all you want, and nothing has to change on your end.

                                              If a new dev joins, all they have to do is clone, install pre-commit (single terminal command, run once), and then it just…works.

                                              If a pre-commit hook changes (spoiler: They basically never do…) or is added/removed, you just modify the YAML, make a PR to the repo, and its merged. Everyone gets latest master and boom, they have the hook.

                                              There is no ‘management’. No need to really document even (although you can). They should be silent and hidden and just run every commit, making it so nobody ever has to worry about them unless their code breaks the checks (linters, etc).

                                              1. 1

                                                Thank you.

                                                You’re talking about?:

                                                https://pre-commit.com/

                                                Not?:

                                                https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks

                                                I take it the former runs code on pre-commit for any cloned git repo (trusted or not) when installed locslly, while the latter needs setup after initially cloning to work?

                                                So it changes git behavior to run code pre-commit on every repo - but doesn’t directly execute code, rather parses a yaml file?

                                                1. 2

                                                  Yes I am talking about the former. To be clear though I don’t know exact internals of pre-commit, but I don’t THINK it modifies git behavior directly. Instead it just has a hook that runs prior to the actual git commit command executing, and it DOES execute code, but does so in a containerized way I believe. The YAML file is just a config file that acts sorta like a helm chart does; providing configuration values, what command to run – it’s different for each hook.

                                                  If you’re curious, you can see an example in one of my personal repos where I am defining a pre-commit hook to run the “Ruff” tool that does linting/formatting on my project.

                                                  Also, a note: Pre-commit hooks only execute against the files inside the commit too! So they tend to be quite performant. My ruff checks add maybe like….100ms to the runtime of the commit command. This can obviously vary – I’ve had some that take a few seconds, but in any case they never feel like they’re “blocking” you.

                                                  1. 1

                                                    FWIW I’d consider 100ms to be quite bad given that jj commits tend to take < 10ms.

                                                    I agree with folks in general that in jj, almost no commits made by developers will pass pre-commit checks so a rethinking/different model is required. (For most of my professional life I’ve just used a combination of IDE/LSP feedback and CI for this, a long with a local script or two to run. I also locally commit and even push a lot of broken/WIP code.)

                                                    I’m also curious how the developer experience is maintained with pre-commit hooks. I was on the source control at Meta for many years and there was an intense, data-driven focus on performance and success metrics for blocking operations like commit. Do most authors of pre-commit hooks bring a similar level of care to their work?

                                          2. 2

                                            There has been some interesting discussion in response to my question about pre-commit hooks.

                                            The thing I’m (still) curious about is how to choose the point in the workflow where the checks happen. From the replies, I get the impression that pre-commit hooks are popular in situations where there isn’t a build step, so there isn’t a hook point before developers run the code. But do programmers in these situations run the tests before commit? If so, why not run the lints as part of the tests? If the lints are part of the standard tests, then you can run the same tests in CI, which suggests to me that the pre-commit hook configuration is redundant.

                                            I’m asking these questions because pre-commit hooks imply something about the development workflow that I must be missing. How can you get to the point of pushing a branch that hasn’t gone through a build and test script? Why does it make sense to block a commit that no-one else will see, instead of making the developer’s local tests fail, and blocking the merge request because of the lint failure?

                                            For auto-formatting in particular, I don’t trust it to produce a good result. I would not like a pre-commit hook that reformats code and commits a modified version that I had not reviewed first. (The likelihood of weird reformatting depends somewhat on the formatter: clang-format and rustfmt sometimes make a mess and need to be persuaded to produce an acceptable layout with a few subtle rephrasings.)

                                            1. 3

                                              For auto-formatting you can choose to either have it silently fix things, or block the commit and error out. But yes, generally you can never rely on pre-commit hooks to prevent checks because there’s no way to enforce all developers use it. You need to rely on CI for that either way.

                                              The benefit to running the checks before pushing to a pull request for instance, is that it reduces notifications, ensures reviewers don’t waste time, reduces the feedback / fix loop, etc. Generally just increases development velocity.

                                              But all those benefits can also be achieved with pre-push. So I see the argument for running the checks prior to pushing to the remote. I fail to see the argument for running the checks prior to committing. Someone elsewhere in the thread pointed out a checker that apparently inherently needs to run pre-commit, so I guess there’s that? I don’t know the specifics of that checker, but seems like it’s poorly designed to me.

                                          3. 7

                                            I don’t think I had seen https://pre-commit.com/ until @chandlerc linked to it

                                            We always have one dev that introduces Husky to the repo and then everybody else has to add --no-verify to their commits because that’s easier than going into an argument with the kind of dev that would introduce Husky to the repo.

                                            Most devs underestimate (as in have absolutely no idea as to the amount of rigor necessary here) what goes into creating pre-commit checkers that are actually usable and improve the project without making the developer experience miserable.

                                            1. 4

                                              I mentioned this in another comment, but pre-commit (the hook) will never be feasible for the simple fact that “committing” isn’t something you explicitly do in Jujutsu. It’s quite a mindshift for sure, but it’s one of those things that once you get used to it, you wonder how you ever did things the old way.

                                              The good news is that pre-push is totally possible, and as you noted, there is work underway to make the integration points there nicer. AIUI the issues you linked are getting closer to completion (just don’t expect them being fixed to mean that you’ll have the ability to run pre-commit hooks).

                                              1. 1

                                                Find a rust capable dev, start hacking on it and engage with the Jujutsu crowd on IRC (#jujutsu at libera) or Discord. The developers are very approachable and can use additional PRs. That way everyone can eventually benefit from a completed hooks implementation.

                                              2. 1

                                                I never understood Mercurial, though I didn’t try very hard once GitHub became a thing. Since there are some fans here, can anyone point me to a good “hg for git users” primer?

                                                1. 5

                                                  Mercurial itself is no longer the best starting point for learning about the ideas of Mercurial. It’s too hard to setup (it’s not even good without plugins and a lot of configuration), the community is too small, the momentum not existent, it’s git compatibility isn’t as good, etc.

                                                  If you’re interested in using something Mercurial-like today, your two best options are:

                                                  1. 1

                                                    I’d just learn jj at this point. I think roughly everything that makes Mercurial good is also captured in jj, along with some new ideas that are mind-bending at first but plainly correct once you get used to them.