Repository navigation
fix(web): new worktree base no longer sticks to an unborn branch while a project clones #35103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a13de33
7c37353
35e739a
32b0d45
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,6 +19,7 @@ import { | |||||||||
| import { GitBranchIcon } from "lucide-react"; | ||||||||||
| import { | ||||||||||
| useEffect, | ||||||||||
| useEffectEvent, | ||||||||||
| useImperativeHandle, | ||||||||||
| useOptimistic, | ||||||||||
| useState, | ||||||||||
|
|
@@ -33,6 +34,7 @@ import { readLocalApi } from "../localApi"; | |||||||||
| import { useOpenPrLink } from "../lib/openPullRequestLink"; | ||||||||||
| import { usePaginatedBranches } from "../state/queries"; | ||||||||||
| import { readThreadShell, useProject, useQuietThreadShell } from "../state/entities"; | ||||||||||
| import { useProjectClone } from "../state/projectClones"; | ||||||||||
| import { useEnvironmentQuery } from "../state/query"; | ||||||||||
| import { readEnvironmentScope, useEnvironmentScope } from "~/state/session"; | ||||||||||
| import { threadEnvironment } from "../state/threads"; | ||||||||||
|
|
@@ -53,6 +55,7 @@ import { | |||||||||
| resolveBranchToolbarValue, | ||||||||||
| resolveDraftEnvModeAfterBranchChange, | ||||||||||
| resolveEffectiveEnvMode, | ||||||||||
| resolveWorktreeBaseBranchSeed, | ||||||||||
| sanitizeNewRefName, | ||||||||||
| shouldIncludeBranchPickerItem, | ||||||||||
| } from "./BranchToolbar.logic"; | ||||||||||
|
|
@@ -264,6 +267,16 @@ export function BranchToolbarBranchSelector({ | |||||||||
| query: sanitizeNewRefName(trimmedBranchQuery), | ||||||||||
| }); | ||||||||||
| const refs = branchRefState.refs; | ||||||||||
| const isProjectCloneDone = useProjectClone(activeProjectRef)?.phase === "done"; | ||||||||||
| // An Effect Event: refreshStatus is a new closure each render, and its settlement re-renders us. | ||||||||||
| const refreshClonedBranchStatus = useEffectEvent((target: EnvironmentId, cwd: string) => { | ||||||||||
| void refreshStatus({ environmentId: target, input: { cwd, localOnly: true } }); | ||||||||||
| }); | ||||||||||
| // oxlint-disable-next-line t3code/no-unreasoned-state-sync -- refs listed while the project was cloning are empty; refetch them from the server once the clone lands | ||||||||||
| useEffect(() => { | ||||||||||
| if (!isProjectCloneDone || branchCwd === null) return; | ||||||||||
| refreshClonedBranchStatus(environmentId, branchCwd); | ||||||||||
| }, [branchCwd, environmentId, isProjectCloneDone]); | ||||||||||
| const branchByName = new Map(refs.map((refName) => [refName.name, refName] as const)); | ||||||||||
| const normalizedBranchQuery = trimmedBranchQuery.toLowerCase(); | ||||||||||
| const prReference = parsePullRequestReference(trimmedBranchQuery); | ||||||||||
|
|
@@ -294,7 +307,6 @@ export function BranchToolbarBranchSelector({ | |||||||||
| const createBranchItemValue = canCreateBranch | ||||||||||
| ? `__create_new_branch__:${trimmedBranchQuery}` | ||||||||||
| : null; | ||||||||||
| const defaultBranchName = refs.find((refName) => refName.isDefault)?.name ?? null; | ||||||||||
| const pickerCreateBranchItemValue = hasExactBranchMatch ? null : createBranchItemValue; | ||||||||||
| const branchPickerItems = [ | ||||||||||
| ...(checkoutPullRequestItemValue ? [checkoutPullRequestItemValue] : []), | ||||||||||
|
|
@@ -536,9 +548,17 @@ export function BranchToolbarBranchSelector({ | |||||||||
| }); | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| const worktreeBaseBranchCandidate = isInitialBranchesLoadPending | ||||||||||
| const activeThreadBranchListed = | ||||||||||
| activeThreadBranch === null || activeThreadBranch !== resolvedActiveBranch | ||||||||||
| ? null | ||||||||||
| : listedActiveBranch !== null || queriedActiveBranch !== undefined | ||||||||||
| ? true | ||||||||||
| : activeBranchRefQuery.data !== null | ||||||||||
| ? false | ||||||||||
|
Comment on lines
+556
to
+557
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Treat a truncated active-branch query as unknown, not absent. The active-branch query requests only 10 refs. Return Suggested fix
Committable suggestion
|
||||||||||
| : null; | ||||||||||
| const worktreeBaseBranchSeed = isInitialBranchesLoadPending | ||||||||||
| ? null | ||||||||||
| : (defaultBranchName ?? currentGitBranch); | ||||||||||
| : resolveWorktreeBaseBranchSeed({ activeThreadBranch, activeThreadBranchListed, refs }); | ||||||||||
|
|
||||||||||
| // The server sets started threads' branches; defaulting here would detach the agent from its worktree. | ||||||||||
| const threadStarted = serverSession !== null; | ||||||||||
|
|
@@ -548,19 +568,17 @@ export function BranchToolbarBranchSelector({ | |||||||||
| threadStarted || | ||||||||||
| effectiveEnvMode !== "worktree" || | ||||||||||
| activeWorktreePath || | ||||||||||
| activeThreadBranch || | ||||||||||
| !worktreeBaseBranchCandidate | ||||||||||
| !worktreeBaseBranchSeed | ||||||||||
| ) { | ||||||||||
| return; | ||||||||||
| } | ||||||||||
| setThreadBranch(worktreeBaseBranchCandidate, null, true); | ||||||||||
| setThreadBranch(worktreeBaseBranchSeed, null, true); | ||||||||||
| }, [ | ||||||||||
| activeThreadBranch, | ||||||||||
| activeWorktreePath, | ||||||||||
| effectiveEnvMode, | ||||||||||
| setThreadBranch, | ||||||||||
| threadStarted, | ||||||||||
| worktreeBaseBranchCandidate, | ||||||||||
| worktreeBaseBranchSeed, | ||||||||||
| ]); | ||||||||||
|
|
||||||||||
| const handleOpenChange = (open: boolean) => { | ||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟠 High
components/BranchToolbarBranchSelector.tsx:556A saved worktree base is overwritten with the default branch when it is absent from the first
activeBranchRefQuerypage, even if the ref still exists. That query uses a substring match with a limit of 10, soactiveBranchRefQuery.data !== nulldoes not establish that the ref was deleted; only mark it missing after an exact existence check or exhaustive lookup.🚀 Reply "fix it for me" or copy this AI Prompt for your agent: