github / github/app

Copilot app ships a bundled git with `core.symlinks=false`, silently breaking all symlinks in app-created worktrees on Windows

Đang mở
#3,280 0 bình luận 0 reaction 1 người được giao Được @Chuxel nhận Xem trên GitHub
triage
Ngôn ngữ chính
Không có dữ liệu ngôn ngữ
Star
2.1k
Fork
153
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

### 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_

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.