Git Index Lock iCloud Drive bird Reindex Fix

Why git commands hang 30-60s on iCloud Drive and fail with index.lock errors. The cause is macOS bird reindex. Real fixes, not just rm index.lock.

Share

Git Index Lock That iCloud Drive's bird Keeps Re-Taking | AI PM Portfolio

The Git Index Lock That iCloud Drive's bird Reindex Kept Re-Taking

April 11, 2026 · 8 min read · Claude Code / DevEx

Last Updated: 2026-04-11

When git commands hang for 30-60 seconds on macOS and fail with "Unable to create .git/index.lock: File exists," the cause is usually iCloud Drive's bird sync daemon reindexing your .git directory. Removing the lock file is a temporary fix because bird re-takes it during the next reindex cycle. The permanent solution is to move repositories outside iCloud-synced directories entirely and keep cloud sync for artifacts, not version control internals.

What does the git index lock iCloud Drive problem look like?

You are deep in a coding session. You run git status and nothing happens. The terminal sits there, cursor blinking, for 30 seconds, 60 seconds, sometimes 2 full minutes. No output. No error. Just silence. Eventually it either completes or you kill it with Ctrl+C.

Then you try git commit and get this:

# The error you see
fatal: Unable to create '/Users/you/Documents/project/.git/index.lock': File exists.

Another git process seems to be running in this repository, e.g.
an editor opened by 'git commit'. Please make sure all processes
are terminated then try again. If it still fails, a git process
may have crashed in this repository earlier:
remove the file manually to continue.

You check for rogue git processes. None. You run rm .git/index.lock. The commit works. Five minutes later, the same thing happens again. You remove the lock again. It comes back. You start to suspect your machine is haunted.

It is not haunted. It is bird.

What is bird and why does it take the git index lock?

The bird process (/System/Library/PrivateFrameworks/iCloudDriveCore.framework/Versions/A/Support/bird) is macOS's iCloud Drive sync daemon. It monitors file changes in iCloud-synced directories -- typically ~/Documents and ~/Desktop -- and synchronizes them with Apple's cloud servers.

When you perform bulk file operations -- deleting a large node_modules, running a major git rebase, or clearing build caches -- bird detects hundreds or thousands of filesystem changes. It enters a reindex cycle that can run for 10-50 minutes, during which it walks through every file in the synced directory tree. That walk includes .git/ internals.

Git's index lock mechanism (.git/index.lock) is an exclusive file-based lock. Any process that opens the index for writing creates this lock file. Git commands like git status, git add, and git commit all need to refresh the index, which requires the lock. When bird has the .git directory open for sync reconciliation, it creates filesystem-level contention that either prevents git from acquiring the lock or causes git to wait indefinitely for I/O to complete.

I measured this during an actual incident: after freeing 33 GB by deleting cached files from ~/Documents, bird ran at 12% CPU for 53 consecutive minutes. Every git status during that window hung for 2+ minutes. A git commit hung for 5+ minutes before I killed it, which left a stale .git/index.lock that blocked all subsequent git operations until manually removed.

Why does removing index.lock not permanently fix it?

The standard advice for .git/index.lock errors is straightforward:

# The standard fix
rm -f .git/index.lock
# Then retry your git command
git commit -m "your message"

This works when the lock was left by a crashed git process. But when the cause is bird, the lock returns because the reindex cycle is still running. You remove the lock, bird continues scanning, filesystem contention rebuilds, and your next git command hangs again within minutes.

The lock file is a symptom, not the disease. The disease is a cloud sync daemon that treats .git/ as ordinary files to be synchronized.

What is the kill-loop anti-pattern and why is it dangerous?

When developers discover that bird keeps re-creating the contention, some reach for an aggressive workaround:

# DO NOT DO THIS -- the kill-loop anti-pattern
while true; do
  rm -f .git/index.lock
  sleep 0.5
done

# Or worse:
pkill -9 git  # Kill any hanging git process
rm -f .git/index.lock  # Remove the lock
# Repeat in a loop

Warning: Repeatedly killing git processes mid-write and removing lock files can corrupt the git index. A corrupted index means git status shows phantom changes, git commit fails with read-tree errors, and git reset makes things worse. Once the index is corrupted, the only reliable recovery is a fresh clone from the remote. I have seen this firsthand -- every attempt to repair a corrupted index (via git reset, git read-tree, or git checkout HEAD -- .) created deeper corruption and more zombie processes. I documented this pattern while running parallel AI agents.

