Skip to content

Better worktree handling#3120

Merged
dgageot merged 8 commits into
docker:mainfrom
dgageot:better-worktrees
Jun 15, 2026
Merged

Better worktree handling#3120
dgageot merged 8 commits into
docker:mainfrom
dgageot:better-worktrees

Conversation

@dgageot

@dgageot dgageot commented Jun 15, 2026

Copy link
Copy Markdown
Member
  • Create worktree sooner
  • Add documentation
  • Reopen sessions in their worktree
  • Let the user choose a worktree base

@dgageot dgageot requested a review from a team as a code owner June 15, 2026 08:01
docker-agent

This comment was marked as outdated.

dgageot added 5 commits June 15, 2026 10:19
Toolsets capture their working directory when they are built during
LoadTeam. Because the worktree was created afterwards, the shell tool
stayed rooted in the user's checkout while file-editing tools wrote into
the worktree — so shell git commands operated on the wrong branch.

Create the worktree first and install it as the working directory before
LoadTeam runs, so every tool (the shell included) operates inside the
worktree. Extracted loadTeamInWorktree to keep the ordering explicit and
testable, and tear down the worktree if the load fails so a failure
leaves nothing behind.
--working-dir already selects the directory the worktree is branched
from: addGatewayFlags' PersistentPreRunE chdirs into it before the run,
so os.Getwd() (the worktree's base) reflects it. Make that contract
explicit by branching loadTeamInWorktree from the resolved base dir, and
add a test proving the worktree is created from the --working-dir repo
(shared object store, same HEAD) rather than the process's original cwd.
When --session names an existing session that ran in a worktree, the
toolsets must be rooted in that worktree again. Re-passing --worktree is
not an option: Create fails when the worktree already exists. Instead,
peek the resumed session's stored WorkingDir (via a new backend
ResumeWorkingDir) before LoadTeam and adopt it as the working directory,
so the shell and every other tool reattach to the worktree transparently.

An explicit --worktree/--worktree-pr on the resuming run still takes
precedence. Misses (no --session, relative refs, unknown IDs, empty or
vanished dirs) fall back to normal working-directory resolution.
By default a --worktree branches from the repository's current HEAD.
--worktree-base lets callers branch from another ref (a branch, tag,
commit, or remote-tracking ref). When the base names a remote-tracking
ref like origin/main, that remote branch is fetched first so the
worktree starts from the latest remote state — removing the need for
callers to fetch themselves before creating a worktree.

Implemented as a worktree.WithBase option so existing callers are
unaffected. --worktree-base requires --worktree and is mutually
exclusive with --worktree-pr, --remote and --sandbox.
…base

Clarify that every tool (the shell included) runs inside the worktree,
that --worktree composes with --working-dir, and that a worktree run is
resumed with --session (not by re-passing --worktree). Document the new
--worktree-base flag and add matching examples.
@dgageot dgageot force-pushed the better-worktrees branch from 8df6e3f to 212c2c4 Compare June 15, 2026 08:22
@aheritier aheritier added area/sessions For features/issues/fixes related to session lifecycle (resume, persistence, export) kind/feat PR adds a new feature (maps to feat: commit prefix) area/cli CLI commands, flags, output formatting labels Jun 15, 2026
dgageot added 3 commits June 15, 2026 10:45
A bare "git fetch <remote> <branch>" only guarantees FETCH_HEAD is
updated; under non-standard fetch configurations (custom refspecs,
--no-tags, tagOpt) refs/remotes/<remote>/<branch> can stay stale, so the
subsequent "git worktree add ... <remote>/<branch>" would branch from an
outdated commit. Use a fully-qualified refspec so the remote-tracking ref
is always updated, and cover the missing-fetch-config case with a test.
ResumeWorkingDir bailed out on relative session refs (e.g. --session -1),
so resuming the last session via an offset silently dropped worktree
reattachment. The shared session store is already open at this point, so
resolve the relative ref against it like an explicit ID does.
When LoadTeam fails because ctx was cancelled (Ctrl-C), removing the
worktree with that same cancelled context immediately kills the git
subprocess, leaving the worktree and branch orphaned. Use
context.WithoutCancel(ctx) so cleanup runs to completion, matching the
pattern already used for the interactive-exit cleanup path.
@dgageot

dgageot commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

Addressed the review feedback (3 new commits, branch already on top of latest main):

  • fetchBase could branch from a stale ref — switched to a fully-qualified refspec (refs/heads/<branch>:refs/remotes/<remote>/<branch>) so the remote-tracking ref is updated regardless of the repo's fetch configuration (custom refspecs, --no-tags, tagOpt, …). A bare git fetch <remote> <branch> only guarantees FETCH_HEAD. Added a regression test that drops remote.origin.fetch and verifies the worktree still starts at the latest remote commit.
  • Relative --session refs skipped worktree reattachmentResumeWorkingDir now resolves relative refs (e.g. --session -1) against the already-open shared store instead of bailing out, so resuming the last session reattaches to its worktree just like an explicit ID does.
  • Worktree cleanup used a cancelled context — the post-load-failure teardown now uses context.WithoutCancel(ctx) so a Ctrl-C-cancelled LoadTeam doesn't immediately kill the git worktree remove subprocess and orphan the worktree, matching the interactive-exit cleanup path.

task lint and task test both pass.

@dgageot dgageot merged commit 8be4c53 into docker:main Jun 15, 2026
5 checks passed
@dgageot dgageot deleted the better-worktrees branch June 15, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/cli CLI commands, flags, output formatting area/sessions For features/issues/fixes related to session lifecycle (resume, persistence, export) kind/feat PR adds a new feature (maps to feat: commit prefix)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants