Skip to content

feat(vcs): native jj (Jujutsu) support for .jj-only repos - #58

Merged
bfirestone merged 14 commits into
mainfrom
feat/jj-native-support
Jun 23, 2026
Merged

feat(vcs): native jj (Jujutsu) support for .jj-only repos#58
bfirestone merged 14 commits into
mainfrom
feat/jj-native-support

Conversation

@bfirestone

Copy link
Copy Markdown
Contributor

Native jj (Jujutsu) support

Adds native Jujutsu support to arc for repos that have only a .jj directory and no .git (i.e. jj git init without --colocate, or jj git clone). Implements Approach A from the design (epic arc-0ek8.03rhja).

Problem

arc's VCS-aware features assumed git. In a native-jj repo, two things broke:

  1. Remote detectiondetectGitRemote shelled git remote get-url origin, which fails with no .git, leaving Workspace.GitRemote empty.
  2. Out-of-tree workspace → project mapping — the resolver's fallback only understood git worktree pointers, so a jj workspace outside any registered path couldn't map back to its project.

Approach

jj's on-disk layout mirrors git worktrees, and jj's storage backend is a git repo — so this is clean, with no jj binary dependency in production:

Concept git worktree (gitfs) jj workspace (jjfs)
Secondary checkout .git file → gitdir: pointer .jj/repo file → relative pointer
Main checkout .git dir .jj/repo dir
Backing remote the .git repo .jj/repo/store/git_target → backing git dir

Changes

  • internal/jjfs — pure-Go .jj detection, secondary-workspace→main mapping, and git_target backend resolution (mirrors internal/gitfs; relative and absolute pointers).
  • internal/vcs — dispatcher: DetectMainRepo (git first, jj fallback, canonicalized via core.NormalizePath) and DetectRemote (git first, jj backing git dir fallback). git-first ordering means existing git behavior is unchanged.
  • Wiringinternal/api/resolver.go and cmd/arc/paths.go's detectGitRemote now route through vcs.
  • Testsinternal/testutil/jjtest helper (shells real jj, skips when absent) + native-jj integration tests that pass against jj 0.42.
  • CI — installs the jj binary so the integration tests run.

Testing

  • Feature packages (jjfs, vcs, jjtest, cmd/arc) all green; golangci-lint clean.
  • 3 native-jj integration tests ran and passed against real jj 0.42 (native remote detection, secondary-workspace mapping, remote from inside a secondary workspace).
  • No jj binary invoked in production code paths.

Notes for reviewers

  • 5 pre-existing macOS-only test failures (gitfs ×3, api ×2) are /var/private/var symlink artifacts in git-worktree resolution — not caused by this PR, they pass on Linux CI. Tracked as follow-up arc-0ek8.08g0rw.
  • The leading commit e3cc36f is an empty workspace-setup artifact — safe to drop via squash-merge.

Closes the work in epic arc-0ek8.03rhja.

bfirestone and others added 14 commits June 22, 2026 15:49
Ignore .workspaces/ (jj workspace dogfood dir) on the git side.
Creates internal/jjfs and internal/vcs with exact public signatures
defined by the design contracts, plus compile-time and runtime contract
tests. Bodies are stubs (return ""); implementation lands in later tasks.
…nd lookup

Pure-Go implementation of FindJJEntry, DetectMainRepo, and DetectGitBackend
with behavior tests covering main workspace, secondary workspace pointer
resolution, native relative git_target, and non-jj directories.
…tion

Replaces stub bodies with DetectMainRepo (gitfs-first, jjfs fallback,
canonicalized via core.NormalizePath) and DetectRemote (git-C first,
jjfs backend fallback). Adds four behavior tests using the real git binary.
…cher

- internal/api/resolver.go: replace gitfs.DetectMainRepo with vcs.DetectMainRepo
  so the worktree fallback supports both git linked worktrees and native jj
  workspaces; result is canonicalized via core.NormalizePath.
- cmd/arc/paths.go: replace inline git shell-out in detectGitRemote with
  vcs.DetectRemote so arc init / arc paths add populate GitRemote from
  native jj repos (via the jj backing git directory). Remove now-unused
  os/exec import.
Add internal/testutil/jjtest with RequireJJ, Run, InitNative, AddRemote,
and AddWorkspace helpers that shell out to the jj binary in isolated temp
dirs. Add integration tests in internal/vcs that exercise DetectRemote and
DetectMainRepo against a real native jj repo and secondary workspace.
Use 0o600 for test-written files and a named dirPerm/cfgPerm const in
jjtest, matching the gittest idiom. No behavior change.
The jj release tarball nests the binary in a versioned directory, so
'tar -C /usr/local/bin jj' failed with 'jj: Not found in archive'.
Extract the full archive and find the jj binary instead.
Replaces the fragile manual curl/tar download (which broke on the
release tarball's nested binary layout) with the maintained
taiki-e/install-action, pinned to jj 0.42.0.
taiki-e/install-action doesn't support jj and its cargo-binstall
fallback fails (crate is jj-cli, not binstall-ready). The manual
release-tarball download with a find-based binary locate works.
Accidentally committed during a gitignore investigation.
- resolver.go: update stale doc comment to cover jj secondary workspaces
- jjfs.go: document the jj 0.42 on-disk layout assumptions + the
  DetectMainRepo layout derivation
- vcs_test.go: add colocated-jj test proving git-first ordering when
  both .git and .jj are present
- test.yml: add a 3x retry loop to the jj download (matches the
  Playwright install idiom) so a flaky fetch can't redden the unit gate
@bfirestone

Copy link
Copy Markdown
Contributor Author

Note for reviewers: vcs.DetectMainRepo wraps the worktree/workspace result in core.NormalizePath (symlink canonicalization). For the git path this is a small latent fix folded in alongside the jj work — previously gitfs.DetectMainRepo's result was passed to ResolveProjectByPath un-canonicalized, so a registered canonical path could miss a match when the detected path differed only by a symlink. Flagging it so it isn't mistaken for a pure add. (Surfaced by a slop review of the branch.)

@bfirestone
bfirestone merged commit 4c2222b into main Jun 23, 2026
3 checks passed
@bfirestone
bfirestone deleted the feat/jj-native-support branch June 23, 2026 20:24
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