# Table of Contents - [Unknown](#unknown) - [Redirecting](#redirecting) - [Jujutsu docs (prerelease)](#jujutsu-docs-prerelease-) - [Installation and setup - Jujutsu docs (prerelease)](#installation-and-setup-jujutsu-docs-prerelease-) - [Tutorial and bird's eye view - Jujutsu docs (prerelease)](#tutorial-and-bird-s-eye-view-jujutsu-docs-prerelease-) - [Working with Gerrit - Jujutsu docs (prerelease)](#working-with-gerrit-jujutsu-docs-prerelease-) - [Working with GitHub - Jujutsu docs (prerelease)](#working-with-github-jujutsu-docs-prerelease-) - [Testimonials - Jujutsu docs (prerelease)](#testimonials-jujutsu-docs-prerelease-) - [FAQ - Jujutsu docs (prerelease)](#faq-jujutsu-docs-prerelease-) - [Community-built tools - Jujutsu docs (prerelease)](#community-built-tools-jujutsu-docs-prerelease-) - [Working copy - Jujutsu docs (prerelease)](#working-copy-jujutsu-docs-prerelease-) - [Conflicts - Jujutsu docs (prerelease)](#conflicts-jujutsu-docs-prerelease-) - [Bookmarks - Jujutsu docs (prerelease)](#bookmarks-jujutsu-docs-prerelease-) - [Operation log - Jujutsu docs (prerelease)](#operation-log-jujutsu-docs-prerelease-) - [Glossary - Jujutsu docs (prerelease)](#glossary-jujutsu-docs-prerelease-) - [Divergent changes - Jujutsu docs (prerelease)](#divergent-changes-jujutsu-docs-prerelease-) - [Multiple remotes - Jujutsu docs (prerelease)](#multiple-remotes-jujutsu-docs-prerelease-) - [Fileset language - Jujutsu docs (prerelease)](#fileset-language-jujutsu-docs-prerelease-) - [Git comparison - Jujutsu docs (prerelease)](#git-comparison-jujutsu-docs-prerelease-) - [Other related work - Jujutsu docs (prerelease)](#other-related-work-jujutsu-docs-prerelease-) - [Git command table - Jujutsu docs (prerelease)](#git-command-table-jujutsu-docs-prerelease-) - [Revset language - Jujutsu docs (prerelease)](#revset-language-jujutsu-docs-prerelease-) - [Git compatibility - Jujutsu docs (prerelease)](#git-compatibility-jujutsu-docs-prerelease-) - [Templating language - Jujutsu docs (prerelease)](#templating-language-jujutsu-docs-prerelease-) - [Sapling comparison - Jujutsu docs (prerelease)](#sapling-comparison-jujutsu-docs-prerelease-) - [Core tenets - Jujutsu docs (prerelease)](#core-tenets-jujutsu-docs-prerelease-) - [Design doc blueprint - Jujutsu docs (prerelease)](#design-doc-blueprint-jujutsu-docs-prerelease-) - [Architecture - Jujutsu docs (prerelease)](#architecture-jujutsu-docs-prerelease-) - [Concurrency - Jujutsu docs (prerelease)](#concurrency-jujutsu-docs-prerelease-) - [Style guide - Jujutsu docs (prerelease)](#style-guide-jujutsu-docs-prerelease-) - [Design docs - Jujutsu docs (prerelease)](#design-docs-jujutsu-docs-prerelease-) - [Code of conduct - Jujutsu docs (prerelease)](#code-of-conduct-jujutsu-docs-prerelease-) - [Releasing - Jujutsu docs (prerelease)](#releasing-jujutsu-docs-prerelease-) - [Conflicts - Jujutsu docs (prerelease)](#conflicts-jujutsu-docs-prerelease-) - [git-submodule-storage - Jujutsu docs (prerelease)](#git-submodule-storage-jujutsu-docs-prerelease-) - [Temporary voting for governance - Jujutsu docs (prerelease)](#temporary-voting-for-governance-jujutsu-docs-prerelease-) - [Settings - Jujutsu docs (prerelease)](#settings-jujutsu-docs-prerelease-) - [Development roadmap - Jujutsu docs (prerelease)](#development-roadmap-jujutsu-docs-prerelease-) - [JJ run - Jujutsu docs (prerelease)](#jj-run-jujutsu-docs-prerelease-) - [git-submodules - Jujutsu docs (prerelease)](#git-submodules-jujutsu-docs-prerelease-) - [Governance - Jujutsu docs (prerelease)](#governance-jujutsu-docs-prerelease-) - [Working on Windows - Jujutsu docs (prerelease)](#working-on-windows-jujutsu-docs-prerelease-) - [Sparse patterns v2 - Jujutsu docs (prerelease)](#sparse-patterns-v2-jujutsu-docs-prerelease-) - [CLI reference - Jujutsu docs (prerelease)](#cli-reference-jujutsu-docs-prerelease-) - [Tracking branches - Jujutsu docs (prerelease)](#tracking-branches-jujutsu-docs-prerelease-) - [Copy tracking and tracing - Jujutsu docs (prerelease)](#copy-tracking-and-tracing-jujutsu-docs-prerelease-) - [Guidelines and "How to...?" - Jujutsu docs (prerelease)](#guidelines-and-how-to-jujutsu-docs-prerelease-) - [Changelog - Jujutsu docs (prerelease)](#changelog-jujutsu-docs-prerelease-) - [Unknown](#unknown) - [Paying employers and their employees - Jujutsu docs (prerelease)](#paying-employers-and-their-employees-jujutsu-docs-prerelease-) --- # Unknown \# NOTE: ensure you update docs/revsets.md with documentation when # adding/updating any of these aliases \[revsets\] fix = "reachable(@, mutable())" simplify-parents = "reachable(@, mutable())" # log revset is also used as the default short-prefixes. If it failed to # evaluate, lengthy warning messages would be printed. Use present(expr) to # suppress symbol resolution error. log = "present(@) | ancestors(immutable\_heads().., 2) | present(trunk())" # Emit the working-copy branch first, which is usually most interesting. # This also helps stabilize output order. log-graph-prioritize = "present(@)" sign = "reachable(@, mutable())" \[revset-aliases\] # trunk() can be overridden as '@'. Use present(trunk()) if # symbol resolution error should be suppressed. 'trunk()' = ''' latest( remote\_bookmarks(exact:"main", exact:"origin") | remote\_bookmarks(exact:"master", exact:"origin") | remote\_bookmarks(exact:"trunk", exact:"origin") | remote\_bookmarks(exact:"main", exact:"upstream") | remote\_bookmarks(exact:"master", exact:"upstream") | remote\_bookmarks(exact:"trunk", exact:"upstream") | root() ) ''' # If immutable\_heads() failed to evaluate, many jj commands wouldn't work. Use # present(expr) to suppress symbol resolution error. 'builtin\_immutable\_heads()' = 'present(trunk()) | tags() | untracked\_remote\_bookmarks()' 'immutable\_heads()' = 'builtin\_immutable\_heads()' 'immutable()' = '::(immutable\_heads() | root())' 'mutable()' = '~immutable()' --- # Redirecting Redirecting to [latest/](https://jj-vcs.github.io/jj/latest/) ... --- # Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/#jujutsua-version-control-system) [](https://github.com/jj-vcs/jj/edit/main/docs/index.md "Edit this page") Jujutsu—a version control system[¶](https://jj-vcs.github.io/jj/prerelease/#jujutsua-version-control-system "Permanent link") ============================================================================================================================== ![](https://jj-vcs.github.io/jj/prerelease/images/jj-logo.svg "jj logo") Welcome to `jj`'s documentation website![¶](https://jj-vcs.github.io/jj/prerelease/#welcome-to-jjs-documentation-website "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------- The complete list of the available documentation pages is located in the sidebar on the left of the page. The sidebar may be hidden; if so, you can open it either by widening your browser window or by clicking on the hamburger menu that appears in this situation. Additional help is available using the `jj help` command if you have `jj` installed. You may want to jump to: * Documentation for the [latest released version of `jj`](https://jj-vcs.github.io/jj/latest) . * Documentation for the [unreleased version of `jj`](https://jj-vcs.github.io/jj/prerelease) . This version of the docs corresponds to the `main` branch of the `jj` repo. Some useful links[¶](https://jj-vcs.github.io/jj/prerelease/#some-useful-links "Permanent link") ------------------------------------------------------------------------------------------------- * [GitHub repo for `jj`](https://github.com/jj-vcs/jj) * Overview of `jj` in the repo's [README](https://github.com/jj-vcs/jj?tab=readme-ov-file#readme) * [Installation and setup](https://jj-vcs.github.io/jj/prerelease/install-and-setup/) * [Tutorial and bird's eye view](https://jj-vcs.github.io/jj/prerelease/tutorial/) * [Working with Gerrit](https://jj-vcs.github.io/jj/prerelease/gerrit/) * [Working with GitHub](https://jj-vcs.github.io/jj/prerelease/github/) * [Development roadmap](https://jj-vcs.github.io/jj/prerelease/roadmap/) * [Changelog](https://jj-vcs.github.io/jj/prerelease/changelog/) --- # Installation and setup - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#installation-and-setup) [](https://github.com/jj-vcs/jj/edit/main/docs/install-and-setup.md "Edit this page") Installation and setup[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#installation-and-setup "Permanent link") ============================================================================================================================= Installation[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#installation "Permanent link") --------------------------------------------------------------------------------------------------------- ### Download pre-built binaries for a release[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#download-pre-built-binaries-for-a-release "Permanent link") There are [pre-built binaries](https://github.com/jj-vcs/jj/releases/latest) of the last released version of `jj` for Windows, Mac, or Linux (the "musl" version should work on all distributions). #### Cargo Binstall[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#cargo-binstall "Permanent link") If you use [`cargo-binstall`](https://github.com/cargo-bins/cargo-binstall) , you can install binaries of the latest `jj` release from GitHub as follows: `# Will put the jj binary for the latest release in ~/.cargo/bin by default cargo binstall --strategies crate-meta-data jj-cli` Without the `--strategies` option, you may get equivalent binaries that should be compiled from the same source code. Note If you'd like to install a prerelease version, you'll need to use one of the options below. ### Linux[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#linux "Permanent link") #### From Source[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#from-source "Permanent link") First make sure that you have a Rust version >= 1.88 and that the `build-essential` package is installed by running something like this: `sudo apt-get install build-essential` Now run either: `# To install the *prerelease* version from the main branch cargo install --git https://github.com/jj-vcs/jj.git --locked --bin jj jj-cli` or: `# To install the latest release cargo install --locked --bin jj jj-cli` #### Arch Linux[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#arch-linux "Permanent link") You can install the `jujutsu` package from the [official extra repository](https://archlinux.org/packages/extra/x86_64/jujutsu/) : `pacman -S jujutsu` Or install from the [AUR repository](https://aur.archlinux.org/packages/jujutsu-git) with an [AUR Helper](https://wiki.archlinux.org/title/AUR_helpers) : `yay -S jujutsu-git` #### NixOS[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#nixos "Permanent link") If you're on NixOS you can install a **released** version of `jj` using the [nixpkgs `jujutsu` package](https://search.nixos.org/packages?channel=unstable&show=jujutsu) . To install a **prerelease** version, you can use the flake for this repository. For example, if you want to run `jj` loaded from the flake, use: `nix run 'github:jj-vcs/jj'` You can also add this flake url to your system input flakes. Or you can install the flake to your user profile: `# Installs the prerelease version from the main branch nix profile install 'github:jj-vcs/jj'` #### Homebrew[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#homebrew "Permanent link") If you use Homebrew, you can run: `# Installs the latest release brew install jj` #### Gentoo Linux[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#gentoo-linux "Permanent link") `dev-vcs/jj` is available in the [GURU](https://wiki.gentoo.org/wiki/Project:GURU) repository. Details on how to enable the GURU repository can be found [here](https://wiki.gentoo.org/wiki/Project:GURU/Information_for_End_Users) . Once you have synced the GURU repository, you can install `dev-vcs/jj` via Portage: `emerge -av dev-vcs/jj` #### openSUSE Tumbleweed[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#opensuse-tumbleweed "Permanent link") `jujutsu` can be installed from the official [openSUSE-Tumbleweed-Oss](http://download.opensuse.org/tumbleweed/repo/oss/) repository: `zypper install jujutsu` ### Mac[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#mac "Permanent link") #### From Source[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#from-source_1 "Permanent link") First make sure that you have a Rust version >= 1.88. You may also need to run: `xcode-select --install` Now run either: `# To install the *prerelease* version from the main branch cargo install --git https://github.com/jj-vcs/jj.git \ --locked --bin jj jj-cli` or: `# To install the latest release cargo install --locked --bin jj jj-cli` #### Homebrew[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#homebrew_1 "Permanent link") If you use Homebrew, you can run: `# Installs the latest release brew install jj` #### MacPorts[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#macports "Permanent link") You can also install `jj` via [the MacPorts `jujutsu` port](https://ports.macports.org/port/jujutsu/) : `# Installs the latest release sudo port install jujutsu` ### Windows[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#windows "Permanent link") First make sure that you have a Rust version >= 1.88. Now run either: `# To install the *prerelease* version from the main branch cargo install --git https://github.com/jj-vcs/jj.git --locked --bin jj jj-cli` or: `# To install the latest release cargo install --locked --bin jj jj-cli` via winget: `# To install the latest release via winget winget install jj-vcs.jj` via scoop: `# To install the latest release via scoop scoop install main/jj` Initial configuration[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#initial-configuration "Permanent link") --------------------------------------------------------------------------------------------------------------------------- You may want to configure your name and email so commits are made in your name. `$ jj config set --user user.name "Martin von Zweigbergk" $ jj config set --user user.email "martinvonz@google.com"` Command-line completion[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#command-line-completion "Permanent link") ------------------------------------------------------------------------------------------------------------------------------- Jujutsu provides 2 different command-line completion scripts. ### Standard completions[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#standard-completions "Permanent link") The standard completion script provides completions for `jj` subcommmands and options. ### Dynamic completions[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#dynamic-completions "Permanent link") The dynamic completion script provides completions for `jj` subcommands and options, as well as additional completions, including bookmarks, aliases, revisions, operations and files. Dynamic completions can be context aware, for example they respect the global flags `--repository` and `--at-operation` as well as some command-specific ones like `--revision`, `--from`, and `--to`. Dynamic completions are not the default/only option since the underlying engine [is still labeled unstable](https://github.com/clap-rs/clap/issues/3166) . We expect to transition to them as the default once the engine is stabilized. Please let us know if you encounter any issues with dynamic completions. Which completion script should I use? Generally, dynamic completions provide a much better completion experience. Although the underlying engine is deemed unstable, there have not been many issues in practice. Dynamic completions are the preferred option for many contributors and users. We recommend using the dynamic completion script, and falling back to the standard completion script if there are any issues. ### Bash[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#bash "Permanent link") StandardDynamic `source <(jj util completion bash)` `source <(COMPLETE=bash jj)` ### Zsh[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#zsh "Permanent link") StandardDynamic `autoload -U compinit compinit source <(jj util completion zsh)` `source <(COMPLETE=zsh jj)` ### Fish[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#fish "Permanent link") Note No configuration is required with fish >= 4.0.2 which loads dynamic completions by default. StandardDynamic `jj util completion fish | source` `COMPLETE=fish jj | source` ### Nushell[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#nushell "Permanent link") Standard `` jj util completion nushell | save completions-jj.nu use completions-jj.nu * # Or `source completions-jj.nu` `` ### Xonsh[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#xonsh "Permanent link") Standard `source-bash $(jj util completion)` ### Powershell[¶](https://jj-vcs.github.io/jj/prerelease/install-and-setup/#powershell "Permanent link") Standard Insert this line in your `$PROFILE` file (usually `$HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1`): `Invoke-Expression (& { (jj util completion power-shell | Out-String) })` --- # Tutorial and bird's eye view - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/tutorial/#tutorial) [](https://github.com/jj-vcs/jj/edit/main/docs/tutorial.md "Edit this page") Tutorial[¶](https://jj-vcs.github.io/jj/prerelease/tutorial/#tutorial "Permanent link") ======================================================================================== > **Hint:** This tutorial covers the basics accurately, but since it was written Jujutsu has gained many new features. For a tutorial covering a wider range of features, you may find the (not quite finished) [tutorial by Steve Klabnik](https://steveklabnik.github.io/jujutsu-tutorial/) > helpful. If you're looking for a tutorial that doesn't require experience with Git, take a look at [Jujutsu for everyone](https://jj-for-everyone.github.io/) > . This text assumes that the reader is familiar with Git. Preparation[¶](https://jj-vcs.github.io/jj/prerelease/tutorial/#preparation "Permanent link") ---------------------------------------------------------------------------------------------- If you haven't already, make sure you [install and configure Jujutsu](https://jj-vcs.github.io/jj/prerelease/install-and-setup/) . Cloning a Git repository[¶](https://jj-vcs.github.io/jj/prerelease/tutorial/#cloning-a-git-repository "Permanent link") ------------------------------------------------------------------------------------------------------------------------ > **Hint:** Most identifiers used in this tutorial will be different when you try this at home! Let's start by cloning GitHub's Hello-World repo using `jj`: ``# Note the "git" before "clone" (there is no support for cloning native jj # repos yet) $ jj git clone https://github.com/octocat/Hello-World Fetching into new repo in "/tmp/tmp.O1DWMiaKd4/Hello-World" remote: Enumerating objects: 13, done. remote: Total 13 (delta 0), reused 0 (delta 0), pack-reused 13 (from 1) bookmark: master@origin [new] untracked bookmark: octocat-patch-1@origin [new] untracked bookmark: test@origin [new] untracked Setting the revset alias `trunk()` to `master@origin` Working copy (@) now at: kntqzsqt d7439b06 (empty) (no description set) Parent commit (@-) : orrkosyo 7fd1a60b master | (empty) Merge pull request #6 from Spaceghost/patch-1 Added 1 files, modified 0 files, removed 0 files $ cd Hello-World`` Running `jj st` (short for `jj status`) now yields something like this: `$ jj st The working copy has no changes. Working copy (@) : kntqzsqt d7439b06 (empty) (no description set) Parent commit (@-): orrkosyo 7fd1a60b master | (empty) Merge pull request #6 from Spaceghost/patch-1` Let's look at that output as it introduces new concepts. You can see two commits: Parent and working copy. Both are identified using two separate identifiers: the "change ID" and the "commit ID". The parent commit, for example, has the change ID `orrkosyo` and the commit ID `7fd1a60b`. > **Git users:** The commit ID/hash is what you're used to from Git and should match what you see when you look at the repository using `git log` in a Git checkout of the repository. The change ID however, is a new concept, unique to Jujutsu. We can also see from the output above that our working copy is an actual commit with a commit ID (`d7439b06` in the example). When you make a change in the working copy, the working-copy commit gets automatically amended by the next `jj` command. > **Git users:** This is a huge difference from Git where the working copy is a separate concept and not yet a commit. Changes[¶](https://jj-vcs.github.io/jj/prerelease/tutorial/#changes "Permanent link") -------------------------------------------------------------------------------------- A change is a commit that can evolve while keeping a stable identifier (similar to Gerrit's Change-Id). In other words: You can make changes to files in a change, resulting in a new commit hash, but the change ID will remain the same. You can see that our clone operation automatically created a new change: `Working copy (@) : kntqzsqt d7439b06 (empty) (no description set)` This new change has the ID `kntqzsqt` and it is currently empty (contains no changes compared to the parent) and has no description. Creating our first change[¶](https://jj-vcs.github.io/jj/prerelease/tutorial/#creating-our-first-change "Permanent link") -------------------------------------------------------------------------------------------------------------------------- Let's say we want to edit the `README` file in the repo to say "Goodbye" instead of "Hello". Start by describing the change (adding a commit message) so we don't forget what we're working on: ``# This brings up $EDITOR (or `nano` or `Notepad` by default). # Enter something like "Say goodbye" in the editor and then save the file and close # the editor. $ jj describe Working copy (@) now at: kntqzsqt e427edcf (empty) Say goodbye Parent commit (@-) : orrkosyo 7fd1a60b master | (empty) Merge pull request #6 from Spaceghost/patch-1`` Now make the change in the README: ``# Adjust as necessary for compatibility with your flavor of `sed` $ sed -i 's/Hello/Goodbye/' README $ jj st Working copy changes: M README Working copy (@) : kntqzsqt 5d39e19d Say goodbye Parent commit (@-): orrkosyo 7fd1a60b master | (empty) Merge pull request #6 from Spaceghost/patch-1`` Note that you didn't have to tell Jujutsu to add the change like you would with `git add`. You actually don't even need to tell it when you add new files or remove existing files. To untrack a path, add it to your `.gitignore` and run `jj file untrack `. Also note that the commit hash for our current change (`kntqzsqt`) changed from `e427edcf` to `5d39e19d`! To see the diff, run `jj diff`: ``$ jj diff --git # Feel free to skip the `--git` flag diff --git a/README b/README index 980a0d5f19..1ce3f81130 100644 --- a/README +++ b/README @@ -1,1 +1,1 @@ -Hello World! +Goodbye World!`` Jujutsu's diff format currently defaults to inline coloring of the diff (like `git diff --color-words`), so we used `--git` above to make the diff readable in this tutorial. As you may have noticed, the working-copy commit's ID changed both when we edited the description and when we edited the README. However, the parent commit stayed the same. Each change to the working-copy commit amends the previous version. So how do we tell Jujutsu that we are done amending the current change and want to start working on a new one? That is what `jj new` is for. That will create a new commit on top of your current working-copy commit. The new commit is for the working-copy changes. So, let's say we're now done with this change, so we create a new change: `$ jj new Working copy (@) now at: mpqrykyp aef4df99 (empty) (no description set) Parent commit (@-) : kntqzsqt 5d39e19d Say goodbye $ jj st The working copy has no changes. Working copy (@) : mpqrykyp aef4df99 (empty) (no description set) Parent commit (@-): kntqzsqt 5d39e19d Say goodbye` If we later realize that we want to make further changes, we can make them in the working copy and then run `jj squash`. That command squashes (moves) the changes from a given commit into its parent commit. Like most commands, it acts on the working-copy commit by default. When run on the working-copy commit, it behaves very similar to `git commit --amend`. Alternatively, we can use `jj edit ` to resume editing a commit in the working copy. Any further changes in the working copy will then amend the commit. Whether you choose to create a new change and squash, or to edit, typically depends on how done you are with the change; if the change is almost done, it makes sense to use `jj new` so you can easily review your adjustments with `jj diff` before running `jj squash`. To view how a change has evolved over time, we can use `jj evolog` to see each recorded change for the current commit. This records changes to the working copy, message, squashes, rebases, etc. The log command and "revsets"[¶](https://jj-vcs.github.io/jj/prerelease/tutorial/#the-log-command-and-revsets "Permanent link") -------------------------------------------------------------------------------------------------------------------------------- You're probably familiar with `git log`. Jujutsu has very similar functionality in its `jj log` command: `$ jj log @ mpqrykyp martinvonz@google.com 2023-02-12 15:00:22 aef4df99 │ (empty) (no description set) ○ kntqzsqt martinvonz@google.com 2023-02-12 14:56:59 5d39e19d │ Say goodbye ◆ orrkosyo octocat@nowhere.com 2012-03-06 15:06:50 master 7fd1a60b │ (empty) Merge pull request #6 from Spaceghost/patch-1 ~` The `@` indicates the working-copy commit. The first ID on a line (e.g. "mpqrykyp" above) is the change ID. The second ID is the commit ID. You can give either ID to commands that take revisions as arguments. We will generally prefer change IDs because they stay the same when the commit is rewritten. By default, `jj log` lists your local commits, with some remote commits added for context. The `~` indicates that the commit has parents that are not included in the graph. We can use the `--revisions`/`-r` flag to select a different set of revisions to list. The flag accepts a ["revset"](https://jj-vcs.github.io/jj/prerelease/revsets/) , which is an expression in a simple language for specifying revisions. For example, `@` refers to the working-copy commit, `root()` refers to the root commit, `bookmarks()` refers to all commits pointed to by bookmarks (similar to Git's branches). We can combine expressions with `|` for union, `&` for intersection and `~` for difference. For example: `$ jj log -r '@ | root() | bookmarks()' @ mpqrykyp martinvonz@google.com 2023-02-12 15:00:22 aef4df99 │ (empty) (no description set) ~ (elided revisions) ◆ orrkosyo octocat@nowhere.com 2012-03-06 15:06:50 master 7fd1a60b │ (empty) Merge pull request #6 from Spaceghost/patch-1 ~ (elided revisions) ◆ zzzzzzzz root() 00000000` The `00000000` commit (change ID `zzzzzzzz`) is a virtual commit that's called the "root commit". It's the root commit of every repo. The `root()` function in the revset matches it. There are also operators for getting the parents (`foo-`), children (`foo+`), ancestors (`::foo`), descendants (`foo::`), DAG range (`foo::bar`, like `git log --ancestry-path`), range (`foo..bar`, same as Git's). See [the revset documentation](https://jj-vcs.github.io/jj/prerelease/revsets/) for all revset operators and functions. > **Hint:** If the default `jj log` omits some commits you expect to see, you can always run `jj log -r ::` (or, equivalently, `jj log -r 'all()'`) to see all the commits. Conflicts[¶](https://jj-vcs.github.io/jj/prerelease/tutorial/#conflicts "Permanent link") ------------------------------------------------------------------------------------------ Now let's see how Jujutsu deals with merge conflicts. We'll start by making some commits. We use `jj new` with the `--message`/`-m` option to set change descriptions (commit messages) right away. ``# Start creating a chain of commits off of the `master` bookmark $ jj new master -m A; echo a > file1 Working copy (@) now at: nuvyytnq 00a2aeed (empty) A Parent commit (@-) : orrkosyo 7fd1a60b master | (empty) Merge pull request #6 from Spaceghost/patch-1 Added 0 files, modified 1 files, removed 0 files $ jj new -m B1; echo b1 > file1 Working copy (@) now at: ovknlmro 967d9f9f (empty) B1 Parent commit (@-) : nuvyytnq 5dda2f09 A $ jj new -m B2; echo b2 > file1 Working copy (@) now at: puqltutt 8ebeaffa (empty) B2 Parent commit (@-) : ovknlmro 7d7c6e6b B1 $ jj new -m C; echo c > file2 Working copy (@) now at: qzvqqupx 62a3c6d3 (empty) C Parent commit (@-) : puqltutt daa6ffd5 B2 $ jj log @ qzvqqupx martinvonz@google.com 2023-02-12 15:07:41 2370ddf3 │ C ○ puqltutt martinvonz@google.com 2023-02-12 15:07:33 daa6ffd5 │ B2 ○ ovknlmro martinvonz@google.com 2023-02-12 15:07:24 7d7c6e6b │ B1 ○ nuvyytnq martinvonz@google.com 2023-02-12 15:07:05 5dda2f09 │ A │ ○ kntqzsqt martinvonz@google.com 2023-02-12 14:56:59 5d39e19d ├─╯ Say goodbye ◆ orrkosyo octocat@nowhere.com 2012-03-06 15:06:50 master 7fd1a60b │ (empty) Merge pull request #6 from Spaceghost/patch-1 ~`` We now have a few commits, where A, B1, and B2 modify the same file, while C modifies a different file. Let's now rebase B2 directly onto A. We use the `--source`/`-s` option on the change ID of B2, and `--destination`/`-d` option on A. ``$ jj rebase -s puqltutt -d nuvyytnq # Replace the IDs by what you have for B2 and A Rebased 2 commits to destination Working copy (@) now at: qzvqqupx 1978b534 (conflict) C Parent commit (@-) : puqltutt f7fb5943 (conflict) B2 Added 0 files, modified 1 files, removed 0 files Warning: There are unresolved conflicts at these paths: file1 2-sided conflict New conflicts appeared in 2 commits: qzvqqupx 1978b534 (conflict) C puqltutt f7fb5943 (conflict) B2 Hint: To resolve the conflicts, start by creating a commit on top of the first conflicted commit: jj new puqltutt Then use `jj resolve`, or edit the conflict markers in the file directly. Once the conflicts are resolved, you can inspect the result with `jj diff`. Then run `jj squash` to move the resolution into the conflicted commit. $ jj log @ qzvqqupx martinvonz@google.com 2023-02-12 15:08:33 1978b534 conflict │ C × puqltutt martinvonz@google.com 2023-02-12 15:08:33 f7fb5943 conflict │ B2 │ ○ ovknlmro martinvonz@google.com 2023-02-12 15:07:24 7d7c6e6b ├─╯ B1 ○ nuvyytnq martinvonz@google.com 2023-02-12 15:07:05 5dda2f09 │ A │ ○ kntqzsqt martinvonz@google.com 2023-02-12 14:56:59 5d39e19d ├─╯ Say goodbye ◆ orrkosyo octocat@nowhere.com 2012-03-06 15:06:50 master 7fd1a60b │ (empty) Merge pull request #6 from Spaceghost/patch-1 ~`` There are several things worth noting here. First, the `jj rebase` command said "Rebased 2 commits". That's because we asked it to rebase commit B2 with the `-s` option, which also rebases descendants (commit C in this case). Second, because B2 modified the same file (and word) as B1, rebasing it resulted in conflicts, as the output indicates. Third, the conflicts did not prevent the rebase from completing successfully, nor did it prevent C from getting rebased on top. Now let's resolve the conflict in B2. We'll do that by creating a new commit on top of B2. Once we've resolved the conflict, we'll squash the conflict resolution into the conflicted B2. That might look like this: ``$ jj new puqltutt # Replace the ID by what you have for B2 Working copy (@) now at: zxoosnnp c7068d1c (conflict) (empty) (no description set) Parent commit (@-) : puqltutt f7fb5943 (conflict) B2 Added 0 files, modified 0 files, removed 1 files Warning: There are unresolved conflicts at these paths: file1 2-sided conflict $ jj st The working copy has no changes. Working copy (@) : zxoosnnp c7068d1c (conflict) (empty) (no description set) Parent commit (@-): puqltutt f7fb5943 (conflict) B2 Warning: There are unresolved conflicts at these paths: file1 2-sided conflict Hint: To resolve the conflicts, start by creating a commit on top of the conflicted commit: jj new puqltutt Then use `jj resolve`, or edit the conflict markers in the file directly. Once the conflicts are resolved, you can inspect the result with `jj diff`. Then run `jj squash` to move the resolution into the conflicted commit. $ cat file1 <<<<<<< Conflict 1 of 1 %%%%%%% Changes from base to side #1 -b1 +a +++++++ Contents of side #2 b2 >>>>>>> Conflict 1 of 1 ends $ echo resolved > file1 $ jj st Working copy changes: M file1 Working copy (@) : zxoosnnp c2a31a06 (no description set) Parent commit (@-): puqltutt f7fb5943 (conflict) B2 Hint: Conflict in parent commit has been resolved in working copy $ jj squash Rebased 1 descendant commits Working copy (@) now at: ntxxqymr e3c279cc (empty) (no description set) Parent commit (@-) : puqltutt 2c7a658e B2 Existing conflicts were resolved or abandoned from 2 commits. $ jj log @ ntxxqymr martinvonz@google.com 2023-02-12 19:34:09 e3c279cc │ (empty) (no description set) │ ○ qzvqqupx martinvonz@google.com 2023-02-12 19:34:09 b9da9d28 ├─╯ C ○ puqltutt martinvonz@google.com 2023-02-12 19:34:09 2c7a658e │ B2 │ ○ ovknlmro martinvonz@google.com 2023-02-12 15:07:24 7d7c6e6b ├─╯ B1 ○ nuvyytnq martinvonz@google.com 2023-02-12 15:07:05 5dda2f09 │ A │ ○ kntqzsqt martinvonz@google.com 2023-02-12 14:56:59 5d39e19d ├─╯ Say goodbye ◆ orrkosyo octocat@nowhere.com 2012-03-06 15:06:50 master 7fd1a60b │ (empty) Merge pull request #6 from Spaceghost/patch-1 ~`` Note that commit C automatically got rebased on top of the resolved B2, and that C is also resolved (since it modified only a different file). By the way, if we want to get rid of B1 now, we can run `jj abandon ovknlmro`. That will hide the commit from the log output and will rebase any descendants to its parent. The operation log[¶](https://jj-vcs.github.io/jj/prerelease/tutorial/#the-operation-log "Permanent link") ---------------------------------------------------------------------------------------------------------- Jujutsu keeps a record of all changes you've made to the repo in what's called the "operation log". Use the `jj op` (short for `jj operation`) family of commands to interact with it. To list the operations, use `jj op log`: `$ jj op log @ d3b77addea49 martinvonz@vonz.svl.corp.google.com 3 minutes ago, lasted 3 milliseconds │ squash commits into f7fb5943a6b9460eb106dba2fac5cac1625c6f7a │ args: jj squash ○ 6fc1873c1180 martinvonz@vonz.svl.corp.google.com 3 minutes ago, lasted 1 milliseconds │ snapshot working copy │ args: jj st ○ ed91f7bcc1fb martinvonz@vonz.svl.corp.google.com 6 minutes ago, lasted 1 milliseconds │ new empty commit │ args: jj new puqltutt ○ 367400773f87 martinvonz@vonz.svl.corp.google.com 12 minutes ago, lasted 3 milliseconds │ rebase commit daa6ffd5a09a8a7d09a65796194e69b7ed0a566d and descendants │ args: jj rebase -s puqltutt -d nuvyytnq [many more lines]` The most useful command is `jj undo`, which will undo your last operation. ``$ jj undo Reverted operation: d3b77addea49 (2025-05-12 00:27:27) squash commits into f7fb5943a6b9460eb106dba2fac5cac1625c6f7a Working copy (@) now at: zxoosnnp 63874fe6 (no description set) Parent commit (@-) : puqltutt f7fb5943 (conflict) B2 New conflicts appeared in 2 commits: qzvqqupx 1978b534 (conflict) C puqltutt f7fb5943 (conflict) B2 Hint: To resolve the conflicts, start by creating a commit on top of the first conflicted commit: jj new puqltutt Then use `jj resolve`, or edit the conflict markers in the file directly. Once the conflicts are resolved, you can inspect the result with `jj diff`. Then run `jj squash` to move the resolution into the conflicted commit. $ jj log @ zxoosnnp martinvonz@google.com 2023-02-12 19:34:09 63874fe6 │ (no description set) │ × qzvqqupx martinvonz@google.com 2023-02-12 15:08:33 1978b534 conflict ├─╯ C × puqltutt martinvonz@google.com 2023-02-12 15:08:33 f7fb5943 conflict │ B2 │ ○ ovknlmro martinvonz@google.com 2023-02-12 15:07:24 7d7c6e6b ├─╯ B1 ○ nuvyytnq martinvonz@google.com 2023-02-12 15:07:05 5dda2f09 │ A │ ○ kntqzsqt martinvonz@google.com 2023-02-12 14:56:59 5d39e19d ├─╯ Say goodbye ◆ orrkosyo octocat@nowhere.com 2012-03-06 15:06:50 master 7fd1a60b │ (empty) Merge pull request #6 from Spaceghost/patch-1 ~`` As you can perhaps see, that undid the `jj squash` invocation we used for squashing the conflict resolution into commit B2 earlier. Notice that it also updated the working copy. You can also view the repo the way it looked after some earlier operation. For example, if you want to see `jj log` output right after the `jj rebase` operation, try `jj log --at-op=367400773f87` but use the hash from your own `jj op log`. Moving content changes between commits[¶](https://jj-vcs.github.io/jj/prerelease/tutorial/#moving-content-changes-between-commits "Permanent link") ---------------------------------------------------------------------------------------------------------------------------------------------------- You have already seen how `jj squash` can combine the changes from two commits into one. There are several other commands for changing the contents of existing commits. We'll need some more complex content to test these commands, so let's create a few more commits: `$ jj new master -m abc; printf 'a\nb\nc\n' > file Working copy (@) now at: ztqrpvnw f94e49cf (empty) abc Parent commit (@-) : orrkosyo 7fd1a60b master | (empty) Merge pull request #6 from Spaceghost/patch-1 Added 0 files, modified 0 files, removed 1 files $ jj new -m ABC; printf 'A\nB\nc\n' > file Working copy (@) now at: kwtuwqnm 6f30cd1f (empty) ABC Parent commit (@-) : ztqrpvnw 51002261 ab $ jj new -m ABCD; printf 'A\nB\nC\nD\n' > file Working copy (@) now at: mrxqplyk a6749154 (empty) ABCD Parent commit (@-) : kwtuwqnm 30aecc08 ABC $ jj log -r master::@ @ mrxqplyk martinvonz@google.com 2023-02-12 19:38:21 b98c607b │ ABCD ○ kwtuwqnm martinvonz@google.com 2023-02-12 19:38:12 30aecc08 │ ABC ○ ztqrpvnw martinvonz@google.com 2023-02-12 19:38:03 51002261 │ abc ◆ orrkosyo octocat@nowhere.com 2012-03-06 15:06:50 master 7fd1a60b │ (empty) Merge pull request #6 from Spaceghost/patch-1 ~` We "forgot" to capitalize "c" in the second commit when we capitalized the other letters. We then fixed that in the third commit when we also added "D". It would be cleaner to move the capitalization of "c" into the second commit. We can do that by running `jj squash` with the `--interactive`/`-i` option on the third commit. Remember that `jj squash` moves all the changes from one commit into its parent. `jj squash -i` moves only part of the changes into its parent. Now try that: ``$ jj squash -i Hint: Using default editor ':builtin'; run `jj config set --user ui.diff-editor :builtin` to disable this message. Rebased 1 descendant commits Working copy (@) now at: mrxqplyk 52a6c7fd ABCD Parent commit (@-) : kwtuwqnm 643061ac ABC`` That will bring up the built-in diff editor[1](https://jj-vcs.github.io/jj/prerelease/tutorial/#fn:alternative_diff_editors) with a diff of the changes in the "ABCD" commit. Expand the file by clicking on `(+)` or with right arrow, then select the sections/line to include by clicking or using space. Once complete, press `c` to confirm changes, or `q` to exit without saving. You can also use the mouse to click on the menu items to see more options (keyboard navigation is currently limited). If we look at the diff of the second commit, we now see that all three lines got capitalized: `$ jj diff -r @- --git diff --git a/file b/file index de980441c3..b1e67221af 100644 --- a/file +++ b/file @@ -1,3 +1,3 @@ -a -b -c +A +B +C` The child change ("ABCD" in our case) will have the same content _state_ after the `jj squash` command. That means that you can move any changes you want into the parent change, even if they touch the same word, and it won't cause any conflicts. Let's try one final command for changing the contents of an existing commit. That command is `jj diffedit`, which lets you edit the changes in a commit without checking it out. ``$ jj diffedit -r @- Hint: Using default editor ':builtin'; run `jj config set --user ui.diff-editor :builtin` to disable this message. Rebased 1 descendant commits Working copy (@) now at: mrxqplyk 1c72cd50 (conflict) ABCD Parent commit (@-) : kwtuwqnm 70985eaa ABC Added 0 files, modified 1 files, removed 0 files Warning: There are unresolved conflicts at these paths: file 2-sided conflict New conflicts appeared in 1 commits: mrxqplyk 1c72cd50 (conflict) ABCD Hint: To resolve the conflicts, start by creating a commit on top of the conflicted commit: jj new mrxqplyk Then use `jj resolve`, or edit the conflict markers in the file directly. Once the conflicts are resolved, you can inspect the result with `jj diff`. Then run `jj squash` to move the resolution into the conflicted commit.`` In the diff editor, use the arrow keys and spacebar to select all lines but the last. Press 'c' to save the changes and close it. You can now inspect the rewritten commit with `jj diff -r @-` again, and you should see your deletion of the last line. Unlike `jj squash -i`, which left the content state of the commit unchanged, `jj diffedit` (typically) results in a different state, which means that descendant commits may have conflicts. Another command for rewriting contents of existing commits is `jj split`. Now that you've seen how `jj squash -i` and `jj diffedit` work, you can hopefully figure out how it works (with the help of the instructions in the diff). * * * 1. There are many other diff editors you could use. For example, if you have [Meld](https://meldmerge.org/) installed and in the PATH, you can use it via `jj squash -i --tool meld` or a fancier config with `jj squash -i --tool meld-3`. You can configure the default with the [`ui.diff-editor` option](https://jj-vcs.github.io/jj/prerelease/config/#editing-diffs) ; those docs also explain how to specify a path to an executable if it is not in the PATH. [↩](https://jj-vcs.github.io/jj/prerelease/tutorial/#fnref:alternative_diff_editors "Jump back to footnote 1 in the text") --- # Working with Gerrit - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/gerrit/#using-jujutsu-with-gerrit-code-review) [](https://github.com/jj-vcs/jj/edit/main/docs/gerrit.md "Edit this page") Using Jujutsu with Gerrit Code Review[¶](https://jj-vcs.github.io/jj/prerelease/gerrit/#using-jujutsu-with-gerrit-code-review "Permanent link") ================================================================================================================================================ JJ and Gerrit share the same mental model, which makes Gerrit feel like a natural collaboration tool for JJ. JJ tracks a "change identity" across rewrites, and Gerrit’s `Change-Id` tracks the same logical change across patch sets. JJ and Gerrit's `Change-Id`s aren’t natively compatible yet, but they’re philosophically aligned. `jj gerrit upload` bridges the gap today by adding a Gerrit-style `Change-Id` while JJ keeps its own notion of change identity on the client. In practice, that means small, clean commits that evolve over time, exactly how Gerrit wants you to work. This guide assumes a basic understanding of Git, Gerrit, and Jujutsu. Set up a Gerrit remote[¶](https://jj-vcs.github.io/jj/prerelease/gerrit/#set-up-a-gerrit-remote "Permanent link") ------------------------------------------------------------------------------------------------------------------ Jujutsu communicates with Gerrit by pushing commits to a Git remote. If you're starting from an existing Git repository with Gerrit remotes already configured, you can use `jj git init --colocate` to start using JJ in that repo. Otherwise, set up your Gerrit remote. `# Option 1: Start JJ in an existing Git repo with Gerrit remotes $ jj git init --colocate # Option 2: Add a Gerrit remote to a JJ repo $ jj git remote add gerrit https://review.gerrithub.io/yourname/yourproject # Option 3: Clone the repo via jj $ jj git clone https://review.gerrithub.io/your/project` If you used option 2 You can configure default values in your repository config by appending the below to `.jj/repo/config.toml`, like so: `[gerrit] default-remote = "gerrit" # name of the Git remote to push to default-remote-branch = "main" # target branch in Gerrit` Basic workflow[¶](https://jj-vcs.github.io/jj/prerelease/gerrit/#basic-workflow "Permanent link") -------------------------------------------------------------------------------------------------- `jj gerrit upload` takes one or more revsets, and uploads the stack of commits ending in them to Gerrit. Each JJ change will map to a single Gerrit change based on the JJ change ID. This should be what you want most of the time, but if you want to associate a JJ change with a specific change already uploaded to Gerrit, you can copy the Change-Id footer from Gerrit to the bottom of the commit description in JJ. > Note: Gerrit identifies and updates changes by the `Change-Id` trailer. When you re-upload a commit with the same `Change-Id`, Gerrit creates a new patch set. ### Upload a single change[¶](https://jj-vcs.github.io/jj/prerelease/gerrit/#upload-a-single-change "Permanent link") `# upload the previous commit (@-) for review to main $ jj gerrit upload -r @-` Selecting revisions (revsets)[¶](https://jj-vcs.github.io/jj/prerelease/gerrit/#selecting-revisions-revsets "Permanent link") ------------------------------------------------------------------------------------------------------------------------------ `jj gerrit upload` accepts one or more `-r/--revisions` arguments. Each argument may expand to multiple commits. Common patterns: * `-r @-`: the commit previous to the one you're currently working on * `-r A..B`: commits that are ancestors of B but not of A See the [revsets](https://jj-vcs.github.io/jj/prerelease/revsets/) guide for more information. ### Preview without pushing[¶](https://jj-vcs.github.io/jj/prerelease/gerrit/#preview-without-pushing "Permanent link") Use `--dry-run` to see which commits would be modified and pushed, and where, without changing anything or contacting the remote. `$ jj gerrit upload -r '@-' --for main --dry-run` Target branch and remote selection[¶](https://jj-vcs.github.io/jj/prerelease/gerrit/#target-branch-and-remote-selection "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------ There are a few way of specifying the target remote for your projects: * Please run `jj config set --user gerrit.default-remote-branch ` to set your default branch across all repos * Please run `jj config set --repo gerrit.default-remote-branch ` to set your default branch for this specific repo. * Use `--remote-branch ` to override this for one specific occasion. The remote used to push is determined as follows: * If you have more than one origin, or the origin isn't called gerrit, run `jj config set --repo gerrit.default_remote ` to set-up a default remote. * To upload to a specific remote as a one-off thing, use `--remote ` Updating changes after review[¶](https://jj-vcs.github.io/jj/prerelease/gerrit/#updating-changes-after-review "Permanent link") -------------------------------------------------------------------------------------------------------------------------------- To address review feedback, update your revisions, then run `jj gerrit upload` again with the same revsets. Gerrit will add new patch sets to the existing changes instead of creating new ones. Examples: `# Edit an earlier commit in the stack $ jj edit xcv # position on the stack to edit --- Apply needed edits --- $ jj gerrit upload -r xcv` `Change-Id` management[¶](https://jj-vcs.github.io/jj/prerelease/gerrit/#change-id-management "Permanent link") ---------------------------------------------------------------------------------------------------------------- When uploading, `jj gerrit upload` adds a `Change-Id` footer based on the JJ change id. That means that any changes made to a JJ change will become a new patch set on the Gerrit change during the next upload. Keep this association in mind when splitting or squashing changes. For example, when splitting a change, the portion that you want associated with the original Gerrit change should remain in the original JJ change (the first half of the split). Similarly, when squashing new changes, you typically want to squash into the change that was previously uploaded to Gerrit. If your JJ changes no longer align with the desired mapping to Gerrit changes, you can manually copy a Gerrit `Change-Id` footer into your JJ change description to directly assign a JJ change to an exist Gerrit change. As an alternative to `jj gerrit upload`'s automatic `Change-Id` mapping, you can configure JJ to automatically add `Change-Id` footers to all change descriptions: `[templates] commit_trailers = ''' if( !trailers.contains_key("Change-Id"), format_gerrit_change_id_trailer(self) ) '''` In this case, the Gerrit change mapping is defined entirely by the `Change-Id` footers. When splitting or squashing changes, be sure to keep the `Change-Id` footers associated with the desired changes. Be sure not to duplicate the same `Change-Id` across different changes. Gerrit will reject pushes that contain duplicate `Change-Id`s, but if the uploads are done separately, you may unintentionally overwrite an existing change. --- # Working with GitHub - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/github/#using-jujutsu-with-github-and-gitlab-projects) [](https://github.com/jj-vcs/jj/edit/main/docs/github.md "Edit this page") Using Jujutsu with GitHub and GitLab Projects[¶](https://jj-vcs.github.io/jj/prerelease/github/#using-jujutsu-with-github-and-gitlab-projects "Permanent link") ================================================================================================================================================================ This guide assumes a basic understanding of either Git or Mercurial. Basic workflow[¶](https://jj-vcs.github.io/jj/prerelease/github/#basic-workflow "Permanent link") -------------------------------------------------------------------------------------------------- The simplest way to start with Jujutsu is to create a stack of commits first. You will only need to create a bookmark when you need to push the stack to a remote. There are two primary workflows: using a generated bookmark name or naming a bookmark. ### Using a generated bookmark name[¶](https://jj-vcs.github.io/jj/prerelease/github/#using-a-generated-bookmark-name "Permanent link") In this example we're letting Jujutsu auto-create a bookmark. `# Start a new commit off of the default bookmark. $ jj new main # Refactor some files, then add a description and start a new commit $ jj commit -m 'refactor(foo): restructure foo()' # Add a feature, then add a description and start a new commit $ jj commit -m 'feat(bar): add support for bar' # Let Jujutsu generate a bookmark name and push that to GitHub. Note that we # push the working-copy commit's *parent* because the working-copy commit # itself is empty. $ jj git push -c @-` ### Using a named bookmark[¶](https://jj-vcs.github.io/jj/prerelease/github/#using-a-named-bookmark "Permanent link") In this example, we create a bookmark named `bar` and then push it to the remote. ``# Start a new commit off of the default bookmark. $ jj new main # Refactor some files, then add a description and start a new commit $ jj commit -m 'refactor(foo): restructure foo()' # Add a feature, then add a description and start a new commit $ jj commit -m 'feat(bar): add support for bar' # Create a bookmark so we can push it to GitHub. Note that we created the bookmark # on the working-copy commit's *parent* because the working copy itself is empty. $ jj bookmark create bar -r @- # `bar` now contains the previous two commits. # Push the bookmark to GitHub (pushes only `bar`) $ jj git push --allow-new`` While it's possible to create a bookmark in advance and commit on top of it in a Git-like manner, you will then need to move the bookmark manually when you create a new commits. Unlike Git, Jujutsu will not do it automatically. Updating the repository[¶](https://jj-vcs.github.io/jj/prerelease/github/#updating-the-repository "Permanent link") -------------------------------------------------------------------------------------------------------------------- As of October 2023, Jujutsu has no equivalent to a `git pull` command (see [issue #1039](https://github.com/jj-vcs/jj/issues/1039) ). Until such a command is added, you need to use `jj git fetch` followed by a `jj rebase -d $main_bookmark` to update your changes. Working in a Git colocated repository[¶](https://jj-vcs.github.io/jj/prerelease/github/#working-in-a-git-colocated-repository "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------------ After doing `jj git init`, which [colocates](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#colocated-jujutsugit-repos) the `.jj` and `.git` directories, Git will be in a [detached HEAD state](https://git-scm.com/docs/git-checkout#_detached_head) , which is unusual, as Git mainly works with named branches; jj does not. In a colocated repository, every `jj` command will automatically synchronize Jujutsu's view of the repo with Git's view. For example, `jj commit` updates the HEAD of the Git repository, enabling an incremental migration. `$ nvim docs/tutorial.md $ # Do some more work. $ jj commit -m "Update tutorial" # Create a bookmark on the working-copy commit's parent $ jj bookmark create doc-update -r @- $ jj git push --allow-new` Working in a Jujutsu repository[¶](https://jj-vcs.github.io/jj/prerelease/github/#working-in-a-jujutsu-repository "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------ In a Jujutsu repository, the workflow is simplified. If there's no need for explicitly named bookmarks, you can just generate one for a change. As Jujutsu is able to create a bookmark for a revision. `$ # Do your work $ jj commit $ # Push change "mw", letting Jujutsu automatically create a bookmark called $ # "push-mwmpwkwknuz" $ jj git push --change mw` Addressing review comments[¶](https://jj-vcs.github.io/jj/prerelease/github/#addressing-review-comments "Permanent link") -------------------------------------------------------------------------------------------------------------------------- There are two workflows for addressing review comments, depending on your project's preference. Many projects prefer that you address comments by adding commits to your bookmark[1](https://jj-vcs.github.io/jj/prerelease/github/#fn:1) . Some projects (such as Jujutsu and LLVM) instead prefer that you keep your commits clean by rewriting them and then force-pushing[2](https://jj-vcs.github.io/jj/prerelease/github/#fn:2) . ### Adding new commits[¶](https://jj-vcs.github.io/jj/prerelease/github/#adding-new-commits "Permanent link") If your project prefers that you address review comments by adding commits on top, you can do that by doing something like this: ``$ # Create a new commit on top of the `your-feature` bookmark from above. $ jj new your-feature $ # Address the comments by updating the code. Then review the changes. $ jj diff $ # Give the fix a description and create a new working-copy on top. $ jj commit -m 'address pr comments' $ # Update the bookmark to point to the new commit. $ jj bookmark move your-feature --to @- $ # Push it to your remote $ jj git push`` Notably, the above workflow creates a new commit for you. The same can be achieved without creating a new commit. Warning We strongly suggest to `jj new` after the example below, as all further edits still get amended to the previous commit. ``$ # Create a new commit on top of the `your-feature` bookmark from above. $ jj new your-feature $ # Address the comments by updating the code. Then review the changes. $ jj diff $ # Give the fix a description. $ jj describe -m 'address pr comments' $ # Update the bookmark to point to the current commit. $ jj bookmark move your-feature --to @ $ # Push it to your remote $ jj git push`` ### Rewriting commits[¶](https://jj-vcs.github.io/jj/prerelease/github/#rewriting-commits "Permanent link") If your project prefers that you keep commits clean, you can do that by doing something like this: ``$ # Create a new commit on top of the second-to-last commit in `your-feature`, $ # as reviewers requested a fix there. $ jj new your-feature- # NOTE: the trailing hyphen is not a typo! $ # Address the comments by updating the code. Then review the changes. $ jj diff $ # Squash the changes into the parent commit $ jj squash $ # Push the updated bookmark to the remote. Jujutsu automatically makes it a $ # force push $ jj git push --bookmark your-feature`` The hyphen after `your-feature` comes from the [revset](https://jj-vcs.github.io/jj/prerelease/revsets/) syntax. Working with other people's bookmarks[¶](https://jj-vcs.github.io/jj/prerelease/github/#working-with-other-peoples-bookmarks "Permanent link") ----------------------------------------------------------------------------------------------------------------------------------------------- By default, `jj git clone` imports the default remote bookmark (which is usually `main` or `master`), but `jj git fetch` doesn't import new remote bookmarks to local bookmarks. This means that if you want to iterate or test another contributor's bookmark, you'll need to do `jj new @` onto it. If you want to import all remote bookmarks including inactive ones, set `git.auto-local-bookmark = true` in the config file. Then you can specify a contributor's bookmark as `jj new ` instead of `jj new @`. You can find more information on that setting [here](https://jj-vcs.github.io/jj/prerelease/config/#automatic-local-bookmark-creation) . Using GitHub CLI[¶](https://jj-vcs.github.io/jj/prerelease/github/#using-github-cli "Permanent link") ------------------------------------------------------------------------------------------------------ GitHub CLI will have trouble finding the proper Git repository path in jj repos that aren't [colocated](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#colocated-jujutsugit-repos) (see [issue #1008](https://github.com/jj-vcs/jj/issues/1008) ). You can configure the `$GIT_DIR` environment variable to point it to the right path: `$ GIT_DIR=.jj/repo/store/git gh issue list` You can make that automatic by installing [direnv](https://direnv.net/) and defining hooks in a `.envrc` file in the repository root to configure `$GIT_DIR`. Just add this line into `.envrc`: `export GIT_DIR=$PWD/.jj/repo/store/git` and run `direnv allow` to approve it for direnv to run. Then GitHub CLI will work automatically even in repos that aren't colocated so you can execute commands like `gh issue list` normally. Useful Revsets[¶](https://jj-vcs.github.io/jj/prerelease/github/#useful-revsets "Permanent link") -------------------------------------------------------------------------------------------------- Log all revisions across all local bookmarks that aren't on the main bookmark nor on any remote: `$ jj log -r 'bookmarks() & ~(main | remote_bookmarks())'` Log all revisions that you authored, across all bookmarks that aren't on any remote: `$ jj log -r 'mine() & bookmarks() & ~remote_bookmarks()'` Log all remote bookmarks that you authored or committed to: `$ jj log -r 'remote_bookmarks() & (mine() | committer(your@email.com))'` Log all ancestors of the current working copy that aren't on any remote: `$ jj log -r 'remote_bookmarks()..@'` Merge conflicts[¶](https://jj-vcs.github.io/jj/prerelease/github/#merge-conflicts "Permanent link") ---------------------------------------------------------------------------------------------------- For a detailed overview, how Jujutsu handles conflicts, revisit the [tutorial](https://jj-vcs.github.io/jj/prerelease/tutorial/#conflicts) . Using several remotes[¶](https://jj-vcs.github.io/jj/prerelease/github/#using-several-remotes "Permanent link") ---------------------------------------------------------------------------------------------------------------- It is common to use several remotes when contributing to a shared repository. For example, "upstream" can designate the remote where the changes will be merged through a pull-request while "origin" is your private fork of the project. `$ jj git clone --remote upstream https://github.com/upstream-org/repo $ cd repo $ jj git remote add origin git@github.com:your-org/your-repo-fork` This will automatically setup your repository to track the main bookmark from the upstream repository, typically `main@upstream` or `master@upstream`. You might want to `jj git fetch` from "upstream" and to `jj git push` to "origin". You can configure the default remotes to fetch from and push to in your configuration file (for example, `.jj/repo/config.toml`): `[git] fetch = "upstream" push = "origin"` The default for both `git.fetch` and `git.push` is "origin". If you usually work on a project from several computers, you may configure `jj` to fetch from both repositories by default, in order to keep your own bookmarks synchronized through your `origin` repository: `[git] fetch = ["upstream", "origin"] push = "origin"` * * * 1. This is a GitHub-style review, as GitHub currently only is able to compare bookmarks. [↩](https://jj-vcs.github.io/jj/prerelease/github/#fnref:1 "Jump back to footnote 1 in the text") 2. If you're wondering why we prefer clean commits in this project, see e.g. [this blog post](https://jg.gg/2018/09/29/stacked-diffs-versus-pull-requests/)  [↩](https://jj-vcs.github.io/jj/prerelease/github/#fnref:2 "Jump back to footnote 2 in the text") --- # Testimonials - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/testimonials/#testimonials) [](https://github.com/jj-vcs/jj/edit/main/docs/testimonials.md "Edit this page") Testimonials[¶](https://jj-vcs.github.io/jj/prerelease/testimonials/#testimonials "Permanent link") ==================================================================================================== You might not be ready to make the jump to Jujutsu yet. It's understandable; new tools come with new lessons, failures, and ideas to absorb. They require practice. In order to provide some motivation, we've collected a number of real, 100% authentic testimonials — from our loving users, our silly developers — all to tip the scales and get you on our side! What the users have to say[¶](https://jj-vcs.github.io/jj/prerelease/testimonials/#what-the-users-have-to-say "Permanent link") -------------------------------------------------------------------------------------------------------------------------------- > I've spent many years of my career working on version control. What I like most about Jujutsu is how it has non-obvious solutions to UX problems that we've run into in the past. What most people may not realize is that there are many novel features which all interlock to make it easy to use. > > For example, consider Jujutsu's support for automatically rebasing descendants of amended revisions. When we implemented that in Mercurial, we ran into an issue: what if there's a merge conflict? Our solution was to warn users and just not perform the auto-rebase. Now, suddenly, users have to understand that there can be old versions of the same revision visible in their log, and learn how to fix this state. > > In contrast, Jujutsu's solution is to simply make merge conflicts first-class. This is not just an improvement in general, it is also specifically an improvement for auto-rebase — users no longer have to learn about old versions of a revision unless they want to look at the obslog. > > Over and over, I'm struck by how well Jujutsu demonstrates this kind of evolved thinking, which as an experienced version control developer I deeply appreciate. — Rain, engineer at Oxide Computer Company, former VCS developer > Jujutsu is amazing... I couldn't have come up with anything remotely as elegant. > > It's so rare that a solution attacks the innermost core of a problem so thoroughly, I genuinely feel blessed to be in its presence. And also a bit vindicated in not even trying to learn to use any of the tools that felt like more crutches stacked upon a sand castle — Anonymous user, speaking from the shadows > It's the easiest time I've ever had learning a tool this deeply this quickly, because of the ability to experiment and undo, instead of triple-checking before trying a new scary command. — Scott Olson, advanced Git user and now a Jujutsu user > I initially started to use Jujutsu for personal repos, and it has quickly gone from "neat, let's try this more" to "very neat, added to my permanent config and automatically installed for new machines". — Poliorcetics, on GitHub > when i worked on the rust compiler, my job was to chain together a bunch of strange and cursed tools that broke often. jujutsu breaks about half as much, so that's pretty good i guess — jyn514, Rust contributor > Jujutsu is pretty cool, you can even keep most of your existing workflows — Ben, who doesn't want you keeping your existing workflow > Wait, it's not called Jujitsu? — Phil, Mercurial contributor (who doesn't have to learn Git, now that Jujutsu exists) > When I heard about Jujutsu I decided to try it out before forming an opinion. Technically it never formed, because I haven't considered going back. — gul banana, computer programmer > muwhahaha, I have corrupted my cofounder and now 100% of brontosource dev is on JJ. — Matt Kulunkundis, cofounder BrontoSource > jj is genuinely the first tool since Rust that has me excited. — David Barsky, rust-analyzer and `tokio-rs/tracing` developer > Today I did some complex surgery on a legacy repo that has been committed to for years by a number of individuals with varying hygienic habits. \[..\] > > Now, this still took me about two hours using jj. But at no point did I seriously consider physical violence to humans or inanimate objects. Everything progressed calmly and methodically. I was able to introspect changes before I made them, and when I did screw up… `jj undo` to the rescue. — An unknown scientist, who couldn't have done it with Git. > jj was my first exposure to revsets and at first I was "why would I ever need that", but now after exercising that math part of my brain that's been stale since high school, they're indispensable — Marijan Smetko, who previously only knew Git. > I think my favorite thing about jj is it makes the way I abused Git via lazygit the standard way of doing things — Isaac Corbrey, reformed Git user What the developers have to say[¶](https://jj-vcs.github.io/jj/prerelease/testimonials/#what-the-developers-have-to-say "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------ > I've been a FOSS contributor using Git for over 16 years, and Jujutsu continues to amaze me every day. It has that sweet simplicity I was fond of in Darcs, but it boils down all my most core and fundamental workflows — developed over years of experience — into a simple set of primitives. The internal design is simple and beautiful; it looks like a database, making the implementation elegant, safe, and extensible. All this, using the same Git repositories my coworkers use. > > It's like if you found out one day that you built your entire home on a vein of rich gold. Every day I seem to find new and beautiful emergent behaviors, all adding up to a tool that is greater than the sum of its parts. — Austin Seipp, "No 1. Jujutsu Fan" > Honestly, I implemented signing support mostly for that sweet dopamine hit that you get from the green checkmark on GitHub. Yeah. — Anton Bulakh, contributor and dopamine enthusiast > I'm sometimes still surprised that navigating with `jj next` and `jj prev` works. — Philip Metzger, author of `jj next` and `jj prev` > I'm surprised when it works. — Martin von Zweigbergk, project creator and leader Spread the word yourself[¶](https://jj-vcs.github.io/jj/prerelease/testimonials/#spread-the-word-yourself "Permanent link") ---------------------------------------------------------------------------------------------------------------------------- Are you satisfied with Jujutsu? Ready to recommend it to a Jujillion of your friends and coworkers? Great! The easiest way to help the project grow is word of mouth. So make sure to talk to them about it and show off your hip new tool. Maybe post a link to it on your other favorite tool that you love using, Slack? If you're not sure what to say, we hired the cheapest marketing team we could find to design a list of Pre-Approved Endorsements in their laboratory. Just copy and paste these right into a text box! Shilling for an open source project has never been easier than this. > Jujutsu is an alright tool. I guess. > > Jujutsu is my favorite software tool of all time. I am saying this for no particular reason, definitely not because I was paid to. > > I love Jujutsu. I love Jujutsu. I love Jujutsu. I love Jujutsu. I love Jujutsu. I love Jujutsu. I love Jujutsu. I love Jujutsu. --- # FAQ - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/FAQ/#frequently-asked-questions) [](https://github.com/jj-vcs/jj/edit/main/docs/FAQ.md "Edit this page") Frequently asked questions[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#frequently-asked-questions "Permanent link") ======================================================================================================================= ### Why does my bookmark not move to the new commit after `jj new/commit`?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#why-does-my-bookmark-not-move-to-the-new-commit-after-jj-newcommit "Permanent link") If you're familiar with Git, you might expect the current bookmark to move forward when you commit. However, Jujutsu does not have a concept of a "current bookmark". To move bookmarks, use `jj bookmark move`. ### I made a commit and `jj git push --all` says "Nothing changed" instead of pushing it. What do I do?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#i-made-a-commit-and-jj-git-push-all-says-nothing-changed-instead-of-pushing-it-what-do-i-do "Permanent link") `jj git push --all` pushes all _bookmarks_, not all revisions. You have two options: * Using `jj git push --change` will automatically create a bookmark and push it. * Using `jj bookmark` commands to create or move a bookmark to either the commit you want to push or a descendant of it. Unlike Git, Jujutsu doesn't do this automatically (see previous question). ### Where is my commit, why is it not visible in `jj log`?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#where-is-my-commit-why-is-it-not-visible-in-jj-log "Permanent link") Is your commit visible with `jj log -r 'all()'`? If yes, you should be aware that `jj log` only shows a subset of the commits in the repo by default. Most commits that exist on a remote are not shown. Local commits and their immediate parents (for context) are shown. The thinking is that you are more likely to interact with this set of commits. You can configure the set of revisions to show by default by overriding `revsets.log` as described in [config](https://jj-vcs.github.io/jj/prerelease/config/) . If not, the revision may have been abandoned (e.g. because you used `jj abandon`, or because it's an obsolete version that's been rewritten with `jj rebase`, `jj describe`, etc). In that case, `jj log -r commit_id` should show the revision as "hidden". `jj new commit_id` should make the revision visible again. See [revsets](https://jj-vcs.github.io/jj/prerelease/revsets/) and [templates](https://jj-vcs.github.io/jj/prerelease/templates/) for further guidance. ### What are elided revisions in the output of `jj log`? How can I display them?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#what-are-elided-revisions-in-the-output-of-jj-log-how-can-i-display-them "Permanent link") "Elided revisions" appears in the log when one revision descends from another, both are in the revset, but the revisions connecting them are _not_ in the revset. For example, suppose you log the revset `tyl|mus` which contains exactly two revisions: `$ jj log -r 'tyl|mus' ○ musnqzvt me@example.com 1 minute ago 9a09f8a5 │ Revision C ~ (elided revisions) ○ tylynnzk me@example.com 1 minute ago f26967c8 │ Revision A` Only the two revisions in the revset are displayed. The text "(elided revisions)" is shown to indicate that `musnqzvt` descends from `tylynnzk`, but the nodes connecting them are not in the revset. To view the elided revisions, change the [revset expression](https://jj-vcs.github.io/jj/prerelease/revsets/) so it includes the connecting revisions. The `connected()` revset function does exactly this: `$ jj log -r 'connected(tyl|mus)' ○ musnqzvt me@example.com 43 seconds ago 9a09f8a5 │ Revision C ○ rsvnrznr me@example.com 43 seconds ago 5b490f30 │ Revision B ○ tylynnzk me@example.com 43 seconds ago f26967c8 │ Revision A` ### How can I get `jj log` to show me what `git log` would show me?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#how-can-i-get-jj-log-to-show-me-what-git-log-would-show-me "Permanent link") Use `jj log -r ..`. The `..` [operator](https://jj-vcs.github.io/jj/prerelease/revsets/#operators) lists all visible commits in the repo, excluding the root (which is never interesting and is shared by all repos). ### Can I monitor how `jj log` evolves?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#can-i-monitor-how-jj-log-evolves "Permanent link") The simplest way to monitor how the history as shown by `jj log` evolves is by using the [watch(1)](https://man7.org/linux/man-pages/man1/watch.1.html) command (or [hwatch](https://github.com/blacknon/hwatch?tab=readme-ov-file#configuration) or [viddy](https://github.com/sachaos/viddy) ). For example: `watch --color jj --ignore-working-copy log --color=always` This will continuously update the (colored) log output in the terminal. The `--ignore-working-copy` option avoids conflicts with manual operations during the creation of snapshots. Martin used watch in a [tmux](https://github.com/tmux/tmux/wiki) pane during his presentation [Jujutsu - A Git-compatible VCS](https://www.youtube.com/watch?v=LV0JzI8IcCY) . A similar result can be achieved with [watchexec](https://github.com/watchexec/watchexec) , which triggers on op log changes instead of using a two-second interval: `watchexec --quiet --clear --restart --watch=.jj/repo/op_heads/heads --ignore-nothing --wrap-process=none -- jj --ignore-working-copy log` Alternatively, you can use [jj-fzf](https://github.com/tim-janik/jj-fzf) , where the central piece is the `jj log` view and common operations can be carried out via key bindings while the log view updates. The wiki lists additional TUIs and GUIs beyond the terminal: [GUI-and-TUI](https://github.com/jj-vcs/jj/wiki/GUI-and-TUI) ### Should I colocate my repository?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#should-i-colocate-my-repository "Permanent link") Colocating a Jujutsu repository allows you to use both Jujutsu and Git in the same working copy. The benefits of doing so are: * You can use Git commands when you're not sure how to do something with Jujutsu, Jujutsu hasn't yet implemented a feature (e.g., tagging), or you simply prefer Git in some situations. * Tooling that expects a Git repository still works (IDEs, build tooling, etc.) The [colocation documentation describes the drawbacks](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#colocated-jujutsugit-repos) but the most important ones are: * Interleaving `git` and `jj` commands may create confusing bookmark conflicts or divergent changes. * If the working copy commit or its parent contain any conflicted files, tools expecting a Git repo may interpret the commit contents or its diff in a wrong and confusing way. You should avoid doing mutating operations with Git tools and ignore the confusing information such tools present for conflicted commits (unless you are curious about [the details of how `jj` stores conflicts](https://jj-vcs.github.io/jj/prerelease/technical/conflicts/) ). See [#3979](https://github.com/jj-vcs/jj/issues/3979) for plans to improve this situation. * Jujutsu commands may be a little slower in very large repositories due to importing and exporting changes to Git. Most repositories are not noticeably affected by this. If you primarily use Jujutsu to modify the repository, the drawbacks are unlikely to affect you. Try colocating while you learn Jujutsu, then switch if you find a specific reason not to colocate. ### `jj` is said to record the working copy after `jj log` and every other command. Where can I see these automatic "saves"?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#jj-is-said-to-record-the-working-copy-after-jj-log-and-every-other-command-where-can-i-see-these-automatic-saves "Permanent link") Indeed, every `jj` command updates the current "working-copy" revision, marked with `@` in `jj log`. You can notice this by how the [commit ID](https://jj-vcs.github.io/jj/prerelease/glossary/#commit-id) of the working copy revision changes when it's updated. Note that, unless you move to another revision (with `jj new` or `jj edit`, for example), the [change ID](https://jj-vcs.github.io/jj/prerelease/glossary/#change-id) will not change. If you expected to see a historical view of your working copy changes in the parent-child relationships between commits you can see in `jj log`, this is simply not what they mean. What you can see in `jj log` is that after the working copy commit gets amended (after any edit), the commit ID changes. You can see the actual history of working copy changes using `jj evolog`. This will show the history of the commits that were previously the "working-copy commit", since the last time the change id of the working copy commit changed. The obsolete changes will be marked as "hidden". They are still accessible with any `jj` command (`jj diff`, for example), but you will need to use the commit id to refer to hidden commits. You can also use `jj evolog -r` on revisions that were previously the working-copy revisions (or on any other revisions). Use `jj evolog -p` as an easy way to see the evolution of the commit's contents. ### Can I prevent Jujutsu from recording my unfinished work? I'm not ready to commit it.[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#can-i-prevent-jujutsu-from-recording-my-unfinished-work-im-not-ready-to-commit-it "Permanent link") Jujutsu automatically records new files in the current working-copy commit and doesn't provide a way to prevent that. However, you can easily record intermediate drafts of your work. If you think you might want to go back to the current state of the working-copy commit, simply use `jj new`. There's no need for the commit to be "finished" or even have a description. Then future edits will go into a new working-copy commit on top of the now former working-copy commit. Whenever you are happy with another set of edits, use `jj squash` to amend the previous commit. If you have changes you _never_ want to put in a public commit, see: [How can I keep my scratch files in the repository without committing them?](https://jj-vcs.github.io/jj/prerelease/FAQ/#how-can-i-keep-my-scratch-files-in-the-repository-without-committing-them) For more options see the next question. ### Can I interactively create a new commit from only some of the changes in the working copy, like `git add -p && git commit` or `hg commit -i`?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#can-i-interactively-create-a-new-commit-from-only-some-of-the-changes-in-the-working-copy-like-git-add-p-git-commit-or-hg-commit-i "Permanent link") Since the changes are already in the working-copy commit, the equivalent to `git add -p && git commit`/`git commit -p`/`hg commit -i` is to split the working-copy commit with `jj split -i` (or the practically identical `jj commit -i`). For the equivalent of `git commit --amend -p`/`hg amend -i`, use `jj squash -i`. ### Is there something like `git rebase --interactive` or `hg histedit`?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#is-there-something-like-git-rebase-interactive-or-hg-histedit "Permanent link") It is often sufficient to use some form of `jj rebase` with `-A/-B`. For example, if you have a linear chain of revisions `A` through `C` and want to move `C` before `B`, use `jj rebase -r C -B B`. See `jj help rebase` for more examples. There is a [tracking issue](https://github.com/jj-vcs/jj/issues/1531) you can check for updates. To squash or split commits, use `jj squash` and `jj split`. ### How can I keep my scratch files in the repository without committing them?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#how-can-i-keep-my-scratch-files-in-the-repository-without-committing-them "Permanent link") You can set `snapshot.auto-track` to only start tracking new files matching the configured pattern (e.g. `"none()"`). Changes to already tracked files will still be snapshotted by every command. Files not matching the pattern can be tracked with `jj file track`. You can keep your notes and other scratch files in the repository, if you add a wildcard pattern to either the repo's `gitignore` or your global `gitignore`. Something like `*.scratch` or `*.scratchpad` should do, after that rename the files you want to keep around to match the pattern. If you keep your scratch files in their own directory with no tracked files, you can create a `.gitignore` file in that directory containing only `*`. This will ignore everything in the directory including the `.gitignore` file itself. If `$EDITOR` integration is important, something like `scratchpad.*` may be more helpful, as you can keep the filename extension intact (it matches `scratchpad.md`, `scratchpad.rs` and more). Another option is to add a directory to the global `.gitignore` which then stores all your temporary files and notes. For example, you could add `scratch/` to `~/.git/ignore` and then store arbitrary files in `/scratch/`. You can find more details on `gitignore` files [here](https://git-scm.com/docs/gitignore) . ### How can I avoid committing my local-only changes to tracked files?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#how-can-i-avoid-committing-my-local-only-changes-to-tracked-files "Permanent link") Suppose your repository tracks a file like `secret_config.json`, and you make some changes to that file to work locally. Since Jujutsu automatically commits the working copy, there's no way to prevent Jujutsu from committing changes to the file. But, you never want to push those changes to the remote repository. One solution is to keep these changes in a separate commit branched from the trunk. To use those changes in your working copy, _merge_ the private commit into your branch. Suppose you have a commit "Add new feature": `$ jj log @ xxxxxxxx me@example.com 2024-08-21 11:13:21 ef612875 │ Add new feature ◉ yyyyyyyy me@example.com 2024-08-21 11:13:09 main b624cf12 │ Existing work ~` First, create a new commit branched from main and add your private changes: `$ jj new main -m "private: my credentials" Working copy (@) now at: wwwwwwww 861de9eb (empty) private: my credentials Parent commit (@-) : yyyyyyyy b624cf12 main | Existing work Added 0 files, modified 1 files, removed 0 files $ echo '{ "password": "p@ssw0rd1" }' > secret_config.json` Now create a merge commit with the branch you're working on and the private commit: `$ jj new xxxxxxxx wwwwwwww Working copy (@) now at: vvvvvvvv ac4d9fbe (empty) (no description set) Parent commit (@-) : xxxxxxxx ef612875 Add new feature Parent commit (@-) : wwwwwwww 2106921e private: my credentials Added 0 files, modified 1 files, removed 0 files $ jj log @ vvvvvvvv me@example.com 2024-08-22 08:57:40 ac4d9fbe ├─╮ (empty) (no description set) │ ◉ wwwwwwww me@example.com 2024-08-22 08:57:40 2106921e │ │ private: my credentials ◉ │ xxxxxxxx me@example.com 2024-08-21 11:13:21 ef612875 ├─╯ Add new feature ◉ yyyyyyyy me@example.com 2024-08-21 11:13:09 main b624cf12 │ Existing work ~` Now you're ready to work: * Your work in progress _xxxxxxxx_ is the first parent of the merge commit. * The private commit _wwwwwwww_ is the second parent of the merge commit. * The working copy (_vvvvvvvv_) contains changes from both. As you work, squash your changes using `jj squash --into xxxxxxxx`. If you need a new empty commit on top of `xxxxxxxx` you can use the `--insert-after` and `--insert-before` options (`-A` and `-B` for short): `# Insert a new commit after xxxxxxxx $ jj new --no-edit -A xxxxxxxx -m "Another feature" Working copy (@) now at: uuuuuuuu 1c3cff09 (empty) Another feature Parent commit (@-) : xxxxxxxx ef612875 Add new feature # Insert a new commit between yyyyyyyy and vvvvvvvv $ jj new --no-edit -A yyyyyyyy -B vvvvvvvv -m "Yet another feature" Working copy (@) now at: tttttttt 938ab831 (empty) Yet another feature Parent commit (@-) : yyyyyyyy b624cf12 Existing work` To avoid pushing change _wwwwwwww_ by mistake, use the configuration [git.private-commits](https://jj-vcs.github.io/jj/prerelease/config/#set-of-private-commits) : `jj config set --user git.private-commits "'''description(glob:'private:*')'''"` ### I accidentally changed files in the wrong commit, how do I move the recent changes into another commit?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#i-accidentally-changed-files-in-the-wrong-commit-how-do-i-move-the-recent-changes-into-another-commit "Permanent link") Let's say we are editing a commit for "featureA", and we forgot to run `jj new` or `jj commit` before doing some work that belongs in a new commit: `$ jj log @ lnvvtrzo jjfan@example.org 2025-02-28 21:01:10 31a347e0 │ featureA ◆ zzzzzzzz root() 00000000 $ cat file # Oh no, the work on "feature B" should be in a separate commit! Done with feature A Working on feature B` The goal is to restore change `lnvvtrzo` to its previous state and split the difference into a new child commit. #### Step 1: Find the commit ID for the "last good version"[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#step-1-find-the-commit-id-for-the-last-good-version "Permanent link") If you pushed `lnvvtrzo` already, then the version you probably want to restore is the version on the remote. For example, if the bookmark is `feature-a`, then the commit you want to restore is `feature-a@origin`. Otherwise, you can find [all the past versions of the working copy revision that `jj` has saved](https://jj-vcs.github.io/jj/prerelease/FAQ/#jj-is-said-to-record-the-working-copy-after-jj-log-and-every-other-command-where-can-i-see-these-automatic-saves) by running `jj evolog`, perhaps with the `--patch` option. The obsolete versions will be marked as "hidden" and will have the same change ID, but will have different commit IDs. This represents the [change](https://jj-vcs.github.io/jj/prerelease/glossary/#change) evolving over time. For example, this is what the evolog might look like after you made two edits to the same change: `$ # Note the word "hidden", the commit IDs on the right, $ # and the unchanging change ID on the left. $ jj evolog @ lnvvtrzo jjfan@example.org 2025-02-28 21:01:10 31a347e0 │ featureA │ -- operation 3cb7392c092c snapshot working copy ○ lnvvtrzo hidden jjfan@example.org 2025-02-28 21:00:51 b8004ab8 │ featureA │ -- operation 1280bfaec893 snapshot working copy ○ lnvvtrzo hidden jjfan@example.org 2025-02-28 20:50:05 e4d831d (no description set) -- operation 0418a5aa94b5 snapshot working copy` Since commit `b800` is hidden, it is considered obsolete and `jj log` (without arguments) will not show it, nor can it be accessed by its change ID `lnvvtrzo`. However, most `jj` operations work normally on such commits if you refer to them by their commit ID. To find out which of these versions is the last time before we started working on feature B (the point where we should have created a new change, but failed to do so), we can look at the actual changes between the `evolog` commits by running `jj evolog --patch`: ``$ # When was the last saved point before we started working on feature B? $ jj evolog --patch --git # We use `--git` to make diffs clear without colors @ lnvvtrzo jjfan@example.org 2025-02-28 21:01:10 31a347e0 │ featureA │ -- operation 3cb7392c092c snapshot working copy │ diff --git a/file b/file │ index 2b455c4207..2a7e05a01a 100644 │ --- a/file │ +++ b/file │ @@ -1,1 +1,2 @@ │ Done with feature A │ +Working on feature B ○ lnvvtrzo hidden jjfan@example.org 2025-02-28 21:00:51 b8004ab8 │ featureA │ -- operation 1280bfaec893 snapshot working copy │ diff --git a/file b/file │ index cb61245109..2b455c4207 │ --- a/file │ +++ b/file │ @@ -1,1 +1,1 @@ │ -Working on feature A │ +Done with feature A ○ lnvvtrzo hidden jjfan@example.org 2025-02-28 20:50:05 e4d831d (no description set) -- operation 0418a5aa94b5 snapshot working copy diff --git a/file b/file index 0000000000..cb61245109 --- /dev/null +++ b/file @@ 0,0 +1,1 @@ +Working on feature A`` In this example, the version of the change when we were actually done with feature A is when we edited the file to say "Done with feature A". This state was saved in the commit with ID `b80` (the second one in the list). The following edit (commit `31a`) belongs in a new change. #### Step 2: Create a new change on top of the original revision[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#step-2-create-a-new-change-on-top-of-the-original-revision "Permanent link") The "featureA" change is currently at commit `31a`: `$ jj log @ lnvvtrzo jjfan@example.org 2025-02-28 21:01:10 31a347e0 │ featureA ◆ zzzzzzzz root() 00000000` We'd like to create a new "featureB" change with the contents of the current commit `31a`, and we'd like the "featureA" change to be reverted to its former state at commit `b80` (see step 1 above for how we found that commit ID). First, we create a new empty child commit on top of `b80`: `$ jj new b80 -m "featureB" Working copy (@) now at: pvnrkl 47171aa (empty) featureB Parent commit (@-) : lnvvtr?? b8004ab featureA` Notice the change ID has "??" appended to it. This indicates that change ID `lnvvtr` is now [divergent](https://jj-vcs.github.io/jj/prerelease/glossary/#divergent-change) : There are two visible commits with the same change ID (commit `b8004ab` and `31a347e0`). This is okay and will be resolved in the next steps. Next, restore the contents of `31a347e0` into the working copy: `$ jj restore --from 31a347e0 Working copy (@) now at: pvnrkl 468104c featureB Parent commit (@-) : lnvvtr?? b8004ea featureA $ cat file Done with feature A Working on feature B` #### Step 3: Move any bookmarks to the original revision[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#step-3-move-any-bookmarks-to-the-original-revision "Permanent link") `$ jj bookmark move --from 31a347e0 --to b8004ea8` #### Step 4: Abandon the unwanted revision[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#step-4-abandon-the-unwanted-revision "Permanent link") `$ jj abandon 31a347e0` Now, we have achieved the exact state we desired: `$ jj log -p --git @ pvnrklkn jjfan@example.org 2025-02-28 21:39:29 468104c2 │ featureB │ diff --git a/file b/file │ index 2b455c4207..2a7e05a01a 100644 │ --- a/file │ +++ b/file │ @@ -1,1 +1,2 @@ │ Done with feature A │ +Working on feature B ○ lnvvtrzo jjfan@example.org 2025-02-28 21:00:51 b8004ab8 │ featureA │ diff --git a/file b/file │ new file mode 100644 │ index 0000000000..2b455c4207 │ --- /dev/null │ +++ b/file │ @@ -0,0 +1,1 @@ │ +Done with feature A ◆ zzzzzzzz root() 00000000 $ jj diff --from b80 --to @- # No output means these are identical $ jj diff --from 31a --to @ # No output means these are identical` ### How do I resume working on an existing change?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#how-do-i-resume-working-on-an-existing-change "Permanent link") There are two ways to resume working on an earlier change: `jj new` then `jj squash`, and `jj edit`. The first is generally recommended, but `jj edit` can be useful. When you use `jj edit`, the revision is directly amended with your new changes, making it difficult to tell what exactly you change. You should avoid using `jj edit` when the revision has a conflict, as you may accidentally break the plain-text annotations on your state without realizing. To start, use `jj new ` to create a change based on that earlier revision. Make your edits, then use `jj squash` to update the earlier revision with those edits. For when you would use git stashing, use `jj edit ` for expected behavior. Other workflows may prefer `jj edit` as well. ### Why are most merge commits marked as "(empty)"?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#why-are-most-merge-commits-marked-as-empty "Permanent link") Jujutsu, like Git, is a snapshot-based VCS. That means that each commit logically records the state of all current files in the repo. The changes in a commit are not recorded but are instead calculated when needed by comparing the commit's state to the parent commit's state. Jujutsu defines the changes in a commit to be relative to the auto-merged parents (if there's only one parent, then that merge is trivial - it's the parent commit's state). As a result, a merge commit that was a clean merge (no conflict resolution, no additional changes) is considered empty. Conversely, if the merge commit contains conflict resolutions or additional changes, then it will be considered non-empty. This definition of the changes in a commit is used throughout Jujutsu. It's used by `jj diff -r` and `jj log -p` to show the changes in a commit. It's used by `jj rebase` to rebase the changes in a commit. It's used in `jj log` to indicate which commits are empty. It's used in the `files()` revset function (and by `jj log `) to find commits that modify a certain path. And so on. How do I revert a merge commit? `jj revert -r ` does nothing[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#how-do-i-revert-a-merge-commit-jj-revert-r-merge-does-nothing "Permanent link") --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Jujutsu defines the changes in a merge commit (and non-merge commits) as the changes made compared to the auto-merged parents. That means that merge commits are often empty. As a result, `jj revert` or a merge commit often results in an empty commit. To revert the changes merged in from the second parent, instead use `jj restore --from ` . Example: `@ | C | \ B D |/ A` To revert the merge in `C`, create a new commit with `jj new C`, then `jj restore --from B`, and then describe the message with something like `jj desc -m "Revert the merge of D into B`. Now, commit `@` undoes the merge of `D` into `B`. If necessary, you can now rebase it elsewhere, e.g. `jj rebase -r @ -d main`. ### How do I deal with divergent changes ('??' after the [change ID](https://jj-vcs.github.io/jj/prerelease/glossary/#change-id) )?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#how-do-i-deal-with-divergent-changes-after-the-change-id "Permanent link") See: [Handling divergent commits](https://jj-vcs.github.io/jj/prerelease/guides/divergence/) . ### How do I deal with conflicted bookmarks ('??' after bookmark name)?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#how-do-i-deal-with-conflicted-bookmarks-after-bookmark-name "Permanent link") A [conflicted bookmark](https://jj-vcs.github.io/jj/prerelease/bookmarks/#conflicts) is a bookmark that refers to multiple different commits because jj couldn't fully resolve its desired position. Resolving conflicted bookmarks is usually done by setting the bookmark to the correct commit using `jj bookmark move --to `. Usually, the different commits associated with the conflicted bookmark should all appear in the log, but if they don't you can use `jj bookmark list`to show all the commits associated with it. ### I'm experiencing `jj` command issues in a Vite/Vitest project, how do I fix this?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#im-experiencing-jj-command-issues-in-a-vitevitest-project-how-do-i-fix-this "Permanent link") When using Vite or Vitest in a Jujutsu repository, you may experience: * Very slow vitest startup times * Timeout errors in `jj` terminal commands * Errors with 3rd party visual tools like `jjk` or `visual-jj` * Corrupted `working_copy.lock` files This happens because Vite watches the `.jj` directory where Jujutsu stores its internal state. This creates unnecessary overhead as Vite processes Jujutsu's frequent internal file changes, which can slow down both tools and occasionally cause file access conflicts. **Solution**: Configure Vite to ignore the `.jj` directory by adding it to the `server.watch.ignored` array inside your Vite configuration, for example: `// vite.config.js export default defineConfig({ // ... other config like plugins, test setup, etc. server: { watch: { ignored: [ "**/.jj/**", ] } }, })` Note: There was a [request](https://github.com/vitejs/vite/issues/20036) to include `.jj` in the default ignore list, but manual configuration remains the recommended approach. ### I want to write a tool which integrates with Jujutsu. Should I use the library or parse the CLI?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#i-want-to-write-a-tool-which-integrates-with-jujutsu-should-i-use-the-library-or-parse-the-cli "Permanent link") There are some trade-offs and there is no definitive answer yet. * Using `jj-lib` avoids parsing command output and makes error handling easier. * `jj-lib` is not a stable API, so you may have to make changes to your tool when the API changes. * The CLI is not stable either, so you may need to make your tool detect the different versions and call the right command. * Using the CLI means that your tool will work with custom-built `jj` binaries, like the one at Google (if you're using the library, you will not be able to detect custom backends and more). ### Why is Jujutsu a separate project? Why were the features not contributed to Git instead?[¶](https://jj-vcs.github.io/jj/prerelease/FAQ/#why-is-jujutsu-a-separate-project-why-were-the-features-not-contributed-to-git-instead "Permanent link") The project started as an experiment with the idea of representing the working copy by a regular commit. I (@martinvonz) considered how this feature would impact the Git CLI if it were added to Git. My conclusion was that it would effectively result in deprecating most existing Git commands and flags in favor of new commands and flags, especially considering I wanted to also support revsets. This seemed unlikely to be accepted by the Git project. --- # Community-built tools - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/community_tools/#community-built-tools-around-jujutsu) [](https://github.com/jj-vcs/jj/edit/main/docs/community_tools.md "Edit this page") Community-built tools around Jujutsu[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#community-built-tools-around-jujutsu "Permanent link") ======================================================================================================================================================= **Important:** Many of these are not complete yet, just like Jujutsu itself. But they already simplify many workflows and can improve your experience. Diffedit3[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#diffedit3 "Permanent link") ------------------------------------------------------------------------------------------------- Diffedit3 is a web-based alternate to Meld, as it no longer is packaged and available for all Distros. Its creator is also a frequent contributor. Find it [here](https://github.com/ilyagr/diffedit3) GG - GUI for JJ[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#gg-gui-for-jj "Permanent link") ----------------------------------------------------------------------------------------------------------- GG is a cross platform GUI for Jujutsu which makes all graph manipulating workflows quite easy. Take a look at its README.md as it quite descriptive. Find it [here](https://github.com/gulbanana/gg) . Hunk.nvim[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#hunknvim "Permanent link") ------------------------------------------------------------------------------------------------ Hunk.nvim is a Neovim based diff-editor for Jujutsu which can be used as an alternative to the default `:builtin` diff-editor. Find it [here](https://github.com/julienvincent/hunk.nvim) . JJ-FZF[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#jj-fzf "Permanent link") ------------------------------------------------------------------------------------------- Centered around the `jj log` graph view, jj-fzf provides previews of diffs, the evolution-log, browses the op log and offers a large number of key bindings for commonly used `jj` operations from rebase to undo, and helps with divergent commits. Find it [here](https://github.com/tim-janik/jj-fzf) . JJ TUI[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#jj-tui "Permanent link") ------------------------------------------------------------------------------------------- This is TUI for Jujutsu built in Ocaml, it is unopiniated and its creator is open to feedback. Find it [here](https://github.com/faldor20/jj_tui) . Jujutsu Kaizen[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#jujutsu-kaizen "Permanent link") ----------------------------------------------------------------------------------------------------------- Jujutsu Kaizen is a plugin for Visual Studio Code. The goal of this extension is to bring the great UX of Jujutsu into the VS Code UI. Its developers are currently focused on achieving parity for commonly used features of VS Code's built-in Git extension, such as the various operations possible via the Source Control view. Find it [here](https://github.com/keanemind/jjk) . LazyJJ[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#lazyjj "Permanent link") ------------------------------------------------------------------------------------------- lazyjj is a lazygit inspired TUI for Jujutsu. Find it [here](https://github.com/Cretezy/lazyjj) . Visual Jujutsu[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#visual-jujutsu "Permanent link") ----------------------------------------------------------------------------------------------------------- VJJ is a fzf (fuzzy finder) wrapper for Jujutsu, which is meant to be used interactively in the terminal. Find it [here](https://github.com/noahmayr/vjj) . VisualJJ[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#visualjj "Permanent link") ----------------------------------------------------------------------------------------------- VisualJJ is a plugin for Visual Studio Code which provides native integration for Jujutsu, not relying on Git colocation. Unlike other tools on this page, VisualJJ is not open-source. Find it [here](https://www.visualjj.com/) . Jujutsu UI[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#jujutsu-ui "Permanent link") --------------------------------------------------------------------------------------------------- jjui is a terminal user interface for working with Jujutsu version control system. Find it [here](https://github.com/idursun/jjui) . Selvejj[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#selvejj "Permanent link") --------------------------------------------------------------------------------------------- Selvejj is a JetBrains plugin for integrating Jujutsu as a first-class VCS within the IDE. Find it [here](https://selvejj.com/) . Finding other integrations[¶](https://jj-vcs.github.io/jj/prerelease/community_tools/#finding-other-integrations "Permanent link") ----------------------------------------------------------------------------------------------------------------------------------- You can find other community contributed tools and integrations in our [Wiki](https://github.com/jj-vcs/jj/wiki) . --- # Working copy - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/working-copy/#working-copy) [](https://github.com/jj-vcs/jj/edit/main/docs/working-copy.md "Edit this page") Working copy[¶](https://jj-vcs.github.io/jj/prerelease/working-copy/#working-copy "Permanent link") ==================================================================================================== Introduction[¶](https://jj-vcs.github.io/jj/prerelease/working-copy/#introduction "Permanent link") ---------------------------------------------------------------------------------------------------- The working copy is where the current working-copy commit's files are written so you can interact with them. It is also where files are read from in order to create new commits (though there are many other ways of creating new commits). Unlike most other VCSs, Jujutsu will automatically create commits from the working-copy contents when they have changed. Most `jj` commands you run will commit the working-copy changes if they have changed. The resulting revision will replace the previous working-copy revision. Also unlike most other VCSs, added files are implicitly tracked by default. That means that if you add a new file to the working copy, it will be automatically committed once you run e.g. `jj st`. Similarly, if you remove a file from the working copy, it will implicitly be untracked. The `snapshot.auto-track` config option controls which paths get automatically tracked when they're added to the working copy. See the [fileset documentation](https://jj-vcs.github.io/jj/prerelease/filesets/) for the syntax. Files with paths matching [ignore files](https://jj-vcs.github.io/jj/prerelease/working-copy/#ignored-files) are never tracked automatically. If you set `snapshot.auto-track` to a non-default value, untracked files can be tracked with `jj file track`. You can use `jj file untrack` to untrack a file while keeping it in the working copy. However, first [ignore](https://jj-vcs.github.io/jj/prerelease/working-copy/#ignored-files) them or remove them from the `snapshot.auto-track` patterns; otherwise they will be immediately tracked again. Conflicts[¶](https://jj-vcs.github.io/jj/prerelease/working-copy/#conflicts "Permanent link") ---------------------------------------------------------------------------------------------- When you check out a commit with conflicts, those conflicts need to be represented in the working copy somehow. However, the file system doesn't understand conflicts. Jujutsu's solution is to add conflict markers to conflicted files when it writes them to the working copy. It also keeps track of the (typically 3) different parts involved in the conflict. Whenever it scans the working copy thereafter, it parses the conflict markers and recreates the conflict state from them. You can resolve conflicts by replacing the conflict markers by the resolved text. You don't need to resolve all conflicts at once. You can even resolve part of a conflict by updating the different parts of the conflict marker. To resolve conflicts in a commit, use `jj new ` to create a working-copy commit on top. You would then have the same conflicts in the working-copy commit. Once you have resolved the conflicts, you can inspect the conflict resolutions with `jj diff`. Then run `jj squash` to move the conflict resolutions into the conflicted commit. Alternatively, you can edit the commit with conflicts directly in the working copy by using `jj edit `. The main disadvantage of that is that it's harder to inspect the conflict resolutions. With the `jj resolve` command, you can use an external merge tool to resolve conflicts that have 2 sides and a base. There is not yet a good way of resolving conflicts between directories, files, and symlinks ([https://github.com/jj-vcs/jj/issues/19](https://github.com/jj-vcs/jj/issues/19) ). You can use `jj restore` to choose one side of the conflict, but there's no way to even see where the involved parts came from. Ignored files[¶](https://jj-vcs.github.io/jj/prerelease/working-copy/#ignored-files "Permanent link") ------------------------------------------------------------------------------------------------------ You probably don't want build outputs and temporary files to be under version control. You can tell Jujutsu to not automatically track certain files by using `.gitignore` files (there's no such thing as `.jjignore` yet). See [https://git-scm.com/docs/gitignore](https://git-scm.com/docs/gitignore) for details about the format. `.gitignore` files are supported in any directory in the working copy, as well as in `$XDG_CONFIG_HOME/git/ignore` and `$GIT_DIR/info/exclude`. Ignored files are never tracked automatically (regardless of the value of `snapshot.auto-track`), but files that were already tracked will remain tracked even if they match ignore patterns. You can untrack such files with the `jj file untrack` command. Workspaces[¶](https://jj-vcs.github.io/jj/prerelease/working-copy/#workspaces "Permanent link") ------------------------------------------------------------------------------------------------ You can have multiple working copies backed by a single repo. Use `jj workspace add` to create a new working copy. The working copy will have a `.jj/` directory linked to the main repo. The working copy and the `.jj/` directory together is called a "workspace". Each workspace can have a different commit checked out. Having multiple workspaces can be useful for running long-running tests in a one while you continue developing in another, for example. If needed, `jj workspace root` prints the root path of the current workspace. When you're done using a workspace, use `jj workspace forget` to make the repo forget about it. The files can be deleted from disk separately (either before or after). Stale working copy[¶](https://jj-vcs.github.io/jj/prerelease/working-copy/#stale-working-copy "Permanent link") ---------------------------------------------------------------------------------------------------------------- Almost all commands go through three main steps: 1. Snapshot the working copy (which gets recorded as an operation) 2. Create new commits etc. "in memory" and record that as a new operation 3. Update the working copy to match the new operation, i.e. to the commit that the operation says that `@` should point to If step 3 doesn't happen for some reason, the working copy is considered "stale". We can detect that because the working copy (`.jj/working_copy/`) keeps track of which operation it was last updated to. When the working copy is stale, use `jj workspace update-stale` to update the files in the working copy. A common reason that step 3 doesn't happen for a working copy is that you rewrote the commit from another workspace. When you modify workspace A's working-copy commit from workspace B, workspace A's working copy will become stale. A working copy can also become stale because some error, such as `^C` prevented step 3 from completing. It's also possible that it was successfully updated in step 3 but the operation has then been lost (e.g. by `jj op abandon` or "spontaneously" by certain storage backends). If the operation has been lost, then `jj workspace update-stale` will create a recovery commit with the contents of the working copy but parented to the current operation's working-copy commit. --- # Conflicts - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/conflicts/#first-class-conflicts) [](https://github.com/jj-vcs/jj/edit/main/docs/conflicts.md "Edit this page") First-class conflicts[¶](https://jj-vcs.github.io/jj/prerelease/conflicts/#first-class-conflicts "Permanent link") =================================================================================================================== Introduction[¶](https://jj-vcs.github.io/jj/prerelease/conflicts/#introduction "Permanent link") ------------------------------------------------------------------------------------------------- Conflicts happen when Jujutsu can't figure out how to merge different changes made to the same file. For instance, this can happen if two people are working on the same file and make different changes to the same part of the file, and then their commits are merged together with `jj new` (or one is rebased onto the other with `jj rebase`). Unlike most other VCSs, Jujutsu can record conflicted states in commits. For example, if you rebase a commit and it results in a conflict, the conflict will be recorded in the rebased commit and the rebase operation will succeed. You can then resolve the conflict whenever you want. Conflicted states can be further rebased, merged, or backed out. Note that what's stored in the commit is a logical representation of the conflict, not conflict _markers_; rebasing a conflict doesn't result in a nested conflict markers (see [technical doc](https://jj-vcs.github.io/jj/prerelease/technical/conflicts/) for how this works). Advantages[¶](https://jj-vcs.github.io/jj/prerelease/conflicts/#advantages "Permanent link") --------------------------------------------------------------------------------------------- The deeper understanding of conflicts has many advantages: * Removes the need for things like `git rebase/merge/cherry-pick/etc --continue`. Instead, you get a single workflow for resolving conflicts: check out the conflicted commit, resolve conflicts, and amend. * Enables the "auto-rebase" feature, where descendants of rewritten commits automatically get rewritten. This feature mostly replaces Mercurial's [Changeset Evolution](https://www.mercurial-scm.org/wiki/ChangesetEvolution) . * Lets us define the change in a merge commit as being compared to the merged parents. That way, we can rebase merge commits correctly (unlike both Git and Mercurial). That includes conflict resolutions done in the merge commit, addressing a common use case for [git rerere](https://git-scm.com/docs/git-rerere) . Since the changes in a merge commit are displayed and rebased as expected, [evil merges](https://git-scm.com/docs/gitglossary/2.22.0#Documentation/gitglossary.txt-aiddefevilmergeaevilmerge) are arguably not as evil anymore. * Allows you to postpone conflict resolution until you're ready for it. You can easily keep all your work-in-progress commits rebased onto upstream's head if you like. * [Criss-cross merges](https://stackoverflow.com/questions/26370185/how-do-criss-cross-merges-arise-in-git) and [octopus merges](https://git-scm.com/docs/git-merge#Documentation/git-merge.txt-octopus) become trivial (implementation-wise); some cases that Git can't currently handle, or that would result in nested conflict markers, can be automatically resolved. * Enables collaborative conflict resolution. (This assumes that you can share the conflicts with others, which you probably shouldn't do if some people interact with your project using Git.) For information about how conflicts are handled in the working copy, see [here](https://jj-vcs.github.io/jj/prerelease/working-copy/#conflicts) . Conflict markers[¶](https://jj-vcs.github.io/jj/prerelease/conflicts/#conflict-markers "Permanent link") --------------------------------------------------------------------------------------------------------- Conflicts are "materialized" using _conflict markers_ in various contexts. For example, when you run `jj new` or `jj edit` on a commit with a conflict, it will be materialized in the working copy. Conflicts are also materialized when they are part of diff output (e.g. `jj show` on a commit that introduces or resolves a conflict). As an example, imagine that you have a file which contains the following text, all in lowercase: `apple grape orange` One person replaces the word "grape" with "grapefruit" in commit A, while another person changes every line to uppercase in commit B. If you merge the changes together with `jj new A B`, the resulting commit will have a conflict since Jujutsu can't figure out how to combine these changes. Therefore, Jujutsu will materialize the conflict in the working copy using conflict markers, which would look like this: `<<<<<<< Conflict 1 of 1 %%%%%%% Changes from base to side #1 apple -grape +grapefruit orange +++++++ Contents of side #2 APPLE GRAPE ORANGE >>>>>>> Conflict 1 of 1 ends` The markers `<<<<<<<` and `>>>>>>>` indicate the start and end of a conflict respectively. The marker `+++++++` indicates the start of a snapshot, while the marker `%%%%%%%` indicates the start of a diff to apply to the snapshot. Therefore, to resolve this conflict, you would apply the diff (changing "grape" to "grapefruit") to the snapshot (the side with every line in uppercase), editing the file to look like this: `APPLE GRAPEFRUIT ORANGE` In practice, conflicts are usually 2-sided, meaning that there's only 2 conflicting changes being merged together at a time, but Jujutsu supports conflicts with arbitrarily many sides, which can happen when merging 3 or more commits at once. In that case, you would see a single snapshot section and multiple diff sections. Compared to just showing the content of each side of the conflict, the main benefit of Jujutsu's style of conflict markers is that you don't need to spend time manually comparing the sides to spot the differences between them. This is especially beneficial for many-sided conflicts, since resolving them just requires applying each diff to the snapshot one-by-one. Alternative conflict marker styles[¶](https://jj-vcs.github.io/jj/prerelease/conflicts/#alternative-conflict-marker-styles "Permanent link") --------------------------------------------------------------------------------------------------------------------------------------------- If you prefer to just see the contents of each side of the conflict without the diff, Jujutsu also supports a "snapshot" style, which can be enabled by setting the `ui.conflict-marker-style` config option to "snapshot": `<<<<<<< Conflict 1 of 1 +++++++ Contents of side #1 apple grapefruit orange ------- Contents of base apple grape orange +++++++ Contents of side #2 APPLE GRAPE ORANGE >>>>>>> Conflict 1 of 1 ends` Some tools expect Git-style conflict markers, so Jujutsu also supports [Git's "diff3" style](https://git-scm.com/docs/git-merge#_how_conflicts_are_presented) conflict markers by setting the `ui.conflict-marker-style` config option to "git": `<<<<<<< Side #1 (Conflict 1 of 1) apple grapefruit orange ||||||| Base apple grape orange ======= APPLE GRAPE ORANGE >>>>>>> Side #2 (Conflict 1 of 1 ends)` This conflict marker style only supports 2-sided conflicts though, so it falls back to the similar "snapshot" conflict markers if there are more than 2 sides to the conflict. Long conflict markers[¶](https://jj-vcs.github.io/jj/prerelease/conflicts/#long-conflict-markers "Permanent link") ------------------------------------------------------------------------------------------------------------------- Some files may contain lines which could be confused for conflict markers. For instance, a line could start with `=======`, which looks like a Git-style conflict marker. To ensure that it's always unambiguous which lines are conflict markers and which are just part of the file contents, `jj` sometimes uses conflict markers which are longer than normal: `<<<<<<<<<<<<<<< Conflict 1 of 1 %%%%%%%%%%%%%%% Changes from base to side #1 -Heading +HEADING ======= +++++++++++++++ Contents of side #2 New Heading =========== >>>>>>>>>>>>>>> Conflict 1 of 1 ends` Conflicts with missing terminating newline[¶](https://jj-vcs.github.io/jj/prerelease/conflicts/#conflicts-with-missing-terminating-newline "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------------------------- When materializing conflicts, `jj` outputs them in a line-based format. This format is easiest to interpret for text files that consist of a series of lines, with each line terminated by a newline character (`\n`). This means that a text file should either be empty, or it should end with a newline character. While most text files follow this convention, some do not. When `jj` encounters a missing terminating newline character in a conflict, it will add a comment to the conflict markers to make the conflict easier to interpret. If you don't care about whether your file ends with a terminating newline character, you can generally ignore this comment and resolve the conflict normally. For instance, if a file originally contained `grape` with no terminating newline character, and one person changed `grape` to `grapefruit`, while another person added the missing newline character to make `grape\n`, the resulting conflict would look like this: `<<<<<<< Conflict 1 of 1 +++++++ Contents of side #1 (no terminating newline) grapefruit %%%%%%% Changes from base to side #2 (adds terminating newline) -grape +grape >>>>>>> Conflict 1 of 1 ends` Therefore, a resolution of this conflict could be `grapefruit\n`, with the terminating newline character added. --- # Bookmarks - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/bookmarks/#bookmarks) [](https://github.com/jj-vcs/jj/edit/main/docs/bookmarks.md "Edit this page") Bookmarks[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#bookmarks "Permanent link") =========================================================================================== Introduction[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#introduction "Permanent link") ------------------------------------------------------------------------------------------------- Bookmarks are named pointers to revisions (just like branches are in Git). You can move them without affecting the target revision's identity. Bookmarks automatically move when revisions are rewritten (e.g. by `jj rebase`). You can pass a bookmark's name to commands that want a revision as argument. For example, `jj new main` will create a new revision on top of the `main` bookmark. Use `jj bookmark list` to list bookmarks and `jj bookmark ` to create, move, or delete bookmarks. There is currently no concept of an active/current/checked-out bookmark. Mapping to Git branches[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#mapping-to-git-branches "Permanent link") ----------------------------------------------------------------------------------------------------------------------- Jujutsu maps its bookmarks to Git branches when interacting with Git repos. For example, `jj git push --bookmark foo` will push the state of the `foo` bookmark to the `foo` branch on the Git remote. Similarly, if you create a `bar` branch in the backing Git repo, then a subsequent `jj git import` will create a `bar` bookmark (reminder: that import happens automatically in [colocated repos](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#colocated-jujutsugit-repos) ). Remotes and tracked bookmarks[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#remotes-and-tracked-bookmarks "Permanent link") ----------------------------------------------------------------------------------------------------------------------------------- Jujutsu records the last seen position of a bookmark on each remote (just like Git's remote-tracking branches). This record is updated on every `jj git fetch` and `jj git push` of the bookmark. You can refer to the remembered remote bookmark positions with `@`, such as `jj new main@origin`. `jj` does not provide a way to manually edit these recorded positions. A remote bookmark can be associated with a local bookmark of the same name. This is called a **tracked remote bookmark** (which maps to a Git remote branch when using the Git backend). When you pull a tracked bookmark from a remote, any changes compared to the current record of the remote's state will be propagated to the corresponding local bookmark, which will be created if it doesn't exist already. Details: how `fetch` pulls bookmarks Let's say you run `jj git fetch --remote origin` and, during the fetch, `jj` determines that the remote's `main` bookmark has been moved so that its target is now ahead of the local record in `main@origin`. `jj` will then update `main@origin` to the new target. If `main@origin` is **tracked**, `jj` will also apply the change to the local bookmark `main`. If the local target has also been moved compared to `main@origin` (probably because you ran `jj bookmark set main`), then the two updates will be merged. If one is ahead of the other, then that target will become the new target. Otherwise, the local bookmark will become conflicted (see the ["Conflicts" section](https://jj-vcs.github.io/jj/prerelease/bookmarks/#conflicts) below for details). Most commands don't show the tracked remote bookmark if it has the same target as the local bookmark. The local bookmark (without `@`) is considered the bookmark's desired target. Consequently, if you want to update a bookmark on a remote, you first update the bookmark locally and then push the update to the remote. If a local bookmark also exists on some remote but points to a different target there, `jj log` will show the bookmark name with an asterisk suffix (e.g. `main*`). That is meant to remind you that you may want to push the bookmark to some remote. If you want to know the internals of bookmark tracking, consult the [Design Doc](https://jj-vcs.github.io/jj/prerelease/design/tracking-branches/) . ### Terminology summary[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#terminology-summary "Permanent link") * A **remote bookmark** is a bookmark ref on the remote. `jj` can find out its actual state only when it's actively communicating with the remote. However, `jj` does store the last-seen position of the remote bookmark; this is the commit `jj show @` would show. This notion is completely analogous to Git's "remote-tracking branches". * A **tracked (remote) bookmark** is defined above. You can make a remote bookmark tracked with the [`jj bookmark track` command](https://jj-vcs.github.io/jj/prerelease/bookmarks/#manually-tracking-a-bookmark) , for example. * A **tracking (local) bookmark** is the local bookmark that `jj` tries to keep in sync with the tracked remote bookmark. For example, after `jj bookmark track mybookmark@origin`, there will be a local bookmark `mybookmark` that's tracking the remote `mybookmark@origin` bookmark. A local bookmark can track a bookmark of the same name on 0 or more remotes. The notion of tracked bookmarks serves a similar function to the Git notion of an "upstream branch". Unlike Git, a single local bookmark can be tracking remote bookmarks on multiple remotes, and the names of the local and remote bookmarks must match. ### Manually tracking a bookmark[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#manually-tracking-a-bookmark "Permanent link") To track a bookmark permanently use `jj bookmark track @`. It will now be imported as a local bookmark until you untrack it or it is deleted on the remote. Example: `$ # List all available bookmarks, as we want our colleague's bookmark. $ jj bookmark list --all $ # Find the bookmark. $ # [...] $ # Actually track the bookmark. $ jj bookmark track @ # Example: jj bookmark track my-feature@origin $ # From this point on, will be imported when fetching from . $ jj git fetch --remote $ # A local bookmark should have been created or updated while fetching. $ jj new # Do some local testing, etc.` ### Untracking a bookmark[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#untracking-a-bookmark "Permanent link") To stop following a remote bookmark, you can `jj bookmark untrack` it. After that, subsequent fetches of that remote will no longer move the local bookmark to match the position of the remote bookmark. Example: `$ # List all local and remote bookmarks. $ jj bookmark list --all $ # Find the bookmark we no longer want to track. $ # [...] # # Actually untrack it. $ jj bookmark untrack @ # Example: jj bookmark untrack stuff@origin $ # From this point on, this remote bookmark won't be imported anymore. $ # The local bookmark (e.g. stuff) is unaffected. It may or may not still $ # be tracking bookmarks on other remotes (e.g. stuff@upstream).` ### Listing tracked bookmarks[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#listing-tracked-bookmarks "Permanent link") To list tracked bookmarks, you can `jj bookmark list --tracked` or `jj bookmark list -t`. This command omits local Git-tracking bookmarks by default. You can see if a specific bookmark is tracked with `jj bookmark list --tracked `. ### Automatic tracking of bookmarks & `git.auto-local-bookmark` option[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#automatic-tracking-of-bookmarks-gitauto-local-bookmark-option "Permanent link") There are two situations where `jj` tracks bookmarks automatically. `jj git clone` automatically sets up the default remote bookmark (e.g. `main@origin`) as tracked. When you push a local bookmark, the newly created bookmark on the remote is marked as tracked. By default, every other remote bookmark is marked as "not tracked" when it's fetched. If desired, you need to manually `jj bookmark track` them. This works well for repositories where multiple people work on a large number of bookmarks. The default can be changed by setting the config `git.auto-local-bookmark = true`. Then, `jj git fetch` tracks every _newly fetched_ bookmark with a local bookmark. Branches that already existed before the `jj git fetch` are not affected. This is similar to Mercurial, which fetches all its bookmarks (equivalent to Git's branches) by default. Bookmark updates[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#bookmark-updates "Permanent link") --------------------------------------------------------------------------------------------------------- Currently Jujutsu automatically updates local bookmarks when these conditions are met: * When a commit has been rewritten (e.g, when you rebase) bookmarks and the working-copy will move along with it. * When a commit has been abandoned, all associated bookmarks will be deleted. You could describe the updates as following along the change-id of the current bookmark commit, even if it isn't entirely accurate. Pushing bookmarks: Safety checks[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#pushing-bookmarks-safety-checks "Permanent link") ---------------------------------------------------------------------------------------------------------------------------------------- Before `jj git push` actually moves, creates, or deletes a remote bookmark, it makes several safety checks. 1. `jj` will contact the remote and check that the actual state of the remote bookmark matches `jj`'s record of its last known position. If there is a conflict, `jj` will refuse to push the bookmark. In this case, you need to run `jj git fetch --remote ` and resolve the resulting bookmark conflict. Then, you can try `jj git push` again. If you are familiar with Git, this makes `jj git push` similar to `git push --force-with-lease`. There are a few cases where `jj git push` will succeed even though the remote bookmark is in an unexpected location. These are the cases where `jj git fetch` would not create a bookmark conflict and would not move the local bookmark, e.g. if the unexpected location is identical to the local position of the bookmark. 2. The local bookmark must not be [conflicted](https://jj-vcs.github.io/jj/prerelease/bookmarks/#conflicts) . If it is, you would need to use `jj bookmark move`, for example, to resolve the conflict. This makes `jj git push` safe even if `jj git fetch` is performed on a timer in the background (this situation is a known issue[1](https://jj-vcs.github.io/jj/prerelease/bookmarks/#fn:known-issue) with some forms of `git push --force-with-lease`). If the bookmark moves on a remote in a problematic way, `jj git fetch` will create a conflict. This should ensure that the user becomes aware of the conflict before they can `jj git push` and override the bookmark on the remote. 3. If the remote bookmark already exists on the remote, it must be [tracked](https://jj-vcs.github.io/jj/prerelease/bookmarks/#remotes-and-tracked-bookmarks) . If the bookmark does not already exist on the remote, there is no problem; `jj git push --allow-new` will create the remote bookmark and mark it as tracked. Conflicts[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#conflicts "Permanent link") ------------------------------------------------------------------------------------------- Bookmarks can end up in a conflicted state. When that happens, `jj status` will include information about the conflicted bookmarks (and instructions for how to mitigate it). `jj bookmark list` will have details. `jj log` will show the bookmark name with a double question mark suffix (e.g. `main??`) on each of the conflicted bookmark's potential target revisions. Using the bookmark name to look up a revision will resolve to all potential targets. That means that `jj new main` will error out, complaining that the revset resolved to multiple revisions. Both local bookmarks (e.g. `main`) and the remote bookmark (e.g. `main@origin`) can have conflicts. Both can end up in that state if concurrent operations were run in the repo. The local bookmark more typically becomes conflicted because it was updated both locally and on a remote. To resolve a conflicted state in a local bookmark (e.g. `main`), you can move the bookmark to the desired target with `jj bookmark move`. You may want to first either merge the conflicted targets with `jj new` (e.g. `jj new 'all:main'`), or you may want to rebase one side on top of the other with `jj rebase`. To resolve a conflicted state in a remote bookmark (e.g. `main@origin`), simply pull from the remote (e.g. `jj git fetch`). The conflict resolution will also propagate to the local bookmark (which was presumably also conflicted). Ease of use[¶](https://jj-vcs.github.io/jj/prerelease/bookmarks/#ease-of-use "Permanent link") ----------------------------------------------------------------------------------------------- The use of bookmarks is frequent in some workflows, for example, when interacting with Git repositories containing branches. To this end, one-letter shortcuts have been implemented, both for the `jj bookmark` command itself through an alias (as `jj b`), and for its subcommands. For example, `jj bookmark create BOOKMARK-NAME -r@` can be abbreviated as `jj b c BOOKMARK-NAME -r@`. * * * 1. See "A general note on safety" in [https://git-scm.com/docs/git-push#Documentation/git-push.txt---no-force-with-lease](https://git-scm.com/docs/git-push#Documentation/git-push.txt---no-force-with-lease)  [↩](https://jj-vcs.github.io/jj/prerelease/bookmarks/#fnref:known-issue "Jump back to footnote 1 in the text") --- # Operation log - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/operation-log/#operation-log) [](https://github.com/jj-vcs/jj/edit/main/docs/operation-log.md "Edit this page") Operation log[¶](https://jj-vcs.github.io/jj/prerelease/operation-log/#operation-log "Permanent link") ======================================================================================================= Introduction[¶](https://jj-vcs.github.io/jj/prerelease/operation-log/#introduction "Permanent link") ----------------------------------------------------------------------------------------------------- Jujutsu records each operation that modifies the repo in the "operation log". You can see the log with `jj op log`. Each operation object contains a snapshot of how the repo looked at the end of the operation. We call this snapshot a "view" object. The view contains information about where each bookmark, tag, and Git ref (in Git-backed repos) pointed, as well as the set of heads in the repo, and the current working-copy commit in each workspace. The operation object also (in addition to the view) contains pointers to the operation(s) immediately before it, as well as metadata about the operation, such as timestamps, username, hostname, description. The operation log allows you to undo operations one-by-one (`jj undo`) or even revert a specific one which isn't the most recent operation (`jj op revert`). It also lets you restore the entire repo to the way it looked at an earlier point (`jj op restore`). When referring to operations, you can use `@` to represent the current operation. The following operators are supported: * `x-`: Parents of `x` (e.g. `@-`) * `x+`: Children of `x` Divergent operations[¶](https://jj-vcs.github.io/jj/prerelease/operation-log/#divergent-operations "Permanent link") --------------------------------------------------------------------------------------------------------------------- One benefit of the operation log (and the reason for its creation) is that it allows lock-free concurrency -- you can run concurrent `jj` commands without corrupting the repo, even if you run the commands on different machines that access the repo via a distributed file system (as long as the file system guarantees that a write is only visible once previous writes are visible). When you run a `jj` command, it will start by loading the repo at the latest operation. It will not see any changes written by concurrent commands. If there are conflicts, you will be informed of them by subsequent `jj st` and/or `jj log` commands. As an example, let's say you had started editing the description of a change and then also update the contents of the change (maybe because you had forgotten the editor). When you eventually close your editor, the command will succeed and e.g. `jj log` will indicate that the change has diverged. Loading an old version of the repo[¶](https://jj-vcs.github.io/jj/prerelease/operation-log/#loading-an-old-version-of-the-repo "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------------- The top-level `--at-operation/--at-op` option allows you to load the repo at a specific operation. This can be useful for understanding how your repo got into the current state. It can be even more useful for understanding why someone else's repo got into its current state. When you use `--at-op`, the automatic snapshotting of the working copy will not take place. When referring to a revision with the `@` symbol (as many commands do by default), that will resolve to the working-copy commit recorded in the operation's view (which is actually how it always works -- it's just the snapshotting that's skipped with `--at-op`). As a top-level option, `--at-op` can be passed to any command. However, you will typically only want to run read-only commands. For example, `jj log`, `jj st`, and `jj diff` all make sense. It's still possible to run e.g. `jj --at-op= describe`. That's equivalent to having started `jj describe` back when the specified operation was the most recent operation and then let it run until now (which can be done for that particular command by not closing the editor). There's practically no good reason to do that other than to simulate concurrent commands. --- # Glossary - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/glossary/#glossary) [](https://github.com/jj-vcs/jj/edit/main/docs/glossary.md "Edit this page") Glossary[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#glossary "Permanent link") ======================================================================================== Anonymous branch[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#anonymous-branch "Permanent link") -------------------------------------------------------------------------------------------------------- An anonymous branch is a chain of commits that doesn't necessarily have any [bookmarks](https://jj-vcs.github.io/jj/prerelease/glossary/#bookmark) pointing to it or to any of its descendants. Unlike Git, Jujutsu keeps commits on anonymous branches around until they are explicitly abandoned. Visible anonymous branches are tracked by the [view](https://jj-vcs.github.io/jj/prerelease/glossary/#view) , which stores a list of [heads](https://jj-vcs.github.io/jj/prerelease/glossary/#head) of such branches. Backend[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#backend "Permanent link") -------------------------------------------------------------------------------------- A backend is an implementation of the storage layer. There is currently only one production-ready builtin commit backend: the Git backend. The Git backend stores commits in a Git repository. There are several backends used for testing. Google also has its own cloud-based backend. There are also pluggable backends for storing other information than commits, such as the "operation store backend" for storing [the operation log](https://jj-vcs.github.io/jj/prerelease/glossary/#operation-log) . Bookmark[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#bookmark "Permanent link") ---------------------------------------------------------------------------------------- A bookmark is a named pointer to a [commit](https://jj-vcs.github.io/jj/prerelease/glossary/#commit) . They are similar to Git's [branches](https://jj-vcs.github.io/jj/prerelease/glossary/#branch) and even more similar to [Mercurial's bookmarks](https://wiki.mercurial-scm.org/Bookmarks) . See [here](https://jj-vcs.github.io/jj/prerelease/bookmarks/) for details. Unlike in Git, there is no concept of a "current bookmark"; bookmarks _do not_ move when you create a new commit. Bookmarks _do_ automatically follow the commit if it gets [rewritten](https://jj-vcs.github.io/jj/prerelease/glossary/#rewrite) . Branch[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#branch "Permanent link") ------------------------------------------------------------------------------------ In the context of `jj`, the work "branch" usually refers to an [anonymous branch](https://jj-vcs.github.io/jj/prerelease/glossary/#anonymous-branch) or, less formally, a branch of the commit "tree" (which is itself an informal way to refer to the commit graph, parts of which might resemble a tree even when it's not mathematically a tree). We also sometimes discuss Git's branches and branches on Git remotes. Locally, these correspond to [bookmarks](https://jj-vcs.github.io/jj/prerelease/glossary/#bookmark) . In a colocated repository, each local Git branch corresponds to a `jj` bookmark. Change[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#change "Permanent link") ------------------------------------------------------------------------------------ A change is a commit as it [evolves over time](https://jj-vcs.github.io/jj/prerelease/glossary/#rewrite) . Changes themselves don't exist as an object in the data model; only the change ID does. The change ID is a property of a commit. Change ID[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#change-id "Permanent link") ------------------------------------------------------------------------------------------ A change ID is a unique identifier for a [change](https://jj-vcs.github.io/jj/prerelease/glossary/#change) . They are typically 16 bytes long and are often randomly generated. By default, `jj log` presents them as a sequence of 12 letters in the k-z range, at the beginning of a line. These are actually hexadecimal numbers that use "digits" z-k instead of 0-9a-f. Commit[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#commit "Permanent link") ------------------------------------------------------------------------------------ A snapshot of the files in the repository at a given point in time (technically a [tree object](https://jj-vcs.github.io/jj/prerelease/glossary/#tree) ), together with some metadata. The metadata includes the author, the date, and pointers to the commit's parents. Through the pointers to the parents, the commits form a [Directed Acyclic Graph (DAG)](https://en.wikipedia.org/wiki/Directed_acyclic_graph) . Note that even though commits are stored as snapshots, they are often treated as differences between snapshots, namely compared to their parent's snapshot. If they have more than one parent, then the difference is computed against the result of merging the parents. For example, `jj diff` will show the differences introduced by a commit compared to its parent(s), and `jj rebase` will apply those changes onto another base commit. The word "revision" is used as a synonym for "commit". Commit ID[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#commit-id "Permanent link") ------------------------------------------------------------------------------------------ A commit ID is a unique identifier for a [commit](https://jj-vcs.github.io/jj/prerelease/glossary/#commit) . They are 20 bytes long when using the Git backend. They are presented in regular hexadecimal format at the end of the line in `jj log`, using 12 hexadecimal digits by default. When using the Git backend, the commit ID is the Git commit ID. Colocated repos[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#colocated-repos "Permanent link") ------------------------------------------------------------------------------------------------------ When using the Git [backend](https://jj-vcs.github.io/jj/prerelease/glossary/#backend) and the backing Git repository's `.git/` directory is a sibling of `.jj/`, we call the repository colocated. Most tools designed for Git can be easily used on such repositories. `jj` and `git` commands can be used interchangeably. See [here](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#colocated-jujutsugit-repos) for details. Conflict[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#conflict "Permanent link") ---------------------------------------------------------------------------------------- Conflicts can occur in many places. The most common type is conflicts in files. Those are the conflicts that users coming from other VCSs are usually familiar with. You can see them in `jj status` and in `jj log` (the red "conflict" label at the end of the line). See [here](https://jj-vcs.github.io/jj/prerelease/conflicts/) for details. Conflicts can also occur in [bookmarks](https://jj-vcs.github.io/jj/prerelease/glossary/#bookmark) . For example, if you moved a bookmark locally, and it was also moved on the remote, then the bookmark will be in a conflicted state after you pull from the remote. See [here](https://jj-vcs.github.io/jj/prerelease/bookmarks/#conflicts) for details. Similar to a bookmark conflict, when a [change](https://jj-vcs.github.io/jj/prerelease/glossary/#change) is rewritten locally and remotely, for example, then the change will be in a conflicted state. We call that a [divergent change](https://jj-vcs.github.io/jj/prerelease/glossary/#divergent-change) . Divergent change[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#divergent-change "Permanent link") -------------------------------------------------------------------------------------------------------- A divergent change is a [change](https://jj-vcs.github.io/jj/prerelease/glossary/#change) that has more than one [visible commit](https://jj-vcs.github.io/jj/prerelease/glossary/#visible-commits) . Head[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#head "Permanent link") -------------------------------------------------------------------------------- A head is a commit with no descendants. The context in which it has no descendants varies. For example, the `heads(X)` [revset function](https://jj-vcs.github.io/jj/prerelease/revsets/#functions) returns commits that have no descendants within the set `X` itself. The [view](https://jj-vcs.github.io/jj/prerelease/glossary/#view) records which anonymous heads (heads without a bookmark pointing to them) are visible at a given [operation](https://jj-vcs.github.io/jj/prerelease/glossary/#operation) . Note that this is quite different from Git's [HEAD](https://git-scm.com/book/en/v2/Git-Internals-Git-References#ref_the_ref) . Hidden commits, abandoned commits[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#hidden-commits-abandoned-commits "Permanent link") ----------------------------------------------------------------------------------------------------------------------------------------- See [visible commits](https://jj-vcs.github.io/jj/prerelease/glossary/#visible-commits) . Operation[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#operation "Permanent link") ------------------------------------------------------------------------------------------ A snapshot of the [visible commits](https://jj-vcs.github.io/jj/prerelease/glossary/#visible-commits) and [bookmarks](https://jj-vcs.github.io/jj/prerelease/glossary/#bookmark) at a given point in time (technically a [view object](https://jj-vcs.github.io/jj/prerelease/glossary/#view) ), together with some metadata. The metadata includes the username, hostname, timestamps, and pointers to the operation's parents. Operation log[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#operation-log "Permanent link") -------------------------------------------------------------------------------------------------- The operation log is the [DAG](https://en.wikipedia.org/wiki/Directed_acyclic_graph) formed by [operation](https://jj-vcs.github.io/jj/prerelease/glossary/#operation) objects, much in the same way that commits form a DAG, which is sometimes called the "commit history". When operations happen in sequence, they form a single line in the graph. Operations that happen concurrently from jj's perspective result in forks and merges in the DAG. Repository[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#repository "Permanent link") -------------------------------------------------------------------------------------------- Basically everything under `.jj/`, i.e. the full set of [operations](https://jj-vcs.github.io/jj/prerelease/glossary/#operation) and [commits](https://jj-vcs.github.io/jj/prerelease/glossary/#commit) . Remote[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#remote "Permanent link") ------------------------------------------------------------------------------------ A remote is a reference to a copy of your repository. The most common case is that the remote is hosted on the internet or another network, but local remotes are also possible. Remotes are useful when working on a project with multiple collaborators. As Jujutsu is compatible with Git you can use all popular providers that are also available for Git. For example [GitHub](https://github.com/) , [GitLab](https://gitlab.com/) or [Codeberg](https://codeberg.org/) . Revision[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#revision "Permanent link") ---------------------------------------------------------------------------------------- A synonym for [Commit](https://jj-vcs.github.io/jj/prerelease/glossary/#commit) . Revset[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#revset "Permanent link") ------------------------------------------------------------------------------------ Jujutsu supports a functional language for selecting a set of revisions. Expressions in this language are called "revsets". See [here](https://jj-vcs.github.io/jj/prerelease/revsets/) for details. We also often use the term "revset" for the set of revisions selected by a revset. Rewrite[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#rewrite "Permanent link") -------------------------------------------------------------------------------------- To "rewrite" a commit means to create a new version of that commit with different contents, metadata (including parent pointers), or both. Rewriting a commit results in a new commit, and thus a new [commit ID](https://jj-vcs.github.io/jj/prerelease/glossary/#commit-id) , but the [change ID](https://jj-vcs.github.io/jj/prerelease/glossary/#change-id) generally remains the same. Some examples of rewriting a commit would be changing its description or rebasing it. Modifying the working copy rewrites the working copy commit. Root commit[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#root-commit "Permanent link") ---------------------------------------------------------------------------------------------- The root commit is a virtual commit at the root of every repository. It has a commit ID consisting of all '0's (`00000000...`) and a change ID consisting of all 'z's (`zzzzzzzz...`). It can be referred to in [revsets](https://jj-vcs.github.io/jj/prerelease/glossary/#revset) by the function `root()`. Note that our definition of "root commit" is different from Git's; Git's "root commits" are the first commit(s) in the repository, i.e. the commits `jj log -r 'root()+'` will show. Tree[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#tree "Permanent link") -------------------------------------------------------------------------------- A tree object represents a snapshot of a directory in the repository. Tree objects are defined recursively; each tree object only has the files and directories contained directly in the directory it represents. Tracked bookmarks and tracking bookmarks[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#tracked-bookmarks-and-tracking-bookmarks "Permanent link") -------------------------------------------------------------------------------------------------------------------------------------------------------- A remote bookmark can be made "tracked" with the `jj bookmark track` command. This results in a "tracking" local bookmark that tracks the remote bookmark. See [the bookmarks documentation](https://jj-vcs.github.io/jj/prerelease/bookmarks/#terminology-summary) for a more detailed definition of these terms. Visible commits[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#visible-commits "Permanent link") ------------------------------------------------------------------------------------------------------ Visible commits are the commits you see in `jj log -r 'all()'`. They are the commits that are reachable from an anonymous head in the [view](https://jj-vcs.github.io/jj/prerelease/glossary/#view) . Ancestors of a visible commit are implicitly visible. Intuitively, visible commits are the "latest versions" of a revision with a given [change id](https://jj-vcs.github.io/jj/prerelease/glossary/#change-id) . A commit that's abandoned or [rewritten](https://jj-vcs.github.io/jj/prerelease/glossary/#rewrite) stops being visible and is labeled as "hidden". Such commits are no longer accessible using a change id, but they are still accessible by their [commit id](https://jj-vcs.github.io/jj/prerelease/glossary/#commit-id) . View[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#view "Permanent link") -------------------------------------------------------------------------------- A view is a snapshot of bookmarks and their targets, anonymous heads, and working-copy commits. The anonymous heads define which commits are [visible](https://jj-vcs.github.io/jj/prerelease/glossary/#visible-commits) . A view object is similar to a [tree](https://jj-vcs.github.io/jj/prerelease/glossary/#tree) object in that it represents a snapshot without history, and an [operation](https://jj-vcs.github.io/jj/prerelease/glossary/#operation) object is similar to a [commit](https://jj-vcs.github.io/jj/prerelease/glossary/#commit) object in that it adds metadata and history. Workspace[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#workspace "Permanent link") ------------------------------------------------------------------------------------------ A workspace is a [working copy](https://jj-vcs.github.io/jj/prerelease/glossary/#working-copy) and an associated [repository](https://jj-vcs.github.io/jj/prerelease/glossary/#repository) . There can be multiple workspaces for a single repository. Each workspace has a `.jj/` directory, but the [commits](https://jj-vcs.github.io/jj/prerelease/glossary/#commit) and [operations](https://jj-vcs.github.io/jj/prerelease/glossary/#operation) will be stored in the initial workspace; the other workspaces will have pointers to the initial workspace. See [here](https://jj-vcs.github.io/jj/prerelease/working-copy/#workspaces) for details. This is what Git calls a "worktree". Working copy[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#working-copy "Permanent link") ------------------------------------------------------------------------------------------------ The working copy contains the files you're currently working on. It is automatically snapshot at the beginning of almost every `jj` command, thus creating a new [working-copy commit](https://jj-vcs.github.io/jj/prerelease/glossary/#working-copy-commit) if any changes had been made in the working copy. Conversely, the working copy is automatically updated to the state of the working-copy commit at the end of almost every `jj` command. See [here](https://jj-vcs.github.io/jj/prerelease/working-copy/) for details. This is what Git calls a "working tree". Working-copy commit[¶](https://jj-vcs.github.io/jj/prerelease/glossary/#working-copy-commit "Permanent link") -------------------------------------------------------------------------------------------------------------- A commit that corresponds to the current state of the working copy. There is one working-copy commit per [workspace](https://jj-vcs.github.io/jj/prerelease/glossary/#workspace) . The current working-copy commits are tracked in the [operation log](https://jj-vcs.github.io/jj/prerelease/glossary/#operation-log) . --- # Divergent changes - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/guides/divergence/#handling-divergent-changes) [](https://github.com/jj-vcs/jj/edit/main/docs/guides/divergence.md "Edit this page") Handling divergent changes[¶](https://jj-vcs.github.io/jj/prerelease/guides/divergence/#handling-divergent-changes "Permanent link") ===================================================================================================================================== What are divergent changes?[¶](https://jj-vcs.github.io/jj/prerelease/guides/divergence/#what-are-divergent-changes "Permanent link") -------------------------------------------------------------------------------------------------------------------------------------- A [divergent change](https://jj-vcs.github.io/jj/prerelease/glossary/#divergent-change) occurs when multiple [visible commits](https://jj-vcs.github.io/jj/prerelease/glossary/#visible-commits) have the same change ID. Normally, when commits are rewritten, the original version (the "predecessor") becomes hidden and the new commit (the "successor") is visible. Thus, only one commit with a given change ID is visible at a time. But, a hidden commit can become visible again. This can happen if: * A visible descendant is added locally. For example, `jj new REV` will make `REV` visible even if it was hidden before. * A visible descendant is fetched from a remote. If the hidden commit was pushed to a remote, others may base new commits off of them. When their new commits are fetched, their visibility makes the hidden commit visible again. * It is made the working copy. `jj edit REV` will make `REV` and all its ancestors visible if it wasn't already. * Some other operations make hidden commits visible. For example, adding a bookmark to a hidden commit makes it visible with the assumption that you are now working with that commit again. Divergent changes also occur if two different users or processes amend the same change, creating two visible successors. This can happen when: * Another author modifies commits in a branch that you have also modified locally. * You perform operations on the same change from different workspaces of the same repository. * Two programs modify the repository at the same time. For example, you run `jj describe` and, while writing your commit description, an IDE integration fetches and rebases the branch you're working on. How do I resolve divergent changes?[¶](https://jj-vcs.github.io/jj/prerelease/guides/divergence/#how-do-i-resolve-divergent-changes "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------------------ When you encounter divergent changes, you have several strategies to choose from. The best approach depends on whether you want to keep the content from one commit, both commits, or merge them together. Note that revsets must refer to the divergent commit using its commit ID since the change ID is ambiguous. ### Strategy 1: Abandon one of the commits[¶](https://jj-vcs.github.io/jj/prerelease/guides/divergence/#strategy-1-abandon-one-of-the-commits "Permanent link") If one of the divergent commits is clearly obsolete or incorrect, simply abandon it: `# Abandon the unwanted commit using its commit ID jj abandon # You can abandon several at once with: # jj abandon abc def 123 # jj abandon abc::` This is the simplest solution when you know which version to keep. ### Strategy 2: Duplicate and abandon[¶](https://jj-vcs.github.io/jj/prerelease/guides/divergence/#strategy-2-duplicate-and-abandon "Permanent link") If you want to keep both versions as separate changes with different change IDs, you can duplicate one of the commits to generate a new change ID, then abandon the original: `# Duplicate one of the commits to create a new change ID jj duplicate # Abandon the original commit jj abandon ` This preserves both versions of the content while resolving the divergence. ### Strategy 3: Squash the commits together[¶](https://jj-vcs.github.io/jj/prerelease/guides/divergence/#strategy-3-squash-the-commits-together "Permanent link") When you want to combine the content from both divergent commits: `# Squash one commit into the other jj squash --from --into ` This combines the changes from both commits into a single commit. The source commit will be abandoned. ### Strategy 4: Ignore the divergence[¶](https://jj-vcs.github.io/jj/prerelease/guides/divergence/#strategy-4-ignore-the-divergence "Permanent link") Divergence isn't an error. If the divergence doesn't cause immediate problems, you can leave it as-is. If both commits are part of immutable history, this may be your only option. However, it can be inconvenient since you cannot refer to divergent changes unambiguously using their change ID. --- # Multiple remotes - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/guides/multiple-remotes/#multiple-remotes) [](https://github.com/jj-vcs/jj/edit/main/docs/guides/multiple-remotes.md "Edit this page") Multiple remotes[¶](https://jj-vcs.github.io/jj/prerelease/guides/multiple-remotes/#multiple-remotes "Permanent link") ======================================================================================================================= When using multiple [remote repositories](https://jj-vcs.github.io/jj/prerelease/glossary/#remote) , how you configure them in Jujutsu depends on your workflow and the role each remote plays. The setup varies based on whether you are contributing to an upstream project, or integrating changes from another repository. Nomenclature[¶](https://jj-vcs.github.io/jj/prerelease/guides/multiple-remotes/#nomenclature "Permanent link") --------------------------------------------------------------------------------------------------------------- A remote named `origin` is one you have write-access to and is usually where you push changes. A remote named `upstream` is the more well-known repository. You may not be able to push to this repository. The trunk in each repository is assumed to be `main`, so the remote bookmarks are `main@origin` and `main@upstream`. Contributing upstream with a GitHub-style fork[¶](https://jj-vcs.github.io/jj/prerelease/guides/multiple-remotes/#contributing-upstream-with-a-github-style-fork "Permanent link") ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- This is a GitHub-style fork used to contribute to the upstream repository. `upstream` is the canonical upstream remote, and `origin` is where you push contributions, most likely so you can open pull requests. Actions you might take: * Fetch from `upstream` to get the latest changes. * Push `main` to `origin` to keep it up-to-date. * Push `my-feature` to `origin`, then open a pull request to `upstream`. To support this scenario, you should: * Track `main@upstream` so your local `main` branch is updated whenever you fetch from `upstream`. * Track `main@origin` so when you `jj git push`, your fork's `main` branch is updated. * Set `main@upstream` as the `trunk()` revset alias so it is immutable. `# Fetch from both remotes by default $ jj config set --repo git.fetch '["upstream", "origin"]' # Push only to the fork by default $ jj config set --repo git.push origin # Track both remote bookmarks $ jj bookmark track main@upstream main@origin # The upstream repository defines the trunk $ jj config set --repo 'revset-aliases."trunk()"' main@upstream` Maintaining an independent repository that integrates changes from upstream[¶](https://jj-vcs.github.io/jj/prerelease/guides/multiple-remotes/#maintaining-an-independent-repository-that-integrates-changes-from-upstream "Permanent link") --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- This is a repository that was originally cloned from upstream, but now contains changes in its `main` branch that are not upstream and might never be contributed back. * `origin` is the repository you are working in. * `upstream` is the repository you periodically integrate changes from. Actions you might take: * Fetch from `origin` to get the latest changes. * Push bookmarks to `origin`. * Merge pull requests into `main@origin`. * Periodically fetch from `main@upstream` and merge, rebase, or duplicate its changes into `main@origin`. To support this scenario, you should: * Track only `main@origin` so your local `main` branch is updated whenever you fetch from `origin`, and so you can push to it if necessary. * _Do not_ track `main@upstream`. * Set `main@origin` as the `trunk()` revset alias so it is immutable. `# Fetch from origin or both remotes by default $ jj config set --repo git.fetch '["origin"]' # or: jj config set --repo git.fetch '["upstream", "origin"]' # Push only to origin by default $ jj config set --repo git.push origin # Track only the origin bookmark $ jj bookmark track main@origin $ jj bookmark untrack main@upstream # The origin repository defines the trunk $ jj config set --repo 'revset-aliases."trunk()"' main@origin` Other workflows[¶](https://jj-vcs.github.io/jj/prerelease/guides/multiple-remotes/#other-workflows "Permanent link") --------------------------------------------------------------------------------------------------------------------- Other workflows may be supported. Some general guidance for this: * Set `trunk()` to be the remote bookmark you usually rebase upon. If you always rebase against upstream, set it to `main@upstream`. * Tracking a remote bookmark `main@origin` means it and `main` represent the same branch. When one moves, the other should move with it. If you want them to _automatically_ move together, you should track the remote bookmark. If not, do not track it. If you have a workflow that is not well-supported, discussion is welcome in [Discord](https://discord.gg/dkmfj3aGQN) . There is also an [open discussion](https://github.com/jj-vcs/jj/issues/7072) for enhancing how bookmark tracking works. --- # Fileset language - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/filesets/#filesets) [](https://github.com/jj-vcs/jj/edit/main/docs/filesets.md "Edit this page") Filesets[¶](https://jj-vcs.github.io/jj/prerelease/filesets/#filesets "Permanent link") ======================================================================================== Jujutsu supports a functional language for selecting a set of files. Expressions in this language are called "filesets" (the idea comes from [Mercurial](https://repo.mercurial-scm.org/hg/help/filesets) ). The language consists of file patterns, operators, and functions. Quoting file names[¶](https://jj-vcs.github.io/jj/prerelease/filesets/#quoting-file-names "Permanent link") ------------------------------------------------------------------------------------------------------------ Many `jj` commands accept fileset expressions as positional arguments. File names passed to these commands [must be quoted](https://jj-vcs.github.io/jj/prerelease/templates/#string-literals) if they contain whitespace or meta characters. However, as a special case, quotes can be omitted if the expression has no operators nor function calls. For example: * `jj diff 'Foo Bar'` (shell quotes are required, but inner quotes are optional) * `jj diff '~"Foo Bar"'` (both shell and inner quotes are required) * `jj diff '"Foo(1)"'` (both shell and inner quotes are required) Glob characters aren't considered meta characters, but shell quotes are still required: * `jj diff '~glob:**/*.rs'` File patterns[¶](https://jj-vcs.github.io/jj/prerelease/filesets/#file-patterns "Permanent link") -------------------------------------------------------------------------------------------------- The following patterns are supported. In all cases, we do not mention any shell quoting that might be necessary, and the quotes around `"path"` are optional if the path [has no special characters](https://jj-vcs.github.io/jj/prerelease/filesets/#quoting-file-names) . * `"path"` or `cwd:"path"`: Matches cwd-relative path prefix (file or files under directory recursively.) * `file:"path"` or `cwd-file:"path"`: Matches cwd-relative file (or exact) path. * `glob:"pattern"` or `cwd-glob:"pattern"`: Matches file paths with cwd-relative Unix-style shell [wildcard `pattern`](https://docs.rs/globset/latest/globset/#syntax) . For example, `glob:"*.c"` will match all `.c` files in the current working directory non-recursively. * `glob-i:"pattern"` or `cwd-glob-i:"pattern"`: Like `glob:` but case-insensitive. For example, `glob-i:"*.TXT"` will match both `file.txt` and `FILE.TXT`. * `root:"path"`: Matches workspace-relative path prefix (file or files under directory recursively.) * `root-file:"path"`: Matches workspace-relative file (or exact) path. * `root-glob:"pattern"`: Matches file paths with workspace-relative Unix-style shell [wildcard `pattern`](https://docs.rs/globset/latest/globset/#syntax) . * `root-glob-i:"pattern"`: Like `root-glob:` but case-insensitive. Operators[¶](https://jj-vcs.github.io/jj/prerelease/filesets/#operators "Permanent link") ------------------------------------------------------------------------------------------ The following operators are supported. `x` and `y` below can be any fileset expressions. * `~x`: Matches everything but `x`. * `x & y`: Matches both `x` and `y`. * `x ~ y`: Matches `x` but not `y`. * `x | y`: Matches either `x` or `y` (or both). (listed in order of binding strengths) You can use parentheses to control evaluation order, such as `(x & y) | z` or `x & (y | z)`. Functions[¶](https://jj-vcs.github.io/jj/prerelease/filesets/#functions "Permanent link") ------------------------------------------------------------------------------------------ You can also specify patterns by using functions. * `all()`: Matches everything. * `none()`: Matches nothing. Examples[¶](https://jj-vcs.github.io/jj/prerelease/filesets/#examples "Permanent link") ---------------------------------------------------------------------------------------- Show diff excluding `Cargo.lock`. `jj diff '~Cargo.lock'` List files in `src` excluding Rust sources. `jj file list 'src ~ glob:"**/*.rs"'` Split a revision in two, putting `foo` into the second commit. `jj split '~foo'` --- # Git comparison - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/git-comparison/#comparison-with-git) [](https://github.com/jj-vcs/jj/edit/main/docs/git-comparison.md "Edit this page") Comparison with Git[¶](https://jj-vcs.github.io/jj/prerelease/git-comparison/#comparison-with-git "Permanent link") ==================================================================================================================== Introduction[¶](https://jj-vcs.github.io/jj/prerelease/git-comparison/#introduction "Permanent link") ------------------------------------------------------------------------------------------------------ This document attempts to describe how Jujutsu is different from Git. See [the Git-compatibility doc](https://jj-vcs.github.io/jj/prerelease/git-compatibility/) for information about how the `jj` command interoperates with Git repos. See [the Git command table](https://jj-vcs.github.io/jj/prerelease/git-command-table/) for a table of similar commands. Overview[¶](https://jj-vcs.github.io/jj/prerelease/git-comparison/#overview "Permanent link") ---------------------------------------------------------------------------------------------- Here is a list of conceptual differences between Jujutsu and Git, along with links to more details where applicable and available. There's a [table](https://jj-vcs.github.io/jj/prerelease/git-command-table/) explaining how to achieve various use cases. * **The working copy is automatically committed.** That results in a simpler and more consistent CLI because the working copy is now treated like any other commit. [Details](https://jj-vcs.github.io/jj/prerelease/working-copy/) . * **There's no index (staging area).** Because the working copy is automatically committed, an index-like concept doesn't make sense. The index is very similar to an intermediate commit between `HEAD` and the working copy, so workflows that depend on it can be modeled using proper commits instead. Jujutsu has excellent support for moving changes between commits. [Details](https://jj-vcs.github.io/jj/prerelease/git-comparison/#the-index) . * **No need for branch names (but they are supported as [bookmarks](https://jj-vcs.github.io/jj/prerelease/glossary/#bookmark) ).** Git lets you check out a commit without attaching a branch to it. It calls this state "detached HEAD". This is the normal state in Jujutsu (there's actually no way -- yet, at least -- to have an active branch/bookmark). However, Jujutsu keeps track of all visible heads (leaves) of the commit graph, so the commits won't get lost or garbage-collected. * **No current branch.** Git lets you check out a branch, making it the 'current branch', and new commits will automatically update the branch. This is necessary in Git because Git might otherwise lose track of the new commits. Jujutsu does not have a corresponding concept of a 'current bookmark'; instead, you update bookmarks manually. For example, if you start work on top of a commit with a bookmark, new commits are created on top of the bookmark, then you issue a later command to update the bookmark. * **Conflicts can be committed.** No commands fail because of merge conflicts. The conflicts are instead recorded in commits and you can resolve them later. [Details](https://jj-vcs.github.io/jj/prerelease/conflicts/) . This lets us rebase conflict and conflict resolutions, and thereby addressing most `git rerere` use cases. * **Descendant commits are automatically rebased.** Whenever you rewrite a commit (e.g. by running `jj rebase`), all its descendants commits will automatically be rebased on top. Branches pointing to it will also get updated, and so will the working copy if it points to any of the rebased commits. * **No "evil merges".** In Git, a merge commit that contains changes that are not in any parent are called [evil merges](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-evilmerge) . We believe the reason for this opinionated term is that Git has historically not been very good at working with such commits. For example, `git show` doesn't show them well (without `--remerge-diff` from 2022), and `git rebase` drops the changes (without `--rebase-merges` from 2018). Jujutsu defines the changes in a commit as being relative to the auto-merged parents in all contexts, so you can safely include change in merge commits. * **Bookmarks/branches are identified by their names (across remotes).** For example, if you pull from a remote that has a `main` branch, you'll get a bookmark by that name in your local repo. If you then move it and push back to the remote, the `main` branch on the remote will be updated. [Details](https://jj-vcs.github.io/jj/prerelease/bookmarks/) . * **The operation log replaces reflogs.** The operation log is similar to reflogs, but is much more powerful. It keeps track of atomic updates to all refs at once (Jujutsu thus improves on Git's per-ref history much in the same way that Subversion improved on RCS's per-file history). The operation log powers e.g. the undo functionality. [Details](https://jj-vcs.github.io/jj/prerelease/operation-log/) * **There's a single, virtual root commit.** Like Mercurial, Jujutsu has a virtual commit (with a hash consisting of only zeros) called the "root commit" (called the "null revision" in Mercurial). This commit is a common ancestor of all commits. That removes the awkward state Git calls the "unborn branch" state (which is the state a newly initialized Git repo is in), and related command-line flags (e.g. `git rebase --root`, `git checkout --orphan`). The index[¶](https://jj-vcs.github.io/jj/prerelease/git-comparison/#the-index "Permanent link") ------------------------------------------------------------------------------------------------ Git's ["index"](https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified) has multiple roles. One role is as a cache of file system information. Jujutsu has something similar. Unfortunately, Git exposes the index to the user, which makes the CLI unnecessarily complicated (learning what the different flavors of `git reset` do, especially when combined with commits and/or paths, usually takes a while). Jujutsu, like Mercurial, doesn't make that mistake. 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, or `jj squash ` to move a specific file. --- # Other related work - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/related-work/#related-work) [](https://github.com/jj-vcs/jj/edit/main/docs/related-work.md "Edit this page") Related work[¶](https://jj-vcs.github.io/jj/prerelease/related-work/#related-work "Permanent link") ==================================================================================================== Similar tools: * [git-branchless](https://github.com/arxanas/git-branchless) : Helps you use a branchless workflow in your Git repo. Supports anonymous branching, undo, and faster rebase (`git move`). Under heavy development and quickly gaining new features. * [Sapling](https://sapling-scm.com/) : A heavily modified fork of [Mercurial](https://www.mercurial-scm.org/) developed and used at Meta. It is compatible with Git, has undo functionality, and [a graphical interface](https://sapling-scm.com/docs/addons/isl) . See [how it is different from Jujutsu](https://jj-vcs.github.io/jj/prerelease/sapling-comparison/) . * [GitUp](https://gitup.co/) : A Mac-only GUI for Git. Like Jujutsu, supports undo and restoring the repo to an earlier snapshot. Backed by its [GitUpKit library](https://github.com/git-up/GitUp#gitupkit) . * [Gitless](https://gitless.com/) : Another attempt at providing a simpler interface for Git. Like Jujutsu, does not have an "index"/"staging area" concept. Also doesn't move the working-copy changes between branches (which we do simply as a consequence of making the working copy a commit). * [Breezy](https://www.breezy-vcs.org/) : Another VCS that's similar in that it has multiple storage backends, including its own format as well as .git support. * [GitButler](https://gitbutler.com/) : A Git client that works with multiple virtual branches simultaneously, first-class conflicts, and operations history. --- # Git command table - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/git-command-table/#git-command-table) [](https://github.com/jj-vcs/jj/edit/main/docs/git-command-table.md "Edit this page") Git command table[¶](https://jj-vcs.github.io/jj/prerelease/git-command-table/#git-command-table "Permanent link") =================================================================================================================== Note that all `jj` commands can be run on any commit (not just the working-copy commit), but that's left out of the table to keep it simple. For example, `jj squash -r ` will move the diff from that revision into its parent. | Use case | Git command | Jujutsu command | Notes | | --- | --- | --- | --- | | Create a new repo | `git init` | `jj git init [--no-colocate]` | | | Clone an existing repo | `git clone [--origin ]` | `jj git clone [--remote ]` | There is no support for cloning non-Git repos yet. | | Update the local repo with all bookmarks/branches from a remote | `git fetch []` | `jj git fetch [--remote ]` | There is no support for fetching into non-Git repos yet. | | Update a remote repo with all bookmarks/branches from the local repo | `git push --all []` | `jj git push --all [--remote ]` | There is no support for pushing from non-Git repos yet. | | Update a remote repo with a single bookmark from the local repo | `git push ` | `jj git push --bookmark [--remote ]` | There is no support for pushing from non-Git repos yet. | | Add a remote target to the repo | `git remote add ` | `jj git remote add ` | | | Show summary of current work and repo status | `git status` | `jj st` | | | Show diff of the current change | `git diff HEAD` | `jj diff` | | | Show diff of another change | `git diff ^ ` | `jj diff -r ` | | | Show diff from another change to the current change | `git diff ` | `jj diff --from ` | | | Show diff from change A to change B | `git diff A B` | `jj diff --from A --to B` | | | Show all the changes in A..B | `git diff A...B` | `jj diff -r A..B` | | | Show description and diff of a change | `git show ` | `jj show ` | | | Add a file to the current change | `touch filename; git add filename` | `touch filename` | | | Remove a file from the current change | `git rm filename` | `rm filename` | | | Remove a previously tracked file from the current change, but keep it in the working copy | `git rm --cached filename` | `jj file untrack filename` | File name must match an ignore pattern to remain untracked. See [the documentation for working copies](https://jj-vcs.github.io/jj/prerelease/working-copy/#introduction)
for more. | | Modify a file in the current change | `echo stuff >> filename` | `echo stuff >> filename` | | | Finish work on the current change and start a new change | `git commit -a` | `jj commit` | | | See log of ancestors of the current commit | `git log --oneline --graph --decorate` | `jj log -r ::@` | | | See log of all reachable commits | `git log --oneline --graph --decorate --branches` | `jj log -r 'all()'` or `jj log -r ::` | | | Show log of commits not on the main branch | (TODO) | `jj log` | | | Show log of commits adding/removing the string "stuff" | `git log -G stuff` | `jj log -r 'diff_contains(stuff)'` | | | List versioned files in the working copy | `git ls-files --cached` | `jj file list` | | | Search among files versioned in the repository | `git grep foo` | `grep foo $(jj file list)` or `rg --no-require-git foo` | | | Abandon the current change and start a new change | `git reset --hard` (cannot be undone) | `jj abandon` | | | Make the current change empty | `git reset --hard` (same as abandoning a change since Git has no concept of a "change") | `jj restore` | | | Abandon the parent of the working copy, but keep its diff in the working copy | `git reset --soft HEAD~` | `jj squash --from @-` | | | Discard working copy changes in some files | `git restore ...` or `git checkout HEAD -- ...` | `jj restore ...` | | | Edit description (commit message) of the current change | Not supported | `jj describe` | | | Edit description (commit message) of the previous change | `git commit --amend --only` | `jj describe @-` | | | Temporarily put away the current change | `git stash` | `jj new @-` | The old working-copy commit remains as a sibling commit. The old working-copy commit X can be restored with `jj edit X`. | | Start working on a new change based on the

bookmark/branch | `git switch -c topic main` or `git checkout -b topic main` (may need to stash or commit first) | `jj new main` | | | Merge branch A into the current change | `git merge A` | `jj new @ A` | | | Check out a named revision (or branch) to examine source | `git checkout v1.0.1` | `jj new v1.0.1` | Creates new empty change on top (see `jj new main`) | | Move bookmark/branch A onto bookmark/branch B | `git rebase B A` (may need to rebase other descendant branches separately) | `jj rebase -b A -d B` | | | Move change A and its descendants onto change B | `git rebase --onto B A^ ` (may need to rebase other descendant bookmarks separately) | `jj rebase -s A -d B` | | | Reorder changes from A-B-C-D to A-C-B-D | `git rebase -i A` | `jj rebase -r C --before B` | | | Move the diff in the current change into the parent change | `git commit --amend -a` | `jj squash` | | | Interactively move part of the diff in the current change into the parent change | `git add -p; git commit --amend` | `jj squash -i` | | | Move the diff in the working copy into an ancestor | `git commit --fixup=X; git rebase -i --autosquash X^` | `jj squash --into X` | | | Interactively move part of the diff in an arbitrary change to another arbitrary change | Not supported | `jj squash -i --from X --into Y` | | | Interactively split the changes in the working copy in two | `git commit -p` | `jj split` | | | Interactively split an arbitrary change in two | Not supported (can be emulated with the "edit" action in `git rebase -i`) | `jj split -r ` | | | Interactively edit the diff in a given change | Not supported (can be emulated with the "edit" action in `git rebase -i`) | `jj diffedit -r ` | | | Resolve conflicts and continue interrupted operation | `echo resolved > filename; git add filename; git rebase/merge/cherry-pick --continue` | `echo resolved > filename; jj squash` | Operations don't get interrupted, so no need to continue. | | Create a copy of a commit on top of another commit | `git co ; git cherry-pick ` | `jj duplicate -d ` | | | Find the root of the working copy (or check if in a repo) | `git rev-parse --show-toplevel` | `jj workspace root` | | | List bookmarks/branches | `git branch` | `jj bookmark list` or `jj b l` for short | | | Create a bookmark/branch | `git branch ` | `jj bookmark create -r ` | | | Move a bookmark/branch forward | `git branch -f ` | `jj bookmark move --to ` or `jj b m --to ` for short | | | Move a bookmark/branch backward or sideways | `git branch -f ` | `jj bookmark move --to --allow-backwards` | | | Delete a bookmark/branch | `git branch --delete ` | `jj bookmark delete ` | | | See log of operations performed on the repo | Not supported | `jj op log` | | | Undo an earlier operation | Not supported | `jj undo` | A matching `jj redo` command exists as well. | | Create a commit that cancels out a previous commit | `git revert ` | `jj revert -r -B @` | | | Show what revision and author last modified each line of a file | `git blame ` | `jj file annotate ` | | --- # Revset language - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/revsets/#revsets) [](https://github.com/jj-vcs/jj/edit/main/docs/revsets.md "Edit this page") Revsets[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#revsets "Permanent link") ===================================================================================== Jujutsu supports a functional language for selecting a set of revisions. Expressions in this language are called "revsets" (the idea comes from [Mercurial](https://www.mercurial-scm.org/repo/hg/help/revsets) ). The language consists of symbols, operators, and functions. Most `jj` commands accept a revset (or multiple). Many commands, such as `jj edit ` expect the revset to resolve to a single commit; it is an error to pass a revset that resolves to more than one commit (or zero commits) to such commands. The words "revisions" and "commits" are used interchangeably in this document. Hidden revisions[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#hidden-revisions "Permanent link") ------------------------------------------------------------------------------------------------------- Most revsets search only the [visible commits](https://jj-vcs.github.io/jj/prerelease/glossary/#visible-commits) . Other commits are only included if you explicitly mention them (e.g. by commit ID, `@` symbol, or `at_operation()` function). If hidden commits are specified, their ancestors also become available to the search space. They are included in `all()`, `x..`, `~x`, etc., but not in `..visible_heads()`, etc. For example, `hidden_id | all()` is equivalent to `hidden_id | ::(hidden_id | visible_heads())`. Symbols[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#symbols "Permanent link") ------------------------------------------------------------------------------------- The `@` expression refers to the working copy commit in the current workspace. Use `@` to refer to the working-copy commit in another workspace. Use `@` to refer to a remote-tracking bookmark. A full commit ID refers to a single commit. A unique prefix of the full commit ID can also be used. It is an error to use a non-unique prefix. A full change ID refers to a visible commit with that change ID. A unique prefix of the full change ID can also be used. It is an error to use a non-unique prefix or [a divergent change ID](https://jj-vcs.github.io/jj/prerelease/glossary/#divergent-change) . Use [single or double quotes](https://jj-vcs.github.io/jj/prerelease/templates/#string-literals) to prevent a symbol from being interpreted as an expression. For example, `"x-"` is the symbol `x-`, not the parents of symbol `x`. Taking shell quoting into account, you may need to use something like `jj log -r '"x-"'`. ### Priority[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#priority "Permanent link") Jujutsu attempts to resolve a symbol in the following order: 1. Tag name 2. Bookmark name 3. Git ref 4. Commit ID or change ID To override the priority, use the appropriate [revset function](https://jj-vcs.github.io/jj/prerelease/revsets/#functions) . For example, to resolve `abc` as a commit ID even if there happens to be a bookmark by the same name, use `commit_id(abc)`. This is particularly useful in scripts. Operators[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#operators "Permanent link") ----------------------------------------------------------------------------------------- The following operators are supported. `x` and `y` below can be any revset, not only symbols. * `x-`: Parents of `x`, can be empty. * `x+`: Children of `x`, can be empty. * `x::`: Descendants of `x`, including the commits in `x` itself. Equivalent to `x::visible_heads()` if no hidden revisions are mentioned. * `x..`: Revisions that are not ancestors of `x`. Equivalent to `~::x`, and `x..visible_heads()` if no hidden revisions are mentioned. * `::x`: Ancestors of `x`, including the commits in `x` itself. Shorthand for `root()::x`. * `..x`: Ancestors of `x`, including the commits in `x` itself, but excluding the root commit. Shorthand for `root()..x`. Equivalent to `::x ~ root()`. * `x::y`: Descendants of `x` that are also ancestors of `y`. Equivalent to `x:: & ::y`. This is what `git log` calls `--ancestry-path x..y`. * `x..y`: Ancestors of `y` that are not also ancestors of `x`. Equivalent to `::y ~ ::x`. This is what `git log` calls `x..y` (i.e. the same as we call it). * `::`: All visible commits in the repo. Equivalent to `all()`, and `root()::visible_heads()` if no hidden revisions are mentioned. * `..`: All visible commits in the repo, but excluding the root commit. Equivalent to `~root()`, and `root()..visible_heads()` if no hidden revisions are mentioned. * `~x`: Revisions that are not in `x`. * `x & y`: Revisions that are in both `x` and `y`. * `x ~ y`: Revisions that are in `x` but not in `y`. * `x | y`: Revisions that are in either `x` or `y` (or both). (listed in order of binding strengths) You can use parentheses to control evaluation order, such as `(x & y) | z` or `x & (y | z)`. Examples Given this history: `o D |\ | o C | | o | B |/ o A | o root()` **Operator** `x-` * `D-` ⇒ `{C,B}` * `B-` ⇒ `{A}` * `A-` ⇒ `{root()}` * `root()-` ⇒ `{}` (empty set) * `none()-` ⇒ `{}` (empty set) * `(D|A)-` ⇒ `{C,B,root()}` * `(C|B)-` ⇒ `{A}` **Operator** `x+` * `D+` ⇒ `{}` (empty set) * `B+` ⇒ `{D}` * `A+` ⇒ `{B,C}` * `root()+` ⇒ `{A}` * `none()+` ⇒ `{}` (empty set) * `(C|B)+` ⇒ `{D}` * `(B|root())+` ⇒ `{D,A}` **Operator** `x::` * `D::` ⇒ `{D}` * `B::` ⇒ `{D,B}` * `A::` ⇒ `{D,C,B,A}` * `root()::` ⇒ `{D,C,B,A,root()}` * `none()::` ⇒ `{}` (empty set) * `(C|B)::` ⇒ `{D,C,B}` **Operator** `x..` * `D..` ⇒ `{}` (empty set) * `B..` ⇒ `{D,C}` (note that, unlike `B::`, this includes `C`) * `A..` ⇒ `{D,C,B}` * `root()..` ⇒ `{D,C,B,A}` * `none()..` ⇒ `{D,C,B,A,root()}` * `(C|B)..` ⇒ `{D}` **Operator** `::x` * `::D` ⇒ `{D,C,B,A,root()}` * `::B` ⇒ `{B,A,root()}` * `::A` ⇒ `{A,root()}` * `::root()` ⇒ `{root()}` * `::none()` ⇒ `{}` (empty set) * `::(C|B)` ⇒ `{C,B,A,root()}` **Operator** `..x` * `..D` ⇒ `{D,C,B,A}` * `..B` ⇒ `{B,A}` * `..A` ⇒ `{A}` * `..root()` ⇒ `{}` (empty set) * `..none()` ⇒ `{}` (empty set) * `..(C|B)` ⇒ `{C,B,A}` **Operator** `x::y` * `D::D` ⇒ `{D}` * `B::D` ⇒ `{D,B}` (note that, unlike `B..D`, this includes `B` and excludes `C`) * `B::C` ⇒ `{}` (empty set) (note that, unlike `B..C`, this excludes `C`) * `A::D` ⇒ `{D,C,B,A}` * `root()::D` ⇒ `{D,C,B,A,root()}` * `none()::D` ⇒ `{}` (empty set) * `D::B` ⇒ `{}` (empty set) * `(C|B)::(C|B)` ⇒ `{C,B}` **Operator** `x..y` * `D..D` ⇒ `{}` (empty set) * `B..D` ⇒ `{D,C}` (note that, unlike `B::D`, this includes `C` and excludes `B`) * `B..C` ⇒ `{C}` (note that, unlike `B::C`, this includes `C`) * `A..D` ⇒ `{D,C,B}` * `root()..D` ⇒ `{D,C,B,A}` * `none()..D` ⇒ `{D,C,B,A,root()}` * `D..B` ⇒ `{}` (empty set) * `(C|B)..(C|B)` ⇒ `{}` (empty set) Functions[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#functions "Permanent link") ----------------------------------------------------------------------------------------- You can also specify revisions by using functions. Some functions take other revsets (expressions) as arguments. Function argument syntax In this documentation, optional arguments are indicated with square brackets like `[arg]`. Some arguments also have an optional label which can be used to specify that argument without specifying all previous arguments. For instance, `remote_bookmarks([bookmark_pattern], [[remote=]remote_pattern])` indicates that all of the following usages are valid: * `remote_bookmarks()` * `remote_bookmarks("main")` * `remote_bookmarks("main", "origin")` * `remote_bookmarks("main", remote="origin")` * `remote_bookmarks(remote="origin")` * `parents(x, [depth])`: `parents(x)` is the same as `x-`. `parents(x, depth)` returns the parents of `x` at the given `depth`. For instance, `parents(x, 3)` is equivalent to `x---`. * `children(x, [depth])`: `children(x)` is the same as `x+`. `children(x, depth)` returns the children of `x` at the given `depth`. For instance, `children(x, 3)` is equivalent to `x+++`. * `ancestors(x, [depth])`: `ancestors(x)` is the same as `::x`. `ancestors(x, depth)` returns the ancestors of `x` limited to the given `depth`. * `descendants(x, [depth])`: `descendants(x)` is the same as `x::`. `descendants(x, depth)` returns the descendants of `x` limited to the given `depth`. * `first_parent(x, [depth])`: `first_parent(x)` is similar to `parents(x)`, but for merges, it only returns the first parent instead of returning all parents. The `depth` argument also works similarly, so `first_parent(x, 2)` is equivalent to `first_parent(first_parent(x))`. * `first_ancestors(x, [depth])`: Similar to `ancestors(x, [depth])`, but only traverses the first parent of each commit. In Git, the first parent of a merge commit is conventionally the branch into which changes are being merged, so `first_ancestors()` can be used to exclude changes made on other branches. * `reachable(srcs, domain)`: All commits reachable from `srcs` within `domain`, traversing all parent and child edges. * `connected(x)`: Same as `x::x`. Useful when `x` includes several commits. * `all()`: All visible commits and ancestors of commits explicitly mentioned. * `none()`: No commits. This function is rarely useful; it is provided for completeness. * `change_id(prefix)`: Commits with the given change ID prefix. If the specified change is divergent, this resolves to multiple commits. It is an error to use a non-unique prefix. Unmatched prefix isn't an error. * `commit_id(prefix)`: Commits with the given commit ID prefix. It is an error to use a non-unique prefix. Unmatched prefix isn't an error. * `bookmarks([pattern])`: All local bookmark targets. If `pattern` is specified, this selects the bookmarks whose name match the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . For example, `bookmarks(push)` would match the bookmarks `push-123` and `repushed` but not the bookmark `main`. If a bookmark is in a conflicted state, all its possible targets are included. * `remote_bookmarks([bookmark_pattern], [[remote=]remote_pattern])`: All remote bookmarks targets across all remotes. If just the `bookmark_pattern` is specified, the bookmarks whose names match the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) across all remotes are selected. If both `bookmark_pattern` and `remote_pattern` are specified, the selection is further restricted to just the remotes whose names match `remote_pattern`. For example, `remote_bookmarks(push, ri)` would match the bookmarks `push-123@origin` and `repushed@private` but not `push-123@upstream` or `main@origin` or `main@upstream`. If a bookmark is in a conflicted state, all its possible targets are included. While Git-tracking bookmarks can be selected by `@git`, these bookmarks aren't included in `remote_bookmarks()`. * `tracked_remote_bookmarks([bookmark_pattern], [[remote=]remote_pattern])`: All targets of tracked remote bookmarks. Supports the same optional arguments as `remote_bookmarks()`. * `untracked_remote_bookmarks([bookmark_pattern], [[remote=]remote_pattern])`: All targets of untracked remote bookmarks. Supports the same optional arguments as `remote_bookmarks()`. * `tags([pattern])`: All tag targets. If `pattern` is specified, this selects the tags whose name match the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . For example, `tags(v1)` would match the tags `v123` and `rev1` but not the tag `v2`. If a tag is in a conflicted state, all its possible targets are included. * `git_refs()`: All Git ref targets as of the last import. If a Git ref is in a conflicted state, all its possible targets are included. * `git_head()`: The Git `HEAD` target as of the last import. * `visible_heads()`: All visible heads (same as `heads(all())` if no hidden revisions are mentioned). * `root()`: The virtual commit that is the oldest ancestor of all other commits. * `heads(x)`: Commits in `x` that are not ancestors of other commits in `x`. Equivalent to `x ~ ::x-`. Note that this is different from [Mercurial's](https://repo.mercurial-scm.org/hg/help/revsets) `heads(x)` function, which is equivalent to `x ~ x-`. * `roots(x)`: Commits in `x` that are not descendants of other commits in `x`. Equivalent to `x ~ x+::`. Note that this is different from [Mercurial's](https://repo.mercurial-scm.org/hg/help/revsets) `roots(x)` function, which is equivalent to `x ~ x+`. * `latest(x, [count])`: Latest `count` commits in `x`, based on committer timestamp. The default `count` is 1. * `fork_point(x)`: The fork point of all commits in `x`. The fork point is the common ancestor(s) of all commits in `x` which do not have any descendants that are also common ancestors of all commits in `x`. It is equivalent to the revset `heads(::x_1 & ::x_2 & ... & ::x_N)`, where `x_{1..N}` are commits in `x`. If `x` resolves to a single commit, `fork_point(x)` resolves to `x`. * `bisect(x)`: Finds commits in the input set for which about half of the input set are descendants. The current implementation deals somewhat poorly with non-linear history. * `exactly(x, count)`: Evaluates `x`, and errors if it is not of exactly size `count`. Otherwise, returns `x`. This is useful in particular with `count=1` when you want to ensure that some revset expression has exactly one target. * `merges()`: Merge commits. * `description(pattern)`: Commits that have a description matching the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . A non-empty description is usually terminated with newline character. For example, `description(exact:"")` matches commits without description, and `description(exact:"foo\n")` matches commits with description `"foo\n"`. * `subject(pattern)`: Commits that have a subject matching the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . A subject is the first line of the description (without newline character.) * `author(pattern)`: Commits with the author's name or email matching the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . Equivalent to `author_name(pattern) | author_email(pattern)`. * `author_name(pattern)`: Commits with the author's name matching the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . * `author_email(pattern)`: Commits with the author's email matching the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . * `author_date(pattern)`: Commits with author dates matching the specified [date pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#date-patterns) . * `mine()`: Commits where the author's email matches the email of the current user. Equivalent to `author_email(exact-i:)` * `committer(pattern)`: Commits with the committer's name or email matching the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . Equivalent to `committer_name(pattern) | committer_email(pattern)`. * `committer_name(pattern)`: Commits with the committer's name matching the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . * `committer_email(pattern)`: Commits with the committer's email matching the given [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . * `committer_date(pattern)`: Commits with committer dates matching the specified [date pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#date-patterns) . * `signed()`: Commits that are cryptographically signed. * `empty()`: Commits modifying no files. This also includes `merges()` without user modifications and `root()`. * `files(expression)`: Commits modifying paths matching the given [fileset expression](https://jj-vcs.github.io/jj/prerelease/filesets/) . Paths are relative to the directory `jj` was invoked from. A directory name will match all files in that directory and its subdirectories. For example, `files(foo)` will match files `foo`, `foo/bar`, `foo/bar/baz`. It will _not_ match `foobar` or `bar/foo`. Some file patterns might need quoting because the `expression` must also be parsable as a revset. For example, `.` has to be quoted in `files(".")`. * `diff_contains(text, [files])`: Commits containing diffs matching the given `text` pattern line by line. The search paths can be narrowed by the `files` expression. All modified files are scanned by default, but it is likely to change in future version to respect the command line path arguments. For example, `diff_contains("TODO", "src")` will search revisions where "TODO" is added to or removed from files under "src". * `conflicts()`: Commits with conflicts. * `present(x)`: Same as `x`, but evaluated to `none()` if any of the commits in `x` doesn't exist (e.g. is an unknown bookmark name.) * `coalesce(revsets...)`: Commits in the first revset in the list of `revsets` which does not evaluate to `none()`. If all revsets evaluate to `none()`, then the result of `coalesce` will also be `none()`. * `working_copies()`: The working copy commits across all the workspaces. * `at_operation(op, x)`: Evaluates `x` at the specified [operation](https://jj-vcs.github.io/jj/prerelease/glossary/#operation) . For example, `at_operation(@-, visible_heads())` will return all heads which were visible at the previous operation. Since `at_operation(op, x)` brings all commits that were visible at the operation to the search space, `at_operation(op, x) | all()` is equivalent to `at_operation(op, x) | ::(at_operation(op, x | visible_heads()) | visible_heads())`. Examples Given this history: `o E | | o D |/| | o C | | o | B |/ o A | o root()` **function** `reachable()` * `reachable(E, A..)` ⇒ `{E,D,C,B}` * `reachable(D, A..)` ⇒ `{E,D,C,B}` * `reachable(C, A..)` ⇒ `{E,D,C,B}` * `reachable(B, A..)` ⇒ `{E,D,C,B}` * `reachable(A, A..)` ⇒ `{}` (empty set) **function** `connected()` * `connected(E|A)` ⇒ `{E,B,A}` * `connected(D|A)` ⇒ `{D,C,B,A}` * `connected(A)` ⇒ `{A}` **function** `heads()` * `heads(E|D)` ⇒ `{E,D}` * `heads(E|C)` ⇒ `{E,C}` * `heads(E|B)` ⇒ `{E}` * `heads(E|A)` ⇒ `{E}` * `heads(A)` ⇒ `{A}` **function** `roots()` * `roots(E|D)` ⇒ `{E,D}` * `roots(E|C)` ⇒ `{E,C}` * `roots(E|B)` ⇒ `{B}` * `roots(E|A)` ⇒ `{A}` * `roots(A)` ⇒ `{A}` **function** `fork_point()` * `fork_point(E|D)` ⇒ `{B}` * `fork_point(E|C)` ⇒ `{A}` * `fork_point(E|B)` ⇒ `{B}` * `fork_point(E|A)` ⇒ `{A}` * `fork_point(D|C)` ⇒ `{C}` * `fork_point(D|B)` ⇒ `{B}` * `fork_point(B|C)` ⇒ `{A}` * `fork_point(A)` ⇒ `{A}` * `fork_point(none())` ⇒ `{}` String patterns[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns "Permanent link") ----------------------------------------------------------------------------------------------------- Functions that perform string matching support the following pattern syntax (the quotes are optional): * `"string"` or `substring:"string"`: Matches strings that contain `string`. * `exact:"string"`: Matches strings exactly equal to `string`. * `glob:"pattern"`: Matches strings with Unix-style shell [wildcard `pattern`](https://docs.rs/globset/latest/globset/#syntax) . * `regex:"pattern"`: Matches substrings with [regular expression `pattern`](https://docs.rs/regex/latest/regex/#syntax) . You can append `-i` after the kind to match case‐insensitively (e.g. `glob-i:"fix*jpeg*"`). Date patterns[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#date-patterns "Permanent link") ------------------------------------------------------------------------------------------------- Functions that perform date matching support the following pattern syntax: * `after:"string"`: Matches dates exactly at or after the given date. * `before:"string"`: Matches dates before, but not including, the given date. Date strings can be specified in several forms, including: * 2024-02-01 * 2024-02-01T12:00:00 * 2024-02-01T12:00:00-08:00 * 2024-02-01 12:00:00 * 2 days ago * 5 minutes ago * yesterday * yesterday 5pm * yesterday 10:30 * yesterday 15:30 Aliases[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#aliases "Permanent link") ------------------------------------------------------------------------------------- New symbols and functions can be defined in the config file, by using any combination of the predefined symbols/functions and other aliases. Alias functions can be overloaded by the number of parameters. However, builtin function will be shadowed by name, and can't co-exist with aliases. For example: `[revset-aliases] 'HEAD' = '@-' 'user()' = 'user("me@example.org")' 'user(x)' = 'author(x) | committer(x)'` ### Built-in Aliases[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#built-in-aliases "Permanent link") The following aliases are built-in and used for certain operations. These functions are defined as aliases in order to allow you to overwrite them as needed. See [revsets.toml](https://github.com/jj-vcs/jj/blob/main/cli/src/config/revsets.toml) for a comprehensive list. * `trunk()`: Resolves to the head commit for the default bookmark of the default remote, or the remote named `upstream` or `origin`. This is set at the repository level upon initialization of a Jujutsu repository. If the default bookmark cannot be resolved during initialization, the default global configuration tries the bookmarks `main`, `master`, and `trunk` on the `upstream` and `origin` remotes. If more than one potential trunk commit exists, the newest one is chosen. If none of the bookmarks exist, the revset evaluates to `root()`. You can [override](https://jj-vcs.github.io/jj/prerelease/config/) this as appropriate. If you do, make sure it always resolves to exactly one commit. For example: `[revset-aliases] 'trunk()' = 'your-bookmark@your-remote'` * `builtin_immutable_heads()`: Resolves to `present(trunk()) | tags() | untracked_remote_bookmarks()`. It is used as the default definition for `immutable_heads()` below. It is not recommended to redefine this alias. Prefer to redefine `immutable_heads()` instead. * `immutable_heads()`: Resolves to `present(trunk()) | tags() | untracked_remote_bookmarks()` by default. It is actually defined as `builtin_immutable_heads()`, and can be overridden as required. See [here](https://jj-vcs.github.io/jj/prerelease/config/#set-of-immutable-commits) for details. * `immutable()`: The set of commits that `jj` treats as immutable. This is equivalent to `::(immutable_heads() | root())`. It is not recommended to redefine this alias. Note that modifying this will _not_ change whether a commit is immutable. To do that, edit `immutable_heads()`. * `mutable()`: The set of commits that `jj` treats as mutable. This is equivalent to `~immutable()`. It is not recommended to redefined this alias. Note that modifying this will _not_ change whether a commit is immutable. To do that, edit `immutable_heads()`. The `all:` modifier[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#the-all-modifier "Permanent link") ---------------------------------------------------------------------------------------------------------- Certain commands (such as `jj rebase`) can take multiple revset arguments, and each of these may resolve to one-or-many revisions. If you set the `ui.always-allow-large-revsets` option to `false`, `jj` will not allow revsets that resolve to more than one revision — a so-called "large revset" — and will ask you to confirm that you want to proceed by prefixing it with the `all:` modifier. _This option is planned to be removed._ An `all:` modifier before a revset expression does not otherwise change its meaning. Strictly speaking, it is not part of the revset language. The notation is similar to the modifiers like `glob:` allowed before [string patterns](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) . For example, `jj rebase -r w -d xyz+` will rebase `w` on top of the child of `xyz` as long as `xyz` has exactly one child. If `xyz` has more than one child, the `all:` modifier is _not_ specified, and `ui.always-allow-large-revsets` is `false`, `jj rebase -r w -d xyz+` will return an error. If `ui.always-allow-large-revsets` was `true` (the default), the above command would act as if `all:` was set (see the next paragraph). With the `all:` modifier, `jj rebase -r w -d all:xyz+` will make `w` into a merge commit if `xyz` has more than one child. The `all:` modifier confirms that the user expected `xyz` to have more than one child. A more useful example: if `w` is a merge commit, `jj rebase -s w -d all:w- -d xyz` will add `xyz` to the list of `w`'s parents. Examples[¶](https://jj-vcs.github.io/jj/prerelease/revsets/#examples "Permanent link") --------------------------------------------------------------------------------------- Show the parent(s) of the working-copy commit (like `git log -1 HEAD`): `jj log -r @-` Show all ancestors of the working copy (like plain `git log`) `jj log -r ::@` Show commits not on any remote bookmark: `jj log -r 'remote_bookmarks()..'` Show commits not on `origin` (if you have other remotes like `fork`): `jj log -r 'remote_bookmarks(remote=origin)..'` Show the initial commits in the repo (the ones Git calls "root commits"): `jj log -r 'root()+'` Show some important commits (like `git --simplify-by-decoration`): `jj log -r 'tags() | bookmarks()'` Show local commits leading up to the working copy, as well as descendants of those commits: `jj log -r '(remote_bookmarks()..@)::'` Show commits authored by "martinvonz" and containing the word "reset" in the description: `jj log -r 'author(martinvonz) & description(reset)'` --- # Git compatibility - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#git-compatibility) [](https://github.com/jj-vcs/jj/edit/main/docs/git-compatibility.md "Edit this page") Git compatibility[¶](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#git-compatibility "Permanent link") =================================================================================================================== Jujutsu has two backends for storing commits. One of them uses a regular Git repo, which means that you can collaborate with Git users without them even knowing that you're not using the `git` CLI. See `jj help git` for help about the `jj git` family of commands, and e.g. `jj help git push` for help about a specific command (use `jj git push -h` for briefer help). Supported features[¶](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#supported-features "Permanent link") --------------------------------------------------------------------------------------------------------------------- The following list describes which Git features Jujutsu is compatible with. For a comparison with Git, including how workflows are different, see the [Git-comparison doc](https://jj-vcs.github.io/jj/prerelease/git-comparison/) . * **Configuration: Partial.** The only configuration from Git (e.g. in `~/.gitconfig`) that's respected is the following. Feel free to file a bug if you miss any particular configuration options. * The configuration of remotes (`[remote ""]`). Only the names and URLs are respected (simple fetch refspecs are respected when branches are not explicitly specified on the CLI, and [only the last pushurl](https://github.com/jj-vcs/jj/issues/4889) is respected). * `core.excludesFile` * **Authentication: Yes.** `git` is used for remote operations under the hood. * **Branches: Yes.** You can read more about [how branches work in Jujutsu](https://jj-vcs.github.io/jj/prerelease/bookmarks/) and [how they interoperate with Git](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#branches) . * **Tags: Partial.** You can check out tagged commits by name (pointed to be either annotated or lightweight tags.) You can also create lightweight tags, but you cannot create annotated tags. * **.gitignore: Yes.** Patterns in `.gitignore` files are supported. So are ignores in `.git/info/exclude` or configured via Git's `core.excludesfile` config. Since working-copy files are snapshotted by every `jj` command, you might need to run `jj file untrack` to exclude newly ignored files from the working-copy commit. It's recommended to set up the ignore patterns earlier. The `.gitignore` support uses a native implementation, so please report a bug if you notice any difference compared to `git`. * **.gitattributes: No.** There's [#53](https://github.com/jj-vcs/jj/issues/53) about adding support for at least the `eol` attribute. * **Hooks: No.** There's [#405](https://github.com/jj-vcs/jj/issues/405) specifically for providing the checks from [https://pre-commit.com](https://pre-commit.com/) . * **Merge commits: Yes.** Octopus merges (i.e. with more than 2 parents) are also supported. * **Detached HEAD: Yes.** Jujutsu supports anonymous branches, so this is a natural state. * **Orphan branch: Yes.** Jujutsu has a virtual root commit that appears as parent of all commits Git would call "root commits". * **Staging area: Kind of.** The staging area will be ignored. For example, `jj diff` will show a diff from the Git HEAD to the working copy. There are [ways of fulfilling your use cases without a staging area](https://jj-vcs.github.io/jj/prerelease/git-comparison/#the-index) . * **Garbage collection: Yes.** It should be safe to run `git gc` in the Git repo, but it's not tested, so it's probably a good idea to make a backup of the whole workspace first. There's [no garbage collection and repacking of Jujutsu's own data structures yet](https://github.com/jj-vcs/jj/issues/12) , however. * **Bare repositories: Yes.** You can use `jj git init --git-repo=` to create a repo backed by a bare Git repo. * **Submodules: No.** They will not show up in the working copy, but they will not be lost either. * **Partial clones: No.** * **Shallow clones: Kind of.** Shallow commits all have the virtual root commit as their parent. However, deepening or fully unshallowing a repository is currently not yet supported and will cause issues. * **git-worktree: No.** However, there's native support for multiple working copies backed by a single repo. See the `jj workspace` family of commands. * **Sparse checkouts: No.** However, there's native support for sparse checkouts. See the `jj sparse` command. * **Signed commits: Yes.** You can sign commits automatically [by configuration](https://jj-vcs.github.io/jj/prerelease/config/#commit-signing) , or use the `jj sign` command. * **Git LFS: No.** ([#80](https://github.com/jj-vcs/jj/issues/80) ) Creating an empty repo[¶](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#creating-an-empty-repo "Permanent link") ----------------------------------------------------------------------------------------------------------------------------- To create an empty repo using the Git backend, use `jj git init `. Since the command creates a Jujutsu repo, it will have a `.jj/` directory. The underlying Git repo will be inside of that directory (currently in `.jj/repo/store/git/`). Creating a repo backed by an existing Git repo[¶](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#creating-a-repo-backed-by-an-existing-git-repo "Permanent link") ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- To create a Jujutsu repo backed by a Git repo you already have on disk, use `jj git init --git-repo= `. The repo will work similar to a [Git worktree](https://git-scm.com/docs/git-worktree) , meaning that the working copies files and the record of the working-copy commit will be separate, but the commits will be accessible in both repos. Use `jj git import` to update the Jujutsu repo with changes made in the Git repo. Use `jj git export` to update the Git repo with changes made in the Jujutsu repo. Creating a repo by cloning a Git repo[¶](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#creating-a-repo-by-cloning-a-git-repo "Permanent link") ----------------------------------------------------------------------------------------------------------------------------------------------------------- To create a Jujutsu repo from a remote Git URL, use `jj git clone []`. For example, `jj git clone https://github.com/octocat/Hello-World` will clone GitHub's "Hello-World" repo into a directory by the same name. By default, the remote repository will be named `origin`. You can use a name of your choice by adding `--remote ` to the `jj git clone` command. Colocated Jujutsu/Git repos[¶](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#colocated-jujutsugit-repos "Permanent link") -------------------------------------------------------------------------------------------------------------------------------------- A colocated Jujutsu repo is a hybrid Jujutsu/Git repo. This is the default for Git-backed repositories created with `jj git init` or `jj git clone`. The Git repo and the Jujutsu repo then share the same working copy. Jujutsu will import and export from and to the Git repo on every `jj` command automatically. This mode is very convenient when tools (e.g. build tools) expect a Git repo to be present. It is allowed to mix `jj` and `git` commands in such a repo in any order. However, it may be easier to keep track of what is going on if you mostly use read-only `git` commands and use `jj` to make changes to the repo. One reason for this (see below for more) is that `jj` commands will usually put the git repo in a "detached HEAD" state, since in `jj` there is not concept of a "currently tracked branch". Before doing mutating Git commands, you may need to tell Git what the current branch should be with a `git switch` command. You can undo the results of mutating `git` commands using `jj undo` and `jj op restore`. Inside `jj op log`, changes by `git` will be represented as an "import git refs" operation. There are a few downsides to this mode of operation. Generally, using colocated repos may require you to deal with more involved Jujutsu and Git concepts. You can disable colocation with the `--no-colocate` flag on the commands `jj git init` and `jj git clone` or by setting the configuration `git.colocate = false`. * Interleaving `jj` and `git` commands increases the chance of confusing branch conflicts or [conflicted (AKA divergent) change ids](https://jj-vcs.github.io/jj/prerelease/glossary/#divergent-change) . These never lose data, but can be annoying. Such interleaving can happen unknowingly. For example, some IDEs can cause it because they automatically run `git fetch` in the background from time to time. * In colocated repos with a very large number of branches or other refs, `jj` commands can get noticeably slower because of the automatic `jj git import` executed on each command. This can be mitigated by occasionally running `jj util gc` to speed up the import (that command includes packing the Git refs). * Git tools will have trouble with revisions that contain conflicted files. While `jj` renders these files with conflict markers in the working copy, they are stored in a non-human-readable fashion inside the repo. Git tools will often see this non-human-readable representation. * When a `jj` branch is conflicted, the position of the branch in the Git repo will disagree with one or more of the conflicted positions. The state of that branch in git will be labeled as though it belongs to a remote named "git", e.g. `branch@git`. * Jujutsu will ignore Git's staging area. It will not understand merge conflicts as Git represents them, unfinished `git rebase` states, as well as other less common states a Git repository can be in. * Colocated repositories are less resilient to [concurrency](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/#syncing-with-rsync-nfs-dropbox-etc) issues if you share the repo using an NFS filesystem or Dropbox. In general, such use of Jujutsu is not currently thoroughly tested. * There may still be bugs when interleaving mutating `jj` and `git` commands, usually having to do with a branch pointer ending up in the wrong place. We are working on the known ones, and are not aware of any major ones. Please report any new ones you find, or if any of the known bugs are less minor than they appear. ### Converting a repo into a colocated repo[¶](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#converting-a-repo-into-a-colocated-repo "Permanent link") A Jujutsu repo backed by a Git repo has a full Git repo inside, so it is technically possible (though not officially supported) to convert it into a colocated repo like so: `# Ignore the .jj directory in Git echo '/*' > .jj/.gitignore # Move the Git repo mv .jj/repo/store/git .git # Tell jj where to find it (do not use on Windows! See below.) echo -n '../../../.git' > .jj/repo/store/git_target # Make the Git repository non-bare and set HEAD git config --unset core.bare # Convince jj to update .git/HEAD to point to the working-copy commit's parent jj new && jj undo` Warning On Windows, the `echo` command will append line endings and cause `jj` to complain about the contents of `git_target`. Instead of the `echo -n ...` line, use: `Set-Content -Path .jj/repo/store/git_target -Value ../../../.git -NoNewLine` We may officially support this in the future. If you try this, we would appreciate feedback and bug reports. Branches[¶](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#branches "Permanent link") ------------------------------------------------------------------------------------------------- TODO: Describe how branches are mapped Format mapping details[¶](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#format-mapping-details "Permanent link") ----------------------------------------------------------------------------------------------------------------------------- Paths are assumed to be UTF-8. I have no current plans to support paths with other encodings. Commits created by `jj` have a ref starting with `refs/jj/` to prevent GC. Commit metadata that cannot be represented in Git commits (such as the Change ID and information about conflicts) is stored outside of the Git repo (currently in `.jj/store/extra/`). Commits with conflicts cannot be represented in Git. They appear in the Git commit as as root directories called`.jjconflict-base-*/` and `.jjconflict-side-*/`. Note that the purpose of this representation is only to prevent GC of the relevant trees; the authoritative information is in the Git-external storage mentioned in the paragraph above. As long as you use `jj` commands to work with them, you won't notice those paths. If, on the other hand, you use e.g. `git switch` to check one of them out, you will see those directories in your working copy. If you then run e.g. `jj status`, the resulting snapshot will contain those directories, making it look like they replaced all the other paths in your repo. You will probably want to run `jj abandon` to get back to the state with the unresolved conflicts. Change IDs are stored in git commit headers as reverse hex encodings. These is a non-standard header and is not preserved by all `git` tooling. For example, the header is preserved by a `git commit --amend`, but is not preserved through a rebase operation. GitHub and other major forges seem to preserve them for the most part. This functionality is currently behind a `git.write-change-id-header` flag. --- # Templating language - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/templates/#templates) [](https://github.com/jj-vcs/jj/edit/main/docs/templates.md "Edit this page") Templates[¶](https://jj-vcs.github.io/jj/prerelease/templates/#templates "Permanent link") =========================================================================================== Jujutsu supports a functional language to customize output of commands. The language consists of literals, keywords, operators, functions, and methods. A couple of `jj` commands accept a template via `-T`/`--template` option. Keywords[¶](https://jj-vcs.github.io/jj/prerelease/templates/#keywords "Permanent link") ----------------------------------------------------------------------------------------- Keywords represent objects of different types; the types are described in a follow-up section. In addition to context-specific keywords, the top-level object can be referenced as `self`. ### Commit keywords[¶](https://jj-vcs.github.io/jj/prerelease/templates/#commit-keywords "Permanent link") In `jj log` templates, all 0-argument methods of [the `Commit` type](https://jj-vcs.github.io/jj/prerelease/templates/#commit-type) are available as keywords. For example, `commit_id` is equivalent to `self.commit_id()`. ### Operation keywords[¶](https://jj-vcs.github.io/jj/prerelease/templates/#operation-keywords "Permanent link") In `jj op log` templates, all 0-argument methods of [the `Operation` type](https://jj-vcs.github.io/jj/prerelease/templates/#operation-type) are available as keywords. For example, `current_operation` is equivalent to `self.current_operation()`. Operators[¶](https://jj-vcs.github.io/jj/prerelease/templates/#operators "Permanent link") ------------------------------------------------------------------------------------------- The following operators are supported. * `x.f()`: Method call. * `-x`: Negate integer value. * `!x`: Logical not. * `x * y`, `x / y`, `x % y`: Multiplication/division/remainder. Operands must be `Integer`s. * `x + y`, `x - y`: Addition/subtraction. Operands must be `Integer`s. * `x >= y`, `x > y`, `x <= y`, `x < y`: Greater than or equal/greater than/ lesser than or equal/lesser than. Operands must be `Integer`s. * `x == y`, `x != y`: Equal/not equal. Operands must be either `Boolean`, `Integer`, or `String`. * `x && y`: Logical and, short-circuiting. * `x || y`: Logical or, short-circuiting. * `x ++ y`: Concatenate `x` and `y` templates. (listed in order of binding strengths) Global functions[¶](https://jj-vcs.github.io/jj/prerelease/templates/#global-functions "Permanent link") --------------------------------------------------------------------------------------------------------- The following functions are defined. * `fill(width: Integer, content: Template) -> Template`: Fill lines at the given `width`. * `indent(prefix: Template, content: Template) -> Template`: Indent non-empty lines by the given `prefix`. * `pad_start(width: Integer, content: Template, [fill_char: Template])`: Pad (or right-justify) content by adding leading fill characters. The `content` shouldn't have newline character. * `pad_end(width: Integer, content: Template, [fill_char: Template])`: Pad (or left-justify) content by adding trailing fill characters. The `content` shouldn't have newline character. * `pad_centered(width: Integer, content: Template, [fill_char: Template])`: Pad content by adding both leading and trailing fill characters. If an odd number of fill characters are needed, the trailing fill will be one longer than the leading fill. The `content` shouldn't have newline characters. * `truncate_start(width: Integer, content: Template, [ellipsis: Template])`: Truncate `content` by removing leading characters. The `content` shouldn't have newline character. If `ellipsis` is provided and `content` was truncated, prepend the `ellipsis` to the result. * `truncate_end(width: Integer, content: Template, [ellipsis: Template])`: Truncate `content` by removing trailing characters. The `content` shouldn't have newline character. If `ellipsis` is provided and `content` was truncated, append the `ellipsis` to the result. * `hash(content: Stringify) -> String`: Hash the input and return a hexadecimal string representation of the digest. * `label(label: Stringify, content: Template) -> Template`: Apply label to the content. The `label` is evaluated as a space-separated string. * `raw_escape_sequence(content: Template) -> Template`: Preserves any escape sequences in `content` (i.e., bypasses sanitization) and strips labels. Note: This function is intended for escape sequences and as such, its output is expected to be invisible / of no display width. Outputting content with nonzero display width may break wrapping, indentation etc. * `stringify(content: Stringify) -> String`: Format `content` to string. This effectively removes color labels. * `json(value: Serialize) -> String`: Serialize `value` in JSON format. * `if(condition: Boolean, then: Template, [else: Template]) -> Template`: Conditionally evaluate `then`/`else` template content. * `coalesce(content: Template...) -> Template`: Returns the first **non-empty** content. * `concat(content: Template...) -> Template`: Same as `content_1 ++ ... ++ content_n`. * `separate(separator: Template, content: Template...) -> Template`: Insert separator between **non-empty** contents. * `surround(prefix: Template, suffix: Template, content: Template) -> Template`: Surround **non-empty** content with texts such as parentheses. * `config(name: String) -> ConfigValue`: Look up configuration value by `name`. Built-in Aliases[¶](https://jj-vcs.github.io/jj/prerelease/templates/#built-in-aliases "Permanent link") --------------------------------------------------------------------------------------------------------- * `hyperlink(url, text)`: Creates a clickable hyperlink using [OSC8 escape sequences](https://github.com/Alhadis/OSC8-Adoption) . The `text` will be displayed and clickable, linking to the given `url` in terminals that support OSC8 hyperlinks. Types[¶](https://jj-vcs.github.io/jj/prerelease/templates/#types "Permanent link") ----------------------------------------------------------------------------------- ### `AnnotationLine` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#annotationline-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_ The following methods are defined. * `.commit() -> Commit`: Commit responsible for changing the relevant line. * `.content() -> Template`: Line content including newline character. * `.line_number() -> Integer`: 1-based line number. * `.original_line_number() -> Integer`: 1-based line number in the original commit. * `.first_line_in_hunk() -> Boolean`: False when the directly preceding line references the same commit. ### `Boolean` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#boolean-type "Permanent link") _Conversion: `Boolean`: yes, `Serialize`: yes, `Template`: yes_ No methods are defined. Can be constructed with `false` or `true` literal. ### `Commit` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#commit-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: no_ This type cannot be printed. The following methods are defined. * `.description() -> String` * `.trailers() -> List`: The trailers at the end of the commit description that are formatted as `: `. These are returned in the same order as they appear in the description, and there may be multiple `Trailer`s with the same key. * `.change_id() -> ChangeId` * `.commit_id() -> CommitId` * `.parents() -> List` * `.author() -> Signature` * `.committer() -> Signature` * `.signature() -> Option`: Cryptographic signature if the commit was signed. * `.mine() -> Boolean`: Commits where the author's email matches the email of the current user. * `.working_copies() -> List`: For multi-workspace repositories, returns a list of workspace references for each workspace whose working-copy commit matches the current commit. * `.current_working_copy() -> Boolean`: True for the working-copy commit of the current workspace. * `.bookmarks() -> List`: Local and remote bookmarks pointing to the commit. A tracking remote bookmark will be included only if its target is different from the local one. * `.local_bookmarks() -> List`: All local bookmarks pointing to the commit. * `.remote_bookmarks() -> List`: All remote bookmarks pointing to the commit. * `.tags() -> List` * `.git_refs() -> List` * `.git_head() -> Boolean`: True for the Git `HEAD` commit. * `.divergent() -> Boolean`: True if the commit's change id corresponds to multiple visible commits. * `.hidden() -> Boolean`: True if the commit is not visible (a.k.a. abandoned). * `.immutable() -> Boolean`: True if the commit is included in [the set of immutable commits](https://jj-vcs.github.io/jj/prerelease/config/#set-of-immutable-commits) . * `.contained_in(revset: String) -> Boolean`: True if the commit is included in [the provided revset](https://jj-vcs.github.io/jj/prerelease/revsets/) . * `.conflict() -> Boolean`: True if the commit contains merge conflicts. * `.empty() -> Boolean`: True if the commit modifies no files. * `.diff([files: String]) -> TreeDiff`: Changes from the parents within [the `files` expression](https://jj-vcs.github.io/jj/prerelease/filesets/) . All files are compared by default, but it is likely to change in future version to respect the command line path arguments. * `.files([files: String]) -> List`: Files that exist in this commit, matching [the `files` expression](https://jj-vcs.github.io/jj/prerelease/filesets/) . Use `.diff().files()` to list changed files. * `.root() -> Boolean`: True if the commit is the root commit. ### `CommitEvolutionEntry` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#commitevolutionentry-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: no_ This type cannot be printed. The following methods are defined. * `.commit() -> Commit`: New commit. * `.operation() -> Operation`: Operation where the commit was created or rewritten. ### `ChangeId` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#changeid-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ The following methods are defined. * `.normal_hex() -> String`: Normal hex representation (0-9a-f) instead of the canonical "reversed" (z-k) representation. * `.short([len: Integer]) -> String` * `.shortest([min_len: Integer]) -> ShortestIdPrefix`: Shortest unique prefix. ### `CommitId` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#commitid-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ The following methods are defined. * `.short([len: Integer]) -> String` * `.shortest([min_len: Integer]) -> ShortestIdPrefix`: Shortest unique prefix. ### `CommitRef` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#commitref-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ The following methods are defined. * `.name() -> RefSymbol`: Local bookmark or tag name. * `.remote() -> Option`: Remote name if this is a remote ref. * `.present() -> Boolean`: True if the ref points to any commit. * `.conflict() -> Boolean`: True if [the bookmark or tag is conflicted](https://jj-vcs.github.io/jj/prerelease/bookmarks/#conflicts) . * `.normal_target() -> Option`: Target commit if the ref is not conflicted and points to a commit. * `.removed_targets() -> List`: Old target commits if conflicted. * `.added_targets() -> List`: New target commits. The list usually contains one "normal" target. * `.tracked() -> Boolean`: True if the ref is tracked by a local ref. The local ref might have been deleted (but not pushed yet.) * `.tracking_present() -> Boolean`: True if the ref is tracked by a local ref, and if the local ref points to any commit. * `.tracking_ahead_count() -> SizeHint`: Number of commits ahead of the tracking local ref. * `.tracking_behind_count() -> SizeHint`: Number of commits behind of the tracking local ref. * `.synced() -> Boolean`: For a local bookmark, true if synced with all tracked remotes. For a remote bookmark, true if synced with the tracking local bookmark. ### `ConfigValue` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#configvalue-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ This type can be printed in TOML syntax. The following methods are defined. * `.as_boolean() -> Boolean`: Extract boolean. * `.as_integer() -> Integer`: Extract integer. * `.as_string() -> String`: Extract string. This does not convert non-string value (e.g. integer) to string. * `.as_string_list() -> List`: Extract list of strings. ### `CryptographicSignature` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#cryptographicsignature-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_ The following methods are defined. * `.status() -> String`: The signature's status (`"good"`, `"bad"`, `"unknown"`, `"invalid"`). * `.key() -> String`: The signature's key id representation (for GPG and SSH, this is the public key fingerprint). * `.display() -> String`: The signature's display string (for GPG, this is the formatted primary user ID; for SSH, this is the principal). Warning Calling any of `.status()`, `.key()`, or `.display()` is slow, as it incurs the performance cost of verifying the signature (for example shelling out to `gpg` or `ssh-keygen`). Though consecutive calls will be faster, because the backend caches the verification result. Info As opposed to calling any of `.status()`, `.key()`, or `.display()`, checking for signature presence through boolean coercion is fast: `if(commit.signature(), "commit has a signature", "commit is unsigned")` ### `DiffStats` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#diffstats-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: no, `Template`: yes_ This type can be printed as a histogram of the changes. The following methods are defined. * `.total_added() -> Integer`: Total number of insertions. * `.total_removed() -> Integer`: Total number of deletions. ### `Email` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#email-type "Permanent link") _Conversion: `Boolean`: yes, `Serialize`: yes, `Template`: yes_ The email field of a signature may or may not look like an email address. It may be empty, may not contain the symbol `@`, and could in principle contain multiple `@`s. The following methods are defined. * `.local() -> String`: the part of the email before the first `@`, usually the username. * `.domain() -> String`: the part of the email after the first `@` or the empty string. ### `Integer` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#integer-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ No methods are defined. ### `List` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#list-type "Permanent link") _Conversion: `Boolean`: yes, `Serialize`: maybe, `Template`: maybe_ A list can be implicitly converted to `Boolean`. The following methods are defined. * `.len() -> Integer`: Number of elements in the list. * `.join(separator: Template) -> Template`: Concatenate elements with the given `separator`. * `.filter(|item| expression) -> List`: Filter list elements by predicate `expression`. Example: `description.lines().filter(|s| s.contains("#"))` * `.map(|item| expression) -> ListTemplate`: Apply template `expression` to each element. Example: `parents.map(|c| c.commit_id().short())` * `.any(|item| expression) -> Boolean`: Returns true if any element satisfies the predicate `expression`. Example: `parents.any(|c| c.description().contains("fix"))` * `.all(|item| expression) -> Boolean`: Returns true if all elements satisfy the predicate `expression`. Example: `parents.all(|c| c.mine())` ### `List` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#listtrailer-type "Permanent link") The following methods are defined. See also the `List` type. * `.contains_key(key: Stringify) -> Boolean`: True if the commit description contains at least one trailer with the key `key`. ### `ListTemplate` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#listtemplate-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: no, `Template`: yes_ The following methods are defined. See also the `List` type. * `.join(separator: Template) -> Template` ### `Operation` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#operation-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: no_ This type cannot be printed. The following methods are defined. * `.current_operation() -> Boolean` * `.description() -> String` * `.id() -> OperationId` * `.tags() -> String` * `.time() -> TimestampRange` * `.user() -> String` * `.snapshot() -> Boolean`: True if the operation is a snapshot operation. * `.root() -> Boolean`: True if the operation is the root operation. * `.parents() -> List` ### `OperationId` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#operationid-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ The following methods are defined. * `.short([len: Integer]) -> String` ### `Option` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#option-type "Permanent link") _Conversion: `Boolean`: yes, `Serialize`: maybe, `Template`: maybe_ An option can be implicitly converted to `Boolean` denoting whether the contained value is set. If set, all methods of the contained value can be invoked. If not set, an error will be reported inline on method call. On comparison between two optional values or optional and non-optional values, unset value is not an error. Unset value is considered less than any set values. ### `RefSymbol` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#refsymbol-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ [A `String` type](https://jj-vcs.github.io/jj/prerelease/templates/#string-type) , but is formatted as revset symbol by quoting and escaping if necessary. Unlike strings, this cannot be implicitly converted to `Boolean`. ### `RepoPath` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#repopath-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ A slash-separated path relative to the repository root. The following methods are defined. * `.display() -> String`: Format path for display. The formatted path uses platform-native separator, and is relative to the current working directory. * `.parent() -> Option`: Parent directory path. ### `Serialize` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#serialize-type "Permanent link") An expression that can be serialized in machine-readable format such as JSON. Note Field names and value types in the serialized output are usually stable across jj versions, but the backward compatibility isn't guaranteed. If the underlying data model is updated, the serialized output may change. ### `ShortestIdPrefix` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#shortestidprefix-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ The following methods are defined. * `.prefix() -> String` * `.rest() -> String` * `.upper() -> ShortestIdPrefix` * `.lower() -> ShortestIdPrefix` ### `Signature` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#signature-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ The following methods are defined. * `.name() -> String` * `.email() -> Email` * `.timestamp() -> Timestamp` ### `SizeHint` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#sizehint-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: no_ This type cannot be printed. The following methods are defined. * `.lower() -> Integer`: Lower bound. * `.upper() -> Option`: Upper bound if known. * `.exact() -> Option`: Exact value if upper bound is known and it equals to the lower bound. * `.zero() -> Boolean`: True if upper bound is known and is `0`. Equivalent to `.upper() == 0`. ### `String` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#string-type "Permanent link") _Conversion: `Boolean`: yes, `Serialize`: yes, `Template`: yes_ A string can be implicitly converted to `Boolean`. The following methods are defined. * `.len() -> Integer`: Length in UTF-8 bytes. * `.contains(needle: Stringify) -> Boolean`: Whether the string contains the provided stringifiable value as a substring. * `.match(needle: StringPattern) -> String`: Extracts the first matching part of the string for the given pattern. An empty string is returned if there is no match. * `.replace(pattern: StringPattern, replacement: Stringify, [limit: Integer]) -> String`: Replace occurrences of the given `pattern` with the `replacement` string. By default, all occurrences are replaced. If `limit` is specified, at most that many occurrences are replaced. Supports capture groups in patterns using `$0` (entire match), `$1`, `$2` etc. * `.first_line() -> String` * `.lines() -> List`: Split into lines excluding newline characters. * `.split(separator: StringPattern, [limit: Integer]) -> List`: Split into substrings by the given `separator` pattern. If `limit` is specified, it determines the maximum number of elements in the result, with the remainder of the string returned as the final element. A `limit` of 0 returns an empty list. * `.upper() -> String` * `.lower() -> String` * `.starts_with(needle: Stringify) -> Boolean` * `.ends_with(needle: Stringify) -> Boolean` * `.remove_prefix(needle: Stringify) -> String`: Removes the passed prefix, if present. * `.remove_suffix(needle: Stringify) -> String`: Removes the passed suffix, if present. * `.trim() -> String`: Removes leading and trailing whitespace * `.trim_start() -> String`: Removes leading whitespace * `.trim_end() -> String`: Removes trailing whitespace * `.substr(start: Integer, end: Integer) -> String`: Extract substring. The `start`/`end` indices should be specified in UTF-8 bytes. Negative values count from the end of the string. * `.escape_json() -> String`: Serializes the string in JSON format. This function is useful for making machine-readable templates. For example, you can use it in a template like `'{ "foo": ' ++ foo.escape_json() ++ ' }'` to return a JSON/JSONL. #### String literals[¶](https://jj-vcs.github.io/jj/prerelease/templates/#string-literals "Permanent link") String literals must be surrounded by single or double quotes (`'` or `"`). A double-quoted string literal supports the following escape sequences: * `\"`: double quote * `\\`: backslash * `\t`: horizontal tab * `\r`: carriage return * `\n`: new line * `\0`: null * `\e`: escape (i.e., `\x1b`) * `\xHH`: byte with hex value `HH` Other escape sequences are not supported. Any UTF-8 characters are allowed inside a string literal, with two exceptions: unescaped `"`\-s and uses of `\` that don't form a valid escape sequence. A single-quoted string literal has no escape syntax. `'` can't be expressed inside a single-quoted string literal. ### `Stringify` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#stringify-type "Permanent link") An expression that can be converted to a `String`. Any types that can be converted to `Template` can also be `Stringify`. Unlike `Template`, color labels are stripped. ### `StringPattern` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#stringpattern-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_ These are the exact same as the [String pattern type](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) in revsets, except that quotes are mandatory. Literal strings may be used, which are interpreted as case-sensitive substring matching. Currently `StringPattern` values cannot be passed around as values and may only occur directly in the call site they are used in. ### `Template` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#template-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: no, `Template`: yes_ Most types can be implicitly converted to `Template`. No methods are defined. ### `Timestamp` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#timestamp-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ The following methods are defined. * `.ago() -> String`: Format as relative timestamp. * `.format(format: String) -> String`: Format with [the specified strftime-like format string](https://docs.rs/chrono/latest/chrono/format/strftime/) . * `.utc() -> Timestamp`: Convert timestamp into UTC timezone. * `.local() -> Timestamp`: Convert timestamp into local timezone. * `.after(date: String) -> Boolean`: True if the timestamp is exactly at or after the given date. Supported date formats are the same as the revset [Date pattern type](https://jj-vcs.github.io/jj/prerelease/revsets/#date-patterns) . * `.before(date: String) -> Boolean`: True if the timestamp is before, but not including, the given date. Supported date formats are the same as the revset [Date pattern type](https://jj-vcs.github.io/jj/prerelease/revsets/#date-patterns) . ### `TimestampRange` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#timestamprange-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ The following methods are defined. * `.start() -> Timestamp` * `.end() -> Timestamp` * `.duration() -> String` ### `Trailer` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#trailer-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: no, `Template`: yes_ The following methods are defined. * `.key() -> String` * `.value() -> String` ### `TreeDiff` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#treediff-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_ This type cannot be printed. The following methods are defined. * `.files() -> List`: Changed files. * `.color_words([context: Integer]) -> Template`: Format as a word-level diff with changes indicated only by color. * `.git([context: Integer]) -> Template`: Format as a Git diff. * `.stat([width: Integer]) -> DiffStats`: Calculate stats of changed lines. * `.summary() -> Template`: Format as a list of status code and path pairs. ### `TreeDiffEntry` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#treediffentry-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_ This type cannot be printed. The following methods are defined. * `.path() -> RepoPath`: Path to the entry. If the entry is a copy/rename, this points to the target (or right) entry. * `.status() -> String`: One of `"modified"`, `"added"`, `"removed"`, `"copied"`, or `"renamed"`. * `.source() -> TreeEntry`: The source (or left) entry. * `.target() -> TreeEntry`: The target (or right) entry. ### `TreeEntry` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#treeentry-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_ This type cannot be printed. The following methods are defined. * `.path() -> RepoPath`: Path to the entry. * `.conflict() -> Boolean`: True if the entry is a merge conflict. * `.file_type() -> String`: One of `"file"`, `"symlink"`, `"tree"`, `"git-submodule"`, or `"conflict"`. * `.executable() -> Boolean`: True if the entry is an executable file. ### `WorkspaceRef` type[¶](https://jj-vcs.github.io/jj/prerelease/templates/#workspaceref-type "Permanent link") _Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_ The following methods are defined. * `.name() -> RefSymbol`: Returns the workspace name as a symbol. * `.target() -> Commit`: Returns the working-copy commit of this workspace. Color labels[¶](https://jj-vcs.github.io/jj/prerelease/templates/#color-labels "Permanent link") ------------------------------------------------------------------------------------------------- Template fragments are usually labeled with the command name, the context (or the top-level object), and the method names. You can [customize the output colors](https://jj-vcs.github.io/jj/prerelease/config/#custom-colors-and-styles) by using these labels. For example, the following template is labeled as `op_log operation id short`: `jj op log -T 'self.id().short()'` In addition to that, you can insert arbitrary labels by `label(label, content)` function. To inspect how output fragments are labeled, use `--color=debug` option. Configuration[¶](https://jj-vcs.github.io/jj/prerelease/templates/#configuration "Permanent link") --------------------------------------------------------------------------------------------------- The default templates and aliases() are defined in the `[templates]` and `[template-aliases]` sections of the config respectively. The exact definitions can be seen in the [`cli/src/config/templates.toml`](https://github.com/jj-vcs/jj/blob/main/cli/src/config/templates.toml) file in jj's source tree. New keywords and functions can be defined as aliases, by using any combination of the predefined keywords/functions and other aliases. Alias functions can be overloaded by the number of parameters. However, builtin functions will be shadowed by name, and can't co-exist with aliases. For example: `[template-aliases] 'commit_change_ids' = ''' concat( format_field("Commit ID", commit_id), format_field("Change ID", change_id), ) ''' 'format_field(key, value)' = 'key ++ ": " ++ value ++ "\n"'` Examples[¶](https://jj-vcs.github.io/jj/prerelease/templates/#examples "Permanent link") ----------------------------------------------------------------------------------------- Get short commit IDs of the working-copy parents: `jj log --no-graph -r @ -T 'parents.map(|c| c.commit_id().short()).join(",")'` Show machine-readable list of full commit and change IDs: `jj log --no-graph -T 'commit_id ++ " " ++ change_id ++ "\n"'` Print the description of the current commit, defaulting to `(no description set)`: `jj log -r @ --no-graph -T 'coalesce(description, "(no description set)\n")'` --- # Sapling comparison - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/sapling-comparison/#comparison-with-sapling) [](https://github.com/jj-vcs/jj/edit/main/docs/sapling-comparison.md "Edit this page") Comparison with Sapling[¶](https://jj-vcs.github.io/jj/prerelease/sapling-comparison/#comparison-with-sapling "Permanent link") ================================================================================================================================ Introduction[¶](https://jj-vcs.github.io/jj/prerelease/sapling-comparison/#introduction "Permanent link") ---------------------------------------------------------------------------------------------------------- This document attempts to describe how jj is different from [Sapling](https://sapling-scm.com/) . Sapling is a VCS developed by Meta. It was announced about 3 years after development started on jj. It is a heavily modified fork of [Mercurial](https://www.mercurial-scm.org/) . Because jj has copied many ideas from Mercurial, there are many similarities between the two tools, such as: * A user-friendly CLI * A "[revset](https://jj-vcs.github.io/jj/prerelease/revsets/) " language for selecting revisions * Good support for working with stacked commits, including tracking "anonymous heads" (no "detached HEAD" state like in Git) and `split` commands, and automatically rebasing descendant commits when you amend a commit. * Flexible customization of output using [templates](https://jj-vcs.github.io/jj/prerelease/templates/) Differences[¶](https://jj-vcs.github.io/jj/prerelease/sapling-comparison/#differences "Permanent link") -------------------------------------------------------------------------------------------------------- Here is a list of some differences between jj and Sapling. * **Working copy:** When using Sapling (like most VCSs), the user explicitly tells the tool when to create a commit and which files to include. When using jj, the working copy is [automatically snapshotted by every command](https://jj-vcs.github.io/jj/prerelease/working-copy/) . New files are automatically tracked and deleted files are automatically untracked. This has several advantages: * The working copy is effectively backed up every time you run a command. * No commands fail because you have changes in the working copy ("abort: 1 conflicting file changes: ..."). No need for `sl shelve`. * Simpler and more consistent CLI because the working copy is treated like any other commit. * **Conflicts:** Like most VCSs, Sapling requires the user to resolve conflicts before committing. jj lets you [commit conflicts](https://jj-vcs.github.io/jj/prerelease/conflicts/) . Note that it's a representation of the conflict that's committed, not conflict markers (`<<<<<<<` etc.). This also has several advantages: * Merge conflicts won't prevent you from checking out another commit. * You can resolve the conflicts when you feel like it. * Rebasing descendants always succeeds. Like jj, Sapling automatically rebases, but it will fail if there are conflicts. * Merge commits can be rebased correctly (Sapling sometimes fails). * You can rebase conflicts and conflict resolutions. * **Undo:** jj's undo is powered by [the operation log](https://jj-vcs.github.io/jj/prerelease/operation-log/) , which records how the repo has changed over time. Sapling has a similar feature with its [MetaLog](https://sapling-scm.com/docs/internals/metalog) . They seem to provide similar functionality, but jj also exposes the log to the user via `jj op log`, so you can tell how far back you want to go back. Sapling has `sl debugmetalog`, but that seems to show the history of a single commit, not the whole repo's history. Thanks to jj snapshotting the working copy, it's possible to undo changes to the working copy. For example, if you `jj undo` a `jj commit`, `jj diff` will show the same changes as before `jj commit`, but if you `sl undo` a `sl commit`, the working copy will be clean. * **Git interop:** Sapling supports cloning, pushing, and pulling from a remote Git repo. jj also does, and it also supports sharing a working copy with a Git repo, so you can use `jj` and `git` interchangeably in the same repo. * **Polish:** Sapling is more polished and feature-complete. Sapling has very nice built-in web UI called [Interactive Smartlog](https://sapling-scm.com/docs/addons/isl) , which lets you drag and drop commits to rebase them, among other things. * **Forge workflow:** Sapling has `sl pr submit --stack`, which lets you push a stack of commits as separate GitHub PRs, including setting the base branch. It only supports GitHub. jj doesn't have any direct integration with GitHub or any other forge. However, it has `jj git push --change` for automatically creating branches for specified commits. You have to specify each commit you want to create a branch for by using `jj git push --change X --change Y ...`, and you have to manually set up any base branches in GitHub's UI (or GitLab's or ...). On subsequent pushes, you can update all at once by specifying something like `jj git push -r main..@` (to push all branches on the current stack of commits from where it forked from `main`). --- # Core tenets - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/core_tenets/#jujutsus-core-tenets) [](https://github.com/jj-vcs/jj/edit/main/docs/core_tenets.md "Edit this page") Core tenets =========== Jujutsu's Core Tenets[¶](https://jj-vcs.github.io/jj/prerelease/core_tenets/#jujutsus-core-tenets "Permanent link") -------------------------------------------------------------------------------------------------------------------- Jujutsu's core tenets are: * Separation of logic and UI: It should be as easy to create new UIs (CLIs, GUIs, TUIs, servers) without having to duplicate logic. * Easy-to-use APIs: It should be easy to create new commands. For example, each command should not have to worry about concurrency, working-copy state, and rebasing descendants of rewritten commits. * User-friendliness: Making the working copy a commit is simpler. This is how the project started. * The repository is the source of truth: Most commands should operate on the commit graph. The working copy is just one way of editing commits. * Pluggable storage: Must be easy to integrate with different commit storage, virtual file systems and more. * Git-interop: Git is everywhere. We need to have good interop to be adopted. * All operations must be able to scale to Google-scale repos (lots of commits, lots of files): Laziness is important, must avoid accessing data unnecessarily. * Having as few states as possible. * Make it incredibly hard to lose work in your repository. * Concurrent modifications to the repo should be safe. * Allow concurrent edits on any commit, pending or finished. * Make a "stacked diffs" workflow as easy as possible. --- # Design doc blueprint - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#title) [](https://github.com/jj-vcs/jj/edit/main/docs/design_doc_blueprint.md "Edit this page") Title[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#title "Permanent link") ============================================================================================== > A cool name for your Project Author: [Your-Name](mailto:your-name@reachable.com) > If there are multiple authors, just list them all Summary[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#summary "Permanent link") -------------------------------------------------------------------------------------------------- A short summary of your project/re-design/component and what problems it addresses in about 3-10 sentences. State of the Feature as of `$VERSION` (optional)[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#state-of-the-feature-as-of-version-optional "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The state of the feature you want to improve and where it currently falls short. If there's nothing to compare to, leave it out. Prior work (optional)[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#prior-work-optional "Permanent link") ---------------------------------------------------------------------------------------------------------------------------- Does this feature exist somewhere else and which tradeoffs it made. > If there's no prior work, then use the related work section below. Goals and non-goals[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#goals-and-non-goals "Permanent link") -------------------------------------------------------------------------------------------------------------------------- Direct goals of the project and features deemed not worth pursuing. Overview[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#overview "Permanent link") ---------------------------------------------------------------------------------------------------- A detailed overview of the project and the improvements it brings. ### Detailed Design[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#detailed-design "Permanent link") The place to describe all new interfaces and interactions and how it plays into the existing code and behavior. This is the place for all nitty-gritty details which interact with the system. Alternatives considered (optional)[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#alternatives-considered-optional "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------------------ Other alternatives to your suggested approach, and why they fall short. Issues addressed (optional)[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#issues-addressed-optional "Permanent link") ---------------------------------------------------------------------------------------------------------------------------------------- A list of issues which are addressed by this design. Related Work (optional)[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#related-work-optional "Permanent link") -------------------------------------------------------------------------------------------------------------------------------- If there's a feature in another VCS which shares some similarities to your proposed work, it belongs here. An example would be Jujutsu sparse workspaces and Perforce client workspaces. Future Possibilities[¶](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/#future-possibilities "Permanent link") ---------------------------------------------------------------------------------------------------------------------------- The section for things which could be added to it or deemed out of scope during the discussion. --- # Architecture - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#architecture) [](https://github.com/jj-vcs/jj/edit/main/docs/technical/architecture.md "Edit this page") Architecture[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#architecture "Permanent link") ============================================================================================================== Data model[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#data-model "Permanent link") ---------------------------------------------------------------------------------------------------------- The commit data model is similar to [Git's object model](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) , but with some differences. Separation of library from UI[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#separation-of-library-from-ui "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------------ The `jj` binary consists of two Rust crates: the library crate (`jj-lib`) and the CLI crate (`jj-cli`). The library crate is currently only used by the CLI crate, but it is meant to also be usable from a GUI or TUI, or in a server serving requests from multiple users. As a result, the library should avoid interacting directly with the user via the terminal or by other means; all input/output is handled by the CLI crate [1](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#fn:1) . Since the library crate is meant to usable in a server, it also cannot read configuration from the user's home directory, or from user-specific environment variables. A lot of thought has gone into making the library crate's API easy to use, but not much has gone into "details" such as which collection types are used, or which symbols are exposed in the API. Storage-independent APIs[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#storage-independent-apis "Permanent link") -------------------------------------------------------------------------------------------------------------------------------------- One overarching principle in the design is that it should be easy to change where data is stored. The goal was to be able to put storage on local-disk by default but also be able to move storage to the cloud at Google (and for anyone). To that end, commits (and trees, files, etc.) are stored by the commit backend, operations (and views) are stored by the operation backend, the heads of the operation log are stored by the "op heads" backend, the commit index is stored by the index backend, and the working copy is stored by the working copy backend. The interfaces are defined in terms of plain Rust data types, not tied to a specific format. The working copy doesn't have its own trait defined yet, but its interface is small and easy to create traits for when needed. The commit backend to use when loading a repo is specified in the `.jj/repo/store/type` file. There are similar files for the other backends (`.jj/repo/index/type`, `.jj/repo/op_store/type`, `.jj/repo/op_heads/type`). Design of the library crate[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#design-of-the-library-crate "Permanent link") -------------------------------------------------------------------------------------------------------------------------------------------- ### Overview[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#overview "Permanent link") Here's a diagram showing some important types in the library crate, and how they relate. For example, given a `Workspace`, you can use it to get a `WorkingCopy` or a `RepoLoader`. A `Transaction` is required to acquire a `MutableRepo`, etc. The following sections describe each component. ![Type diagram](https://jj-vcs.github.io/jj/prerelease/technical/types.svg) _This diagram was created with [Excalidraw](https://excalidraw.com/) . You can get a copy of it [at this location](https://excalidraw.com/#json=X081PJVRW2h4iW12qZxc1,H2Yg3qnycVG0Vpq5Z90Miw) , and Right Click > "Copy to Clipboard as SVG"._ ### Backend[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#backend "Permanent link") The `Backend` trait defines the interface each commit backend needs to implement. The current in-tree commit backends are `GitBackend` and `SimpleBackend`. Since there are non-commit backends, the `Backend` trait should probably be renamed to `CommitBackend`. ### GitBackend[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#gitbackend "Permanent link") The `GitBackend` stores commits in a Git repository. It uses `libgit2` to read and write commits and refs. To prevent GC from deleting commits that are still reachable from the operation log, the `GitBackend` stores a ref for each commit in the operation log in the `refs/jj/keep/` namespace. Commit data that is available in Jujutsu's model but not in Git's model is stored in a `StackedTable` in `.jj/repo/store/extra/`. That is currently the change ID and the list of predecessors. For commits that don't have any data in that table, which is any commit created by `git`, we use an empty list as predecessors, and the bit-reversed commit ID as change ID. Because we use the Git Object ID as commit ID, two commits that differ only in their change ID, for example, will get the same commit ID, so we error out when trying to write the second one of them. ### SimpleBackend[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#simplebackend "Permanent link") The `SimpleBackend` is just a proof of concept. It stores objects addressed by their hash, with one file per object. ### Store[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#store "Permanent link") The `Store` type wraps the `Backend` and returns wrapped types for commits and trees to make them easier to use. The wrapped objects have a reference to the `Store` itself, so you can do e.g. `commit.parents()` without having to provide the `Store` as an argument. The `Store` type also provides caching of commits and trees. ### ReadonlyRepo[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#readonlyrepo "Permanent link") A `ReadonlyRepo` represents the state of a repo at a specific operation. It keeps the view object associated with that operation. The repository doesn't know where on disk any working copies live. It knows, via the view object, which commit is supposed to be the current working-copy commit in each workspace. ### MutableRepo[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#mutablerepo "Permanent link") A `MutableRepo` is a mutable version of `ReadonlyRepo`. It has a reference to its base `ReadonlyRepo`, but it has its own copy of the view object and lets the caller modify it. ### Transaction[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#transaction "Permanent link") The `Transaction` object has a `MutableRepo` and metadata that will go into the operation log. When the transaction commits, the `MutableRepo` becomes a view object in the operation log on disk, and the `Transaction` object becomes an operation object. In memory, `Transaction::commit()` returns a new `ReadonlyRepo`. ### RepoLoader[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#repoloader "Permanent link") The `RepoLoader` represents a repository at an unspecified operation. You can think of as a pointer to the `.jj/repo/` directory. It can create a `ReadonlyRepo` given an operation ID. ### TreeState[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#treestate "Permanent link") The `TreeState` type represents the state of the files in a working copy. It keep track of the mtime and size for each tracked file. It knows the `TreeId` that the working copy represents. It has a `snapshot()` method that will use the recorded mtimes and sizes and detect changes in the working copy. If anything changed, it will return a new `TreeId`. It also has `checkout()` for updating the files on disk to match a requested `TreeId`. The `TreeState` type supports sparse checkouts. In fact, all working copies are sparse; they simply track the full repo in most cases. ### WorkingCopy[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#workingcopy "Permanent link") The `WorkingCopy` type has a `TreeState` but also knows which `WorkspaceName` it has and at which operation it was most recently updated. ### Workspace[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#workspace "Permanent link") The `Workspace` type represents the combination of a repo and a working copy ( like Git's 'worktree' concept). The repo view at the current operation determines the desired working-copy commit in each workspace. The `WorkingCopy` determines what is actually in the working copy. The working copy can become stale if the working-copy commit was changed from another workspace (or if the process updating the working copy crashed, for example). ### Git[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#git "Permanent link") The `git` module contains functionality for interoperating with a Git repo, at a higher level than the `GitBackend`. The `GitBackend` is restricted by the `Backend` trait; the `git` module is specifically for Git-backed repos. It has functionality for importing refs from the Git repo and for exporting to refs in the Git repo. It also has functionality for pushing and pulling to/from Git remotes. ### Revsets[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#revsets "Permanent link") A user-provided revset expression string goes through a few different stages to be evaluated: 1. Parse the expression into a `RevsetExpression`, which is close to an AST 2. Resolve symbols and functions like `tags()` into specific commits. After this stage, the expression is still a `RevsetExpression`, but it won't have any `CommitRef` variants in it. 3. Resolve visibility. This stage resolves `visible_heads()` and `all()` and produces a `ResolvedExpression`. 4. Evaluate the `ResolvedExpression` into a `Revset`. This evaluation step is performed by `Index::evaluate_revset()`, allowing the `Revset` implementation to leverage the specifics of a custom index implementation. The first three steps are independent of the index implementation. ### StackedTable[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#stackedtable "Permanent link") `StackedTable` (actually `ReadonlyTable` and `MutableTable`) is a simple disk format for storing key-value pairs sorted by key. The keys have to have the same size but the values can have different sizes. We use our own format because we want [lock-free concurrency](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/) and there doesn't seem to be an existing key-value store we could use. The file format contains a lookup table followed by concatenated values. The lookup table is a sorted list of keys, where each key is followed by the associated value's offset in the concatenated values. A table can have a parent table. When looking up a key, if it's not found in the current table, the parent table is searched. We never update a table in place. If the number of new entries to write is less than half the number of entries in the parent table, we create a new table with the new entries and a pointer to the parent. Otherwise, we copy the entries from the parent table and the new entries into a new table with the grandparent as the parent. We do that recursively so parent tables are at least 2 times as large as child tables. This results in O(log N) amortized insertion time and lookup time. There's no garbage collection of unreachable tables yet. The tables are named by their hash. We keep a separate directory of pointers to the current leaf tables, in the same way as we do [for the operation log](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/#storage) . Design of the CLI crate[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#design-of-the-cli-crate "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------ ### Templates[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#templates "Permanent link") The concept is copied from Mercurial, but the syntax is different. The main difference is that the top-level expression is a template expression, not a string like in Mercurial. There is also no string interpolation (e.g. `"Commit ID: {node}"` in Mercurial). ### Diff-editing[¶](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#diff-editing "Permanent link") Diff-editing works by creating two very sparse working copies, containing only the files we want the user to edit. We then let the user edit the right-hand side of the diff. Then we simply snapshot that working copy to create the new tree. * * * 1. There are a few exceptions, such as for messages printed during automatic upgrades of the repo format [↩](https://jj-vcs.github.io/jj/prerelease/technical/architecture/#fnref:1 "Jump back to footnote 1 in the text") --- # Concurrency - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/#concurrency) [](https://github.com/jj-vcs/jj/edit/main/docs/technical/concurrency.md "Edit this page") Concurrency[¶](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/#concurrency "Permanent link") =========================================================================================================== Introduction[¶](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/#introduction "Permanent link") ------------------------------------------------------------------------------------------------------------- Concurrent editing is a key feature of DVCSs -- that's why they're called _Distributed_ Version Control Systems. A DVCS that didn't let users edit files and create commits on separate machines at the same time wouldn't be much of a distributed VCS. When conflicting changes are made in different clones, a DVCS will have to deal with that when you push or pull. For example, when using Mercurial, if the remote has updated a bookmark called `main` (Mercurial's bookmarks are similar to a Git's branches) and you had updated the same bookmark locally but made it point to a different target, Mercurial would add a bookmark called `main@origin` to indicate the conflict. Git instead prevents the conflict by renaming pulled branches to `origin/main` whether or not there was a conflict. However, most DVCSs treat local concurrency quite differently, typically by using lock files to prevent concurrent edits. Unlike those DVCSs, Jujutsu treats concurrent edits the same whether they're made locally or remotely. One problem with using lock files is that they don't work when the clone is in a distributed file system. Most clones are of course not stored in distributed file systems, but it is a _big_ problem when they are (Mercurial repos frequently get corrupted, for example). Another problem with using lock files is related to complexity of implementation. The simplest way of using lock files is to take coarse-grained locks early: every command that may modify the repo takes a lock at the very beginning. However, that means that operations that wouldn't actually conflict would still have to wait for each other. The user experience can be improved by using finer-grained locks and/or taking the locks later. The drawback of that is complexity. For example, you need to verify that any assumptions you made before locking are still valid after you take the lock. To avoid depending on lock files, Jujutsu takes a different approach by accepting that concurrent changes can always happen. It instead exposes any conflicting changes to the user, much like other DVCSs do for conflicting changes made remotely. ### Syncing with `rsync`, NFS, Dropbox, etc[¶](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/#syncing-with-rsync-nfs-dropbox-etc "Permanent link") Jujutsu's lock-free concurrency means that it's possible to update copies of the clone on different machines and then let `rsync` (or Dropbox, or NFS, etc.) merge them. The working copy may mismatch what's supposed to be checked out, but no changes to the repo will be lost (added commits, moved bookmarks, etc.). If conflicting changes were made, they will appear as conflicts. For example, if a bookmark was moved to two different locations, they will appear in `jj log` in both locations but with a "?" after the name, and `jj status` will also inform the user about the conflict. Note that, for now, there are known bugs in this area. Most notably, with the Git backend, [repository corruption is possible because the backend is not entirely lock-free](https://github.com/jj-vcs/jj/issues/2193) . If you know about the bug, it is relatively easy to recover from. Moreover, such use of Jujutsu is not currently thoroughly tested, especially in the context of [colocated repositories](https://jj-vcs.github.io/jj/prerelease/glossary/#colocated-repos) . While the contents of commits should be safe, concurrent modification of a repository from different computers might conceivably lose some bookmark pointers. Note that, unlike in pure Git, losing a bookmark pointer does not lead to losing commits. Operation log[¶](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/#operation-log "Permanent link") --------------------------------------------------------------------------------------------------------------- The most important piece in the lock-free design is the "operation log". That is what allows us to detect and merge divergent operations. The operation log is similar to a commit DAG (such as in [Git's object model](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) ), but each commit object is instead an "operation" and each tree object is instead a "view". The view object contains the set of visible head commits, bookmarks, tags, and the working-copy commit in each workspace. The operation object contains a pointer to the view object (like how commit objects point to tree objects), pointers to parent operation(s) (like how commit objects point to parent commit(s)), and metadata about the operation. These types are defined in `op_store.proto` The operation log is normally linear. It becomes non-linear if there are divergent operations. When a command starts, it loads the repo at the latest operation. Because the associated view object completely defines the repo state, the running command will not see any changes made by other processes thereafter. When the operation completes, it is written with the start operation as parent. The operation cannot fail to commit (except for disk failures and such). It is left for the next command to notice if there were divergent operations. It will have to be able to do that anyway since the concurrent operation could have arrived via a distributed file system. This model -- where each operation sees a consistent view of the repo and is guaranteed to be able to commit their changes -- greatly simplifies the implementation of commands. It is possible to load the repo at a particular operation with `jj --at-operation= `. If the command is mutational, that will result in a fork in the operation log. That works exactly the same as if any later operations had not existed when the command started. In other words, running commands on a repo loaded at an earlier operation works the same way as if the operations had been concurrent. This can be useful for simulating divergent operations. ### Merging divergent operations[¶](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/#merging-divergent-operations "Permanent link") If Jujutsu tries to load the repo and finds multiple heads in the operation log, it will do a 3-way merge of the view objects based on their common ancestor (possibly several 3-way merges if there were more than two heads). Conflicts are recorded in the resulting view object. For example, if bookmark `main` was moved from commit A to commit B in one operation and moved to commit C in a concurrent operation, then `main` will be recorded as "moved from A to B or C". See the `RefTarget` definition in `op_store.proto`. Because we allow bookmarks (etc.) to be in a conflicted state rather than just erroring out when there are multiple heads, the user can continue to use the repo, including performing further operations on the repo. Of course, some commands will fail when using a conflicted bookmark. For example, `jj new main` when `main` is in a conflicted state will result in an error telling you that `main` resolved to multiple revisions. ### Storage[¶](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/#storage "Permanent link") The operation objects and view objects are stored in content-addressed storage just like Git commits are. That makes them safe to write without locking. We also need a way of finding the current head of the operation log. We do that by keeping the ID of the current head(s) as a file in a directory. The ID is the name of the file; it has no contents. When an operation completes, we add a file pointing to the new operation and then remove the file pointing to the old operation. Writing the new file is what makes the operation visible (if the old file didn't get properly deleted, then future readers will take care of that). This scheme ensures that transactions are atomic. --- # Style guide - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/style_guide/#style-guide) [](https://github.com/jj-vcs/jj/edit/main/docs/style_guide.md "Edit this page") Style guide[¶](https://jj-vcs.github.io/jj/prerelease/style_guide/#style-guide "Permanent link") ================================================================================================= Panics[¶](https://jj-vcs.github.io/jj/prerelease/style_guide/#panics "Permanent link") --------------------------------------------------------------------------------------- Panics are not allowed, especially in code that may run on a server. Calling `.unwrap()` is okay if it's guaranteed to be safe by previous checks or documented invariants. For example, if a function is documented as requiring a non-empty slice as input, it's fine to call `slice[0]` and panic. Markdown[¶](https://jj-vcs.github.io/jj/prerelease/style_guide/#markdown "Permanent link") ------------------------------------------------------------------------------------------- Try to wrap at 80 columns. We don't have a formatter yet. Prefer lower-level tests to end-to-end tests[¶](https://jj-vcs.github.io/jj/prerelease/style_guide/#prefer-lower-level-tests-to-end-to-end-tests "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------------------------------- When possible, prefer lower-level tests that don't use the `jj` binary. End-to-end tests are much slower than similar tests that create a repo using `jj-lib` (roughly 100x slower). It's also often easier to test edge cases in lower-level tests. It can still be useful to add a test case or two to check that the lower-level functionality is correctly hooked up in the CLI. For example, the end-to-end tests for `jj log` don't need to test that all kinds of revsets are evaluated correctly (we have tests in `jj-lib` for that), but they should check that the `-r` flag is respected. Use end-to-end tests for testing the CLI commands themselves. --- # Design docs - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/design_docs/#jujutsu-design-docs) [](https://github.com/jj-vcs/jj/edit/main/docs/design_docs.md "Edit this page") Jujutsu Design Docs[¶](https://jj-vcs.github.io/jj/prerelease/design_docs/#jujutsu-design-docs "Permanent link") ================================================================================================================= Jujutsu uses Design Docs to drive technical decisions on large projects and it is the place to discuss your proposed design or new component. It is a very thorough process, in which the design doc must be approved before PRs for the feature will be accepted. It shares some similarities with [Rust RFCs](https://github.com/rust-lang/rfcs) but mostly addresses _technical_ problems and gauges the technical and social concerns of all stakeholders. So if you want to start building a native backend or the server component for Jujutsu, you'll need to go through this process. Process[¶](https://jj-vcs.github.io/jj/prerelease/design_docs/#process "Permanent link") ----------------------------------------------------------------------------------------- 1. Add a new markdown document to `docs/design`, named after your improvement or project. 2. Describe the current state of the world and the things you want to improve. 3. Wait for the Maintainers and Stakeholders to show up. 4. Iterate until everyone accepts the change in normal codereview fashion. Blueprint (Template)[¶](https://jj-vcs.github.io/jj/prerelease/design_docs/#blueprint-template "Permanent link") ----------------------------------------------------------------------------------------------------------------- You can find the base template of a new Design Doc [here](https://jj-vcs.github.io/jj/prerelease/design_doc_blueprint/) . --- # Code of conduct - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#contributor-covenant-code-of-conduct) [](https://github.com/jj-vcs/jj/edit/main/docs/code-of-conduct.md "Edit this page") Contributor Covenant Code of Conduct[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#contributor-covenant-code-of-conduct "Permanent link") ======================================================================================================================================================= Our Pledge[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#our-pledge "Permanent link") --------------------------------------------------------------------------------------------------- We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. Our Standards[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#our-standards "Permanent link") --------------------------------------------------------------------------------------------------------- Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting Enforcement Responsibilities[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#enforcement-responsibilities "Permanent link") --------------------------------------------------------------------------------------------------------------------------------------- Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. Scope[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#scope "Permanent link") ----------------------------------------------------------------------------------------- This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Enforcement[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#enforcement "Permanent link") ----------------------------------------------------------------------------------------------------- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at two or more of jaraco@jaraco.com, icorbrey@gmail.com, me@waleedkhan.name, and opensource@google.com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. Enforcement Guidelines[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#enforcement-guidelines "Permanent link") --------------------------------------------------------------------------------------------------------------------------- Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1\. Correction[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#1-correction "Permanent link") **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2\. Warning[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#2-warning "Permanent link") **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3\. Temporary Ban[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#3-temporary-ban "Permanent link") **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4\. Permanent Ban[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#4-permanent-ban "Permanent link") **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. Attribution[¶](https://jj-vcs.github.io/jj/prerelease/code-of-conduct/#attribution "Permanent link") ----------------------------------------------------------------------------------------------------- This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/) , version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code\_of\_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html) . Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity) . For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq) . Translations are available at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations) . --- # Releasing - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/releasing/#how-to-do-a-release) [](https://github.com/jj-vcs/jj/edit/main/docs/releasing.md "Edit this page") How to do a release[¶](https://jj-vcs.github.io/jj/prerelease/releasing/#how-to-do-a-release "Permanent link") =============================================================================================================== Update changelog and Cargo versions[¶](https://jj-vcs.github.io/jj/prerelease/releasing/#update-changelog-and-cargo-versions "Permanent link") ----------------------------------------------------------------------------------------------------------------------------------------------- Send a PR similar to [https://github.com/jj-vcs/jj/pull/5215](https://github.com/jj-vcs/jj/pull/5215) . Feel free to copy-edit the changelog in order to: * Populate "Release highlights" if relevant * Put more important items first so the reader doesn't miss them * Make items consistent when it comes to language and formatting * Catch any misplaced changelog items by looking at the CHANGELOG diff. To get the CHANGELOG diff, you can run `jj log -r 'heads(tags())' # Check that this shows the previous version jj diff --from 'heads(tags())' --to main CHANGELOG.md` Make sure to add a corresponding reference link at the bottom of the CHANGELOG for the new version's tag. It should be the github url comparing the previous version tag with the new version tag (e.g. `https://github.com/jj-vcs/jj/compare/v0.32.0...v0.33.0`). Producing the list of contributors is a bit annoying. The current suggestion is to run something like this: `root=$(jj log --no-graph -r 'heads(tags(glob:"v*.*.*") & ::trunk())' -T commit_id) filter=' map(.commits[] | select(.author.login | endswith("[bot]") | not)) | unique_by(.author.login) | map("* \(.commit.author.name) (@\(.author.login))") | .[] ' gh api "/repos/jj-vcs/jj/compare/$root...main" --paginate | jq -sr "$filter" | sort -f` [https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits](https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits) Alternatively, the list can be produced locally: `jj log --no-graph -r 'heads(tags())..main' -T '"* " ++ author ++ "\n"' | sort -fu` Then try to find the right GitHub username for each person and copy their name and username from the GitHub page for the person (e.g. [https://github.com/martinvonz](https://github.com/martinvonz) ). Get the PR through review and get it merged as usual. Create a tag and a GitHub release[¶](https://jj-vcs.github.io/jj/prerelease/releasing/#create-a-tag-and-a-github-release "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------- 1. Go to [https://github.com/jj-vcs/jj/releases](https://github.com/jj-vcs/jj/releases) and click "Draft a new release" 2. Click "Choose a tag" and enter "v0.\\.0" (e.g. "v0.26.0") to create a new tag 3. Click "Target", then "Recent commits", and select the commit from your merged PR 4. Use the name (e.g. "v0.26.0") as "Release title". Paste the changelog entries into the message body 5. Check "Create a discussion for this release" 6. Click "Publish release" Publish the crates to crates.io[¶](https://jj-vcs.github.io/jj/prerelease/releasing/#publish-the-crates-to-cratesio "Permanent link") -------------------------------------------------------------------------------------------------------------------------------------- Go to a terminal and create a new clone of the repo [1](https://jj-vcs.github.io/jj/prerelease/releasing/#fn:1) : `cd $(mktemp -d) jj git clone https://github.com/jj-vcs/jj cd jj jj new v0..0` Publish each crate: `(cd lib/proc-macros && cargo publish) (cd lib && cargo publish) (cd cli && cargo publish)` * * * 1. We recommend publishing from a new clone because `cargo publish` will archive ignored files if they match the patterns in `[include]` ([example](https://github.com/jj-vcs/jj/blob/b95628c398c6c3d11f41bdf53d0aef11f92ee96d/lib/Cargo.toml#L15-L22) ), so it's a security risk to run it an existing clone where you may have left sensitive content in an ignored file. [↩](https://jj-vcs.github.io/jj/prerelease/releasing/#fnref:1 "Jump back to footnote 1 in the text") --- # Conflicts - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/technical/conflicts/#first-class-conflicts) [](https://github.com/jj-vcs/jj/edit/main/docs/technical/conflicts.md "Edit this page") First-class conflicts[¶](https://jj-vcs.github.io/jj/prerelease/technical/conflicts/#first-class-conflicts "Permanent link") ============================================================================================================================= Introduction[¶](https://jj-vcs.github.io/jj/prerelease/technical/conflicts/#introduction "Permanent link") ----------------------------------------------------------------------------------------------------------- Conflicts can happen when two changes are applied to some state. This document is about conflicts between changes to files (not about [conflicts between changes to bookmark targets](https://jj-vcs.github.io/jj/prerelease/technical/concurrency/) , for example). For example, if you merge two branches in a repo, there may be conflicting changes between the two branches. Most DVCSs require you to resolve those conflicts before you can finish the merge operation. Jujutsu instead records the conflicts in the commit and lets you resolve the conflict when you feel like it. Data model[¶](https://jj-vcs.github.io/jj/prerelease/technical/conflicts/#data-model "Permanent link") ------------------------------------------------------------------------------------------------------- When a merge conflict happens, it is recorded as an ordered list of tree objects linked from the commit (instead of the usual single tree per commit). There will always be an odd number of trees linked from the commit. You can think of the first tree as a start tree, and the subsequent pairs of trees to apply the diff between onto the start. Examples: * If the commit has trees A, B, C, D, and E it means that the contents should be calculated as A+(C-B)+(E-D). * A three-way merge between A and C with B as base can be represented as a commit with trees A, B, and C, also known as A+(C-B). The resulting tree contents is calculated on demand. Note that we often don't need to merge the entire tree. For example, when checking out a commit in the working copy, we only need to merge parts of the tree that differs from the tree that was previously checked out in the working copy. As another example, when listing paths with conflicts, we only need to traverse parts of the tree that cannot be trivially resolved; if only one side modified `lib/`, then we don't need to look for conflicts in that sub-tree. When merging trees, if we can't resolve a sub-tree conflict trivially by looking at just the tree id, we recurse into the sub-tree. Similarly, if we can't resolve a file conflict trivially by looking at just the id, we recursive into the hunks within the file. See [here](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#format-mapping-details) for how conflicts are stored when using the Git commit backend. Conflict simplification[¶](https://jj-vcs.github.io/jj/prerelease/technical/conflicts/#conflict-simplification "Permanent link") --------------------------------------------------------------------------------------------------------------------------------- Remember that a 3-way merge can be written `A+C-B`. If one of those states is itself a conflict, then we simply insert the conflict expression there. Then we simplify by removing canceling terms. These two steps are implemented in `Merge::flatten()` and `Merge::simplify()` in [`merge.rs`](https://github.com/jj-vcs/jj/blob/main/lib/src/merge.rs) . For example, let's say commit B is based on A and is rebased to C, where it results in conflicts (`C+(B-A)`), which the user leaves unresolved. If the commit is then rebased to D, the result will be `D+((C+(B-A))-C)`. That expression can be simplified to `D+(B-A)`, which is a regular 3-way merge between D and B with A as base (no trace of C). This is what lets the user keep old commits rebased to head without resolving conflicts and still not get messy recursive conflicts. As another example, let's go through what happens when you back out a conflicted commit. Let's say we have the usual `E = C+(B-A)` conflict on top of non-conflict state `C`. We then revert that change. Reverting a change means applying its reverse diff `-(E-C)`, so the result is `E+(C-E) = (C+(B-A))+(C-(C+(B-A)))`, which we can simplify to just `C` (i.e. no conflict). Same-change rule[¶](https://jj-vcs.github.io/jj/prerelease/technical/conflicts/#same-change-rule "Permanent link") ------------------------------------------------------------------------------------------------------------------- When all sides of a conflict make the same change, [we automatically consider it resolved to that value](https://github.com/jj-vcs/jj/blob/53272510bf879086d83bb5eea1406f75ba31f138/lib/src/merge.rs#L85-L99) by default. We call this "the same-change rule". This behavior matches what Git and Mercurial do. Darcs, on the other hand, considers it a conflict. The automatic conflict resolution we do is lossy in terms of conflict algebra; it means that rebasing a commit onto a commit that has the same changes (or a subset thereof) and then rebasing it back will lose changes (for a real-life example see [bug #6369](https://github.com/jj-vcs/jj/issues/6369) ). We do it because it is more user-friendly in the vast majority of cases. --- # git-submodule-storage - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#git-submodule-storage) [](https://github.com/jj-vcs/jj/edit/main/docs/design/git-submodule-storage.md "Edit this page") Git submodule storage[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#git-submodule-storage "Permanent link") ====================================================================================================================================== Objective[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#objective "Permanent link") -------------------------------------------------------------------------------------------------------------- Decide what approach(es) to Git submodule storage we should pursue. The decision will be recorded in [./git-submodules.md](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/) . Use cases to consider[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#use-cases-to-consider "Permanent link") -------------------------------------------------------------------------------------------------------------------------------------- The submodule storage format should support the workflows specified in the [submodules roadmap](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/) . It should be obvious how "Phase 1" requirements will be supported, and we should have an idea of how "Phases 2,3,X" might be supported. Notable use cases and workflows are noted below. ### Fetching submodule commits[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#fetching-submodule-commits "Permanent link") Git's protocol is designed for communicating between copies of the same repository. Notably, a Git fetch calculates the list of required objects by performing reachability checks between the refs on the local and the remote side. We should expect that this will only work well if the submodule repository is stored as a local Git repository. Rolling our own Git fetch is too complex to be worth the effort. ### "jj op restore" and operation log format[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#jj-op-restore-and-operation-log-format "Permanent link") We want `jj op restore` to restore to an "expected" state in the submodule. There is a potential distinction between running `jj op restore` in the superproject vs in the submodule, and the expected behavior may be different in each case, e.g. in the superproject, it might be enough to restore the submodule working copy, but in the submodule, refs also need to be restored. Currently, the operation log only references objects and refs in the superproject, so it is likely that proposed approaches will need to extend this format. It is also worth considering that submodules may be added, updated or removed in superproject commits, thus the list of submodules is likely to change over the repository's lifetime. ### Nested submodules[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#nested-submodules "Permanent link") Git submodules may contain submodules themselves, so our chosen storage schemes should support that. We should consider limiting the recursion depth to avoid nasty edge cases (e.g. cyclical submodules.) that might surprise users. ### Supporting future extensions[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#supporting-future-extensions "Permanent link") There are certain extensions we may want to make in the future, but we don't have a timeline for them today. Proposed approaches should take these extensions into account (e.g. the approach should be theoretically extensible), but a full proposal for implementing them is not necessary. These extensions are: * Non-git subrepos * Colocated Git repos * The superproject using a non-git backend Proposed design[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#proposed-design "Permanent link") -------------------------------------------------------------------------------------------------------------------------- Git submodules will be stored as full jj repos. In the code, jj commands will only interact with the submodule's repo as an entire unit, e.g. it cannot query the submodule's commit backend directly. A well-abstracted submodule will extend well to non-git backends and non-git subrepos. The main challenge with this approach is that the submodule repo can be in a state that is internally valid (when considering only the submodule's repo), but invalid when considering the superproject-submodule system. This will be managed by requiring all submodule interactions go through the superproject so that superproject-submodule coordination can occur. For example, jj will not allow the user to work on the submodule's repo without going through the superproject (unlike Git). The notable workflows could be addressed like so: ### Fetching submodule commits[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#fetching-submodule-commits_1 "Permanent link") The submodule would fetch using the equivalent of `jj git fetch`. It remains to be decided how a "recursive" fetch should work, especially if a newly fetched superproject commit references an unfetched submodule commit. A reasonable approximation would be to fetch all branches in the submodule, and then, if the submodule commit is still missing, gracefully handle it. ### "jj op restore" and operation log format[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#jj-op-restore-and-operation-log-format_1 "Permanent link") As full repos, each submodule will have its own operation log. We will continue to use the existing operation log format, where each operation log tracks their own repo's commits. As commands are run in the superproject, corresponding commands will be run in the submodule as necessary, e.g. checking out a superproject commit will cause a submodule commit to also be checked out. Since there is no association between a superproject operation and a submodule operation, `jj op restore` in the superproject will not restore the submodule to a previous operation. Instead, the appropriate submodule operation(s) will be created. This is sufficient to preserve the superproject-submodule relationship; it precludes "recursive" restore (e.g. restoring branches in the superproject and submodules) but it seems unlikely that we will need such a thing. ### Nested submodules[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#nested-submodules_1 "Permanent link") Since submodules are full repos, they can contain submodules themselves. Nesting is unlikely to complicate any of the core features, since the top-level superproject/submodule relationship is almost identical to the submodule/nested submodule relationship. ### Extending to colocated Git repos[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#extending-to-colocated-git-repos "Permanent link") Git expects submodules to be in `.git/modules`, so it will not understand this storage format. To support colocated Git repos, we will have to change Git to allow a submodule's gitdir to be in an alternate location (e.g. we could add a new `submodule..gitdir` config option). This is a simple change, so it should be feasible. Alternatives considered[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#alternatives-considered "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------ ### Git repos in the main Git backend[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#git-repos-in-the-main-git-backend "Permanent link") Since the Git backend contains a Git repository, an 'obvious' default would be to store them in the Git superproject the same way Git does, i.e. in `.git/modules`. Since Git submodules are full repositories that can have submodules, this storage scheme naturally extends to nested submodules. Most of the work in storing submodules and querying them would be well-isolated to the Git backend, which gives us a lot of flexibility to make changes without affecting the rest of jj. However, the operation log will need a significant rework since it isn't designed to reference submodules, and handling edge cases (e.g. a submodule being added/removed, nested submodules) will be tricky. This is rejected because handling that operation log complexity isn't worth it when very little of the work extends to non-Git backends. ### Store Git submodules as alternate Git backends[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/#store-git-submodules-as-alternate-git-backends "Permanent link") Teach jj to use multiple commit backends and store Git submodules as Git backends. Since submodules are separate from the 'main' backend, a repository can use whatever backend it wants as its 'main' one, while still having Git submodules in the 'alternate' Git backends. This approach extends fairly well to non-Git submodules (which would be stored in non-Git commit backends). However, this requires significantly reworking the operation log to account for multiple commit backends. It is also not clear how nested submodules will be supported since there isn't an obvious way to represent a nested submodule's relationship to its superproject. --- # Temporary voting for governance - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/governance/temporary-voting/#getting-community-buy-in-for-working-group-proposals) [](https://github.com/jj-vcs/jj/edit/main/docs/governance/temporary-voting.md "Edit this page") Getting Community Buy-in for Working Group Proposals[¶](https://jj-vcs.github.io/jj/prerelease/governance/temporary-voting/#getting-community-buy-in-for-working-group-proposals "Permanent link") =================================================================================================================================================================================================== Introduction[¶](https://jj-vcs.github.io/jj/prerelease/governance/temporary-voting/#introduction "Permanent link") ------------------------------------------------------------------------------------------------------------------- We're introducing a temporary process to describe how we'll gain approval to adopt permanent governance policies - basically, how we make social and technical decisions as a community. This temporary process describes how the governance working group can propose these policies and how community members can influence them and vote on them. Once permanent governance policies are in place, the temporary process will stop being used, and the permanent governance policies will be used instead. Context[¶](https://jj-vcs.github.io/jj/prerelease/governance/temporary-voting/#context "Permanent link") --------------------------------------------------------------------------------------------------------- The governance working group was appointed by recommendation from Martin (jj's original author and current sole maintainer), without recommendation or approval from the broader jj community. This isn't a problem in itself - but it does mean that the governance working group (Austin Seipp/aseipp, Waleed Khan/arxanas, Martin von Zweigbergk/martinvonz, and Emily Shaffer/nasamuffin) needs to get some community approval before setting policy for the entire jj project. If we skip this step, we risk being perceived as exercising excessive control over the project. Goals and Non-Goals[¶](https://jj-vcs.github.io/jj/prerelease/governance/temporary-voting/#goals-and-non-goals "Permanent link") --------------------------------------------------------------------------------------------------------------------------------- * This process will be used to approve things like a `governance.md` (describing the formal structure of governance used for this project), technical design approval process, and code review process. * This is **not** a process that will be used forever. It is intended as a temporary process, only used to approve more permanent processes and policies for the project. * This process is used to gather feedback, approval, and acceptance from invested jj community members. Current members of the community should be able to participate in voting without hardship. * Current community members include code committers, code reviewers, those providing user support, those providing quality, actionable feedback, those providing documentation (first-party or third-party), developers of jj-compatible tools and add-ons (like GUIs or IDE extensions), and those providing design input and feedback. * If you feel that you are a member of the community but do not fit into one of these buckets, please reach out to one of the members of the working group to have this list expanded. * This process **is** the primary way for general community members to influence governance policies and processes. It should invite constructive feedback and help us form policies that are acceptable to the jj group as a whole. * It's intended to meet community members where they are - on GitHub and on Discord, where all development occurs and most support and technical discussion occurs. * This is **not** a process for gaining unanimous agreement - there are too many of us for that to be feasible. Instead, it is a process for gaining widespread community approval. Process[¶](https://jj-vcs.github.io/jj/prerelease/governance/temporary-voting/#process "Permanent link") --------------------------------------------------------------------------------------------------------- ### Stage 1: Advance Notice of Effort[¶](https://jj-vcs.github.io/jj/prerelease/governance/temporary-voting/#stage-1-advance-notice-of-effort "Permanent link") The working group lets the community know about upcoming policy drafts they're intending to share for approval. This must happen at least a week before entering stage 3, and ideally should happen even earlier. At this time, the working group should: * Describe why the working group feels this policy is needed * Describe the basic goals the policy should achieve * Describe implementation details that are being considered, if any * Create discussion thread on GitHub (and link to it from Discord). The GitHub discussion thread is the canonical thread for discussion and will be reused through the lifetime of a proposal as it moves through this process. At this time, the community is invited to: * Recommend additional goals, or discuss nuances of the stated goals the working group has already shared * Recommend implementation details The working group will consider these recommendations in good faith, but may choose not to adopt them. ### Stage 2: Proposal Review Period[¶](https://jj-vcs.github.io/jj/prerelease/governance/temporary-voting/#stage-2-proposal-review-period "Permanent link") This stage lasts until the working group feels major concerns have been addressed and the proposal is ready for a vote. However, **at least 72 hours** must elapse between the proposal being published and the vote starting, to allow community members around the globe to read and comment. Typically, this stage should last at least one week. At this time, the working group should: * Share the full text of the proposal as a GitHub pull request (PR) * Link this GitHub PR to the existing Discord notification thread and GitHub discussion * Explain how the proposal meets the goals stated in Stage 1, either within the proposal itself or in commentary next to the proposal At this time, the community is invited to: * Share constructive recommendations in GitHub to modify the text of the proposal, or discuss nuances of the proposal's wording * Share showstopper concerns in GitHub about the proposal, including details about how and why the concern is especially dire Think of this like a code review; the goal of this stage is to build a proposal that is representative of the community's will. Keep recommendations actionable and constructive: "This clause discourages X; if we phrase it like "foo bar baz" it could be less exclusive" is much more productive than "It's obvious that the governance working group doesn't want X!" At the discretion of the working group, but based on the outcome of the discussion, the proposal will go to a vote **or** the proposal will be dropped. ### Stage 3: Proposal Voting Period[¶](https://jj-vcs.github.io/jj/prerelease/governance/temporary-voting/#stage-3-proposal-voting-period "Permanent link") When the working group feels that major concerns have been addressed and is happy with the text of the proposal, the working group will open voting on the proposal. * Voting occurs on GitHub using the poll feature and is advertised heavily on Discord during the voting period. * If community members want to vote but aren't able to use GitHub, they can message nasamuffin@ (on Discord, or nasamuffin at google dot com) with their vote to have it manually included. Only one working group member is listed in order to avoid accidental double-counting. * When voting against, community members should comment on the post explaining why and describe what change would be required for them to abstain or vote in favor. * Generally, assume that the votes may be publicly visible or may be made publicly visible at a later time. * Voting is open for at least 1 week, but may be open as long as 2 weeks when appropriate. After that deadline, the GitHub poll will be locked. * The deadline must be announced at the beginning of the voting period - once voting has begun, the deadline cannot change. * The working group may set the voting period longer to encompass two weekends (for more participation around day jobs), for less urgent or more complex proposals, or to account for holidays during the voting period. * Participants can vote in favor or against. * "Participants" means the group of community members as enumerated at the beginning of this document. **Proposals with 2/3 or more votes in favor at the end of the voting period will be approved.** After voting has concluded, either: * The proposal will be implemented (if accepted) * The proposal may be revised and begin again at stage 2 (if rejected) * The proposal may be abandoned (if rejected) Deciding whether to revise or abandon is up to the discretion of the governance working group. The working group is expected to double-check their assumption that the goals the proposal is attempting to meet are desirable after the proposal fails to be accepted. ### Stage 4: Implementation[¶](https://jj-vcs.github.io/jj/prerelease/governance/temporary-voting/#stage-4-implementation "Permanent link") Typically, implementation will look like merging the document with the policy into the jj codebase and remembering to use that policy in conversations moving forward. In some cases, implementation may also involve nomination of individuals to a group or committee. When this is necessary, expect the policy being proposed to describe how these individuals will be nominated, both initially and moving into the future. It's possible (but unlikely) that during implementation, some obstacle will arise that means the policy doesn't actually work. If this does happen, expect the working group to be transparent with the community about the situation. We may reuse some of all of this process to figure out how to move forward. --- # Settings - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/config/#configuration) [](https://github.com/jj-vcs/jj/edit/main/docs/config.md "Edit this page") Configuration[¶](https://jj-vcs.github.io/jj/prerelease/config/#configuration "Permanent link") ================================================================================================ These are the config settings available to jj/Jujutsu. Config files and TOML[¶](https://jj-vcs.github.io/jj/prerelease/config/#config-files-and-toml "Permanent link") ---------------------------------------------------------------------------------------------------------------- `jj` loads several types of config settings: * The built-in settings. These cannot be edited. They can be viewed in the `cli/src/config/` directory in `jj`'s source repo. * The user settings. These can be edited with `jj config edit --user`. User settings are located in [the user config files](https://jj-vcs.github.io/jj/prerelease/config/#user-config-files) , which can be found with `jj config path --user`. * The repo settings. These can be edited with `jj config edit --repo` and are located in `.jj/repo/config.toml`. * The workspace settings. These can be edited with `jj config edit --workspace` and are located in `.jj/workspace-config.toml` in the workspace root. * Settings [specified in the command-line](https://jj-vcs.github.io/jj/prerelease/config/#specifying-config-on-the-command-line) . These are listed in the order they are loaded; the settings from earlier items in the list are overridden by the settings from later items if they disagree. Every type of config except for the built-in settings is optional. You can enable JSON Schema validation in your editor by adding a `#:schema` reference at the top of your TOML config files. See [JSON Schema Support](https://jj-vcs.github.io/jj/prerelease/config/#json-schema-support) for details. See the [TOML site](https://toml.io/en/) and the [syntax guide](https://toml.io/en/v1.0.0) for a detailed description of the syntax. We cover some of the basics below. The first thing to remember is that the value of a setting (the part to the right of the `=` sign) should be surrounded in quotes if it's a string. ### Dotted style and headings[¶](https://jj-vcs.github.io/jj/prerelease/config/#dotted-style-and-headings "Permanent link") In TOML, anything under a heading can be dotted instead. For example, `user.name = "YOUR NAME"` is equivalent to: `[user] name = "YOUR NAME"` For future reference, here are a couple of more complicated examples, `# Dotted style template-aliases."format_short_id(id)" = "id.shortest(12)" colors."commit_id prefix".bold = true # is equivalent to: [template-aliases] "format_short_id(id)" = "id.shortest(12)" [colors] "commit_id prefix" = { bold = true }` The docs below refer to keys in text using dotted notation, but example blocks will use heading notation to be unambiguous. If you are confident with TOML then use whichever suits you in your config. If you mix dotted keys and headings, **you must put the dotted keys before the first heading**. That's probably enough TOML to keep you out of trouble but the [syntax guide](https://toml.io/en/v1.0.0) is very short if you ever need to check. User settings[¶](https://jj-vcs.github.io/jj/prerelease/config/#user-settings "Permanent link") ------------------------------------------------------------------------------------------------ `[user] name = "YOUR NAME" email = "YOUR_EMAIL@example.com"` Don't forget to change these to your own details! UI settings[¶](https://jj-vcs.github.io/jj/prerelease/config/#ui-settings "Permanent link") -------------------------------------------------------------------------------------------- ### Colorizing output[¶](https://jj-vcs.github.io/jj/prerelease/config/#colorizing-output "Permanent link") Possible values are `always`, `never`, `debug` and `auto` (default: `auto`). `auto` will use color only when writing to a terminal. `debug` will print the active labels alongside the regular colorized output. This setting overrides the `NO_COLOR` environment variable (if set). `[ui] color = "never" # Turn off color` ### Custom colors and styles[¶](https://jj-vcs.github.io/jj/prerelease/config/#custom-colors-and-styles "Permanent link") You can customize the colors used for various elements of the UI. For example: `[colors] commit_id = "green"` The following colors are available: * black * red * green * yellow * blue * magenta * cyan * white * default All of them but "default" come in a bright version too, e.g. "bright red". The "default" color can be used to override a color defined by a parent style (explained below). You can also use a 6-digit hex code for more control over the exact color used: `[colors] change_id = "#ff1525"` `jj` also supports colors from the [ANSI 256-color palette](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) as `ansi-color-`, where `` is a number between 0 and 255: `[colors] commit_id = "ansi-color-81"` If you use a string value for a color, as in the examples above, it will be used for the foreground color. You can also set the background color, reverse colors (swap foreground and background), or make the text bold, italic, or underlined. For that, you need to use a table: `[colors] commit_id = { fg = "green", bg = "#ff1525", bold = true, underline = true } change_id = { reverse = true, italic = true }` The key names are called "labels". The above used `commit_id` as label. You can also create rules combining multiple labels. The rules work a bit like CSS selectors. For example, if you want to color commit IDs green in general but make the commit ID of the working-copy commit also be underlined, you can do this: `[colors] commit_id = "green" "working_copy commit_id" = { underline = true }` Parts of the style that are not overridden - such as the foreground color in the example above - are inherited from the style of the parent label. Which elements can be colored is not yet documented, but see the [default color configuration](https://github.com/jj-vcs/jj/blob/main/cli/src/config/colors.toml) for some examples of what's possible. ### Default command[¶](https://jj-vcs.github.io/jj/prerelease/config/#default-command "Permanent link") When `jj` is run with no explicit subcommand, the value of the `ui.default-command` setting will be used instead. Possible values are any valid subcommand name, subcommand alias, or user-defined alias (defaults to `"log"`). `[ui] default-command = ["log", "--reversed"]` ### Default description[¶](https://jj-vcs.github.io/jj/prerelease/config/#default-description "Permanent link") The editor content of a commit description can be populated by the `draft_commit_description` template. `self` is a [`Commit` object](https://jj-vcs.github.io/jj/prerelease/templates/#commit-type) . `[templates] draft_commit_description = ''' concat( builtin_draft_commit_description, "\nJJ: ignore-rest\n", diff.git(), ) '''` You can override only the `default_commit_description` value if you like, e.g.: `[template-aliases] default_commit_description = ''' " Closes #NNNN " '''` ### Duplicate commit description[¶](https://jj-vcs.github.io/jj/prerelease/config/#duplicate-commit-description "Permanent link") By default, `jj duplicate` copies the descriptions from the original commits. You can customize this behavior by specifying the `duplicate_description` template, which is given a [`Commit` object](https://jj-vcs.github.io/jj/prerelease/templates/#commit-type) of the original commit. `[templates] duplicate_description = ''' concat( description, "\n(cherry picked from commit ", commit_id, ")" ) '''` ### Bookmark listing order[¶](https://jj-vcs.github.io/jj/prerelease/config/#bookmark-listing-order "Permanent link") By default, `jj bookmark list` displays bookmarks sorted alphabetically by name. You can customize this sorting behavior by specifying sort keys in your config file: `[ui] bookmark-list-sort-keys = ["name"]` The configuration works identically to using the `--sort` option for `jj bookmark list`. The following sort keys are supported: `name`, `author-name`, `author-email`, `author-date`, `committer-name`, `committer-email`, `committer-date`. Suffix the key with `-` to sort in descending order. Multiple keys can be supplied here, the first key is the most significant. When the `--sort` option is used with `jj bookmark list`, the configuration is ignored. ### Commit trailers[¶](https://jj-vcs.github.io/jj/prerelease/config/#commit-trailers "Permanent link") You can configure automatic addition of one or more trailers to commit descriptions using the `commit_trailers` template. Each line of the template is an individual trailer, usually in `Key: Value` format. Trailers defined in this template are deduplicated with the existing description: if the entire line of a trailer is already present, it will not be added again. To deduplicate based only on the trailer key, use the `trailers.contains_key(key)` method within the template. `[templates] commit_trailers = ''' format_signed_off_by_trailer(self) ++ if(!trailers.contains_key("Change-Id"), format_gerrit_change_id_trailer(self))'''` Some ready-to-use trailer templates are available for frequently used trailers: * `format_signed_off_by_trailer(commit)` creates a "Signed-off-by" trailer using the committer info. * `format_gerrit_change_id_trailer(commit)` creates a "Change-Id" trailer suitable to be used with Gerrit. It is based Jujutsu's change id. Existing trailers are also accessible via `commit.trailers()`. ### Diff colors and styles[¶](https://jj-vcs.github.io/jj/prerelease/config/#diff-colors-and-styles "Permanent link") In color-words and git diffs, word-level hunks are rendered with underline. You can override the default style with the following keys: `[colors] # Highlight hunks with background "diff removed token" = { bg = "#221111", underline = false } "diff added token" = { bg = "#002200", underline = false } # Alternatively, swap colors "diff token" = { reverse = true, underline = false }` ### Diff format[¶](https://jj-vcs.github.io/jj/prerelease/config/#diff-format "Permanent link") `[ui] # Builtin formats: ":color-words" (default), ":git", # ":summary", ":stat", ":types", ":name-only" # or external command name and arguments (see below) diff-formatter = ":git"` #### Color-words diff options[¶](https://jj-vcs.github.io/jj/prerelease/config/#color-words-diff-options "Permanent link") In color-words diffs, changed words are displayed inline by default. Because it's difficult to read a diff line with many removed/added words, there's a threshold to switch to traditional separate-line format. You can also change the default number of lines of context shown. * `max-inline-alternation`: Maximum number of removed/added word alternation to inline. For example, ` ... ` sequence has 1 alternation, so the line will be inline if `max-inline-alternation >= 1`. ` ... ... ` sequence has 3 alternation. * `0`: disable inlining, making `--color-words` more similar to `--git` * `1`: inline removes-only or adds-only lines * `2`, `3`, ..: inline up to `2`, `3`, .. alternation * `-1`: inline all lines The default is `3`. **This parameter is experimental.** The definition is subject to change. * `conflict`: How conflicts are processed and displayed. * `"materialize"`: compare materialized contents (default) * `"pair"`: compare individual pairs **This parameter is experimental.** * `context`: Number of lines of context to show in the diff. The default is `3`. `[diff.color-words] max-inline-alternation = 3 context = 3` #### Git diff options[¶](https://jj-vcs.github.io/jj/prerelease/config/#git-diff-options "Permanent link") In git diffs you can change the default number of lines of context shown. * `context`: Number of lines of context to show in the diff. The default is `3`. `[diff.git] context = 3` ### Generating diffs by external command[¶](https://jj-vcs.github.io/jj/prerelease/config/#generating-diffs-by-external-command "Permanent link") If `ui.diff-formatter` is not a builtin format, the specified diff command will be called. `[ui] # Use Difftastic by default diff-formatter = ["difft", "--color=always", "$left", "$right"] # Use tool named "" (see below) diff-formatter = ""` The external diff tool can also be enabled by `diff --tool ` argument. For the tool named ``, command arguments can be configured as follows. `[merge-tools.] # program = "" # Defaults to the name of the tool if not specified diff-args = ["--color=always", "$left", "$right"]` * `$left` and `$right` are replaced with the paths to the left and right directories to diff respectively. * `$width` is replaced with the number of terminal columns available to the diff content. * If `diff-args` is not specified, `["$left", "$right"]` will be used by default. * If `diff-args = []`, `jj` will refuse to use this tool for diff formatting. This is a way to explicitly state that a certain tool (e.g. `mergiraf`) does not work for viewing diffs. By default `jj` will invoke external tools with a directory containing the left and right sides. The `diff-invocation-mode` config can change this to file by file invocations as follows: `[ui] diff-formatter = "vimdiff" [merge-tools.vimdiff] diff-invocation-mode = "file-by-file"` By default `jj` will display a warning when the command exits with a non-success error code. The `diff-expected-exit-codes` config can suppress this warning message for specific exit codes: `[merge-tools.delta] diff-expected-exit-codes = [0, 1]` ### Conflict marker style[¶](https://jj-vcs.github.io/jj/prerelease/config/#conflict-marker-style "Permanent link") You can configure which style of conflict markers to use when materializing conflicts: `[ui] # Shows a single snapshot and one or more diffs to apply to it conflict-marker-style = "diff" # Shows a snapshot for each side and base of the conflict conflict-marker-style = "snapshot" # Uses Git's "diff3" conflict markers to support tools that depend on it conflict-marker-style = "git"` For more details about these conflict marker styles, see the [conflicts page](https://jj-vcs.github.io/jj/prerelease/conflicts/#conflict-markers) . ### Set of immutable commits[¶](https://jj-vcs.github.io/jj/prerelease/config/#set-of-immutable-commits "Permanent link") You can configure the set of immutable commits via `revset-aliases."immutable_heads()"`. The default set of immutable heads is `builtin_immutable_heads()`, which in turn is defined as `present(trunk()) | tags() | untracked_remote_bookmarks()`. For example, to also consider the `release@origin` bookmark immutable: `[revset-aliases] "immutable_heads()" = "builtin_immutable_heads() | release@origin"` To prevent rewriting commits authored by other users: ``# The `trunk().. &` bit is an optimization to scan for non-`mine()` commits # only among commits that are not in `trunk()`. [revset-aliases] "immutable_heads()" = "builtin_immutable_heads() | (trunk().. & ~mine())"`` Ancestors of the configured set are also immutable. The root commit is always immutable even if the set is empty. Immutable commits (other than the root commit) can be rewritten using the `--ignore-immutable` CLI flag. Warning Using `--ignore-immutable` will allow you to rewrite any commit in the history, and all descendants, without warning. Use this power wisely, and remember `jj undo`. ### Behavior of prev and next commands[¶](https://jj-vcs.github.io/jj/prerelease/config/#behavior-of-prev-and-next-commands "Permanent link") If you prefer using an "edit-based" workflow, rather than squashing modifications into parent changes, you may find yourself using the `prev` and `next` commands with their `--edit` flag often to move between your changes. You can avoid having to type the `--edit` flag every time you need it by actually making it the default: `[ui.movement] edit = true` You can pass the `--no-edit` flag to `prev` and `next` if you find yourself needing the original behavior. List[¶](https://jj-vcs.github.io/jj/prerelease/config/#list "Permanent link") ------------------------------------------------------------------------------ ### Default Template[¶](https://jj-vcs.github.io/jj/prerelease/config/#default-template "Permanent link") You can configure the template used when no `-T` is specified. * `templates.config_list` for `jj config list` `[templates] # Use builtin config list template config_list = "builtin_config_list"` If you want to see the config variable origin (type and path) when you do `jj config list` you can add this to your config: `[templates] config_list = "builtin_config_list_detailed"` Log[¶](https://jj-vcs.github.io/jj/prerelease/config/#log "Permanent link") ---------------------------------------------------------------------------- ### Default revisions[¶](https://jj-vcs.github.io/jj/prerelease/config/#default-revisions "Permanent link") You can configure the revisions `jj log` would show when neither `-r` nor any paths are specified. ``[revsets] # Show commits that are not in `main@origin` log = "main@origin.."`` The default value for `revsets.log` is `'present(@) | ancestors(immutable_heads().., 2) | present(trunk())'`. ### Prioritize Revsets in the Log over @[¶](https://jj-vcs.github.io/jj/prerelease/config/#prioritize-revsets-in-the-log-over "Permanent link") In some situations the default graph can be hard to read, for example when working with big merges. To improve this behavior you can configure which revset in the `jj log` graph is displayed on the left instead of `@`. The following example will prioritize the change with the description "megamerge" with a fallback to `trunk()` in case no such change exists: `[revsets] log-graph-prioritize = "coalesce(description("megamerge\n"), trunk())"` ### Default Template[¶](https://jj-vcs.github.io/jj/prerelease/config/#default-template_1 "Permanent link") You can configure the template used when no `-T` is specified. * `templates.evolog` for `jj evolog` * `templates.log` for `jj log` * `templates.show` for `jj show` * `templates.op_log` for `jj op log` * `templates.op_show` for `jj op show` `[templates] # Use builtin evolog template evolog = "builtin_evolog_compact" # Use builtin log template log = "builtin_log_compact" # Use builtin show template show = "builtin_log_detailed" # Use builtin op log template op_log = "builtin_op_log_compact" # Use builtin op log template op_show = "builtin_op_log_compact"` If you want to see the full description when you do `jj log` you can add this to your config: `[templates] log = "builtin_log_compact_full_description"` ### Graph style[¶](https://jj-vcs.github.io/jj/prerelease/config/#graph-style "Permanent link") `[ui] # Possible values: "curved" (default), "square", "ascii", "ascii-large" graph.style = "square"` #### Node style[¶](https://jj-vcs.github.io/jj/prerelease/config/#node-style "Permanent link") The symbols used to represent commits or operations can be customized via templates. * `templates.log_node` for commits (with `Option` keywords) * `templates.op_log_node` for operations (with `Operation` keywords) For example: `[templates] log_node = ''' coalesce( if(!self, "🮀"), if(current_working_copy, "@"), if(root, "┴"), if(immutable, "●", "○"), ) ''' op_log_node = 'if(current_operation, "@", "○")'` ### Wrap log content[¶](https://jj-vcs.github.io/jj/prerelease/config/#wrap-log-content "Permanent link") If enabled, `log`/`evolog`/`op log` content will be wrapped based on the terminal width. `[ui] log-word-wrap = true` ### Display of commit and change ids[¶](https://jj-vcs.github.io/jj/prerelease/config/#display-of-commit-and-change-ids "Permanent link") Can be customized by the `format_short_id()` template alias. `[template-aliases] # Highlight unique prefix and show at least 12 characters (default) 'format_short_id(id)' = 'id.shortest(12)' # Just the shortest possible unique prefix 'format_short_id(id)' = 'id.shortest()' # Show unique prefix and the rest surrounded by brackets 'format_short_id(id)' = 'id.shortest(12).prefix() ++ "[" ++ id.shortest(12).rest() ++ "]"' # Always show 12 characters 'format_short_id(id)' = 'id.short(12)'` To customize these separately, use the `format_short_commit_id()` and `format_short_change_id()` aliases: ``[template-aliases] # Uppercase change ids. `jj` treats change and commit ids as case-insensitive. 'format_short_change_id(id)' = 'format_short_id(id).upper()'`` Operation ids can be customized by the `format_short_operation_id()` alias: `[template-aliases] # Always show 12 characters 'format_short_operation_id(id)' = 'id.short(12)'` To get shorter prefixes for certain revisions, set `revsets.short-prefixes`: `[revsets] # Prioritize the current bookmark short-prefixes = "(main..@)::"` ### Relative timestamps[¶](https://jj-vcs.github.io/jj/prerelease/config/#relative-timestamps "Permanent link") Can be customized by the `format_timestamp()` template alias. `[template-aliases] # Full timestamp in ISO 8601 format 'format_timestamp(timestamp)' = 'timestamp' # Relative timestamp rendered as "x days/hours/seconds ago" 'format_timestamp(timestamp)' = 'timestamp.ago()'` `jj op log` defaults to relative timestamps. To use absolute timestamps, you will need to modify the `format_time_range()` template alias. `[template-aliases] 'format_time_range(time_range)' = 'time_range.start() ++ " - " ++ time_range.end()'` ### Author format[¶](https://jj-vcs.github.io/jj/prerelease/config/#author-format "Permanent link") Can be customized by the `format_short_signature()` template alias. `[template-aliases] # Full email address (default) 'format_short_signature(signature)' = 'signature.email()' # Both name and email address 'format_short_signature(signature)' = 'signature' # Username part of the email address 'format_short_signature(signature)' = 'signature.email().local()'` ### Commit timestamp[¶](https://jj-vcs.github.io/jj/prerelease/config/#commit-timestamp "Permanent link") Commits have both an "author timestamp" and "committer timestamp". By default, jj displays the committer timestamp, but can be changed to show the author timestamp instead. The function must return a timestamp because the return value will likely be formatted with `format_timestamp()`. `[template-aliases] 'commit_timestamp(commit)' = 'commit.author().timestamp()'` ### Signature format[¶](https://jj-vcs.github.io/jj/prerelease/config/#signature-format "Permanent link") Can be enabled with `ui.show-cryptographic-signatures`, and customized with `format_short_cryptographic_signature(sig)` and `format_detailed_cryptographic_signature(sig)`. Note that the formatting functions take an `Option`. This allows you to emit a custom message if a signature is not present, but will raise an error if you try to access methods on a signature that is not available. `[ui] # default is false show-cryptographic-signatures = true [template-aliases] 'format_short_cryptographic_signature(sig)' = ''' if(sig, sig.status(), "(no sig)", ) '''` Pager[¶](https://jj-vcs.github.io/jj/prerelease/config/#pager "Permanent link") -------------------------------------------------------------------------------- The default pager is can be set via `ui.pager` or the `PAGER` environment variable. The priority is as follows (environment variables are marked with a `$`): `ui.pager` > `$PAGER` `less -FRX` is the default pager in the absence of any other setting, except on Windows where it is `:builtin`. The special value `:builtin` enables usage of the [integrated pager](https://jj-vcs.github.io/jj/prerelease/config/#builtin-pager) . If you are using a standard Linux distro, your system likely already has `$PAGER` set and that will be preferred over the built-in. To use the built-in: `jj config set --user ui.pager :builtin` It is possible the default will change to `:builtin` for all platforms in the future. Additionally, paging behavior can be toggled via `ui.paginate` like so: `[ui] # Enable pagination for commands that support it (default) paginate = "auto" # Disable all pagination, equivalent to using --no-pager paginate = "never"` ### Builtin pager[¶](https://jj-vcs.github.io/jj/prerelease/config/#builtin-pager "Permanent link") Our builtin pager is based on [`streampager`](https://github.com/markbt/streampager/) but is configured within `jj`'s config. It is configured via the `ui.streampager` table. #### Key bindings[¶](https://jj-vcs.github.io/jj/prerelease/config/#key-bindings "Permanent link") The built-in pager supports both navigation via arrows and Vim-style navigation. Beyond that, here are some useful keybindings for the pager: | Key | Action | | --- | --- | | `Ctrl-c` or `q` | Quit | | `h` or `F1` | Show all key bindings | | `Esc` | Close help or prompt | | `\` | Toggle line wrapping | | `#` | Toggle line numbers | | `Ctrl-r` | Toggle the ruler | The built-in pager does not support mouse input. #### Wrapping config[¶](https://jj-vcs.github.io/jj/prerelease/config/#wrapping-config "Permanent link") Wrapping performed by the pager happens _in addition to_ any wrapping that `jj` itself does. `` [ui.streampager] wrapping = "anywhere" # wrap at screen edge (default) wrapping = "word" # wrap on word boundaries wrapping = "none" # strip long lines, allow scrolling # left and right like `less -S` `` #### Auto-exit, clearing the screen on startup or exit[¶](https://jj-vcs.github.io/jj/prerelease/config/#auto-exit-clearing-the-screen-on-startup-or-exit "Permanent link") You can configure whether the pager clears the screen on startup or exit, and whether it quits automatically on short inputs. When the pager auto-quits, features like word-wrapping are disabled. ``[ui.streampager] # Do not clear screen on exit. Use a full-screen interface for long # output only. Like `less -FX`. interface = "quit-if-one-page" # (default). # Always use a full-screen interface, ask the terminal to clear the # screen on exit. Like `less -+FX`. interface = "full-screen-clear-output" # Use the alternate screen if the input is either long or takes more # than 2 seconds to finish. Similar but not identical to `less -F -+X`. interface = "quit-quickly-or-clear-output"`` #### Showing the ruler on startup[¶](https://jj-vcs.github.io/jj/prerelease/config/#showing-the-ruler-on-startup "Permanent link") `[ui.streampager] # Start with the ruler showing show-ruler = true # (default) # Start with the ruler hidden show-ruler = false` ### Processing contents to be paged[¶](https://jj-vcs.github.io/jj/prerelease/config/#processing-contents-to-be-paged "Permanent link") If you'd like to pass the output through a formatter e.g. [`diff-so-fancy`](https://github.com/so-fancy/diff-so-fancy) before piping it through a pager you must do it using a subshell as, unlike `git` or `hg`, the command will be executed directly. For example: `[ui] pager = ["sh", "-c", "diff-so-fancy | less -RFX"]` Some formatters (like [`delta`](https://github.com/dandavison/delta) ) require git style diffs for formatting. You can configure this style of diff as the default with the `ui.diff-formatter` setting. For example: `[ui] pager = "delta" diff-formatter = ":git"` Aliases[¶](https://jj-vcs.github.io/jj/prerelease/config/#aliases "Permanent link") ------------------------------------------------------------------------------------ You can define aliases for commands, including their arguments. For example: ``[aliases] # `jj l` shows commits on the working-copy commit's (anonymous) bookmark # compared to the `main` bookmark l = ["log", "-r", "(main..@):: | (main..@)-"]`` This alias syntax can only run a single jj command. However, you may want to execute multiple jj commands with a single alias, or run arbitrary scripts that complement your version control workflow. This can be done, but be aware of the danger: Warning The following technique just provides a convenient syntax for running arbitrary code on your system. Using it irresponsibly may cause damage ranging from breaking the behavior of `jj undo` to wiping your file system. Exercise the same amount of caution while writing these aliases as you would when typing commands into the terminal! This feature may be removed or replaced by an embedded scripting language in the future. The command `jj util exec` will simply run any command you pass to it as an argument. Additional arguments are passed through. Here are some examples: `[aliases] my-script = ["util", "exec", "--", "my-jj-script"] # ^^^^ # This makes sure that flags are passed to your script instead of parsed by jj. my-inline-script = ["util", "exec", "--", "bash", "-c", """ set -euo pipefail echo "Look Ma, everything in one file!" echo "args: $@" """, ""] # ^^ # This last empty string will become "$0" in bash, so your actual arguments # are all included in "$@" and start at "$1" as expected.` > Note: Shebangs (e.g. `#!/usr/bin/env`) aren't necessary since you're already explicitly passing your script into the right shell. Editor[¶](https://jj-vcs.github.io/jj/prerelease/config/#editor "Permanent link") ---------------------------------------------------------------------------------- The default editor is set via `ui.editor`, though there are several places to set it. The priority is as follows (environment variables are marked with a `$`): `$JJ_EDITOR` > `ui.editor` > `$VISUAL` > `$EDITOR` Nano is the default editor (Notepad on Windows) in the absence of any other setting, but you could set it explicitly too. `[ui] editor = "pico"` To use NeoVim instead: `[ui] editor = "nvim"` For GUI editors you possibly need to use a `-w` or `--wait`. Some examples: `[ui] editor = "code -w" # VS Code editor = "code.cmd -w" # VS Code on Windows editor = "bbedit -w" # BBEdit editor = "subl -n -w" # Sublime Text editor = "mate -w" # TextMate editor = ["C:/Program Files/Notepad++/notepad++.exe", "-multiInst", "-notabbar", "-nosession", "-noPlugin"] # Notepad++ editor = "idea --temp-project --wait" #IntelliJ` Obviously, you would only set one line, don't copy them all in! Editing diffs[¶](https://jj-vcs.github.io/jj/prerelease/config/#editing-diffs "Permanent link") ------------------------------------------------------------------------------------------------ The `ui.diff-editor` setting affects the default tool used for editing diffs (e.g. `jj split`, `jj squash -i`). If it is not set, the special value `:builtin` is used. It launches a built-in TUI tool (known as [scm-diff-editor](https://github.com/arxanas/scm-record?tab=readme-ov-file#scm-diff-editor) ) to edit the diff in your terminal. You can try a different tool temporarily by doing e.g. `jj split --tool meld` or you can set the option to change the default. This requires that you have an appropriate tool installed, see for example [the instructions for using Meld](https://jj-vcs.github.io/jj/prerelease/config/#using-meld-as-a-diff-editor) . **Suggestion:** If possible, it is recommended to try an external diff tool like [Meld](https://jj-vcs.github.io/jj/prerelease/config/#using-meld-as-a-diff-editor) (see below for some other possibilities) for splitting commits and other diff editing, in addition to the built-in diff editor. It is good to know the capabilities of both. The built-in diff editor does not require external tools to be available, is faster for tasks like picking hunks, and does not require leaving the terminal. External tools give you the flexibility of picking out portions of lines from the diff or even arbitrarily editing the text of the files. If `ui.diff-editor` is a string, e.g. `"meld"`, the arguments will be read from the following config keys. ``[merge-tools.meld] # program = "meld" # Defaults to the name of the tool if not specified program = "/path/to/meld" # May be necessary if `meld` is not in the PATH edit-args = ["--newtab", "$left", "$right"]`` `jj` makes the following substitutions: * `$left` and `$right` are replaced with the paths to the left and right directories to diff respectively. * If no `edit-args` are specified, `["$left", "$right"]` are set by default. * If `edit-args = []`, `jj` will refuse to use this tool for diff editing. This is a way to explicitly state that a certain tool (e.g. `mergiraf`) does not work for diff editing. Finally, `ui.diff-editor` can be a list that specifies a command and its arguments. Some examples: ``[ui] # Use merge-tools.meld.edit-args diff-editor = "meld" # Or `kdiff3`, or `diffedit3`, ... # Specify edit-args inline diff-editor = ["/path/to/binary", "--be-helpful", "$left", "$right"] # Equivalent to ["binary", "$left", "$right"] arguments by default diff-editor = "binary"`` ### Experimental 3-pane diff editing[¶](https://jj-vcs.github.io/jj/prerelease/config/#experimental-3-pane-diff-editing "Permanent link") We offer two special "3-pane" diff editor configs: * `meld-3`, which requires installing [Meld](https://meldmerge.org/) , and * `diffedit3`, which requires installing [`diffedit3`](https://github.com/ilyagr/diffedit3/releases) . `Meld` is a graphical application that is recommended, but can be difficult to install in some situations. `diffedit3` is designed to be easy to install and to be usable in environments where Meld is difficult to use (e.g. over SSH via port forwarding). `diffedit3` starts a local server that can be accessed via a web browser, similarly to [Jupyter](https://jupyter.org/) . There is also the `diffedit3-ssh` which is similar to `diffedit3` but does not try to open the web browser pointing to the local server (the URL printed to the terminal) automatically. `diffedit3-ssh` also always uses ports in between 17376-17380 and fails if they are all busy. This can be useful when working over SSH. Open the fold below for more details of how to set that up. Tips for using \`diffedit3-ssh\` over SSH To use \`diffedit3\` over SSH, you need to set up port forwarding. One way to do this is to start SSH as follows (copy-paste the relevant lines): `ssh -L 17376:localhost:17376 \ -L 17377:localhost:17377 \ -L 17378:localhost:17378 \ -L 17379:localhost:17379 \ -L 17380:localhost:17380 \ myhost.example.com` \`diffedit3-ssh\` is set up to use these 5 ports by default. Usually, only the first of them will be used. The rest are used if another program happens to use one of them, or if you run multiple instances of \`diffedit3\` at the same time. Another way is to add a snippet to \`~/.ssh/config\`: `Host myhost User myself Hostname myhost.example.com LocalForward 17376 localhost:17376 LocalForward 17377 localhost:17377 LocalForward 17378 localhost:17378 LocalForward 17379 localhost:17379 LocalForward 17380 localhost:17380` With that configuration, you should be able to simply \`ssh myhost\`. Setting either `ui.diff-editor = "meld-3"` or `ui.diff-editor = "diffedit3"` will result in the diff editor showing 3 panes: the diff on the left and right, and an editing pane in the middle. This allow you to see both sides of the original diff while editing. If you use `ui.diff-editor = "meld-3"`, note that you can still get the 2-pane Meld view using `jj diff --tool meld`. `diffedit3` has a button you can use to switch to a 2-pane view. To configure other diff editors in this way, you can include `$output` together with `$left` and `$right` in `merge-tools.TOOL.edit-args`. `jj` will replace `$output` with the directory where the diff editor will be expected to put the result of the user's edits. Initially, the contents of `$output` will be the same as the contents of `$right`. ### `JJ-INSTRUCTIONS`[¶](https://jj-vcs.github.io/jj/prerelease/config/#jj-instructions "Permanent link") When editing a diff, jj will include a synthetic file called `JJ-INSTRUCTIONS` in the diff with instructions on how to edit the diff. Any changes you make to this file will be ignored. To suppress the creation of this file, set `ui.diff-instructions = false`. ### Using Meld as a diff editor[¶](https://jj-vcs.github.io/jj/prerelease/config/#using-meld-as-a-diff-editor "Permanent link") [Meld](https://meldmerge.org/) is a nice and polished free diff editor. It can be obtained as follows: * **Linux:** use your favorite package manager, e.g. `sudo apt install meld`. * **Windows:** Meld can be downloaded from [https://meldmerge.org/](https://meldmerge.org/) . * **Mac OS:** Install Homebrew and run `brew install --cask meld`. This will install both an app in `/Applications/Meld.app` and the command-line `meld` command that `jj` uses. You can read about [more details and other options](https://gist.github.com/ilyagr/1b40f6061d8ad320cee4c12843df1a23) but, as of this writing, this is by far the easiest. `jj` has two diff editing configurations that use Meld: `meld` for a 2-pane view and `meld-3` for a [three-pane view](https://jj-vcs.github.io/jj/prerelease/config/#experimental-3-pane-diff-editing) . There is also a `meld` [merge tool](https://jj-vcs.github.io/jj/prerelease/config/#3-way-merge-tools-for-conflict-resolution) that can be useful, but does not support displaying the merge base while merging. ### Using Vim as a diff editor[¶](https://jj-vcs.github.io/jj/prerelease/config/#using-vim-as-a-diff-editor "Permanent link") Using `ui.diff-editor = "vimdiff"` is possible but not recommended. For a better experience, you can follow [instructions from the Wiki](https://github.com/jj-vcs/jj/wiki/Vim,-Neovim#using-vim-as-a-diff-tool) to configure the [DirDiff Vim plugin](https://github.com/will133/vim-dirdiff) and/or the [vimtabdiff Python script](https://github.com/balki/vimtabdiff) . 3-way merge tools for conflict resolution[¶](https://jj-vcs.github.io/jj/prerelease/config/#3-way-merge-tools-for-conflict-resolution "Permanent link") -------------------------------------------------------------------------------------------------------------------------------------------------------- The `ui.merge-editor` key specifies the tool used for three-way merge tools by `jj resolve`. For example: `[ui] # Use merge-tools.meld.merge-args merge-editor = "meld" # Or "vscode" or "vscodium" or "kdiff3" or "vimdiff" # Specify merge-args inline merge-editor = ["meld", "$left", "$base", "$right", "-o", "$output"]` The following tools can be used out of the box, as long as they are installed: * "kdiff3" * "meld" * "mergiraf" * "smerge" * "vimdiff" * "vscode" * "vscodium" Using VS Code as a merge tool works well with VS Code's [Remote Development](https://code.visualstudio.com/docs/remote/remote-overview) functionality, as long as `jj` is called from VS Code's terminal. ### Setting up a custom merge tool[¶](https://jj-vcs.github.io/jj/prerelease/config/#setting-up-a-custom-merge-tool "Permanent link") To use a different tool named `TOOL`, the arguments to pass to the tool MUST be specified either inline or in the `merge-tools.TOOL.merge-args` key. As an example of how to set this key and other tool configuration options, here is the out-of-the-box configuration of the three default tools. (There is no need to copy it to your config file verbatim, but you are welcome to customize it.) `[merge-tools.kdiff3] # program = "kdiff3" # Defaults to the name of the tool if not specified merge-args = ["$base", "$left", "$right", "-o", "$output", "--auto"] [merge-tools.meld] merge-args = ["$left", "$base", "$right", "-o", "$output", "--auto-merge"] [merge-tools.vimdiff] merge-args = ["-f", "-d", "$output", "-M", "$left", "$base", "$right", "-c", "wincmd J", "-c", "set modifiable", "-c", "set write"] program = "vim" merge-tool-edits-conflict-markers = true # See below for an explanation` `jj` makes the following substitutions: * `$output` (REQUIRED) is replaced with the name of the file that the merge tool should output. `jj` will read this file after the merge tool exits. * `$left` and `$right` are replaced with the paths to two files containing the content of each side of the conflict. * `$base` is replaced with the path to a file containing the contents of the conflicted file in the last common ancestor of the two sides of the conflict. * `$marker_length` is replaced with the length of the conflict markers which should be used for the file. This can be useful if the merge tool parses and/or generates conflict markers. Usually, `jj` uses conflict markers of length 7, but they can be longer if necessary to make parsing unambiguous. * `$path` is replaced with the path in the repository at which the file will be eventually stored. It is relative to the root directory of the repository and uses `/` as separators. Unlike `diff-args` or `edit-args`, there is no default value for `merge-args`. If `merge-args` are not specified, the tool cannot be used for conflict resolution. ### Editing conflict markers with a tool or a text editor[¶](https://jj-vcs.github.io/jj/prerelease/config/#editing-conflict-markers-with-a-tool-or-a-text-editor "Permanent link") By default, the merge tool starts with an empty output file. If the tool puts anything into the output file and exits with the 0 exit code, `jj` assumes that the conflict is fully resolved, while if the tool exits with a non-zero exit code, `jj` assumes that the merge should be canceled. This is appropriate for most graphical merge tools. For merge tools which try to automatically resolve conflicts without user input, this behavior may not be desired. For instance, some automatic merge tools use an exit code of 1 to indicate that some conflicts were unable to be resolved and that the output file should contain conflict markers. In that case, you could set the config option `merge-tools.TOOL.merge-conflict-exit-codes = [1]` to tell `jj` to expect conflict markers in the output file if the exit code is 1. If a merge tool produces output using Git's "diff3" conflict style, `jj` should be able to parse it correctly, so many Git merge drivers should be usable with `jj` as well. Some tools (e.g. `vimdiff`) can present a multi-way diff but don't resolve conflict themselves. When using such tools, `jj` can help you by populating the output file with conflict markers before starting the merge tool (instead of leaving the output file empty and letting the merge tool fill it in). To do that, set the `merge-tools.vimdiff.merge-tool-edits-conflict-markers = true` option. With this option set, if the output file still contains conflict markers after the conflict is done, `jj` assumes that the conflict was only partially resolved and parses the conflict markers to get the new state of the conflict. The conflict is considered fully resolved when there are no conflict markers left. The conflict marker style can also be customized per tool using the `merge-tools.TOOL.conflict-marker-style` option, which takes the same values as [`ui.conflict-marker-style`](https://jj-vcs.github.io/jj/prerelease/config/#conflict-marker-style) . Code formatting and other file content transformations[¶](https://jj-vcs.github.io/jj/prerelease/config/#code-formatting-and-other-file-content-transformations "Permanent link") ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The `jj fix` command allows you to efficiently rewrite files in complex commit graphs with no risk of introducing conflicts, using tools like `clang-format` or `prettier`. The tools run as subprocesses that take file content on standard input and repeat it, with any desired changes, on standard output. The file is only rewritten if the subprocess produces a successful exit code. ### Enforce coding style rules[¶](https://jj-vcs.github.io/jj/prerelease/config/#enforce-coding-style-rules "Permanent link") Suppose you want to use `clang-format` to format your `*.c` and `*.h` files, as well as sorting their `#include` directives. `jj fix` provides the file content anonymously on standard input, but the name of the file being formatted may be important for include sorting or other output like error messages. To address this, you can use the `$path` substitution to provide the name of the file in a command argument. `[fix.tools.clang-format] command = ["/usr/bin/clang-format", "--sort-includes", "--assume-filename=$path"] patterns = ["glob:'**/*.c'", "glob:'**/*.h'"]` ### Sort and remove duplicate lines from a file[¶](https://jj-vcs.github.io/jj/prerelease/config/#sort-and-remove-duplicate-lines-from-a-file "Permanent link") `jj fix` can also be used with tools that are not considered code formatters. Suppose you have a list of words in a text file in your repository, and you want to keep the file sorted alphabetically and remove any duplicate words. `[fix.tools.sort-word-list] command = ["sort", "-u"] patterns = ["word_list.txt"]` ### Tools stored inside the workspace[¶](https://jj-vcs.github.io/jj/prerelease/config/#tools-stored-inside-the-workspace "Permanent link") Some fix tools may be stored inside the workspace. For example, a binary may be stored inside `node_modules`. Use the `$root` variable to create an absolute path to such a program: `[fix.tools.biome] # Linux and macOS command = ["$root/node_modules/@biomejs/cli-linux-x64/biome"] # Windows command = ["$root\\node_modules\\@biomejs\\cli-win32-x64\\biome.exe"]` ### Execution order of tools[¶](https://jj-vcs.github.io/jj/prerelease/config/#execution-order-of-tools "Permanent link") If two or more tools affect the same file, they are executed in the ascending lexicographical order of their configured names. This will remain as a tie breaker if other ordering mechanisms are introduced in the future. If you use numbers in tool names to control execution order, remember to include enough leading zeros so that, for example, `09` sorts before `10`. Suppose you want to keep only the 10 smallest numbers in a text file that contains one number on each line. This can be accomplished with `sort` and `head`, but execution order is important. `[fix.tools.1-sort-numbers-file] command = ["sort", "-n"] patterns = ["numbers.txt"] [fix.tools.2-truncate-numbers-file] command = ["head", "-n", "10"] patterns = ["numbers.txt"]` ### Disabling and enabling tools[¶](https://jj-vcs.github.io/jj/prerelease/config/#disabling-and-enabling-tools "Permanent link") Tools can be disabled and enabled with the optional `enabled` config. This allows you to define tools globally but enable them only for specific repositories. In the user configuration, define a disabled tool for running rustfmt: `[fix.tools.rustfmt] enabled = false command = ["rustfmt", "--emit", "stdout"] patterns = ["glob:'**/*.rs'"]` Then to use the tool in a specific repository, set the `enabled` config: `$ jj config set --repo fix.tools.rustfmt.enabled true` Commit Signing[¶](https://jj-vcs.github.io/jj/prerelease/config/#commit-signing "Permanent link") -------------------------------------------------------------------------------------------------- `jj` can be configured to sign and verify the commits it creates using either GnuPG or SSH signing keys. To do this you need to configure a signing backend. Setting the backend to `"none"` disables signing. ### GnuPG Signing[¶](https://jj-vcs.github.io/jj/prerelease/config/#gnupg-signing "Permanent link") ``[signing] behavior = "own" backend = "gpg" ## You can set `key` to anything accepted by `gpg -u` ## If not set then defaults to the key associated with `user.email` # key = "4ED556E9729E000F" # key = "signing@example.com"`` By default the gpg backend will look for a `gpg` binary on your path. If you want to change the program used or specify a path to `gpg` explicitly you can set: `[signing] backends.gpg.program = "gpg2"` Also by default the gpg backend will consider key expiry when verifying commit signatures. To consider expired keys as valid you can set: `[signing] backends.gpg.allow-expired-keys = true` #### PKCS#12 Certificates[¶](https://jj-vcs.github.io/jj/prerelease/config/#pkcs12-certificates "Permanent link") PKCS#12 certificates can be used to sign commits using the `gpgsm` backend. ``[signing] behavior = "own" backend = "gpgsm" ## You can set `key` to anything accepted by `gpgsm -u` ## If not set then defaults to the key associated with `user.email` # key = "4ED556E9729E000F" # key = "signing@example.com"`` By default the gpgsm backend will look for a `gpgsm` binary on your path. If you want to change the program used or specify a path to `gpgsm` explicitly you can set: `[signing] backends.gpgsm.program = "gpgsm"` Also by default the gpgsm backend will consider key expiry when verifying commit signatures. To consider expired keys as valid you can set: `[signing] backends.gpgsm.allow-expired-keys = true` ### SSH Signing[¶](https://jj-vcs.github.io/jj/prerelease/config/#ssh-signing "Permanent link") `[signing] behavior = "own" backend = "ssh" key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGj+J6N6SO+4P8dOZqfR1oiay2yxhhHnagH52avUqw5h" ## You can also use a path instead of embedding the key # key = "~/.ssh/id_for_signing.pub"` By default the ssh backend will look for a `ssh-keygen` binary on your path. If you want to change the program used or specify a path to `ssh-keygen` explicitly you can set: `[signing] backends.ssh.program = "/path/to/ssh-keygen"` When verifying commit signatures the ssh backend needs to be provided with an allowed-signers file containing the public keys of authors whose signatures you want to be able to verify. You can find the format for this file in the [ssh-keygen man page](https://man.openbsd.org/ssh-keygen#ALLOWED_SIGNERS) . This can be provided as follows: `[signing] backends.ssh.allowed-signers = "/path/to/allowed-signers"` Additionally, an SSH KRL or list of revoked public keys (see [ssh-keygen man page](https://man.openbsd.org/ssh-keygen#KEY_REVOCATION_LISTS) ) can be provided with the `revocation-list` option. If a public key is found in this file then any signature relating to it is marked as invalid. `[signing] backends.ssh.revocation-list = "/path/to/revocation-list"` ### Manually signing commits[¶](https://jj-vcs.github.io/jj/prerelease/config/#manually-signing-commits "Permanent link") You can use [`jj sign`](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-sign) /[`jj unsign`](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-unsign) to sign/unsign commits manually. Warning `jj sign` always signs commits, even if they are already signed by the user. While this is cumbersome for users signing via hardware devices, we cannot reliably check if a commit is already signed without creating a signature (see [this issue](https://github.com/jj-vcs/jj/issues/5786) ). ### Automatically signing commits[¶](https://jj-vcs.github.io/jj/prerelease/config/#automatically-signing-commits "Permanent link") The `signing.behavior` configuration option has four different options for what to do with signing commits on modification of a change (e.g., rebasing or edits). * `drop`: do not automatically sign; if a change was signed before modification, drop that signing after modification. * `keep`: if a change was signed before modification, and it was authored by you, attempt to sign it again after the modification. * `own`: sign all commits that were authored by you when you modify them. * `force`: sign all commits after modification, always, even if you are not the author. Instead of signing all commits during creation when `signing.behavior` is set to `own`, the `git.sign-on-push` configuration can be used to sign commits only upon running `jj git push`. All mutable unsigned commits being pushed will be signed prior to pushing. This might be preferred if the signing backend requires user interaction or is slow, so that signing is performed in a single batch operation. `# Configure signing backend as before, but lazily signing only on push. [signing] behavior = "drop" backend = "ssh" key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGj+J6N6SO+4P8dOZqfR1oiay2yxhhHnagH52avUqw5h" [git] sign-on-push = true` Commit Signature Verification[¶](https://jj-vcs.github.io/jj/prerelease/config/#commit-signature-verification "Permanent link") -------------------------------------------------------------------------------------------------------------------------------- By default signature verification and display is **disabled** as it incurs a performance cost when rendering medium to large change logs. You can enable it by setting `ui.show-cryptographic-signatures` to true in your configuration. If you want to display commit signatures in your templates, you can use `commit.signature()` (see [Commit type](https://jj-vcs.github.io/jj/prerelease/templates/#commit-type) ). The returned [CryptographicSignature Type](https://jj-vcs.github.io/jj/prerelease/templates/#cryptographicsignature-type) provides methods to retrieve signature details. Git settings[¶](https://jj-vcs.github.io/jj/prerelease/config/#git-settings "Permanent link") ---------------------------------------------------------------------------------------------- ### Default colocation[¶](https://jj-vcs.github.io/jj/prerelease/config/#default-colocation "Permanent link") When creating a git-backed Jujutsu repository, you can disable colocation which places the `.git` directory next to the `.jj` directory. Colocation allows some amount of two-way interoperability, but it can perform worse in large repos. The setting `git.colocate` is a boolean option that controls whether or not the `jj git init` and `jj git clone` commands should create colocated repositories by default. By default, `git.colocate` is set to `true`. See [Colocated Jujutsu/Git repos](https://jj-vcs.github.io/jj/prerelease/git-compatibility/#colocated-jujutsugit-repos) for more information. ### Default remotes for `jj git fetch` and `jj git push`[¶](https://jj-vcs.github.io/jj/prerelease/config/#default-remotes-for-jj-git-fetch-and-jj-git-push "Permanent link") By default, if a single remote exists it is used for `jj git fetch` and `jj git push`; however if multiple remotes exist, the default remote is assumed to be named `"origin"`, just like in Git. Sometimes this is undesirable, e.g. when you want to fetch from a different remote than you push to, such as a GitHub fork. To change this behavior, you can modify the [repository configuration](https://jj-vcs.github.io/jj/prerelease/config/#config-files-and-toml) variable `git.fetch`, which can be a single remote, or a list of remotes to fetch from multiple places: `jj config set --repo git.fetch "upstream" jj config set --repo git.fetch '["origin", "upstream"]'` By default, the specified remote names matches exactly. You can also use a [string pattern](https://jj-vcs.github.io/jj/prerelease/revsets/#string-patterns) to select remotes using patterns: `jj config set --repo git.fetch "glob:*" jj config set --repo git.fetch '["glob:remote*", "glob:upstream*"]'` Similarly, you can also set the variable `git.push` to cause `jj git push` to push to a different remote: `jj config set --repo git.push "github"` Note that unlike `git.fetch`, `git.push` can currently only be a single remote. This is not a hard limitation, and could be changed in the future if there is demand. ### Automatic local bookmark creation[¶](https://jj-vcs.github.io/jj/prerelease/config/#automatic-local-bookmark-creation "Permanent link") When `jj` imports a new remote-tracking bookmark from Git, it can also create a local bookmark with the same name. This feature is disabled by default because it may be undesirable in some repositories, e.g.: * There is a remote with a lot of historical bookmarks that you don't want to be exported to the colocated Git repo. * There are multiple remotes with conflicting views of that bookmark, resulting in an unhelpful conflicted state. You can enable this behavior by setting `git.auto-local-bookmark` like so, `[git] auto-local-bookmark = true` This setting is applied only to new remote bookmarks. Existing remote bookmarks can be tracked individually by using `jj bookmark track`/`untrack` commands. `# import feature1 bookmark and start tracking it jj bookmark track feature1@origin # delete local gh-pages bookmark and stop tracking it jj bookmark delete gh-pages jj bookmark untrack gh-pages@upstream` ### Automatic local bookmark creation on `jj git clone`[¶](https://jj-vcs.github.io/jj/prerelease/config/#automatic-local-bookmark-creation-on-jj-git-clone "Permanent link") When cloning a new Git repository, `jj` by default creates a local bookmark tracking the default remote bookmark (such as `main` for `main@origin`.) If you aren't going to update the `main` bookmark locally, the tracking bookmark isn't necessary. This behavior can be disabled by `[git] track-default-bookmark-on-clone = false` ### Abandon commits that became unreachable in Git[¶](https://jj-vcs.github.io/jj/prerelease/config/#abandon-commits-that-became-unreachable-in-git "Permanent link") By default, when `jj` imports refs from Git, it will look for commits that used to be [reachable](https://git-scm.com/docs/gitglossary/#Documentation/gitglossary.txt-aiddefreachableareachable) but no longer are reachable. Those commits will then be abandoned, and any descendant commits will be rebased off of them (as usual when commits are abandoned). You can disable this behavior and instead leave the Git-unreachable commits in your repo by setting: `[git] abandon-unreachable-commits = false` ### Allow pushing new bookmarks[¶](https://jj-vcs.github.io/jj/prerelease/config/#allow-pushing-new-bookmarks "Permanent link") `jj git push` does not push newly-created bookmarks by default. If you do not want to specify `--allow-new` every time you have created a new bookmark, you may want to allow new bookmarks to be pushed by default: `[git] push-new-bookmarks = true` ### Generated bookmark names on push[¶](https://jj-vcs.github.io/jj/prerelease/config/#generated-bookmark-names-on-push "Permanent link") `jj git push --change` generates bookmark names with a prefix of "push-" by default. You can pick a different prefix and formatting by setting the `templates.git_push_bookmark` template. For example: `[templates] git_push_bookmark = '"martinvonz/push-" ++ change_id.short()'` This template should include expressions like `change_id` to generate unique and stable bookmark. ### Set of private commits[¶](https://jj-vcs.github.io/jj/prerelease/config/#set-of-private-commits "Permanent link") You can configure the set of private commits by setting `git.private-commits` to a revset. The value is a revset of commits that Jujutsu will refuse to push. If unset, all commits are eligible to be pushed. `[git] # Prevent pushing work in progress or anything explicitly labeled "private" private-commits = "description(glob:'wip:*') | description(glob:'private:*')"` If a commit is in `git.private-commits` but is already on the remote, then it is not considered a private commit. Commits that are immutable are also excluded from the private set. Private commits prevent their descendants from being pushed, since doing so would require pushing the private commit as well. ### Git subprocessing behavior[¶](https://jj-vcs.github.io/jj/prerelease/config/#git-subprocessing-behavior "Permanent link") Git remote interactions are handled by spawning a `git` subprocess. If `git` is not on your OS path, or you want to specify a particular binary, you can: `[git] executable-path = "/path/to/git"` Merge settings[¶](https://jj-vcs.github.io/jj/prerelease/config/#merge-settings "Permanent link") -------------------------------------------------------------------------------------------------- ### Granularity of hunks[¶](https://jj-vcs.github.io/jj/prerelease/config/#granularity-of-hunks "Permanent link") `jj` by default resolves content conflicts by splitting text into line-level hunks and merge them. This can be configured to split hunks further into word-level hunks. * `line`: split into line hunks (default) * `word`: split into word hunks `[merge] hunk-level = "line"` ### Resolution of same-change conflicts[¶](https://jj-vcs.github.io/jj/prerelease/config/#resolution-of-same-change-conflicts "Permanent link") `jj` by default resolves conflicts if all sides made the same change. This matches what Git and Mercurial do (in the 3-way case at least), but not what Darcs does. It also means that repeated 3-way merging of multiple trees may give different results depending on the order of merging. To turn it off, set `same-change = "keep"`. * `keep`: leave same-change conflict unresolved * `accept`: resolve same-change conflict to new value (default) `[merge] same-change = "accept"` Filesystem monitor[¶](https://jj-vcs.github.io/jj/prerelease/config/#filesystem-monitor "Permanent link") ---------------------------------------------------------------------------------------------------------- In large repositories, it may be beneficial to use a "filesystem monitor" to track changes to the working copy. This allows `jj` to take working copy snapshots without having to rescan the entire working copy. This is governed by the `fsmonitor.backend` option. Currently, the valid values are `"none"` or `"watchman"`. ### Watchman[¶](https://jj-vcs.github.io/jj/prerelease/config/#watchman "Permanent link") To configure the Watchman filesystem monitor, set `fsmonitor.backend = "watchman"`. Ensure that you have [installed the Watchman executable on your system](https://facebook.github.io/watchman/docs/install) . You can configure `jj` to use watchman triggers to automatically create snapshots on filesystem changes by setting `fsmonitor.watchman.register-snapshot-trigger = true`. You can check whether Watchman is enabled and whether it is installed correctly using `jj debug watchman status`. Note: `watchman` heavily uses `inotify` and sets up a user watch per-file. On large repositories, this may cause `watchman` to fail and commands like `jj status` to take longer than expected. If you experience this run `jj debug watchman status` and tune your `inotify` limits. Snapshot settings[¶](https://jj-vcs.github.io/jj/prerelease/config/#snapshot-settings "Permanent link") -------------------------------------------------------------------------------------------------------- ### Paths to automatically track[¶](https://jj-vcs.github.io/jj/prerelease/config/#paths-to-automatically-track "Permanent link") All new files in the working copy that don't match the ignore patterns are tracked by default. You can set the `snapshot.auto-track` to set which paths get automatically tracked when they're added to the working copy. See the [fileset documentation](https://jj-vcs.github.io/jj/prerelease/filesets/) for the syntax. Files with paths matching [ignore files](https://jj-vcs.github.io/jj/prerelease/working-copy/#ignored-files) are never tracked automatically. If you set `snapshot.auto-track` to a non-default value, untracked files can be tracked with `jj file track`. You can use `jj file untrack` to untrack a file while keeping it in the working copy. However, first [ignore](https://jj-vcs.github.io/jj/prerelease/working-copy/#ignored-files) them or remove them from the `snapshot.auto-track` patterns; otherwise they will be immediately tracked again. ### Maximum size for new files[¶](https://jj-vcs.github.io/jj/prerelease/config/#maximum-size-for-new-files "Permanent link") By default, as an anti-footgun measure, `jj` will refuse to add new files to the snapshot that are larger than a certain size; the default is 1MiB. This can be changed by setting `snapshot.max-new-file-size` to a different value. For example: `[snapshot] max-new-file-size = "10MiB" # the following is equivalent max-new-file-size = 10485760` The value can be specified using a human readable string with typical suffixes; `B`, `MiB`, `GB`, etc. By default, if no suffix is provided, or the value is a raw integer literal, the value is interpreted as if it were specified in bytes. Files that already exist in the working copy are not subject to this limit. Setting this value to zero will disable the limit entirely. Working copy settings[¶](https://jj-vcs.github.io/jj/prerelease/config/#working-copy-settings "Permanent link") ---------------------------------------------------------------------------------------------------------------- ### EOL conversion settings[¶](https://jj-vcs.github.io/jj/prerelease/config/#eol-conversion-settings "Permanent link") This settings serves the same purpose as the [`core.autocrlf`](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#_core_autocrlf) git config. The line endings conversion won't be applied to files detected as binary files via a heuristics[1](https://jj-vcs.github.io/jj/prerelease/config/#fn:1) regardless of the settings. This is similar to git. `[working-copy] # No EOL conversion. Similar to core.autocrlf = false. eol-conversion = "none" # Apply CRLF to LF EOL conversion when we check files in the backend store from # the local file system but not apply EOL conversion when we check out the code # from the backend store to the local file system. Similar to core.autocrlf = # input. eol-conversion = "input" # Setting this to "input-output" if you want to have CRLF line endings in your # working directory and the repository has LF line endings. Similar to # core.autocrlf = true. eol-conversion = "input-output"` Ways to specify `jj` config: details[¶](https://jj-vcs.github.io/jj/prerelease/config/#ways-to-specify-jj-config-details "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------- ### User config files[¶](https://jj-vcs.github.io/jj/prerelease/config/#user-config-files "Permanent link") An easy way to find the user config file/directory is: `jj config path --user` On all platforms, the user's global `jj` configurations are by default loaded in the following precedence order (with later configs overriding earlier ones): * `$HOME/.jjconfig.toml` * `/jj/config.toml` (preferred) * `/jj/conf.d/*.toml` where `$HOME` represents the user's home directory (`%USERPROFILE%` on Windows), and `` represents the platform-specific configuration directory shown in the table below. The platform-specific location is recommended for better integration with platform services. The files in the `conf.d` directory are loaded in lexicographic order. This allows configs to be split across multiple files and combines well with [Conditional Variables](https://jj-vcs.github.io/jj/prerelease/config/#conditional-variables) . | Platform | Location of `` dir | Example config file location | | --- | --- | --- | | Linux and macOS | `$XDG_CONFIG_HOME` or `$HOME/.config` | `/home/alice/.config/jj/config.toml` | | Windows | `{FOLDERID_RoamingAppData}` | `C:\Users\Alice\AppData\Roaming\jj\config.toml` | On macOS, jj used to put the user config in `~/Library/Application Support`, and jj will still look there for backwards compatibility purposes; this is considered a deprecated location, and you should use the new default `XDG_CONFIG_HOME`. The location of the `jj` user config files/directories can also be overridden with the `JJ_CONFIG` environment variable. If it is not empty, it will be used instead of any configuration files in the default locations. If it is a path to a TOML file, then that file will be loaded instead. If it is a path to a directory, then all the TOML files in that directory will be loaded in lexicographic order and merged. Multiple paths can be specified by separating them with a platform-specific path separator (`:` on Unix-like systems, `;` on Windows). For example, the following could be used to run `jj` without loading any user configs: `JJ_CONFIG= jj log # Ignores any settings specified in the config file.` ### JSON Schema Support[¶](https://jj-vcs.github.io/jj/prerelease/config/#json-schema-support "Permanent link") Many popular editors support TOML file syntax highlighting and validation. To enable schema validation in your editor, add this line at the top of your TOML config files: `#:schema https://jj-vcs.github.io/jj/latest/config-schema.json` This enables features like: * Autocomplete for config keys * Type checking of values * Documentation on hover * Validation of settings Here are some popular editors with TOML schema validation support: * VS Code * Install [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml) * Neovim/Vim * Use with [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) and [taplo](https://github.com/tamasfe/taplo) * Helix * Install [taplo](https://github.com/tamasfe/taplo) * JetBrains IDEs (IntelliJ, PyCharm, etc) * Install [TOML](https://plugins.jetbrains.com/plugin/8195-toml) plugin * Emacs * Install [lsp-mode](https://github.com/emacs-lsp/lsp-mode) and [toml-mode](https://github.com/dryman/toml-mode.el) * Configure [taplo](https://github.com/tamasfe/taplo) as the LSP server ### Specifying config on the command-line[¶](https://jj-vcs.github.io/jj/prerelease/config/#specifying-config-on-the-command-line "Permanent link") You can use one or more `--config`/`--config-file` options on the command line to specify additional configuration settings. This overrides settings defined in config files or environment variables. For example, ``# Must not have spaces around the `=` jj --config ui.color=always --config ui.diff-editor=meld split`` Config value should be specified as a TOML expression. If string value isn't enclosed by any TOML constructs (such as array notation), quotes can be omitted. Here is an example with more advanced TOML constructs: `# Single quotes and the '\' are interpreted by the shell and assume a Unix shell # Double quotes are passed to jj and are parsed as TOML syntax jj log --config \ 'template-aliases."format_timestamp(timestamp)"="""timestamp.format("%Y-%m-%d %H:%M %:::z")"""'` To load an entire TOML document, use `--config-file`: `jj --config-file=extra-config.toml log` ### Conditional variables[¶](https://jj-vcs.github.io/jj/prerelease/config/#conditional-variables "Permanent link") You can conditionally enable config variables by using `--when`. #### Using `[[--scope]]` tables[¶](https://jj-vcs.github.io/jj/prerelease/config/#using-scope-tables "Permanent link") Variables defined in `[[--scope]]` tables are expanded to the root table. `--when` specifies the condition to enable the scope table. If no conditions are specified, the table is always enabled. If multiple conditions are specified, their intersection is used. ``[user] name = "YOUR NAME" email = "YOUR_DEFAULT_EMAIL@example.com" # override user.email if the repository is located under ~/oss [[--scope]] --when.repositories = ["~/oss"] [--scope.user] email = "YOUR_OSS_EMAIL@example.org" # disable pagination for `jj status`, use `delta` for `jj diff` and `jj show` [[--scope]] --when.commands = ["status"] [--scope.ui] paginate = "never" [[--scope]] --when.commands = ["diff", "show"] [--scope.ui] pager = "delta"`` #### Using multiple files[¶](https://jj-vcs.github.io/jj/prerelease/config/#using-multiple-files "Permanent link") `--when` can also be used on the top level of a TOML file, which is convenient when splitting your config across multiple files. The behavior of conditions are the same as when using `[[--scope]]` tables. `# In $XDG_CONFIG_HOME/jj/config.toml [user] name = "YOUR NAME" email = "YOUR_DEFAULT_EMAIL@example.com"` `# In $XDG_CONFIG_HOME/jj/conf.d/work.toml --when.repositories = ["~/the/work/repo"] [user] email = "YOUR_WORK_EMAIL@workplace.com" [revset-aliases] work = "heads(::@ ~ description(exact:''))::" [aliases] wip = ["log", "-r", "work"]` #### Available condition keys[¶](https://jj-vcs.github.io/jj/prerelease/config/#available-condition-keys "Permanent link") * `--when.repositories`: List of paths to match the repository path prefix. Paths should be absolute. Each path component (directory or file name, drive letter, etc.) is compared case-sensitively on all platforms. A path starting with `~` is expanded to the home directory. On Windows, directory separator may be either `\` or `/`. (Beware that `\` needs escape in double-quoted strings.) Use `jj root` to see the workspace root directory. Note that the repository path is in the main workspace if you're using multiple workspaces with `jj workspace`. * `--when.workspaces`: List of paths to match the workspace path prefix. The same concerns about the path as for `--when.repositories` applies. Use `jj root` to see the workspace root directory. * `--when.commands`: List of subcommands to match. Subcommands are space-separated and matched by prefix. ``--when.commands = ["file"] # matches `jj file show`, `jj file list`, etc --when.commands = ["file show"] # matches `jj file show` but *NOT* `jj file list` --when.commands = ["file", "log"] # matches `jj file` *OR* `jj log` (or subcommand of either)`` * `--when.platforms`: List of platforms to match. The values are defined by both [`std::env::consts::FAMILY`](https://doc.rust-lang.org/std/env/consts/constant.FAMILY.html) and [`std::env::consts::OS`](https://doc.rust-lang.org/std/env/consts/constant.OS.html) . `--when.platforms = ["windows"] # matches only Windows --when.platforms = ["linux", "freebsd"] # matches Linux or and FreeBSD, but not macOS --when.platforms = ["unix"] # matches anything in the Unix family (Linux, FreeBSD, macOS, etc.)` * * * 1. To detect if a file is binary, Jujutsu currently checks if there is NULL byte in the file which is different from the algorithm of [`gitoxide`](https://github.com/GitoxideLabs/gitoxide/blob/073487b38ed40bcd7eb45dc110ae1ce84f9275a9/gix-filter/src/eol/utils.rs#L98-L100) or [`git`](https://github.com/git/git/blob/f1ca98f609f9a730b9accf24e5558a10a0b41b6c/convert.c#L94-L103) . Jujutsu doesn't plan to align the binary detection logic with git. [↩](https://jj-vcs.github.io/jj/prerelease/config/#fnref:1 "Jump back to footnote 1 in the text") --- # Development roadmap - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/roadmap/#roadmap) [](https://github.com/jj-vcs/jj/edit/main/docs/roadmap.md "Edit this page") Roadmap[¶](https://jj-vcs.github.io/jj/prerelease/roadmap/#roadmap "Permanent link") ===================================================================================== This documents some of the goals we have. Many of them are quite independent. > **Note:** Most people contributing to Jujutsu do so in their spare time, which means that we cannot attach any target dates to any of the goals below. Support for copies and renames[¶](https://jj-vcs.github.io/jj/prerelease/roadmap/#support-for-copies-and-renames "Permanent link") ----------------------------------------------------------------------------------------------------------------------------------- We want to support copy tracing in a way that leaves it up to the commit backend to either record or detect copies. That should let us work with existing Git repos (Git does not record copies, it detects them on the fly) as well as with very large repos where detection would be too slow. See [design doc](https://jj-vcs.github.io/jj/prerelease/design/copy-tracking/) . Forge integrations[¶](https://jj-vcs.github.io/jj/prerelease/roadmap/#forge-integrations "Permanent link") ----------------------------------------------------------------------------------------------------------- We would like to make it easier to work with various popular forges by providing something like `jj github submit`, `jj gitlab submit`, and `jj gerrit send`. For popular forges, we might include that support by default in the standard `jj` binary. Submodule support[¶](https://jj-vcs.github.io/jj/prerelease/roadmap/#submodule-support "Permanent link") --------------------------------------------------------------------------------------------------------- Git submodules are used frequently enough in large Git repos that we will probably need to [support them](https://github.com/jj-vcs/jj/issues/494) . There are still big open questions around UX. Better Rust API for UIs[¶](https://jj-vcs.github.io/jj/prerelease/roadmap/#better-rust-api-for-uis "Permanent link") --------------------------------------------------------------------------------------------------------------------- UIs like [gg](https://github.com/gulbanana/gg) currently have to duplicate quite a bit of logic from `jj-cli`. We need to make this code not specific to the CLI (e.g. return status objects instead of printing messages) and move it into `jj-lib`. RPC API[¶](https://jj-vcs.github.io/jj/prerelease/roadmap/#rpc-api "Permanent link") ------------------------------------------------------------------------------------- One problem with writing tools using the Rust API is that they will only work with the backends they were compiled with. For example, a regular [gg](https://github.com/gulbanana/gg) build will not work on Google repos because it doesn't have the backends necessary to load them. We want to provide an RPC API for tools that want to work with an unknown build of `jj` by having the tool run something like `jj api` to give it an address to talk to. In addition to helping with the problem of unknown backends, having an RPC API should make it easier for tools like VS Code that are not written in Rust. The RPC API will probably be at a higher abstraction level than the Rust API. See [design doc](https://docs.google.com/document/d/1rOKvutee5TVYpFhh_UDNZDxfUKyrJ8rjCNpFaNHOHwU/edit?usp=sharing&resourcekey=0-922ApyoAjuXN_uTKqmCqjg) . Open-source cloud-based repos (server and daemon process)[¶](https://jj-vcs.github.io/jj/prerelease/roadmap/#open-source-cloud-based-repos-server-and-daemon-process "Permanent link") --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Google has an internal Jujutsu server backed by a database. This server allows commits and repos (operation logs) to be stored in the cloud (i.e. the database). Working copies can still be stored locally. In order to reduce latency, there is a local daemon process that caches reads and writes. It also prefetches of objects it thinks the client might ask for next. In also helps with write latency by optimistically answering write requests (it therefore needs to know the server's hashing scheme so it can return the right IDs). We (the project, not necessarily Google) want to provide a similar experience for all users. We would therefore like to create a similar server and daemon. The daemon might be the same process as for the RPC API mentioned above. Virtual file system (VFS)[¶](https://jj-vcs.github.io/jj/prerelease/roadmap/#virtual-file-system-vfs "Permanent link") ----------------------------------------------------------------------------------------------------------------------- For very large projects and/or large files, it can be expensive to update the working copy. We want to provide a VFS to help with that. Updating the working copy to another commit can then be done simply by telling the VFS to use the other commit as base, without needing to download any large files in the target commit until the user asks for them via the file system. A VFS can also make it cheap to snapshot the working copy by keeping track of all changes compared to the base commit. Having a VFS can also be very benefial for [`jj run`](https://github.com/jj-vcs/jj/issues/1869) , since we can then cheaply create temporary working copies for the commands to run in. Better support for large files[¶](https://jj-vcs.github.io/jj/prerelease/roadmap/#better-support-for-large-files "Permanent link") ----------------------------------------------------------------------------------------------------------------------------------- We have talked about somehow using content-defined chunking (CDC) to reduce storage and transfer costs for large files. Maybe we will store files in our future cloud-based server using the same model as [XetHub](https://web.archive.org/web/20240914200921/https://xethub.com/assets/docs/concepts/xet-storage) . --- # JJ run - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/design/run/#introducing-jj-run) [](https://github.com/jj-vcs/jj/edit/main/docs/design/run.md "Edit this page") Introducing JJ run[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#introducing-jj-run "Permanent link") ============================================================================================================== Authors: [Philip Metzger](mailto:philipmetzger@bluewin.ch) , [Martin von Zweigberk](mailto:martinvonz@google.com) , [Danny Hooper](mailto:hooper@google.com) , [Waleed Khan](mailto:me@waleedkhan.name) Initial Version, 10.12.2022 (view full history [here](https://docs.google.com/document/d/14BiAoEEy_e-BRPHYpXRFjvHMfgYVKh-pKWzzTDi-v-g/edit) ) **Summary:** This Document documents the design of a new `run` command for Jujutsu which will be used to seamlessly integrate with build systems, linters and formatters. This is achieved by running a user-provided command or script across multiple revisions. For more details, read the [Use-Cases of jj run](https://jj-vcs.github.io/jj/prerelease/design/run/#use-cases-of-jj-run) . Preface[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#preface "Permanent link") ---------------------------------------------------------------------------------------- The goal of this Design Document is to specify the correct behavior of `jj run`. The points we decide on here I (Philip Metzger) will try to implement. There exists some prior work in other DVCS: * `git test`: part of [git-branchless](https://github.com/arxanas/git-branchless) . Similar to this proposal for `jj run`. * `hg run`: Google's internal Mercurial extension. Similar to this proposal for `jj run`. Details not available. * `hg fix`: Google's open source Mercurial extension: [source code](https://repo.mercurial-scm.org/hg/file/tip/hgext/fix.py) . A more specialized approach to rewriting file content without full context of the working directory. * `git rebase -x`: runs commands opportunistically as part of rebase. * `git bisect run`: run a command to determine which commit introduced a bug. Context and Scope[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#context-and-scope "Permanent link") ------------------------------------------------------------------------------------------------------------ The initial need for some kind of command runner integrated in the VCS, surfaced in a [github discussion](https://github.com/jj-vcs/jj/issues/405) . In a [discussion on discord](https://discord.com/channels/968932220549103686/969829516539228222/1047958933161119795) about the git-hook model, there was consensus about not repeating their mistakes. For `jj run` there is prior art in Mercurial, git branchless and Google's internal Mercurial. Currently git-branchless `git test` and `hg fix` implement some kind of command runner. The Google internal `hg run` works in conjunction with CitC (Clients in the Cloud) which allows it to lazily apply the current command to any affected file. Currently no open-source Jujutsu backend (Git, Simple) has a fancy virtual filesystem supporting it, so we can't apply this optimization. We could do the same once we have an implementation of the working copy based on a virtual file system. Until then, we have to run the commands in regular local-disk working copies. Goals and Non-Goals[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#goals-and-non-goals "Permanent link") ---------------------------------------------------------------------------------------------------------------- ### Goals[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#goals "Permanent link") * We should be able to apply the command to any revision, published or unpublished. * We should be able to parallelize running the actual command, while preserving a good console output. * The run command should be able to work in any commit, the working-copy commit itself or any other commit. * There should exist some way to signal hard failure. * The command should build enough infrastructure for `jj test`, `jj fix` and `jj format`. * The main goal is to be good enough, as we can always expand the functionality in the future. ### Non-Goals[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#non-goals "Permanent link") * While we should build a base for `jj test`, `jj format` and `jj fix`, we shouldn't mash their use-cases into `jj run`. * The command shouldn't be too smart, as too many assumptions about workflows makes the command confusing for users. * The smart caching of outputs, as user input commands can be unpredictable. makes the command confusing for users. * Avoid the smart caching of outputs, as user input commands can be unpredictable. * Fine grained user facing configuration, as it's unwarranted complexity. * A `fix` subcommand as it cuts too much design space. Use-Cases of jj run[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#use-cases-of-jj-run "Permanent link") ---------------------------------------------------------------------------------------------------------------- **Linting and Formatting:** * `jj run 'pre-commit run' -r $revset` * `jj run 'cargo clippy' -r $revset` * `jj run 'cargo +nightly fmt'` **Large scale changes across repositories, local and remote:** * `jj run 'sed /some/test/' -r 'mine() & ~remote_bookmarks(exact:"origin")'` * `jj run '$rewrite-tool' -r '$revset'` **Build systems:** * `jj run 'bazel build //some/target:somewhere'` * `jj run 'ninja check-lld'` Some of these use-cases should get a specialized command, as this allows further optimization. A command could be `jj format`, which runs a list of formatters over a subset of a file in a revision. Another command could be `jj fix`, which runs a command like `rustfmt --fix` or `cargo clippy --fix` over a subset of a file in a revision. Design[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#design "Permanent link") -------------------------------------------------------------------------------------- ### Base Design[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#base-design "Permanent link") All the work will be done in the `.jj/` directory. This allows us to hide all complexity from the users, while preserving the user's current workspace. We will copy the approach from git-branchless's `git test` of creating a temporary working copy for each parallel command. The working copies will be reused between `jj run` invocations. They will also be reused within `jj run` invocation if there are more commits to run on than there are parallel jobs. We will leave ignored files in the temporary directory between runs. That enables incremental builds (e.g. by letting cargo reuse its `target/` directory). However, it also means that runs potentially become less reproducible. We will provide a flag for removing ignored files from the temporary working copies to address that. Another problem with leaving ignored files in the temporary directories is that they take up space. That is especially problematic in the case of cargo (the `target/` directory often takes up tens of GBs). The same flag for cleaning up ignored files can be used to address that. We may want to also have a flag for cleaning up temporary working copies _after_ running the command. An early version of the command will directly use [Treestate](https://github.com/jj-vcs/jj/blob/af85f552b676d66ed0e9ae0d401cd0c4ffbbeb21/lib/src/working_copy.rs#L117) to to manage the temporary working copies. That means that running `jj` inside the temporary working copies will not work . We can later extend that to use a full [Workspace](https://github.com/jj-vcs/jj/blob/af85f552b676d66ed0e9ae0d401cd0c4ffbbeb21/lib/src/workspace.rs#L54) . To prevent operations in the working copies from impacting the repo, we can use a separate [OpHeadsStore](https://github.com/jj-vcs/jj/blob/main/lib/src/op_heads_store.rs) for it. ### Modifying the Working Copy[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#modifying-the-working-copy "Permanent link") Since the subprocesses will run in temporary working copies, they won't interfere with the user's working copy. The user can therefore continue to work in it while `jj run` is running. We want subprocesses to be able to make changes to the repo by updating their assigned working copy. Let's say the user runs `jj run` on just commits A and B, where B's parent is A. Any changes made on top of A would be squashed into A, forming A'. Similarly B' would be formed by squasing it into B. We can then either do a normal rebase of B' onto A', or we can simply update its parent to A'. The former is useful, e.g. when the subprocess only makes a partial update of the tree based on the parent commit. In addition to these two modes, we may want to have an option to ignore any changes made in the subprocess's working copy. ### Modifying the Repo[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#modifying-the-repo "Permanent link") Once we give the subprocess access to a fork of the repo via separate [OpHeadsStore](https://github.com/jj-vcs/jj/blob/main/lib/src/op_heads_store.rs) , it will be able to create new operations in its fork. If the user runs `jj run -r foo` and the subprocess checks out another commit, it's not clear what that should do. We should probably just verify that the working-copy commit's parents are unchanged after the subprocess returns. Any operations created by the subprocess will be ignored. ### Rewriting the revisions[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#rewriting-the-revisions "Permanent link") Like all commands, `jj run` will refuse to rewrite public/immutable commits. For private/unpublished revisions, we either amend or reparent the changes, which are available as command options. Execution order/parallelism[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#execution-orderparallelism "Permanent link") ------------------------------------------------------------------------------------------------------------------------------- It may be useful to execute commands in topological order. For example, commands with costs proportional to incremental changes, like build systems. There may also be other relevant heuristics, but topological order is an easy and effective way to start. Parallel execution of commands on different commits may choose to schedule commits to still reduce incremental changes in the working copy used by each execution slot/"thread". However, running the command on all commits concurrently should be possible if desired. Executing commands in topological order allows for more meaningful use of any potential features that stop execution "at the first failure". For example, when running tests on a chain of commits, it might be useful to proceed in topological/chronological order, and stop on the first failure, because it might imply that the remaining executions will be undesirable because they will also fail. Dealing with failure[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#dealing-with-failure "Permanent link") ------------------------------------------------------------------------------------------------------------------ It will be useful to have multiple strategies to deal with failures on a single or multiple revisions. The reason for these strategies is to allow customized conflict handling. These strategies then can be exposed in the ui with a matching option. **Continue:** If any subprocess fails, we will continue the work on child revisions. Notify the user on exit about the failed revisions. **Stop:** Signal a fatal failure and cancel any scheduled work that has not yet started running, but let any already started subprocess finish. Notify the user about the failed command and display the generated error from the subprocess. **Fatal:** Signal a fatal failure and immediately stop processing and kill any running processes. Notify the user that we failed to apply the command to the specific revision. We will leave any affected commit in its current state, if any subprocess fails. This allows us to provide a better user experience, as leaving revisions in an undesirable state, e.g partially formatted, may confuse users. Resource constraints[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#resource-constraints "Permanent link") ------------------------------------------------------------------------------------------------------------------ It will be useful to constrain the execution to prevent resource exhaustion. Relevant resources could include: * CPU and memory available on the machine running the commands. `jj run` can provide some simple mitigations like limiting parallelism to "number of CPUs" by default, and limiting parallelism by dividing "available memory" by some estimate or measurement of per-invocation memory use of the commands. * External resources that are not immediately known to jj. For example, commands run in parallel may wish to limit the total number of connections to a server. We might choose to defer any handling of this to the implementation of the command being invoked, instead of trying to communicate that information to jj. Command Options[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#command-options "Permanent link") -------------------------------------------------------------------------------------------------------- The base command of any jj command should be usable. By default `jj run` works on the `@` the current working copy. * \--command, explicit name of the first argument * \-x, for git compatibility (may alias another command) * \-j, --jobs, the amount of parallelism to use * \-k, --keep-going, continue on failure (may alias another command) * \--show, display the diff for an affected revision * \--dry-run, do the command execution without doing any work, logging all intended files and arguments * \--rebase, rebase all parents on the consulitng diff (may alias another command) * \--reparent, change the parent of an effected revision to the new change (may alias another command) * \--clean, remove existing workspaces and remove the ignored files * \--readonly, ignore changes across multiple run invocations * \--error-strategy=`continue|stop|fatal`, see [Dealing with failure](https://jj-vcs.github.io/jj/prerelease/design/run/#dealing-with-failure) ### Integrating with other commands[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#integrating-with-other-commands "Permanent link") `jj log`: No special handling needed `jj diff`: No special handling needed `jj st`: For now reprint the final output of `jj run` `jj op log`: No special handling needed, but awaits further discussion in [#963](https://github.com/jj-vcs/jj/issues/963) `jj undo/jj op revert`: No special handling needed Open Points[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#open-points "Permanent link") ------------------------------------------------------------------------------------------------ Should the command be working copy backend specific? How do we manage the Processes which the command will spawn? Configuration options, User and Repository Wide? Future possibilities[¶](https://jj-vcs.github.io/jj/prerelease/design/run/#future-possibilities "Permanent link") ------------------------------------------------------------------------------------------------------------------ * We could rewrite the file in memory, which is a neat optimization * Exposing some internal state, to allow preciser resource constraints * Integration options for virtual filesystems, which allow them to cache the needed working copies. * A Jujutsu wide concept for a cached working copy, as they could be expensive to materialize. * Customized failure messages, this maybe useful for bots, it could be similar to Bazel's `select(..., message = "arch not supported for $project")`. * Make `jj run` asynchronous by spawning a `main` process, directly return to the user and incrementally updating the output of `jj st`. --- # git-submodules - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#git-submodules) [](https://github.com/jj-vcs/jj/edit/main/docs/design/git-submodules.md "Edit this page") Git submodules[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#git-submodules "Permanent link") ================================================================================================================= This is an aspirational document that describes how jj _will_ support Git submodules. Readers are assumed to have some familiarity with Git and Git submodules. This document is a work in progress; submodules are a big feature, and relevant details will be filled in incrementally. Objective[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#objective "Permanent link") ------------------------------------------------------------------------------------------------------- This proposal aims to replicate the workflows users are used to with Git submodules, e.g.: * Cloning submodules * Making new submodule commits and updating the superproject * Fetching and pushing updates to the submodule's remote * Viewing submodule history When it is convenient, this proposal will also aim to make submodules easier to use than Git's implementation. ### Non-goals[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#non-goals "Permanent link") * Non-Git 'submodules' (e.g. native jj submodules, other VCSes) * Non-Git backends (e.g. Google internal backend) * Changing how Git submodules are implemented in Git Background[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#background "Permanent link") --------------------------------------------------------------------------------------------------------- We mainly want to support Git submodules for feature parity, since Git submodules are a standard feature in Git and are popular enough that we have received user requests for them. Secondarily (and distantly so), Git submodules are notoriously difficult to use, so there is an opportunity to improve the UX over Git's implementation. ### Intro to Git Submodules[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#intro-to-git-submodules "Permanent link") [Git submodules](https://git-scm.com/docs/gitsubmodules) are a feature of Git that allow a repository (submodule) to be embedded inside another repository (the superproject). Notably, a submodule is a full repository, complete with its own index, object store and ref store. It can be interacted with like any other repository, regardless of the superproject. In a superproject commit, submodule information is captured in two places: * A `gitlink` entry in the commit's tree, where the value of the `gitlink` entry is the submodule commit id. This tells Git what to populate in the working tree. * A top level `.gitmodules` file. This file is in Git's config syntax and entries take the form `submodule..*`. These include many settings about the submodules, but most importantly: * `submodule.path` contains the path from the root of the tree to the `gitlink` being described. * `submodule.url` contains the url to clone the submodule from. In the working tree, Git notices the presence of a submodule by the `.git` entry (signifying the root of a Git repository working tree). This is either the submodule's actual Git directory (an "old-form" submodule), or a `.git` file pointing to `/modules/`. The latter is sometimes called the "absorbed form", and is Git's preferred mode of operation. Roadmap[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#roadmap "Permanent link") --------------------------------------------------------------------------------------------------- Git submodules should be implemented in an order that supports an increasing set of workflows, with the goal of getting feedback early and often. When support is incomplete, jj should not crash, but instead provide fallback behavior and warn the user where needed. The goal is to land good support for pure Jujutsu repositories, while colocated repositories will be supported when convenient. This section should be treated as a set of guidelines, not a strict order of work. ### Phase 1: Readonly submodules[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#phase-1-readonly-submodules "Permanent link") This includes work that inspects submodule contents but does not create new objects in the submodule. This requires a way to store submodules in a jj repository that supports readonly operations. #### Outcomes[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#outcomes "Permanent link") * Submodules can be cloned anew * New submodule commits can be fetched * Submodule history and branches can be viewed * Submodule contents are populated in the working copy * Superproject gitlink can be updated to an existing submodule commit * Conflicts in the superproject gitlink can be resolved to an existing submodule commit ### Phase 2: Snapshotting new changes[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#phase-2-snapshotting-new-changes "Permanent link") This allows a user to write new contents to a submodule and its remote. #### Outcomes[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#outcomes_1 "Permanent link") * Changes in the working copy can be recorded in a submodule commit * Submodule branches can be modified * Submodules and their branches can be pushed to their remote ### Phase 3: Merging/rebasing/conflicts[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#phase-3-mergingrebasingconflicts "Permanent link") This allows merging and rebasing of superproject commits in a content-aware way (in contrast to Git, where only the gitlink commit ids are compared), as well as workflows that make resolving conflicts easy and sensible. This can be done in tandem with Phase 2, but will likely require a significant amount of design work on its own. #### Outcomes[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#outcomes_2 "Permanent link") * Merged/rebased submodules result in merged/rebased working copy content * Merged/rebased working copy content can be committed, possibly by creating sensible merged/rebased submodule commits * Merge/rebase between submodule and non-submodule gives a sensible result * Merge/rebase between submodule A and submodule B gives a sensible result ### Phase ?: An ideal world[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#phase-an-ideal-world "Permanent link") I.e. outcomes we would like to see if there were no constraints whatsoever. * Rewriting submodule commits rewrites descendants correctly and updates superproject gitlinks. * Submodule conflicts automatically resolve to the 'correct' submodule commits, e.g. a merge between superproject commits creating a merge of the submodule commits. * Nested submodules are as easy to work with as non-nested submodules. * The operation log captures changes in the submodule. Design[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#design "Permanent link") ------------------------------------------------------------------------------------------------- ### Guiding principles[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#guiding-principles "Permanent link") TODO ### Storing submodules[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#storing-submodules "Permanent link") Possible approaches under discussion. See [./git-submodule-storage.md](https://jj-vcs.github.io/jj/prerelease/design/git-submodule-storage/) . ### Snapshotting new submodule changes[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#snapshotting-new-submodule-changes "Permanent link") TODO ### Merging/rebasing with submodules[¶](https://jj-vcs.github.io/jj/prerelease/design/git-submodules/#mergingrebasing-with-submodules "Permanent link") TODO --- # Governance - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#jujutsu-governance) [](https://github.com/jj-vcs/jj/edit/main/docs/governance/GOVERNANCE.md "Edit this page") Jujutsu Governance[¶](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#jujutsu-governance "Permanent link") ========================================================================================================================= Overview[¶](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#overview "Permanent link") ----------------------------------------------------------------------------------------------------- Jujutsu is an open source project, led, maintained and designed for a worldwide community. Anyone who is interested can join, contribute, and participate in the decision-making process. This document is intended to help you understand how you can do that. Project roles[¶](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#project-roles "Permanent link") --------------------------------------------------------------------------------------------------------------- We greatly appreciate everyone's contributions, and Jujutsu has benefited greatly from people who shared a single idea, change, or a suggestion, without ever becoming a regular contributor. We also want everybody to feel welcome to share their suggestions for the project (as long as you follow the Community Guidelines). There are two special roles for participants in the Jujutsu projects: Maintainers and Contributors. The role of the Maintainer is formally defined. These are the people empowered to collectively make final decisions about most aspects of the project. They are expected to take community's input seriously and to aim for the benefit of the entire community. The role of a Contributor is less formal. In situations where opinions become numerous or contentious, it is acceptable for the maintainers to assign more weight to the voices of the more established Contributors. ### Maintainers[¶](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#maintainers "Permanent link") **Maintainers** are the people who contribute, review, guide, and collectively make decisions about the direction and scope of the project (see: [Decision Making](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#decision-making) ). Maintainers are elected by a [voting process](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#adding-and-removing-maintainers) . A typical Maintainer is not only someone who has made "large" contributions, but someone who has shown they are continuously committed to the project and its community. Some expected responsibilities of maintainers include (but are not exclusively limited to): * Displaying a high level of commitment to the project and its community, and being a role model for others. * Writing patches — a lot of patches, especially "glue code" or "grunt work" or general "housekeeping"; fixing bugs, ensuring documentation is always high quality, consistent UX design, improving processes, making judgments on dependencies, handling security vulnerabilities, and so on and so forth. * Reviewing code submitted by others — with an eye to maintainability, performance, code quality, and "style" (fitting in with the project). * Participating in design discussions, especially with regards to architecture or long-term vision. * Ensuring the community remains a warm and welcoming place, to new and veteran members alike. This is not an exhaustive list, nor is it intended that every Maintainer does each and every one of these individual tasks to equal amounts. Rather this is only a guideline for what Maintainers are expected to conceptually do. In short, Maintainers are the outwardly visible stewards of the project. #### Current list of Maintainers[¶](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#current-list-of-maintainers "Permanent link") The current list of Maintainers: * Austin Seipp (@thoughtpolice) * Benjamin Tan (@bnjmnt4n) * Ilya Grigoriev (@ilyagr) * Martin von Zweigbergk (@martinvonz) * Scott Taylor (@scott2000) * Waleed Khan (@arxanas) * Yuya Nishihara (@yuja) ### Contributors[¶](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#contributors "Permanent link") We consider contributors to be active participants in the project and community who are _not_ maintainers. These are people who might: * Help users by answering questions * Participating in lively and respectful discussions across various channels * Submit high-quality bug reports, reproduce reported bugs, and verifying fixes * Submit patches or pull requests * Provide reviews and input on others' pull requests * Help with testing and quality assurance * Submit feedback about planned features, use cases, or bugs We essentially define them as **people who actively participate in the project**. Examples of things that would _not_ make you a contributor are: * Submitting a single bug report and never returning * Writing blog posts or other evangelism * Using the software in production * Forking the project and maintaining your own version * Writing a third-party tool or add-on While these are all generally quite valuable, we don't consider these ongoing contributions to the codebase or project itself, and on their own do not constitute "active participation". Processes[¶](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#processes "Permanent link") ------------------------------------------------------------------------------------------------------- For the purposes of making decisions across the project, the following processes are defined. ### Decision-Making[¶](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#decision-making "Permanent link") The person proposing a decision to be made (i.e. technical, project direction, etc.) can offer a proposal, along with a 2-to-4 week deadline for discussion. During this time, Maintainers may participate with a vote of: A) Support B) Reject C) Abstain Each Maintainer gets one vote. The total number of "participating votes" is the number of Maintainer votes which are not Abstain. The proposal is accepted when more than half of the participating votes are Support. In the event that a decision is reached before the proposed timeline, said proposal can move on and be accepted immediately. In the event no consensus is reached, a proposal may be re-submitted later on. This document itself is subject to the Decision-Making process by the existing set of Maintainers. ### Adding and Removing Maintainers[¶](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#adding-and-removing-maintainers "Permanent link") An active Contributor may, at any given time, nominate themselves or another Contributor to become a Maintainer. This process is purely optional and no Contributor is expected to do so; however, self-nomination is encouraged for active participants. A vote and discussion by the existing Maintainers will be used to decide the outcome. Note that Contributors should demonstrate a high standard of continuous participation to become a Maintainer; the upper limit on the number of Maintainers is practically bounded, and so rejection should be considered as a real possibility. As the scope of the project changes, this limit may increase, but it is fundamentally fluid. (If you are unsure, you are free to privately ask existing Maintainers before self-nominating if there is room.) A Maintainer may, at any time, cede their responsibility and step down without a vote. A Maintainer can be removed by other Maintainers, subject to a vote of at-least a 2/3rds majority from the existing Maintainer group (excluding the vote of the Maintainer in question). This can be due to lack of participation or conduct violations, among other things. Note that Maintainers are subject to a higher set of behavioral and communicative standards than average contributor or participant. ### Single-Company Influence[¶](https://jj-vcs.github.io/jj/prerelease/governance/GOVERNANCE/#single-company-influence "Permanent link") At most 1/3 of the maintainers may be paid for their contributions by a single company. This is to reduce the risk of a single company controlling the project's direction. If the 1/3 limit gets exceeded because an existing maintainer gets hired by the same company as some other existing maintainer(s), then the maintainers will have to decide how to resolve the situation. The maintainer in question gets to vote, as long as this doesn't mean the company in question has half the votes (usually meaning there are at least 5 maintainers total). --- # Working on Windows - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/windows/#working-on-windows) [](https://github.com/jj-vcs/jj/edit/main/docs/windows.md "Edit this page") Working on Windows[¶](https://jj-vcs.github.io/jj/prerelease/windows/#working-on-windows "Permanent link") =========================================================================================================== Jujutsu works the same on all platforms, but there are some caveats that Windows users should be aware of. Line endings conversion[¶](https://jj-vcs.github.io/jj/prerelease/windows/#line-endings-conversion "Permanent link") --------------------------------------------------------------------------------------------------------------------- Jujutsu currently has a setting, [`working-copy.eol-conversion`](https://jj-vcs.github.io/jj/prerelease/config/#eol-conversion-settings) , similar to Git's [`core.autocrlf`](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#_core_autocrlf) [1](https://jj-vcs.github.io/jj/prerelease/windows/#fn:1) , but does not currently honor `.gitattributes` and the `core.autocrlf` git config, so it is recommended to keep the `working-copy.eol-conversion` setting and the `core.autocrlf` git config in sync[1](https://jj-vcs.github.io/jj/prerelease/windows/#fn:1) . Note If you created a colocated git repo, forget to keep these 2 settings in sync, and result in a dirty working copy with only EOL diffs, you can set the `working-copy.eol-conversion` setting correctly and run `jj abandon` to fix it. The line endings conversion won't be applied to files detected as a binary files via a heuristics[2](https://jj-vcs.github.io/jj/prerelease/windows/#fn:2) regardless of the settings. This behavior is subject to change when we support the text git attribute. Jujutsu may make incorrect decision on whether a file is a binary file and apply line conversion incorrectly, but currently, Jujutsu doesn't support configuring line endings conversion for particular files. If this issue is hit, one should not enable the line conversion setting. Note If Jujutsu applies line endings conversion on incorrect files, you should not enable the line conversion setting and the git `core.autocrlf` setting. See below. To disable line conversion, set the `core.autocrlf` setting to `none` or just remove the setting. `PS> git config core.autocrlf input # We use none instead of input to avoid applying EOL conversion. PS> jj config set --repo working-copy.eol-conversion none # Abandoning the working copy will cause Jujutsu to overwrite all files with # CRLF line endings with the line endings they are committed with, probably LF PS> jj abandon` This means that line endings will be checked out exactly as they are committed and committed exactly as authored. This setting ensures Git will check out files with LF line endings without converting them to CRLF. You'll want to make sure any tooling you use, especially IDEs, preserve LF line endings. Pagination[¶](https://jj-vcs.github.io/jj/prerelease/windows/#pagination "Permanent link") ------------------------------------------------------------------------------------------- On Windows, `jj` will use its integrated pager called `streampager` by default, unless the environment variable `%PAGER%` or the config `ui.pager` is explicitly set. See the [pager section of the config docs](https://jj-vcs.github.io/jj/prerelease/config/#pager) for more details. If the built-in pager doesn't meet your needs and you have Git installed, you can switch to using Git's pager as follows: `PS> jj config set --user ui.pager '["C:\\Program Files\\Git\\usr\\bin\\less.exe", "-FRX"]' PS> jj config set --user ui.paginate auto` Typing `@` in PowerShell[¶](https://jj-vcs.github.io/jj/prerelease/windows/#typing-in-powershell "Permanent link") ------------------------------------------------------------------------------------------------------------------- PowerShell uses `@` as part the [array sub-expression operator](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_arrays?view=powershell-7.4#the-array-sub-expression-operator) , so it often needs to be escaped or quoted in commands: ``PS> jj log -r `@ PS> jj log -r '@'`` One solution is to create a revset alias. For example, to make `HEAD` an alias for `@`: `PS> jj config set --user revset-aliases.HEAD '@' PS> jj log -r HEAD` WSL sets the execute bit on all files[¶](https://jj-vcs.github.io/jj/prerelease/windows/#wsl-sets-the-execute-bit-on-all-files "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------------- When viewing a Windows drive from WSL (via _/mnt/c_ or a similar path), Windows exposes all files with the execute bit set. Since Jujutsu automatically records changes to the working copy, this sets the execute bit on all files committed in your repository. If you only need to access the repository in WSL, the best solution is to clone the repository in the Linux file system (for example, in `~/my-repo`). If you need to use the repository in both WSL and Windows, one solution is to create a workspace in the Linux file system: `PS> jj workspace add --name wsl ~/my-repo` Then only use the `~/my-repo` workspace from Linux. Symbolic link support[¶](https://jj-vcs.github.io/jj/prerelease/windows/#symbolic-link-support "Permanent link") ----------------------------------------------------------------------------------------------------------------- `jj` supports symlinks on Windows only when they are enabled by the operating system. This requires Windows 10 version 14972 or higher, as well as Developer Mode. If those conditions are not satisfied, `jj` will materialize symlinks as ordinary files. For colocated repositories, Git support must also be enabled using the `git config` option `core.symlinks=true`. * * * 1. This poses the question if we should support reading the `core.autocrlf` setting in colocated repos. See details at the [issue](https://github.com/jj-vcs/jj/issues/4048) . [↩](https://jj-vcs.github.io/jj/prerelease/windows/#fnref:1 "Jump back to footnote 1 in the text") [↩](https://jj-vcs.github.io/jj/prerelease/windows/#fnref2:1 "Jump back to footnote 1 in the text") 2. To detect if a file is binary, Jujutsu currently checks if there is 0 byte in the file which is different from the algorithm of [`gitoxide`](https://github.com/GitoxideLabs/gitoxide/blob/073487b38ed40bcd7eb45dc110ae1ce84f9275a9/gix-filter/src/eol/utils.rs#L98-L100) or [`git`](https://github.com/git/git/blob/f1ca98f609f9a730b9accf24e5558a10a0b41b6c/convert.c#L94-L103) . Jujutsu doesn't plan to align the binary detection logic with git. [↩](https://jj-vcs.github.io/jj/prerelease/windows/#fnref:2 "Jump back to footnote 2 in the text") --- # Sparse patterns v2 - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#sparse-patterns-v2-redesign) [](https://github.com/jj-vcs/jj/edit/main/docs/design/sparse-v2.md "Edit this page") Sparse Patterns v2 redesign[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#sparse-patterns-v2-redesign "Permanent link") ====================================================================================================================================== Authors: [Daniel Ploch](mailto:dploch@google.com) **Summary:** This Document documents a redesign of the sparse command and it's internal storage format in jj, in order to facilitate several desirable improvements for large repos. It covers both the migration path and the planned end state. Objective[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#objective "Permanent link") -------------------------------------------------------------------------------------------------- Redesign Sparse Patterns to accommodate more advanced features for native and custom implementations. This includes three main goals: 1. Sparse Patterns should be versioned with the working copy 2. Sparse Patterns should support more [flexible matching rules](https://github.com/jj-vcs/jj/issues/1896) 3. Sparse Patterns should support [client path remapping](https://github.com/jj-vcs/jj/issues/2288) Current State (as of jj 0.13.0)[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#current-state-as-of-jj-0130 "Permanent link") ------------------------------------------------------------------------------------------------------------------------------------------ Sparse patterns are an effectively unordered list of prefix strings: `path/one path/to/dir/two` The _set_ of files identified by the Sparse Patterns is all paths which match any provided prefix. This governs what gets materialized in the working copy on checkout, and what is updated on snapshot. The set is stored in working copy state files which are not versioned in the Op Store. Because all paths are bare strings with no escaping or higher-level formatting, the current design makes it difficult to add new features like exclusions or path remappings. Proposed State (Sparse Patterns v2)[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#proposed-state-sparse-patterns-v2 "Permanent link") ---------------------------------------------------------------------------------------------------------------------------------------------------- Sparse Patterns v2 will be stored as objects in the Op Store, referenced by a `WorkingCopyPatternsId` from the active `View`. They will have a new, ordered structure which can fully represent previous patterns. `/// Analogues of RepoPath, specifically describing paths in the working copy. struct WorkingCopyPathBuf { String } struct WorkingCopyPath { str } pub enum SparsePatternsPathType { Dir, // Everything under /... Files, // Files under /* Exact, // exactly } pub struct SparsePatternsPath { path_type: SparsePatternsPathType, include: bool, // True if included, false if excluded. path: RepoPathBuf, } pub struct WorkingCopyMapping { src_path: RepoPathBuf, dst_path: WorkingCopyPathBuf, recursive: bool, // If false, only immediate children of src_path (files) are renamed. } pub struct WorkingCopyPatterns { sparse_paths: Vec, mappings: Vec, } pub trait OpStore { ... pub fn read_working_copy_patterns(&self, id: &WorkingCopyPatternsId) -> OpStoreResult { ... } pub fn write_working_copy_patterns(&self, sparse_patterns: &WorkingCopyPatterns) -> OpStoreResult { .. } }` To support these more complex behaviors, a new `WorkingCopyPatterns` trait will be introduced, initially only as a thin wrapper around the existing prefix format, but soon to be expanded with richer types and functionality. `impl WorkingCopyPatterns { pub fn to_matcher(&self) -> Box { ... } ... }` ### Command Syntax[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#command-syntax "Permanent link") `SparsePatternsPath` rules can be specified on the CLI and in an editor via a compact syntax: `(include|exclude):(dir|files|exact):` If both prefix terms are omitted, then `include:dir:` is assumed. If any prefix is specified, both must be specified. The editor and CLI will both accept path rules in either format going forward. * `jj sparse set --add foo/bar` is equal to `jj sparse set --add include:dir:foo/bar` * `jj sparse set --add exclude:dir:foo/bar` adds a new `Dir` type rule with `include = false` * `jj sparse set --exclude foo/bar` as a possible shorthand for the above * `jj sparse list` will print the explicit rules Paths will be stored in an ordered, canonical form which unambiguously describes the set of files to be included. Every `--add` command will append to the end of this list before the patterns are canonicalized. Whether a file is included is determined by the first matching rule in reverse order. For example: `include:dir:foo exclude:dir:foo/bar include:dir:foo/bar/baz exclude:dir:foo/bar/baz/qux` Produces rule set which includes "foo/file.txt", excludes "foo/bar/file.txt", includes "foo/bar/baz/file.txt", and excludes "foo/bar/baz/qux/file.txt". If the rules are subtly re-ordered, they become canonicalized to a smaller, but functionally equivalent form: `# Before include:dir:foo exclude:dir:foo/bar/baz/qux include:dir:foo/bar/baz exclude:dir:foo/bar # Canonicalized include:dir:foo exclude:dir:foo/bar` #### Canonicalization[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#canonicalization "Permanent link") There are many ways to represent functionally equivalent `WorkingCopyPatterns`. For instance, the following 4 rule sets are all functionally equivalent: `# Set 1 include:dir:bar include:dir:foo # Set 2 include:dir:foo include:dir:bar # Set 3 include:dir:bar include:dir:bar/baz/qux include:dir:foo # Set 4 include:dir:foo exclude:dir:foo/baz include:dir:bar include:dir:foo/baz` Because these patterns are stored in the Op Store now, it is useful for all of these representations to be rewritten into a minimal, canonical form before serialization. In this case, `Set 1` will be the canonical set. The canonical form of a `WorkingCopyPatterns` is defined as the form such that: * Every rule affects the functionality (there are no redundant rules) * Rules are sorted lexicographically, but with '/' sorted before all else * This special sorting order is useful for constructing path tries ### Working Copy Map[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#working-copy-map "Permanent link") Warning This section is intentionally lacking, more research is needed. All `WorkingCopyPatterns` will come equipped with a default no-op mapping. These mappings are inspired by and similar to [Perforce client views](https://www.perforce.com/manuals/cmdref/Content/CmdRef/views.html) . `vec![WorkingCopyMapping { src_path: RepoPathBuf::root(), dst_path: WorkingCopyPathBuf::root(), recursive: true, }]` `WorkingCopyPatterns` will provide an interface to map working copy paths into repo paths and vice versa. The \`WorkingCopy\`\` trait will apply this mapping to all snapshot and checkout operations, and jj commands which accept relative paths will need to be updated to perform working copy path -> repo path translations as needed. It's not clear at this time _which_ commands will need changing, as some are more likely to refer to repo paths rather than working copy paths. TODO: Expand this section. In particular, the path rules for sparse patterns will _always_ be repo paths, not working copy paths. Thus, if the working copy wants to track "foo" and rename it to "subdir/bar", they must `jj sparse set --add foo` and `jj map set --from foo --to bar`. In other words, the mapping operation can be thought of as always _after_ the sparse operation. #### Command Syntax[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#command-syntax_1 "Permanent link") New commands will enable editing of the `WorkingCopyMapping`s: TODO: Maybe this should be `jj workspace map ...`? * `jj map list` will print all mapping pairs. * `jj map add --from foo --to bar` will add a new mapping to the end of the list. * `jj map remove --from foo` will remove a specific mapping rule. * `jj map edit` will pull up a text editor for manual editing. Like sparse paths, mappings will have a compact text syntax for editing in file form, or for adding a rule textually on the CLI: `"" -> "" [nonrecursive]` Like sparse paths, mapping rules are defined to apply in _order_ and on any save operation will be modified to a minimal canonical form. Thus, `jj map set --from "" --to ""` will always completely wipe the map. The first matching rule in reverse list order determines how a particular repo path should be mapped into the working copy, and likewise how a particular working copy path should be mapped into the repo. For simplicity, the 'last rule wins' applies both for repo->WC conversions, as well as WC->repo conversions, using the same ordering. If a working copy mapping places the same repo file at two distinct working copy paths, snapshotting will fail unless these files are identical. Some specialized filesystems may even treat these as the 'same' file, allowing this to work in some cases. If a working copy mapping places two distinct repo files at the same working copy path, checkout will fail with an error regardless of equivalence. ### Versioning and Storage[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#versioning-and-storage "Permanent link") Updating the active `WorkingCopyPatterns` for a particular working copy will now take place in two separate steps: one transaction which updates the op store, and a separate `LockedWorkingCopy` operation which actually updates the working copy. The working copy proto will no longer store `WorkingCopyPatterns` directly, instead storing only a `WorkingCopyPatternsId`. On mismatch with the current op head, the user will be prompted to run `jj workspace update-stale`. This gives the user the ability to update the active `WorkingCopyPatterns` whilst not interacting with the local working copy, which is useful for custom integrations which may not be _able_ to check out particular working copy patterns due to problems with the backend (encoding, permission errors, etc.). A bad `jj sparse set --add oops` command can thus be undone, even via `jj op undo` if desired. #### View Updates[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#view-updates "Permanent link") The View object will be migrated to store working copy patterns via id. The indirection will save on storage since working copy patterns are not expected to change very frequently. `// Before: pub wc_commit_ids: HashMap, // After: pub struct WorkingCopyInfo { pub commit_id: CommitId, pub wc_patterns_id: WorkingCopyPatternsId, } ... pub wc_info: HashMap,` A View object with no stored working copy patterns will be modified at read time to include the current working copy patterns, thus all `read_view` operations will need to pass in the current working copy patterns for a migration period of at least 6 months. After that, we may choose to auto-fill missing working copy infos with a default `WorkingCopyPatterns` as needed. ### Appendix[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#appendix "Permanent link") #### Related Work[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#related-work "Permanent link") [Perforce client maps](https://www.perforce.com/manuals/cmdref/Content/CmdRef/views.html) are very similar in concept to the entirety of `WorkingCopyPatterns`, and this design aims to achieve similar functionality. The [Josh Project](https://github.com/josh-project/josh) implements partial git clones in a way similar to how sparse patterns try to work. #### Patterns via configuration[¶](https://jj-vcs.github.io/jj/prerelease/design/sparse-v2/#patterns-via-configuration "Permanent link") There may be some scenarios where it is valuable to configure working copy patterns via a configuration file, rather than through explicit commands. Generally this only makes sense for automated repos, with the configuration coming from outside the repo - there are too many caveats and edge cases if the configuration comes from inside the repo and/or is fought with by a human. No configuration syntax is planned at this time but if we add any, we should probably reuse the compact line syntaxes as much as possible for consistency. --- # CLI reference - Jujutsu docs (prerelease) [Skip to content](https://jj-vcs.github.io/jj/prerelease/cli-reference/#command-line-help-for-jj) [](https://github.com/jj-vcs/jj/edit/main/docs/cli-reference.md "Edit this page") Warning This CLI reference is experimental. It is automatically generated, but does not match the `jj help` output exactly. Run `jj help ` for more authoritative documentation. If you see a significant difference, feel free to file a bug, or a PR to note the difference here. Command-Line Help for `jj`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#command-line-help-for-jj "Permanent link") =============================================================================================================================== This document contains the help content for the `jj` command-line program. **Command Overview:** * [`jj`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj) * [`jj abandon`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-abandon) * [`jj absorb`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-absorb) * [`jj bisect`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bisect) * [`jj bisect run`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bisect-run) * [`jj bookmark`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark) * [`jj bookmark create`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-create) * [`jj bookmark delete`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-delete) * [`jj bookmark forget`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-forget) * [`jj bookmark list`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-list) * [`jj bookmark move`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-move) * [`jj bookmark rename`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-rename) * [`jj bookmark set`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-set) * [`jj bookmark track`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-track) * [`jj bookmark untrack`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-untrack) * [`jj commit`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-commit) * [`jj config`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-config) * [`jj config edit`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-config-edit) * [`jj config get`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-config-get) * [`jj config list`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-config-list) * [`jj config path`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-config-path) * [`jj config set`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-config-set) * [`jj config unset`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-config-unset) * [`jj describe`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-describe) * [`jj diff`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-diff) * [`jj diffedit`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-diffedit) * [`jj duplicate`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-duplicate) * [`jj edit`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-edit) * [`jj evolog`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-evolog) * [`jj file`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-file) * [`jj file annotate`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-file-annotate) * [`jj file chmod`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-file-chmod) * [`jj file list`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-file-list) * [`jj file show`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-file-show) * [`jj file track`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-file-track) * [`jj file untrack`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-file-untrack) * [`jj fix`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-fix) * [`jj gerrit`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-gerrit) * [`jj gerrit upload`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-gerrit-upload) * [`jj git`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git) * [`jj git clone`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-clone) * [`jj git export`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-export) * [`jj git fetch`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-fetch) * [`jj git import`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-import) * [`jj git init`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-init) * [`jj git push`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-push) * [`jj git remote`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-remote) * [`jj git remote add`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-remote-add) * [`jj git remote list`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-remote-list) * [`jj git remote remove`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-remote-remove) * [`jj git remote rename`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-remote-rename) * [`jj git remote set-url`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-remote-set-url) * [`jj git root`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-git-root) * [`jj help`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-help) * [`jj interdiff`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-interdiff) * [`jj log`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-log) * [`jj metaedit`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-metaedit) * [`jj new`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-new) * [`jj next`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-next) * [`jj operation`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-operation) * [`jj operation abandon`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-operation-abandon) * [`jj operation diff`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-operation-diff) * [`jj operation log`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-operation-log) * [`jj operation restore`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-operation-restore) * [`jj operation revert`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-operation-revert) * [`jj operation show`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-operation-show) * [`jj parallelize`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-parallelize) * [`jj prev`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-prev) * [`jj rebase`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-rebase) * [`jj redo`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-redo) * [`jj resolve`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-resolve) * [`jj restore`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-restore) * [`jj revert`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-revert) * [`jj root`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-root) * [`jj show`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-show) * [`jj sign`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-sign) * [`jj simplify-parents`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-simplify-parents) * [`jj sparse`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-sparse) * [`jj sparse edit`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-sparse-edit) * [`jj sparse list`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-sparse-list) * [`jj sparse reset`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-sparse-reset) * [`jj sparse set`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-sparse-set) * [`jj split`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-split) * [`jj squash`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-squash) * [`jj status`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-status) * [`jj tag`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-tag) * [`jj tag delete`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-tag-delete) * [`jj tag list`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-tag-list) * [`jj tag set`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-tag-set) * [`jj undo`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-undo) * [`jj unsign`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-unsign) * [`jj util`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-util) * [`jj util completion`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-util-completion) * [`jj util config-schema`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-util-config-schema) * [`jj util exec`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-util-exec) * [`jj util gc`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-util-gc) * [`jj util install-man-pages`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-util-install-man-pages) * [`jj util markdown-help`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-util-markdown-help) * [`jj version`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-version) * [`jj workspace`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-workspace) * [`jj workspace add`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-workspace-add) * [`jj workspace forget`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-workspace-forget) * [`jj workspace list`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-workspace-list) * [`jj workspace rename`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-workspace-rename) * [`jj workspace root`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-workspace-root) * [`jj workspace update-stale`↴](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-workspace-update-stale) `jj`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj "Permanent link") ----------------------------------------------------------------------------------- Jujutsu (An experimental VCS) To get started, see the tutorial \[`jj help -k tutorial`\]. **Usage:** `jj [OPTIONS] [COMMAND]` 'jj help --help' lists available keywords. Use 'jj help -k' to show help for one of these keywords. ###### **Subcommands:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#subcommands "Permanent link") * `abandon` — Abandon a revision * `absorb` — Move changes from a revision into the stack of mutable revisions * `bisect` — Find a bad revision by bisection * `bookmark` — Manage bookmarks \[default alias: b\] * `commit` — Update the description and create a new change on top \[default alias: ci\] * `config` — Manage config options * `describe` — Update the change description or other metadata \[default alias: desc\] * `diff` — Compare file contents between two revisions * `diffedit` — Touch up the content changes in a revision with a diff editor * `duplicate` — Create new changes with the same content as existing ones * `edit` — Sets the specified revision as the working-copy revision * `evolog` — Show how a change has evolved over time * `file` — File operations * `fix` — Update files with formatting fixes or other changes * `gerrit` — Interact with Gerrit Code Review * `git` — Commands for working with Git remotes and the underlying Git repo * `help` — Print this message or the help of the given subcommand(s) * `interdiff` — Compare the changes of two commits * `log` — Show revision history * `metaedit` — Modify the metadata of a revision without changing its content * `new` — Create a new, empty change and (by default) edit it in the working copy * `next` — Move the working-copy commit to the child revision * `operation` — Commands for working with the operation log * `parallelize` — Parallelize revisions by making them siblings * `prev` — Change the working copy revision relative to the parent revision * `rebase` — Move revisions to different parent(s) * `redo` — Redo the most recently undone operation * `resolve` — Resolve conflicted files with an external merge tool * `restore` — Restore paths from another revision * `revert` — Apply the reverse of the given revision(s) * `root` — Show the current workspace root directory (shortcut for `jj workspace root`) * `show` — Show commit description and changes in a revision * `sign` — Cryptographically sign a revision * `simplify-parents` — Simplify parent edges for the specified revision(s) * `sparse` — Manage which paths from the working-copy commit are present in the working copy * `split` — Split a revision in two * `squash` — Move changes from a revision into another revision * `status` — Show high-level repo status \[default alias: st\] * `tag` — Manage tags * `undo` — Undo the last operation * `unsign` — Drop a cryptographic signature * `util` — Infrequently used commands such as for generating shell completions * `version` — Display version information * `workspace` — Commands for working with workspaces ###### **Options:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#options "Permanent link") * `-R`, `--repository ` — Path to repository to operate on By default, Jujutsu searches for the closest .jj/ directory in an ancestor of the current working directory. * `--ignore-working-copy` — Don't snapshot the working copy, and don't update it By default, Jujutsu snapshots the working copy at the beginning of every command. The working copy is also updated at the end of the command, if the command modified the working-copy commit (`@`). If you want to avoid snapshotting the working copy and instead see a possibly stale working-copy commit, you can use `--ignore-working-copy`. This may be useful e.g. in a command prompt, especially if you have another process that commits the working copy. Loading the repository at a specific operation with `--at-operation` implies `--ignore-working-copy`. * `--ignore-immutable` — Allow rewriting immutable commits By default, Jujutsu prevents rewriting commits in the configured set of immutable commits. This option disables that check and lets you rewrite any commit but the root commit. This option only affects the check. It does not affect the `immutable_heads()` revset or the `immutable` template keyword. * `--at-operation ` \[alias: `at-op`\] — Operation to load the repo at Operation to load the repo at. By default, Jujutsu loads the repo at the most recent operation, or at the merge of the divergent operations if any. You can use `--at-op=` to see what the repo looked like at an earlier operation. For example `jj --at-op= st` will show you what `jj st` would have shown you when the given operation had just finished. `--at-op=@` is pretty much the same as the default except that divergent operations will never be merged. Use `jj op log` to find the operation ID you want. Any unambiguous prefix of the operation ID is enough. When loading the repo at an earlier operation, the working copy will be ignored, as if `--ignore-working-copy` had been specified. It is possible to run mutating commands when loading the repo at an earlier operation. Doing that is equivalent to having run concurrent commands starting at the earlier operation. There's rarely a reason to do that, but it is possible. * `--debug` — Enable debug logging * `--color ` — When to colorize output Possible values: `always`, `never`, `debug`, `auto` * `--quiet` — Silence non-primary command output For example, `jj file list` will still list files, but it won't tell you if the working copy was snapshotted or if descendants were rebased. Warnings and errors will still be printed. * `--no-pager` — Disable the pager * `--config ` — Additional configuration options (can be repeated) The name should be specified as TOML dotted keys. The value should be specified as a TOML expression. If string value isn't enclosed by any TOML constructs (such as array notation), quotes can be omitted. * `--config-file ` — Additional configuration files (can be repeated) `jj abandon`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-abandon "Permanent link") --------------------------------------------------------------------------------------------------- Abandon a revision Abandon a revision, rebasing descendants onto its parent(s). The behavior is similar to `jj restore --changes-in`; the difference is that `jj abandon` gives you a new change, while `jj restore` updates the existing change. If a working-copy commit gets abandoned, it will be given a new, empty commit. This is true in general; it is not specific to this command. **Usage:** `jj abandon [OPTIONS] [REVSETS]...` ###### **Arguments:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#arguments "Permanent link") * `` — The revision(s) to abandon (default: @) ###### **Options:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#options_1 "Permanent link") * `--retain-bookmarks` — Do not delete bookmarks pointing to the revisions to abandon Bookmarks will be moved to the parent revisions instead. * `--restore-descendants` — Do not modify the content of the children of the abandoned commits `jj absorb`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-absorb "Permanent link") ------------------------------------------------------------------------------------------------- Move changes from a revision into the stack of mutable revisions This command splits changes in the source revision and moves each change to the closest mutable ancestor where the corresponding lines were modified last. If the destination revision cannot be determined unambiguously, the change will be left in the source revision. The source revision will be abandoned if all changes are absorbed into the destination revisions, and if the source revision has no description. The modification made by `jj absorb` can be reviewed by `jj op show -p`. **Usage:** `jj absorb [OPTIONS] [FILESETS]...` ###### **Arguments:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#arguments_1 "Permanent link") * `` — Move only changes to these paths (instead of all paths) ###### **Options:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#options_2 "Permanent link") * `-f`, `--from ` — Source revision to absorb from Default value: `@` * `-t`, `--into ` \[alias: `to`\] — Destination revisions to absorb into Only ancestors of the source revision will be considered. Default value: `mutable()` `jj bisect`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bisect "Permanent link") ------------------------------------------------------------------------------------------------- Find a bad revision by bisection **Usage:** `jj bisect ` ###### **Subcommands:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#subcommands_1 "Permanent link") * `run` — Run a given command to find the first bad revision `jj bisect run`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bisect-run "Permanent link") --------------------------------------------------------------------------------------------------------- Run a given command to find the first bad revision. Uses binary search to find the first bad revision. Revisions are evaluated by running a given command (see the documentation for `--command` for details). It is assumed that if a given revision is bad, then all its descendants in the input range are also bad. The target of the bisection can be inverted to look for the first good revision by passing `--find-good`. Hint: You can pass your shell as evaluation command. You can then run manual tests in the shell and make sure to exit the shell with appropriate error code depending on the outcome (e.g. `exit 0` to mark the revision as good in Bash or Fish). Example: To run `cargo test` with the changes from revision `xyz` applied: `jj bisect --range v1.0..main -- bash -c "jj duplicate -r xyz -B @ && cargo test"` **Usage:** `jj bisect run [OPTIONS] --range [COMMAND] [ARGS]...` ###### **Arguments:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#arguments_2 "Permanent link") * `` — Command to run to determine whether the bug is present The exit status of the command will be used to mark revisions as good or bad: status 0 means good, 125 means to skip the revision, 127 (command not found) will abort the bisection, and any other non-zero exit status means the revision is bad. The target's commit ID is available to the command in the `$JJ_BISECT_TARGET` environment variable. * `` — Arguments to pass to the command Hint: Use a `--` separator to allow passing arguments starting with `-`. For example `jj bisect run --range=... -- test -f some-file`. ###### **Options:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#options_3 "Permanent link") * `-r`, `--range ` — Range of revisions to bisect This is typically a range like `v1.0..main`. The heads of the range are assumed to be bad. Ancestors of the range that are not also in the range are assumed to be good. * `--find-good` — Whether to find the first good revision instead Inverts the interpretation of exit statuses (excluding special exit statuses). Default value: `false` `jj bookmark`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark "Permanent link") ----------------------------------------------------------------------------------------------------- Manage bookmarks \[default alias: b\] See \[`jj help -k bookmarks`\] for more information. **Usage:** `jj bookmark ` ###### **Subcommands:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#subcommands_2 "Permanent link") * `create` — Create a new bookmark * `delete` — Delete an existing bookmark and propagate the deletion to remotes on the next push * `forget` — Forget a bookmark without marking it as a deletion to be pushed * `list` — List bookmarks and their targets * `move` — Move existing bookmarks to target revision * `rename` — Rename `old` bookmark name to `new` bookmark name * `set` — Create or update a bookmark to point to a certain commit * `track` — Start tracking given remote bookmarks * `untrack` — Stop tracking given remote bookmarks `jj bookmark create`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-create "Permanent link") ------------------------------------------------------------------------------------------------------------------- Create a new bookmark **Usage:** `jj bookmark create [OPTIONS] ...` **Command Alias:** `c` ###### **Arguments:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#arguments_3 "Permanent link") * `` — The bookmarks to create ###### **Options:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#options_4 "Permanent link") * `-r`, `--revision ` \[alias: `to`\] — The bookmark's target revision Default value: `@` `jj bookmark delete`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-delete "Permanent link") ------------------------------------------------------------------------------------------------------------------- Delete an existing bookmark and propagate the deletion to remotes on the next push Revisions referred to by the deleted bookmarks are not abandoned. To delete revisions as well as bookmarks, use `jj abandon`. For example, `jj abandon main..` will abandon revisions belonging to the `` branch (relative to the `main` branch.) If you don't want the deletion of the local bookmark to propagate to any tracked remote bookmarks, use `jj bookmark forget` instead. **Usage:** `jj bookmark delete ...` **Command Alias:** `d` ###### **Arguments:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#arguments_4 "Permanent link") * `` — The bookmarks to delete By default, the specified name matches exactly. Use `glob:` prefix to select bookmarks by [wildcard pattern](https://jj-vcs.github.io/jj/latest/revsets/#string-patterns) . `jj bookmark forget`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-forget "Permanent link") ------------------------------------------------------------------------------------------------------------------- Forget a bookmark without marking it as a deletion to be pushed If a local bookmark is forgotten, any corresponding remote bookmarks will become untracked to ensure that the forgotten bookmark will not impact remotes on future pushes. **Usage:** `jj bookmark forget [OPTIONS] ...` **Command Alias:** `f` ###### **Arguments:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#arguments_5 "Permanent link") * `` — The bookmarks to forget By default, the specified name matches exactly. Use `glob:` prefix to select bookmarks by [wildcard pattern](https://jj-vcs.github.io/jj/latest/revsets/#string-patterns) . ###### **Options:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#options_5 "Permanent link") * `--include-remotes` — When forgetting a local bookmark, also forget any corresponding remote bookmarks A forgotten remote bookmark will not impact remotes on future pushes. It will be recreated on future fetches if it still exists on the remote. If there is a corresponding Git-tracking remote bookmark, it will also be forgotten. `jj bookmark list`[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#jj-bookmark-list "Permanent link") --------------------------------------------------------------------------------------------------------------- List bookmarks and their targets By default, a tracking remote bookmark will be included only if its target is different from the local target. A non-tracking remote bookmark won't be listed. For a conflicted bookmark (both local and remote), old target revisions are preceded by a "-" and new target revisions are preceded by a "+". See \[`jj help -k bookmarks`\] for more information. **Usage:** `jj bookmark list [OPTIONS] [NAMES]...` **Command Alias:** `l` ###### **Arguments:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#arguments_6 "Permanent link") * `` — Show bookmarks whose local name matches By default, the specified name matches exactly. Use `glob:` prefix to select bookmarks by [wildcard pattern](https://jj-vcs.github.io/jj/latest/revsets/#string-patterns) . ###### **Options:**[¶](https://jj-vcs.github.io/jj/prerelease/cli-reference/#options_6 "Permanent link") * `-a`, `--all-remotes` — Show all tracking and non-tracking remote bookmarks including the ones whose targets are synchronized with the local bookmarks * `--remote ` — Show all tracking and non-tracking remote bookmarks belonging to this remote Can be combined with `--tracked` or `--conflicted` to filter the bookmarks shown (can be repeated.) By default, the specified remote name matches exactly. Use `glob:` prefix to select remotes by [wildcard pattern](https://jj-vcs.github.io/jj/latest/revsets/#string-patterns) . * `-t`, `--tracked` — Show remote tracked bookmarks only. Omits local Git-tracking bookmarks by default * `-c`, `--conflicted` — Show conflicted bookmarks only * `-r`, `--revisions ` — Show bookmarks whose local targets are in the given revisions Note that `-r deleted_bookmark` will not work since `deleted_bookmark` wouldn't have a local target. * `-T`, `--template