Skip to content

How to Contribute


We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow.

Contributor License Agreement

Contributions to this project must be accompanied by a Contributor License Agreement. You (or your employer) retain the copyright to your contribution; this simply gives us permission to use and redistribute your contributions as part of the project. Head over to to see your current agreements on file or to sign a new one.

You generally only need to submit a CLA once, so if you've already submitted one (even if it was for a different project), you probably don't need to do it again.

Code reviews

All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult GitHub Help for more information on using pull requests.

Unlike many GitHub projects (but like many VCS projects), we care more about the contents of commits than about the contents of PRs. We review each commit separately, and we don't squash-merge the PR (so please manually squash any fixup commits before sending for review).

Each commit should ideally do one thing. For example, if you need to refactor a function in order to add a new feature cleanly, put the refactoring in one commit and the new feature in a different commit. If the refactoring itself consists of many parts, try to separate out those into separate commits. You can use jj split to do it if you didn't realize ahead of time how it should be split up. Include tests and documentation in the same commit as the code they test and document. The commit message should describe the changes in the commit; the PR description can even be empty, but feel free to include a personal message.

When you address comments on a PR, don't make the changes in a commit on top (as is typical on GitHub). Instead, please make the changes in the appropriate commit. You can do that by creating a new commit on top of the initial commit (jj new <commit>) and then squash in the changes when you're done (jj squash). jj git push will automatically force-push the branch.

When your first PR has been approved, we typically give you contributor access, so you can address any remaining minor comments and then merge the PR yourself when you're ready. If you realize that some comments require non-trivial changes, please ask your reviewer to take another look.

To avoid conflicts of interest, please don't merge a PR that has only been approved by someone from the same organization. Similarly, as a reviewer, there is no need to approve your coworkers' PRs, since the author should await an approval from someone else anyway. It is of course still appreciated if you review and comment on their PRs. Also, if the PR seems completely unrelated to your company's interests, do feel free to approve it.

Community Guidelines

This project follows Google's Open Source Community Guidelines.

Contributing large patches

Before sending a PR for a large change which designs/redesigns or reworks an existing component, we require an architecture review from multiple stakeholders, which we do with Design Docs, see the process here.

Contributing to the documentation

We appreciate bug reports about any problems, however small, lurking in our documentation website or in the jj help <command> docs. If a part of the bug report template does not apply, you can just delete it.

Before reporting a problem with the documentation website, we'd appreciate it if you could check that the problem still exists in the "prerelease" version of the documentation (as opposed to the docs for one of the released versions of jj). You can use the version switcher in the top-left of the website to do so.

If you are willing to make a PR fixing a documentation problem, even better!

The documentation website sources are Markdown files located in the docs/ directory. You do not need to know Rust to work with them. See below for instructions on how to preview the HTML docs as you edit the Markdown files. Doing so is optional, but recommended.

The jj help docs are sourced from the "docstring" comments inside the Rust sources, currently from the cli/src/commands directory. Working on them requires setting up a Rust development environment, as described below, and may occasionally require adjusting a test.

Learning Rust

In addition to the Rust Book and the other excellent resources at, we recommend the "Comprehensive Rust" mini-course for an overview, especially if you are familiar with C++.

Setting up a development environment

To develop jj, the mandatory steps are simply to install Rust (the default installer options are fine), clone the repository, and use cargo build , cargo fmt, cargo clippy --workspace --all-targets, and cargo test --workspace. If you are preparing a PR, there are some additional recommended steps.

You will probably also want to make the gh-pages branch immutable (and thereby hidden from the default jj log output) by running the following in your repo:

jj config set --repo "revset-aliases.'immutable_heads()'" 'remote_branches(exact:"main") | remote_branches(exact:"gh-pages")'


One-time setup:

rustup toolchain add nightly  # wanted for 'rustfmt'
rustup toolchain add 1.76     # also specified in Cargo.toml
cargo install cargo-insta
cargo install cargo-watch
cargo install cargo-nextest

During development (adapt according to your preference):

cargo watch --ignore '.jj/**' -s \
  'cargo clippy --workspace --all-targets \
   && cargo +1.76 check --workspace --all-targets'
cargo +nightly fmt # Occasionally
cargo nextest run --workspace # Occasionally
cargo insta test --workspace --test-runner nextest # Occasionally

WARNING: Build artifacts from debug builds and especially from repeated invocations of cargo test can quickly take up 10s of GB of disk space. Cargo will happily use up your entire hard drive. If this happens, run cargo clean.


These are listed roughly in order of decreasing importance.

  1. Nearly any change to jj's CLI will require writing or updating snapshot tests that use the insta crate. To make this convenient, install the cargo-insta binary. Use cargo insta test --workspace to run tests, and cargo insta review --workspace to update the snapshot tests. The --workspace flag is needed to run the tests on all crates; by default, only the crate in the current directory is tested.

  2. GitHub CI checks require that the code is formatted with the nightly version of rustfmt. To do this on your computer, install the nightly toolchain and use cargo +nightly fmt.

  3. Your code will be rejected if it cannot be compiled with the minimal supported version of Rust ("MSRV"). Currently, jj follows a rather casual MSRV policy: "The current rustc stable version, minus one." As of this writing, that version is 1.76.0.

  4. Your code needs to pass cargo clippy. You can also use cargo +nightly clippy if you wish to see more warnings.

  5. You may also want to install and use cargo-watch. In this case, you should exclude .jj. directory from the filesystem watcher, as it gets updated on every jj log.

  6. To run tests more quickly, use cargo nextest run --workspace. To use nextest with insta, use cargo insta test --workspace --test-runner nextest.

    On Linux, you may be able to speed up nextest even further by using the mold linker, as explained below.

Using mold for faster tests on Linux

