Skip to content

Jujutsu support#754

Open
jennings wants to merge 5 commits intoasheshgoplani:mainfrom
jennings:jujutsu-support
Open

Jujutsu support#754
jennings wants to merge 5 commits intoasheshgoplani:mainfrom
jennings:jujutsu-support

Conversation

@jennings
Copy link
Copy Markdown
Contributor

@jennings jennings commented Apr 24, 2026

Adds support for Jujutsu repositories. Jujutsu (jj) is a git-compatible version control system. It has the concept of "workspaces" which are analogous to git worktrees.

Jujutsu repositories can be colocated with git repositories (the .jj directory is a sibiling of the .git directory it uses as its backing storage), so a directory may be both a Git repository and a jj repository. If so, prefer the jj repository.

  • Add a vcs.Backend interface that is implemented by both git.GitBackend and jujutsu.JJBackend.
  • When creating a worktree, prefer instead to create a Jujutsu workspace with jj workspace if in a jj repository. The user can select "git" instead if they'd prefer for some reason to create a git worktree even though the main repo is jj.
  • Display in the preview window whether it is a Git Worktree or a Jujutsu Workspace, as a new tag next to the agent and group tags.

This is a reopening of #302. I've been using this for several weeks without noticing any egregious bugs.

@asheshgoplani
Copy link
Copy Markdown
Owner

Hi @jennings — thanks for the contribution. Reading through the diff (1617 / 387 lines, 12+ files touched) my main concern isn't the work itself but the impact surface: agent-deck has a lot of users who'll never use jj, and the changes touch generic worktree / repo-detection paths that everyone exercises.

Could you reshape this as opt-in via config? Concrete asks:

  1. Gate the jj branches behind a config flag. Something like [vcs] preferred = "git" | "jj" (default git) in ~/.agent-deck/config.toml. Or detect both at startup and fall back gracefully if the user has both. The goal: a fresh git-only install must not change behavior at all.

  2. Keep generic helpers untouched where possible. If internal/git/setup.go or worktree commands need a sibling internal/jj/ package, that's fine — but the existing git path's hot lines should stay byte-for-byte identical for anyone with preferred = "git".

  3. Tests that prove the off-state is a no-op. A test like TestSetup_JjDisabled_BehavesIdenticalToGit would close the door on accidentally changing default behavior in a future refactor.

  4. CONFLICTING right now — needs a rebase on current main (v1.7.69 + four follow-up commits landed since you opened this).

If reshaping is more than you want to take on, totally understand — let me know and we can either close politely with credit (your work stays in commit history) or I can pick up the reshape with you as Co-authored-by: on the eventual merge. Either path keeps your work attributed.

What sounds right to you?

@asheshgoplani
Copy link
Copy Markdown
Owner

Hi @jennings — wanted to update my earlier review. I read the diff more carefully, and my earlier 'config-gating' ask was redundant. What you've shipped already does the right thing:

  • New internal/vcs.Backend interface (49 lines) abstracting branch + worktree ops
  • New internal/jujutsu/jujutsu.go as a sibling implementation (not modifying git)
  • Auto-detection in cmd/agent-deck/vcs_helper.go::detectAndCreateBackend: tries jujutsu.NewJJBackend first, falls back to git.NewGitBackend. Plain-git users see zero behaviour change.

That's exactly the safety property I was asking for, expressed via auto-detection instead of explicit [vcs] preferred = "git" config.

One remaining concern (not a blocker): detection order is jj-first. If a repo has colocated jj+git layers, jj wins silently. For paranoid git users who don't realize their repo has a jj layer, that's a sharp edge. Optional follow-up — add a [vcs] backend = "git" | "jujutsu" | "auto" config knob (default "auto" preserves current behaviour). Doesn't have to land in this PR; could be a v1.7.72 follow-up.

Path forward:

  1. Rebase needed — main has moved through v1.7.69 + v1.7.70 since you opened this. git fetch upstream && git rebase upstream/main && git push --force-with-lease should be clean against the v1.7.70 changes (mostly TUI + worker-scratch fixes; minimal overlap with your VCS surface).

  2. CI re-run after rebase. If it goes red on something specific, drop a note.

  3. After rebase + green CI, I'm happy to merge. The architecture is the right shape and the test additions in internal/git/git_test.go (+401) are reassuring.

If you've moved on to other things and won't get to the rebase in the next few days, let me know and I can rebase + force-push on your behalf with Co-authored-by: jennings trailers preserving credit. Either path keeps your work attributed properly. No pressure on timing.

@asheshgoplani
Copy link
Copy Markdown
Owner

Quick honest update, @jennings:

I started taking over the rebase as offered earlier, but stopped partway in. Reason: your branch references a generatedName field on NewDialog that's been removed from main since you opened the PR (and your tests still call d.generatedName = "..."). It's not a hard conflict — git cherry-pick succeeds — but the post-rebase state doesn't compile. Resolving requires understanding what your branch did with name generation vs what main now does, and that's local context only you have.

Given that, I'll wait for you to do the rebase. The architecture review still stands (it's good) — it's just the test/field naming alignment with v1.7.70 main that needs your eyes. When you're ready:

  1. git fetch upstream && git rebase upstream/main
  2. Resolve conflicts in internal/ui/home.go (mostly about my recent Bug: Setup script seems not to be run in bare repo setup #742/New Session path field: cursor hidden behind inline autocomplete — can't see what you're editing #765 changes overlapping your VCS abstraction call sites — keep your Backend pattern, drop the duplicated bare-repo guards since NewGitBackend already calls IsGitRepoOrBareProjectRoot internally)
  3. Resolve the generatedName test compilation — either restore the field, rename to whatever you replaced it with, or drop the affected tests if the feature is gone
  4. go test ./internal/ui/... -race -count=1 should pass cleanly
  5. Force-push with lease

If you've genuinely moved on, no judgment — just say so and we'll close politely with credit. Either way your work history is preserved.

@jennings
Copy link
Copy Markdown
Contributor Author

jennings commented Apr 27, 2026

One remaining concern (not a blocker): detection order is jj-first. If a repo has colocated jj+git layers, jj wins silently. For paranoid git users who don't realize their repo has a jj layer, that's a sharp edge.

Just making sure this is clear: A git user will never have a .jj directory, so there is no "jj layer". That directory is created by jj git clone or jj git init just like git creates the .git directory, so its existence means they are a jj user.

Optional follow-up — add a [vcs] backend = "git" | "jujutsu" | "auto" config knob (default "auto" preserves current behaviour). Doesn't have to land in this PR; could be a v1.7.72 follow-up.

Talking through how my branch works today:

  • If the repo is git or not version controlled, then Agent Deck should behave as it does today in all cases.
  • If the repo is jj, only then does the jj backend ever possibly get used.
  • Jujutsu repos can be "colocated" or not. If they are colocated, then there is a normal .git directory alongside the .jj directory, and you can use either jj or git to interact with the repo.
  • So, currently we assume if there's a .jj directory, then you want Agent Deck to prefer Jujutsu workspaces unless you choose "git" in the New Session Dialog.
  • Workspaces don't currently support colocation, so once a workspace/worktree is created, only one VCS will work with it: git worktrees will only work with git, and jj workspaces will only work with jj.

Given all this, I think the settings should maybe be:

[vcs]
preferred_backend = "auto" (default) | "jujutsu" | "git"
# auto = use jujutsu if .jj exists, git if .git, otherwise none

jujutsu-enabled = true # whether to _ever_ use jj, or to treat jj-only repos as not version controlled

What do you think?

@asheshgoplani
Copy link
Copy Markdown
Owner

Fair point @jennings — you're right. jujutsu.NewJJBackend rejects directories without a .jj/ workspace, so a pure-git user gets the git fallback every time. The 'silent jj takeover' concern only applies to colocated repos, where the user explicitly chose to put a jj layer on top. That's not surprise behavior — that's their config. Withdrawing the config-knob ask. Just need the rebase + the generatedName test alignment from my earlier comment, then it's good to go.

@jennings
Copy link
Copy Markdown
Contributor Author

Thank you. I'll look into that and rebase, I don't recall why I'd be touching generatedName.

jennings added 5 commits May 7, 2026 09:30
Create an abstraction vcs.Backend which abstracts over a working copy.
For now, git.GitBackend is the only implementation.

Instead of calling package functions `git.Foo(repoDir)`, now we
construct a `git.GitBackend` and call `b.Foo()`.
Jujutsu repositories should create a jj workspace instead of a git
worktree. However, the concepts are basically the same.

Jujutsu doesn't actually require creating a named bookmark (branch), but
removing the assumption that every worktree has a named branch would be
a lot of work. To start, we can create and advance a bookmark as we do
with git, then possibly relax that later.
@jennings jennings force-pushed the jujutsu-support branch from 98d198a to d3ca011 Compare May 7, 2026 16:52
@jennings
Copy link
Copy Markdown
Contributor Author

jennings commented May 7, 2026

@asheshgoplani I rebased and removed all the tests that used generatedName. Those weren't originally part of my work, this branch was originally based before commit c42358b which did have the generatedName. But that has since been removed and I guess I rebased improperly.

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.

2 participants