What are the real fixes for git slow on iCloud Drive?

There are three tiers of solutions, ranging from immediate relief to permanent fix.

Tier 1: Immediate relief (same session)

When you are mid-session and git is hanging:

# 1. Check if bird is the culprit
ps aux | awk '$3 > 5.0'
# Output when bird is reindexing:
# _spotlight  12345  12.3  1.2 ... /System/.../bird

# 2. Remove the lock file if present
rm -f .git/index.lock

# 3. Run git with a long timeout -- the hang is slow, not stuck
# Let the command complete even if it takes 5+ minutes
git commit -m "your message"

# 4. Use no-optional-locks to skip fsmonitor writes on status
git --no-optional-locks status --short

The key insight: bird's reindex is slow I/O, not a deadlock. Git will eventually complete if you let it wait. Setting a 10-minute timeout on your git commands during a reindex window is more reliable than kill-and-retry loops.

Tier 2: Reduce lock contention (configuration)

# Disable fsmonitor to reduce index lock frequency
git config core.fsmonitor false

# Disable the file system cache to avoid extra lock acquisitions
git config core.fscache false

# Use sparse checkout if you only need specific directories
git sparse-checkout set src/ lib/ app/

Setting core.fsmonitor false prevents git from writing filesystem monitor state on every git status call, which reduces the number of times git needs the index lock. This alone cut my git hang frequency by roughly 60% during bird reindex windows.

Tier 3: Move repositories out of iCloud Drive (permanent fix)

# Clone fresh to a non-iCloud directory
# Do NOT rsync from the iCloud copy -- rsync hangs on dataless files
git clone git@github.com:you/project.git ~/code/project

# Update shell config to point to new location
# In ~/.zshrc:
# export PROJECT_DIR=~/code/project

This is the only permanent fix. When your repo lives outside ~/Documents and ~/Desktop (the two directories macOS syncs to iCloud by default), bird never touches your .git directory. Git operations return to sub-second performance.

Important: do not try to rsync or cp the repo from its current iCloud-synced location. If macOS has evicted files to "dataless placeholders" (the Optimize Mac Storage feature), those reads will hang or time out. A fresh git clone from the remote is faster and safer.

What about the dataless placeholder trap?

There is a second, harder failure mode unique to iCloud Drive. When "Optimize Mac Storage" is enabled (System Settings > iCloud Drive), macOS evicts files that have not been accessed recently, replacing them with zero-byte placeholders. The file's metadata (name, size, permissions) remains, but the actual bytes are stored only in iCloud.

Git's refresh_index operation stats every tracked file. When it hits a dataless placeholder, the read requires a network round-trip through fileproviderd. Under load, this times out:

# The error from a dataless placeholder timeout
error: read error while indexing public/og-image.png: Operation timed out
fatal: mmap failed: Operation timed out

The workaround uses git's skip-worktree flag to tell the index refresh to skip directories with cold files:

# Mark all files in a cold directory as skip-worktree
git ls-files -z public/ | xargs -0 git update-index --skip-worktree

# Now commit works -- git never reads public/* from disk
git commit -m "your changes"

# Restore normal behavior after the commit
git ls-files -z public/ | xargs -0 git update-index --no-skip-worktree

This is a metadata-only index manipulation -- no file I/O on the skipped paths, completes in milliseconds, and is fully reversible. It does not unstage files or remove them from git history. I use this pattern routinely in my AI-native development workflow.

How does git perform across different cloud sync services?

Factor iCloud Drive Dropbox OneDrive Local SSD
Sync daemon bird Dropbox.app OneDrive None
Syncs .git/? Yes (no exclusion API) Yes (can exclude via .dropboxignore) Yes (can exclude via Settings) N/A
File eviction Aggressive (Optimize Mac Storage) Smart Sync (configurable) Files On-Demand (configurable) Never
Reindex behavior Full tree walk, 10-50 min after bulk ops Incremental, typically <5 min Incremental, typically <5 min N/A
git status time (1000-file repo) 0.3s normal / 30-120s during reindex 0.5s normal / 2-5s during sync 0.4s normal / 3-8s during sync 0.1-0.3s always
Index lock conflicts Frequent during reindex Rare (Dropbox handles .git better) Occasional Never (unless parallel git)
.git exclusion support No native API Yes (.dropboxignore since 2020) Yes (folder-level exclusion) N/A
Best for Documents, not code Tolerable with .dropboxignore Tolerable with exclusions All development work