On a machine with a multi-core CPU, one way to speed up cargo nextest on Linux is to use the multi-threaded mold linker. This linker may help if, currently, your CPU is underused while Rust is linking test binaries. Before proceeding with mold, you can check whether this is an issue worth solving using a system monitoring tool such as htop.

mold is packaged for many distributions. On Debian, for example, sudo apt install mold should just work.

A simple way to use mold is via the -run option, e.g.:

mold -run cargo insta test --workspace --test-runner nextest

There will be no indication that a different linker is used, except for higher CPU usage while linking and, hopefully, faster completion. You can verify that mold was indeed used by running readelf -p .comment target/debug/jj.

There are also ways of having Rust use mold by default, see the "How to use" instructions.

On recent versions of MacOS, the default linker Rust uses is already multi-threaded. It should use all the CPU cores without any configuration.

Previewing the HTML documentation

The documentation for jj is automatically published to the website at

When editing documentation, we'd appreciate it if you checked that the result will look as expected when published to the website.

Setting up the prerequisites

To build the website, you must have Python and poetry 1.8+ installed (the latest version is recommended). It is easiest to install poetry via pipx, as explained in the Poetry installation instructions. A few helpful points from the instructions: pipx can often be installed from your distribution, e.g. sudo apt install pipx; this will usually also install Python for you if necessary. Any version of pipx will do. If you are installing pipx manually, you may first need to follow the Python installation instructions.

Once you have poetry installed, you should ask it to install the rest of the required tools into a virtual environment as follows:

poetry install

You may get requests to "unlock a keyring", an error messages about failing to do so, or Poetry may simply hang indefinitely. The workaround is to either to unlock the keyring or to run the following, and then to try poetry install again:

# For sh-compatible shells or recent versions of `fish`

Building the HTML docs locally (with live reload)

The HTML docs are built with MkDocs. After following the above steps, you should be able to view the docs by running

# Note: this and all the commands below should be run from the root of
# the `jj` source tree.
poetry run -- mkdocs serve

and opening in your browser.

As you edit the md files, the website should be rebuilt and reloaded in your browser automatically, unless build errors occur.

You should occasionally check the terminal from which you ran mkdocs serve for any build errors or warnings. Warnings about "GET /versions.json HTTP/1.1" code 404 are expected and harmless.

How to build the entire website (not usually necessary)

The full jj website includes the documentation for several jj versions (prerelease, latest release, and the older releases). The top-level URL redirects to, which in turn redirects to the docs for the last stable version.

The different versions of documentation are managed and deployed with mike, which can be run with poetry run -- mike.

On a POSIX system or WSL, one way to build the entire website is as follows (on Windows, you'll need to understand and adapt the shell script):

  1. Check out jj as a co-located jj + git repository (jj clone --colocate), cloned from your fork of jj (e.g. You can also use a pure Git repo if you prefer.

  2. Make sure includes the gh-pages branch of the jj repo and run git fetch origin gh-pages.

  3. Go to the GitHub repository settings, enable GitHub Pages, and configure them to use the gh-pages branch (this is usually the default).

  4. Run the same sh script that is used in GitHub CI (details below):

    .github/scripts/docs-build-deploy ''\
        prerelease main --push

    This should build the version of the docs from the current commit, deploy it as a new commit to the gh-pages branch, and push the gh-pages branch to the origin.

  5. Now, you should be able to see the full website, including your latest changes to the prerelease version, at

  6. (Optional) The previous steps actually only rebuild and its alias If you'd like to test out version switching back and forth, you can also rebuild the docs for the latest release as follows.

    jj new v1.33.1  # Let's say `jj 1.33.1` is the currently the latest release
    .github/scripts/docs-build-deploy ''\
        v1.33.1 latest --push
  7. (Optional) When you are done, you may want to reset the gh-branches to the same spot as it is in the upstream. If you configured the upstream remote, this can be done with:

    # This will LOSE any changes you made to `gh-pages`
    jj git fetch --remote upstream
    jj branch set gh-pages -r gh-pages@upstream
    jj git push --remote origin --branch gh-pages

    If you want to preserve some of the changes you made, you can do jj branch set my-changes -r gh-pages BEFORE running the above commands.

Explanation of the docs-build-deploy script

The script sets up the site_url mkdocs config to ''. If this config does not match the URL where you loaded the website, some minor website features (like the version switching widget) will have reduced functionality.

Then, the script passes the rest of its arguments to potery run -- mike deploy, which does the rest of the job. Run poetry run -- mike help deploy to find out what the arguments do.

If you need to do something more complicated, you can use poetry run -- mike ... commands. You can also edit the gh-pages branch directly, but take care to avoid files that will be overwritten by future invocations of mike. Then, you can submit a PR based on the gh-pages branch of (instead of the usual main branch).

Modifying protobuffers (this is not common)

Occasionally, you may need to change the .proto files that define jj's data storage format. In this case, you will need to add a few steps to the above workflow.

  • Install the protoc compiler. This usually means either apt-get install protobuf-compiler or downloading an official release. The prost library docs have additional advice.
  • Run cargo run -p gen-protos regularly (or after every edit to a .proto file). This is the same as running cargo run from lib/gen-protos. The gen-protos binary will use the prost-build library to compile the .proto files into .rs files.
  • If you are adding a new .proto file, you will need to edit the list of these files in lib/gen-protos/src/

The .rs files generated from .proto files are included in the repository, and there is a GitHub CI check that will complain if they do not match.


One easy-to-use sampling profiler is samply. For example:

cargo install samply
samply record jj diff
Then just open the link it prints.

Another option is to use the instrumentation we've added manually (using tracing::instrument) in various places. For example:

JJ_TRACE=/tmp/trace.json jj diff
Then go to in Chrome and load /tmp/trace.json from there.