Skip to content

Shadow WIP tree recursively captures symlink-target Git directory #1780

Description

@valkyriweb

Preflight

  • I searched existing issues and this isn't a duplicate
  • I'm on the latest version (or I've noted my version below)

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:

  1. 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.).
  2. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions