[Bug]: New worktree creation reclones already-initialized submodule objects
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- Open a repository with an initialized submodule containing substantial content/history. The measured example has roughly 1 GiB of packed submodule objects already available in the source checkout.
- Set new threads to New worktree, with Start from origin enabled.
- Start a new thread and send its first message.
- Inspect the server trace for
fetchRemote,GitVcsDriver.createWorktree, andGitVcsDriver.createWorktree.updateSubmodules.
Expected behavior
Preparing an independent worktree should reuse locally available submodule objects where possible, preserve the selected gitlink commit, and fetch objects that are missing. The new worktree should remain usable if the source submodule is subsequently removed or pruned.
Actual behavior
The first message waits roughly 18 seconds for worktree preparation in the observed case. Most of that time is submodule initialization, not fetching origin or checking out the superproject.
| Existing server trace step | Duration |
|---|---|
| Fetch origin | 0.522 s |
git worktree add |
0.296 s |
| Recursive submodule initialization | 17.417 s |
Each linked worktree gets a separate submodule Git directory. The current git submodule update --init --recursive invocation clones objects again rather than explicitly using the populated source checkout as a reference. This happens before the first turn is dispatched. Turning off Start from origin would remove only about half a second in this example.
Impact
Major degradation or frequent failure — a repeated startup delay when creating fresh worktree threads; the operation eventually succeeds.
Version or commit
Source inspected and benchmarked at 8faf031c2dac4f147c43fe32e326b76678c20c94.
Environment
Linux, Git 2.55.0. Benchmarks used Node 26.8.1; focused verification of the proposed fix also passed on Node 24.20.0. The affected operation runs in the server's Git driver.
Logs or stack traces
No error is required to reproduce this: the slow operations complete successfully. The relevant redacted timing breakdown is above.
Supporting measurements
A locally tested candidate supplies initialized source submodules as temporary --reference repositories and uses --dissociate to avoid a persistent dependency on their object stores.
| Measurement | Original run 1 | Original run 2 | Candidate run 1 | Candidate run 2 |
|---|---|---|---|---|
| Recursive submodule command | 17.288 s | 17.440 s | 4.721 s | 6.574 s |
Actual createWorktree() driver call |
26.089 s | 23.400 s | 5.721 s | 5.501 s |
The driver-call mean fell from 24.744 s to 5.611 s, approximately 77%. Driver measurements include worktree creation, reference discovery, submodule initialization, base-ref configuration and cache invalidation; they do not include the earlier origin fetch or provider startup. These are separate runs from the original server trace, so their absolute durations differ.
Each comparison used fresh, disk-backed disposable worktrees at the same submodule commit, with interleaved baseline/candidate ordering, the same remote, pack.threads=2, and process priority nice 10. There were two observations per variant per measurement on a shared machine, without flushing system caches. This is evidence for this workload, not a universal speedup claim.
Git Trace2 recorded about 14.84–14.94 s in index-pack for the original submodule commands, versus about 2.12–2.33 s in the candidate's local repack. Network byte savings were not measured directly. Exact submodule HEAD and the absence of persistent alternates were checked.
Focused tests found no correctness regression, including nested/multiple submodules, shallow or uninitialized sources, missing cached commits, and source object-store removal. Windows/macOS, large submodule counts, and performance with local-path remotes remain unmeasured. References still require remote contact; fallback after a failed reference attempt can add work.
Workaround
Reuse an existing worktree when sharing its working directory is appropriate. Disabling Start from origin does not address the dominant submodule cost in this case.
Related reports
#9340 covers a related blocking startup path and progress reporting, particularly with an upstream base. This report isolates repeated submodule object cloning. #7672 explains why automatic submodule initialization is needed; that behavior should be preserved.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in apps/server by tracing fetchRemote, GitVcsDriver.createWorktree, and GitVcsDriver.createWorktree.updateSubmodules. Review the focused verification for nested, multiple, shallow, uninitialized, and missing-commit submodules, then confirm that worktree creation reuses available source objects, preserves the gitlink commit, fetches missing objects, and remains usable after source objects are removed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, typescript
- Domain
- backend, devtools, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100