Skip to content

Offer Jujutsu workspaces as checkouts too - #2151

Merged
bbatsov merged 3 commits into
masterfrom
feature/jj-workspaces
Aug 7, 2026
Merged

Offer Jujutsu workspaces as checkouts too#2151
bbatsov merged 3 commits into
masterfrom
feature/jj-workspaces

Conversation

@bbatsov

@bbatsov bbatsov commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Now that I can test against a real jj, here's the workspace support I left out of #2147.

jj lays workspaces out exactly the way git lays out worktrees, so they drop into the existing model. jj workspace list enumerates them, so an unvisited workspace turns up the same way an unvisited git worktree does.

The identity goes through jj's git backing store rather than stopping at .jj. A modern jj git init leaves a .git at the root, so the first workspace detects as git and later ones detect as jj - resolving both to the same git directory makes them agree they're one repository, and hands jj projects a remote (and therefore siblings) for free.

The worktree plist's :branch becomes :label, since for jj it holds a workspace name. Nothing outside this unreleased cycle used it.

Three things only turned up by running the real thing:

  • jj colorizes template output, so anyone with ui.color = "always" got paths wrapped in escape sequences and every candidate was a directory that didn't exist. --color=never.
  • The remote check had to move ahead of the first file-directory-p, or a TRAMP root costs an ssh connect in the code path written to avoid exactly that.
  • The backing store can name a .git file instead of a directory - a submodule checkout does - and can point at a linked worktree's git dir rather than the shared one. Both are handled now, with a spec that builds a real submodule.

CI installs a pinned jj alongside ripgrep; the specs self-skip without it.

bbatsov added 3 commits August 7, 2026 17:51
jj lays workspaces out the way git lays out worktrees - the first holds a
.jj/repo directory, the rest hold a file naming it - so they fit the
existing checkout model exactly. `jj workspace list' reports them, which
means an unvisited one turns up like an unvisited git worktree does.

The identity goes through jj's git backing store rather than stopping at
.jj. A modern `jj git init' leaves a .git at the root, so the first
workspace is detected as git while later ones are detected as jj;
resolving both to the same git directory makes them agree on being one
repository, and hands jj projects a remote (and so siblings) for free.
The lookup keys off .jj rather than the detected system for the same
reason - a colocated repository has both kinds of checkout and both
belong on the list.

Renames the worktree plist's :branch to :label, since for jj it holds a
workspace name. Nothing outside this cycle used it. The dashboard's own
:branch is a different thing and is untouched.

Three things this needed getting right, all found by testing against a
real jj rather than by reading its docs:

- jj colorizes template output too, so anyone with `ui.color = "always"'
  was getting paths wrapped in escape sequences. Passes --color=never.
- The remote check has to come before any file-system probe, or a TRAMP
  root costs an ssh connect in the code path written to avoid one.
- The backing store can name a .git *file* rather than a directory - a
  submodule checkout does - and can name a linked worktree's git dir
  rather than the shared one. Both now resolve properly.

CI installs a pinned jj so the specs run there; they self-skip without it.
`tar -xz -C dir jj' can't match a member spelled `./jj', which is what
the release tarball actually contains. Extracting the archive whole and
installing the binary out of it sidesteps member-name matching entirely,
so it doesn't matter which tar the image ships.
Both still described a checkout as showing "the branch it has checked
out", which stopped being the whole story once workspaces joined the
list.
@bbatsov
bbatsov merged commit 1129554 into master Aug 7, 2026
5 checks passed
@bbatsov
bbatsov deleted the feature/jj-workspaces branch August 7, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant