`Repository.setConfig()` in the built-in Git extension always uses `git config --add`, causing unbounded duplicate branch config entries
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
Type: Bug
---
### Steps to Reproduce
1. Open a workspace containing a git repository, with the built-in **Git** extension enabled (default) and the **GitHub Pull Requests and Issues** extension installed and signed in.
2. Check out a local branch that has an associated GitHub pull request (e.g. via "Checkout" from the GitHub Pull Requests view, or by opening a branch that already has an open PR).
3. Cause the extension host to reactivate several times while that branch stays checked out — e.g.:
- Run **Developer: Reload Window** a few times, or
- Reconnect a Remote-SSH / Remote-WSL / Codespaces session a few times (each reconnect restarts the extension host), or
- Switch away from and back to the branch a few times.
4. Open a terminal in the repo and run:
```
git config --get-all branch..github-pr-owner-number
```
**Actual result:** The same value is listed multiple times — once per reactivation/association event. The duplicate count grows without bound the more the association logic re-runs (in a long-lived Remote session this can reach dozens or more identical lines for a single branch). The `.git/config` file for that branch section looks like:
```
[branch "feat/example"]
remote = origin
merge = refs/heads/feat/example
github-pr-owner-number = "Owner#repo#42"
github-pr-owner-number = "Owner#repo#42"
github-pr-owner-number = "Owner#repo#42"
...
```
If the branch's associated PR number later changes (e.g. old PR closed, new PR opened for the same branch), the *old* value is never removed — it just keeps appending new duplicate lines for the new value on top of the stale ones, so both stale and current values remain physically present in config.
**Expected result:** Setting a config value for a key that already has exactly one value should **replace** that value, not add an additional line. There should only ever be one `branch..github-pr-owner-number` entry per branch (unless something intentionally wants a multi-valued key).
### Root cause (found via source inspection)
`extensions/git/dist/main.js` implements the public `Repository.setConfig(key, value)` API as an unconditional append:
```js
setConfig(e,t){return this.run(ae.Config(!1),()=>this.repository.config("add","local",e,t))}
```
This always shells out to `git config --add`, never `git config --replace-all` (or a check-existing-then-overwrite). Any extension that calls `repository.setConfig(...)` to persist a value tied to a branch — such as the GitHub Pull Requests extension writing `branch..github-pr-owner-number` every time it associates a branch with its PR — will accumulate duplicate lines every time that code path re-runs (extension activation, branch switch, PR metadata refresh, etc.), rather than keeping a single up-to-date value.
### Impact
**1. PR-comment/active-PR flakiness (your original symptom) — most direct impact.**
The GitHub Pull Requests extension parses `branch..github-pr-owner-number` to decide which PR a checked-out branch belongs to, and that's what drives whether it fetches and overlays that PR's review comments inline. We directly observed `caddie-gateway`'s `feat/power-platform` branch carrying **two different PR numbers** (`#19` then `#21`) simultaneously in config before cleanup. Whether the extension resolves the "correct" one is just an accident of parse order/timing/caching, not a guarantee — which is exactly consistent with comments sometimes rendering and sometimes not, without you changing anything.
**2. Silent failure of any future cleanup by the config's own owner.**
I tested this directly: once a key has 2+ values, `git config --unset ` (without `--unset-all`) **refuses to run**:
```
warning: test.key has multiple values
exit code: 5
```
So if the GitHub Pull Requests extension (or VS Code core) ever tries to *clear* a stale PR association — e.g. when a branch's linked PR is closed/merged and it wants to remove the entry — that unset call fails and does nothing, leaving the stale value permanently stuck alongside future duplicates. The bug isn't just "extra lines get added," it's also "the normal cleanup path can never fix it once it's already duplicated."
**3. Unbounded, compounding growth over the life of a workspace.**
Because the trigger is extension-host reactivation (window reloads, Remote-SSH/WSL reconnects, branch re-visits) rather than a one-time event, the duplicate count has no ceiling. We saw this scale from single digits up to 69 lines for one branch in a workspace that's only been active for one PR cycle — in a long-lived remote dev environment this will keep growing indefinitely, making the problem worse the longer you leave a branch checked out.
**4. Broader blast radius than just this one key.**
The same `Repository.setConfig()` API is used for other branch-scoped values too (e.g. `github-pr-base-branch`, which we also saw duplicated in `caddie-config-repo`). Any extension — not just GitHub Pull Requests — that calls this VS Code API to persist per-branch state is subject to the same silent accumulation, so this isn't a narrowly-scoped one-extension issue; it's a defect in a shared core API.
### Suggested fix
`Repository.setConfig()` should behave like a "set" (overwrite) operation — either check for an existing single value and use `--replace-all`, or default to that flag — rather than unconditionally calling `git config --add`.
VS Code version: Code 1.135.0 (08d4889f9ec4a1685d257b9b95de036c8e1ce1e5, 2026-08-25T14:26:52Z)
OS version: Windows_NT x64 10.0.26200
Modes:
Remote OS version: Linux x64 6.6.114.1-microsoft-standard-WSL2
System Info
|Item|Value|
|---|---|
|CPUs|AMD EPYC 9V74 80-Core Processor (4 x 2596)|
|GPU Status|2d_canvas: unavailable_software
GPU0: VENDOR= 0x1414, DEVICE=0x008c [Microsoft Basic Render Driver], DRIVER_VENDOR=Microsoft, DRIVER_VERSION=10.0.26100.8875 *ACTIVE*
GPU1: VENDOR= 0x1414, DEVICE=0x008c [Microsoft Basic Render Driver], DRIVER_VERSION=10.0.26100.8875
GPU2: VENDOR= 0x1414, DEVICE=0x008c [Microsoft Basic Render Driver], DRIVER_VERSION=10.0.26100.8875
Machine model name:
Machine model version:
direct_rendering_display_compositor: disabled_off_ok
gpu_compositing: disabled_software
multiple_raster_threads: enabled_on
opengl: disabled_off
rasterization: disabled_software
raw_draw: disabled_off_ok
skia_graphite: disabled_off
trees_in_viz: disabled_off
video_decode: disabled_software
video_encode: disabled_software
webgl: unavailable_software
webgpu: unavailable_software
webnn: unavailable_software|
|Load (avg)|undefined|
|Memory (System)|15.99GB (1.81GB free)|
|Process Argv|--crash-reporter-id d446a3c5-96ed-43cc-94b4-fdfcf64f8954|
|Screen Reader|no|
|VM|0%|
|Item|Value|
|---|---|
|Remote|WSL: Ubuntu|
|OS|Linux x64 6.6.114.1-microsoft-standard-WSL2|
|CPUs|AMD EPYC 9V74 80-Core Processor (4 x 0)|
|Memory (System)|7.75GB (1.18GB free)|
|VM|0%|
Extensions (15)
Name|Identifier|Author|Version
---|---|---|---
Remote - SSH|ms-vscode-remote.remote-ssh|ms-vscode-remote|0.128.0
Remote - SSH: Editing Configuration Files|ms-vscode-remote.remote-ssh-edit|ms-vscode-remote|0.87.0
WSL|ms-vscode-remote.remote-wsl|ms-vscode-remote|0.104.3
Remote Development|ms-vscode-remote.vscode-remote-extensionpack|ms-vscode-remote|0.26.0
Remote Explorer|ms-vscode.remote-explorer|ms-vscode|0.5.0
Remote - Tunnels|ms-vscode.remote-server|ms-vscode|1.5.3
GitHub Pull Requests|github.vscode-pull-request-github|GitHub|0.162.0
Chat Customizations Evaluations|ms-vscode.vscode-chat-customizations-evaluations|ms-vscode|1.0.8
Language Support for Java(TM) by Red Hat|redhat.java|redhat|1.55.0
Gradle for Java|vscjava.vscode-gradle|vscjava|3.18.0
Debugger for Java|vscjava.vscode-java-debug|vscjava|0.59.0
Project Manager for Java|vscjava.vscode-java-dependency|vscjava|0.27.6
Extension Pack for Java|vscjava.vscode-java-pack|vscjava|0.31.1
Test Runner for Java|vscjava.vscode-java-test|vscjava|0.46.0
Maven for Java|vscjava.vscode-maven|vscjava|0.45.3
A/B Experiments
```
vsliv368cf:30146710
vswsl492:30256859
binariesv615:30325510
nativeloc1:31344060
dwcopilot:31170013
dwoutputs:31242946
copilot_t_ci:31333650
e5gg6876:31282496
pythonrdcb7:31342333
6518g693:31463988
aj953862:31281341
4f60g487:31327383
envsactivate1:31551504
cloudbuttont:31379625
3efgi100_wstrepl:31403338
ec5jj548:31422691
diffpatch-lysithea-24-production:31572515
cp_cls_t_966_ss:31526232
4je02754:31466945
8hhj4413:31478653
ge8j1254_inline_auto_hint_haiku:31490510
cp_cls_c_1081:31454833
conptydll_true:31498968
e9c30283:31461165
46204921:31447328
ei9d7968:31496641
nes-extended-on:31455476
chat:31457767
8hig5102:31480529
89g7j272:31518289
i2gc6536:31499202
52612955:31516516
ddid_c:31478207
hmra_i5g22:31518061
7df3h592:31512476
cp_cls_t_1082:31535311
logging_enabled_new:31498466
j0d79568:31499440
jb_cp_cls_t_632:31543129
56dj4588:31512888
32d76977:31512328
ha629193:31508444
a1ije391_t:31540920
jbcp_cls_pctr_t:31531130
61138546:31518536
cp_intellij_t_nes:31548657
jf4hg949:31526829
ahp-both-windows:31556933
sessions_3:31532893
ihg5j128:31534457
f412h606:31551416
7g2b5551:31542111
enable_editor_pane_layout:31569726
allow-none:31555437
ac562147:31564510
c7c27ce7:31554789
1h923230:31564177
1532g621_copy:31554320
treatment-23-1:31555779
unuse_dynamic_mcp:31555281
0d8dfbc3:31570291
switchtoauto:31559321
0c1h4866:31566224
vrbsty_tr:31561058
session-mark-done:31558131
5b8j3302:31564601
mangle-name-control:31572633
lunaclean:31573760
permission_prompt_control:1332567
ccr_pr_nudge_control:1319470
vsc_wsm_t:1340632
eh853263:1355661
```
Contributor guide
Assessment
This issue has not been assessed yet.