Copilot app ships a bundled git with `core.symlinks=false`, silently breaking all symlinks in app-created worktrees on Windows
- Lenguaje dominante
- Sin datos de lenguaje
- Estrellas
- 2.1k
- Forks
- 153
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Descripción
### Short summary
Copilot breaks repo symlinks
### Affected version or release
1.1.14
### Installation context
_No response_
### What happened?
## Title
## Environment
- GitHub Copilot app 1.1.14 / GitHub Copilot CLI 1.0.80
- Windows
- Bundled git: `github-copilot-git-2.53.0-4`
- Repo: `msasg/Interstellar/epichan` (Azure DevOps), worktree-backed session
## Summary
The Copilot app ships its own git build and places it first on PATH. That git's **system** gitconfig hardcodes `core.symlinks = false`:
Plain text
```
C:\Users\\AppData\Local\github-copilot-git-2.53.0-4\etc\gitconfig
[core]
symlinks = false
autocrlf = true
fscache = true
```
Every worktree the app creates is therefore checked out with symlink support disabled. Each tracked symlink (git mode `120000`) is written to disk as a **plain text file containing its target path** rather than an actual link. In the affected repo this silently broke **all 28 tracked symlinks**.
This is not a machine privilege limitation. Unprivileged symlink creation is permitted on this host — verified with `New-Item -ItemType SymbolicLink`, which succeeds. The bundled git simply has the flag off, which is the Git-for-Windows installer default when "Enable symbolic links" is left unchecked. The app shipped that default.
## Repro
1. On Windows, use a repo containing tracked symlinks ( has 28).
2. Create a worktree-backed session via the Copilot app.
3. In the session: `Get-Item .claude | Select-Object LinkType`
**Expected:** `SymbolicLink`
**Actual:** empty `LinkType`; `.claude` is a regular file whose contents are the literal string `.copilot`
## Impact
**1. All project skills silently vanish.** The CLI discovers project skills from `.github/skills/`, `.agents/skills/`, or `.claude/skills/`. uses the widely-used pattern of symlinking `.claude` → `.copilot` so one tree serves both Claude-format and Copilot-format tooling. With the symlink unmaterialized, `.claude/skills/` does not exist and all 36 project skills disappear.
The failure mode is bad because it is silent and misleading:
- `copilot skill list` in the worktree prints only the 2 builtin skills, no warning.
- `skill({skill: "file-bug"})` returns `Skill not found: file-bug`.
- The agent then improvises instead of running the codified workflow. In my case the requested skill was ``, which is the only place the correct target Azure DevOps org is documented — improvising would have filed into the wrong org.
- `.copilot/rules/` still loads (they arrive as custom instructions via `AGENTS.md`), so the session *looks* correctly configured. This actively masks the problem.
**2. The damage is far broader than skills.** After enabling `core.symlinks`, git reported 27 additional `T` (typechange) entries that had been invisible:
- All 23 `.github/instructions/*.instructions.md` → `.copilot/rules/*.md` — the VS Code / Copilot Chat rule surface, entirely broken.
- `.github/copilot-instructions.md` → `AGENTS.md` — the repo's top-level instruction file.
- `apps/storybook/public/fonts`, `apps/visual-compare/public/fonts` → `packages/design-system/fonts` — build/runtime font assets missing in three apps.
**3. It hides itself from `git status`.** With `core.symlinks=false`, git treats the plain file's contents as the symlink target, so status reads clean and nothing signals breakage. The 27 typechanges only surfaced *after* flipping the setting.
**4. It is not repo-fixable.** `core.symlinks` lives in per-install/per-clone config. It cannot be committed, and there is no `.gitattributes` equivalent. No repo can defend itself against this.
**5. It is not repo-specific.** Any repo with symlinks, opened in an app worktree on Windows, is affected.
## Evidence that the app owns this
The same repo, same CLI, same machine, behaves correctly when checked out by the user's own Git for Windows:
| Checkout | Created by | `.claude` | `copilot skill list` |
| --- | --- | --- | --- |
| `Q:\\src\\` (main) | user's Git for Windows | `SymbolicLink` → `.copilot` | 36 project skills |
| app worktree | app's bundled git | plain file | 0 project skills |
Config origin trace confirms the source is the app's git, not the repo:
Plain text
```
file:C:/Users//AppData/Local/github-copilot-git-2.53.0-4/etc/gitconfig core.symlinks=false
```
Neither the repo's local config nor the user's global config set it.
## Verification of the fix
Creating a worktree with the flag enabled produces a correct checkout:
Plain text
```
git -C -c core.symlinks=true worktree add --detach HEAD
→ .claude SymbolicLink .copilot .claude\skills resolves: True
```
A global override (`git config --global core.symlinks true`) also wins over the app's system config and fixes new app-created worktrees with no inline flag. After restoring with `git checkout -- .`, all 28 symlinks materialized, working tree clean, and skill discovery returned 38 skills.
## Suggested fix
1. **Primary:** stop shipping `core.symlinks = false` in the bundled git's system gitconfig — or set `core.symlinks=true` explicitly when creating worktrees on hosts where symlink creation is permitted (detectable with a cheap probe).
2. **Fallback for genuinely unprivileged hosts:** after `git worktree add`, detect index entries with mode `120000` that materialized as regular files and warn loudly, rather than proceeding silently.
3. **Defense in depth (CLI):** consider having the CLI also scan `.copilot/skills/` directly. Given the config directory is literally named `.copilot`, requiring a `.claude` symlink to reach it is a sharp edge — and it is the single point of failure that turned a git config default into total skill loss.
## Related observation
The same bundled config sets `core.autocrlf = true`. That is a separate risk for repos containing shell scripts or fixtures with content-hash assertions, and may warrant its own review.
### Steps to reproduce
_No response_
### Expected behavior
_No response_
### Additional context
_No response_
Guía de contribución
Evaluación
Este issue todavía no se ha evaluado.