The critical difference: Dropbox and OneDrive both offer mechanisms to exclude .git directories from sync. iCloud Drive has no such API. According to Apple's FileProvider documentation, there is no supported way to tell bird to skip specific subdirectories. You either sync the entire directory or move the repo elsewhere.

What is the broader lesson about cloud sync and version control?

Cloud sync daemons and version control systems solve the same problem -- keeping files in sync across locations -- but they operate on fundamentally incompatible assumptions. Git uses file-based locks and assumes exclusive filesystem access during write operations. Cloud sync daemons assume they can read and write any file at any time. These assumptions collide inside .git/.

The rule I follow now: sync the artifacts, not the repo. Keep git repositories on local SSD storage (~/code/ or ~/projects/). Let iCloud sync documents, design files, PDFs -- things that do not have internal locking mechanisms. Push code to GitHub/GitLab for cloud backup. This separation eliminates the entire class of problems described in this post.

According to a Stack Overflow thread with 500+ upvotes, the index.lock error is one of the most common git frustrations. The accepted answers all focus on removing the lock file. Almost none mention cloud sync daemons as a root cause, which means developers encounter this problem, apply the surface fix, and encounter it again days later without understanding why.

What is the recovery path when bird has already corrupted the index?

If you have been kill-looping git processes or if bird interfered mid-write and your git index is now corrupted (phantom changes in git status, read-tree errors, packed-refs failures), here is the safe recovery:

  1. Stop all git operations immediately. Further commands deepen the corruption.
  2. Kill zombie git processes: pkill -f "git (status|reset|read-tree)"
  3. Backup any uncommitted work: cp -r src/ /tmp/backup-src/
  4. Fresh clone from remote: git clone --depth=50 --branch main git@github.com:you/project.git ~/code/project-fresh
  5. Copy uncommitted files back and commit normally.
  6. Delete the corrupted repo once verified.

Do not attempt git reset --hard, git read-tree HEAD, or git checkout HEAD -- . on a corrupted index. These commands need a valid index to operate. Running them on a corrupted index creates cascading failures -- I have watched this produce 15+ zombie git processes in a single session. The fresh clone approach saved me 2+ hours compared to repair attempts.

Frequently Asked Questions

Can I just add .git to .gitignore to prevent iCloud from syncing it?

.gitignore is a git concept, not an iCloud concept. iCloud Drive's bird daemon does not read .gitignore files. There is no equivalent of Dropbox's .dropboxignore for iCloud. The only way to prevent bird from syncing .git is to move the repository outside of iCloud-synced directories entirely.

Does turning off "Optimize Mac Storage" fix the dataless placeholder problem?

Technically yes, but it forces macOS to download your entire iCloud Drive contents locally, which can be 10+ GB and creates a massive I/O storm from bird and fileproviderd during the download. This makes the short-term problem worse. Moving the repo to a non-synced directory is faster and has no side effects.

Is this only a problem with large repositories?

No. Even small repos (under 100 files) experience index.lock contention during bird reindex cycles. The severity scales with the number of files in the parent iCloud-synced directory, not the repo size. A 50-file repo inside a ~/Documents folder with 10,000 other files will hang just as badly as a 5,000-file repo.

Will Apple fix this in a future macOS update?

As of macOS 15 (Sequoia), there is no file-level or directory-level exclusion API for iCloud Drive. Apple's FileProvider framework is designed for cloud storage providers to integrate with Finder, not for users to exclude directories. The architectural approach (sync everything in ~/Documents) has been consistent since iCloud Drive launched in macOS Yosemite (2014). Planning around this limitation is more reliable than waiting for a fix.

Symlinks inside iCloud-synced directories are followed by bird, not skipped. Placing a symlink at project/.git pointing to /usr/local/git-internals/project/ does keep the actual git objects outside iCloud, but bird still traverses the symlink target during reindex. The approach is fragile and breaks tools that check whether .git is a directory (including some git GUIs). Moving the entire repo is simpler and more reliable.


Dinesh Challa is an AI Product Manager building production software with Claude Code. Follow him on LinkedIn.

Published April 11, 2026. Part of a series on developer experience lessons from building production software with AI-native tools.