Preflight
What happened?
When a project's .git is a symlink to a sibling directory with another name (e.g. .git -> .git.nosync, a common trick to keep iCloud Drive from syncing the git dir), the shadow repo resolves to .git.nosync/ok. That is inside the shadow work tree under a name git does not treat as special, so the shadow git add stages the shadow repo's own files (.git.nosync/ok/objects/**, index-wip-*, lock, …) and the main git dir into every WIP snapshot.
Each snapshot then contains the previous snapshot's packs, so the shadow repo grows without bound. On my machine (0.30.0) .git.nosync/ok hit ~151 GB: ~135 GB of abandoned objects/pack/tmp_pack_* files (single files of 54 GB and 43 GB, left when gc/repack died on a full disk) plus ~15 GB of loose objects. The disk went to 100% and stayed pegged with I/O. I reproduced it on 0.77.7 with a fresh scratch repo (below).
There's a related gap: the shadow repo has its own info/exclude and does not honor the project's .git/info/exclude. Paths a user has excluded locally (large model files like *.gguf, private folders, credential files) still get snapshotted into the shadow repo. After upgrading, a 1.2 GB *.gguf excluded only via .git/info/exclude reappeared in the shadow object store within a minute of startup.
Expected:
- The shadow repo never stages its own directory or the project's git dir, whatever the git dir is named or wherever it resolves (symlink,
core.worktree, etc.).
- Ideally, the shadow repo honors the project's
.git/info/exclude the way it honors .gitignore.
Local workaround that fixes it (confirmed on 0.77.7): with the server stopped, append /.git.nosync/ (plus whatever you mirror from the project's .git/info/exclude) to <shadow>/info/exclude, delete the refs under refs/wip/ except openknowledge-service, delete <shadow>/index-wip-fanout, then run git gc --prune=now. You have to remove the persistent fan-out index: entries already tracked there survive the new excludes (dropExcludedIndexEntries only drops OK's built-in SHADOW_EXCLUDE_PATTERNS). ensureShadowExcludes preserves the added lines across restarts.
Possible fix: in ensureShadowExcludes / SHADOW_EXCLUDE_PATTERNS, add an anchored exclude for the realpath of the shadow git dir, and for the resolved project git dir when either sits inside workTree, relative to workTree. Consider also mirroring or appending the project's info/exclude, or pointing core.excludesFile at it. A guard that refuses to commit a tree containing <shadow>/objects would also stop the feedback loop outright.
Steps to reproduce
R=/tmp/okrepro; mkdir -p $R && cd $R
git init -q && mv .git .git.nosync && ln -s .git.nosync .git
echo '/.git.nosync/' >> .git/info/exclude # main repo ignores it fine
echo '# hi' > note.md && git add note.md && git commit -qm init
ok init --no-mcp --no-skills --local-only
ok start --no-open-browser -p 47911 &
sleep 25; echo '# v2' >> note.md; git commit -qam v2; sleep 30
G=.git.nosync/ok
git --git-dir=$G ls-tree -r --name-only refs/wip/main/git-upstream -- \
| grep '^\.git\.nosync/' | cut -d/ -f2-3 | sort | uniq -c | sort -rn | head
Output on 0.77.7:
483 ok/objects
14 ok/hooks
2 ok/refs
1 ok/lock
1 ok/last-known-head
1 ok/index-wip-git-upstream.lock
1 ok/index-wip-git-upstream
1 ok/index-wip-fanout
...
.git.nosync/ entries go 262 → 537 between two consecutive snapshots, and the growth keeps compounding.
Platform
macOS (Apple Silicon)
How did you install OpenKnowledge?
Desktop app (DMG)
Version
0.77.7 (originally hit on 0.30.0)
Logs, errors, or screenshots
$ git --git-dir=.git.nosync/ok count-objects -vH # on 0.30.0, before cleanup
count: 51443
size: 15.62 GiB
in-pack: 4722
packs: 4
size-pack: 352.11 MiB
garbage: 17
size-garbage: 135.42 GiB
$ ls -laS .git.nosync/ok/objects/pack | head -4
-r--r--r-- 54074493522 tmp_pack_XsRdN6
-r--r--r-- 43571256632 tmp_pack_feFPFW
-r--r--r-- 21112686889 tmp_pack_jq4W1R
$ git --git-dir=.git.nosync/ok ls-tree --name-only refs/wip/main/git-upstream
HEAD
config
description
hooks
index-wip-git-upstream.lock
info
last-known-head
lock
objects
packed-refs
Preflight
What happened?
When a project's
.gitis a symlink to a sibling directory with another name (e.g..git -> .git.nosync, a common trick to keep iCloud Drive from syncing the git dir), the shadow repo resolves to.git.nosync/ok. That is inside the shadow work tree under a name git does not treat as special, so the shadowgit addstages the shadow repo's own files (.git.nosync/ok/objects/**,index-wip-*,lock, …) and the main git dir into every WIP snapshot.Each snapshot then contains the previous snapshot's packs, so the shadow repo grows without bound. On my machine (0.30.0)
.git.nosync/okhit ~151 GB: ~135 GB of abandonedobjects/pack/tmp_pack_*files (single files of 54 GB and 43 GB, left whengc/repack died on a full disk) plus ~15 GB of loose objects. The disk went to 100% and stayed pegged with I/O. I reproduced it on 0.77.7 with a fresh scratch repo (below).There's a related gap: the shadow repo has its own
info/excludeand does not honor the project's.git/info/exclude. Paths a user has excluded locally (large model files like*.gguf, private folders, credential files) still get snapshotted into the shadow repo. After upgrading, a 1.2 GB*.ggufexcluded only via.git/info/excludereappeared in the shadow object store within a minute of startup.Expected:
core.worktree, etc.)..git/info/excludethe way it honors.gitignore.Local workaround that fixes it (confirmed on 0.77.7): with the server stopped, append
/.git.nosync/(plus whatever you mirror from the project's.git/info/exclude) to<shadow>/info/exclude, delete the refs underrefs/wip/exceptopenknowledge-service, delete<shadow>/index-wip-fanout, then rungit gc --prune=now. You have to remove the persistent fan-out index: entries already tracked there survive the new excludes (dropExcludedIndexEntriesonly drops OK's built-inSHADOW_EXCLUDE_PATTERNS).ensureShadowExcludespreserves the added lines across restarts.Possible fix: in
ensureShadowExcludes/SHADOW_EXCLUDE_PATTERNS, add an anchored exclude for the realpath of the shadow git dir, and for the resolved project git dir when either sits insideworkTree, relative toworkTree. Consider also mirroring or appending the project'sinfo/exclude, or pointingcore.excludesFileat it. A guard that refuses to commit a tree containing<shadow>/objectswould also stop the feedback loop outright.Steps to reproduce
Output on 0.77.7:
.git.nosync/entries go 262 → 537 between two consecutive snapshots, and the growth keeps compounding.Platform
macOS (Apple Silicon)
How did you install OpenKnowledge?
Desktop app (DMG)
Version
0.77.7 (originally hit on 0.30.0)
Logs, errors, or screenshots