Skip to content

fix(git): avoid index.lock contention by using --no-optional-locks - #4805

Closed
SpencerJung wants to merge 1 commit into
manaflow-ai:mainfrom
SpencerJung:fix/4779-git-status-no-optional-locks
Closed

SpencerJung wants to merge 1 commit into
manaflow-ai:mainfrom
SpencerJung:fix/4779-git-status-no-optional-locks

Conversation

@SpencerJung

@SpencerJung SpencerJung commented May 26, 2026 •

Copy link
Copy Markdown

Problem

When cmux polls git status in the background (e.g. for sidebar file-explorer badges), it can create index.lock and interfere with user-initiated git operations such as git rebase, causing:

error: Unable to create '~/work/.git/index.lock': File exists.
Another git process seems to be running in this repository...

This is reproducible quite reliably when cmux's GitStatusProvider runs concurrently with user git commands.

Root Cause

GitStatusProvider.fetchStatus invokes git status --porcelain without any lock-avoidance flags. Git may take optional locks during status, which contend with user operations.

Fix

Add --no-optional-locks to the git status invocation:

runGit(in: repoRoot, arguments: ["status", "--no-optional-locks", "--porcelain"])

This makes the status check read-only and prevents it from taking any optional lock, giving user git operations priority over background polling.

Verification

  • Single-line change, minimal scope
  • --no-optional-locks is supported in Git 2.15+ (cmux targets recent macOS)
  • No schema/lockfile changes

References

Fixes #4779


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.


Summary by cubic

Run git status with --no-optional-locks to prevent background polling from creating index.lock and blocking user operations (e.g., rebase). This makes status checks read-only and avoids contention. Fixes #4779.

Written for commit 73b0bfd. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved file status detection reliability in the file explorer by updating how the application queries repository information.

Review Change Stack

When cmux polls git status in the background, it can create index.lock
and interfere with user-initiated git operations such as rebase,
causing 'Unable to create .git/index.lock: File exists' errors.

Add --no-optional-locks to the git status invocation in GitStatusProvider
so that status checks become read-only and never take an optional lock,
giving user git operations priority over background polling.

Fixes manaflow-ai#4779
@vercel

vercel Bot commented May 26, 2026

Copy link
Copy Markdown

@SpencerJung is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 26, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

A single parameter addition to the git status command in GitStatusProvider.fetchStatus(directory:) to include the --no-optional-locks flag. This prevents the status polling mechanism from acquiring git locks that could interfere with concurrent user git operations.

Changes

Git Status Lock Prevention

Layer / File(s) Summary
Git status no-locks flag
Sources/FileExplorerStore.swift
GitStatusProvider.fetchStatus(directory:) adds --no-optional-locks flag to the git status --porcelain command to avoid acquiring filesystem locks during status polling.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Poem

🐰 A lock blocks the way, cmux held too tight,
Now status polls gently, without lock's bite,
The user's commands flow, rebase runs free,
Git harmony restored, a welcome decree! 🌿


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (2 errors)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error PR introduces NSLock (2×) and DispatchQueue.asyncAfter (2×) in production FileExplorerStore.swift, violating swift-blocking-runtime rule against manual locks and timing-based coordination. Replace NSLock with actor isolation; use async sequences/callbacks instead of asyncAfter for debouncing.
Cmux Swift File And Package Boundaries ❌ Error New 1313-line file mixes UI styling, state ownership, Git parsing, SSH networking, and filesystem monitoring; exceeds 800-line threshold per swift-file-package-boundaries.md rules. Extract GitStatusProvider, SSHFileExplorerTransport, and FileExplorerDirectoryWatcher into separate SwiftPM packages behind clean boundaries instead of one oversized mixed-responsibility file.
✅ Passed checks (15 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding --no-optional-locks to git status to prevent index.lock contention.
Description check ✅ Passed The description includes Problem, Root Cause, Fix, and Verification sections with sufficient detail. Testing section from template is not explicitly shown but PR is a single-line change with minimal scope.
Linked Issues check ✅ Passed The PR directly addresses issue #4779 by adding --no-optional-locks flag to git status, making it read-only and preventing lock contention with user operations.
Out of Scope Changes check ✅ Passed The single-line change only modifies the git status invocation in GitStatusProvider.fetchStatus() as required, with no extraneous changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Cmux Swift Actor Isolation ✅ Passed The PR adds only the --no-optional-locks flag to a git command in GitStatusProvider, introducing no changes to actor isolation, Sendable annotations, or service protocol isolation.
Cmux No Hacky Sleeps ✅ Passed Check excludes Swift files (covered by swift-blocking-runtime.md). PR changes only FileExplorerStore.swift (Swift) adding --no-optional-locks flag; no sleeps, timers, or polling introduced.
Cmux Swift Concurrency ✅ Passed PR adds --no-optional-locks to git status command with no new legacy async patterns; GitStatusProvider remains synchronous with no DispatchQueue, @escaping, @Published, or Task usage.
Cmux Swift @Concurrent ✅ Passed PR only adds a Git command flag to a string argument; no changes to async/await, @concurrent annotations, or actor isolation, so concurrency rules don't apply.
Cmux Swift Logging ✅ Passed The PR adds --no-optional-locks flag to git status command; no logging violations found. All 5 NSLog statements in the new file are properly guarded with #if DEBUG blocks per allowed rules.
Cmux User-Facing Error Privacy ✅ Passed The PR adds --no-optional-locks flag only to internal git command invocation; no user-facing errors, alerts, or sensitive details are exposed. Error handling silently suppresses git output.
Cmux Full Internationalization ✅ Passed PR only modifies internal git command flags (adding --no-optional-locks to git status) with no user-facing text, string catalog changes, or localization requirements.
Cmux Swiftui State Layout ✅ Passed PR modifies only git status flag. FileExplorerStore's ObservableObject state is existing legacy code touched incidentally with no new SwiftUI view violations.
Cmux Architecture Rethink ✅ Passed The change adds --no-optional-locks to git status, addressing root cause of lock contention. It's a small correctness fix with clear invariant and doesn't introduce new polling/dispatch patterns.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR modifies only git status invocation with --no-optional-locks flag; no NSWindow, NSPanel, NSWindowController, or SwiftUI Window/WindowGroup changes present.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented May 26, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds --no-optional-locks to the local git status --porcelain invocation in GitStatusProvider.fetchStatus to prevent cmux's background polling from creating index.lock and interfering with user-initiated git operations.

  • The local code path (fetchStatus) is correctly fixed with a single-line change.
  • The SSH code path (fetchStatusSSH) still constructs git status --porcelain without --no-optional-locks, leaving the same lock-contention bug present for SSH-backed repositories.

Confidence Score: 3/5

Safe to merge for local repositories, but the SSH code path retains the original lock-contention bug and should be updated before merging if SSH-backed repos are a supported configuration.

The fix is correct and minimal for local git repositories, which is likely the most common case. However, fetchStatusSSH still shells out git status --porcelain without --no-optional-locks, meaning users working in SSH-backed repositories can still hit index.lock contention during background polling. The omission is a straightforward one-word fix, but it is a present gap in the stated goal of the PR.

Sources/FileExplorerStore.swift — specifically the fetchStatusSSH method around line 1200, where the SSH shell command still uses git status --porcelain without the lock-avoidance flag.

Important Files Changed

Filename Overview
Sources/FileExplorerStore.swift Adds --no-optional-locks to local git status to prevent index.lock contention, but the parallel SSH code path in fetchStatusSSH still uses git status --porcelain without the flag, leaving the same race condition present for SSH-backed repositories.

Sequence Diagram

sequenceDiagram
    participant DW as DirectoryWatcher
    participant FES as FileExplorerStore
    participant GPS as GitStatusProvider
    participant Git as git process

    DW->>FES: filesystem change event
    FES->>FES: refreshGitStatus()
    alt local repo
        FES->>GPS: fetchStatus(directory:)
        GPS->>Git: git status --no-optional-locks --porcelain ✅
        Git-->>GPS: porcelain output
        GPS-->>FES: [String: GitFileStatus]
    else SSH repo
        FES->>GPS: fetchStatusSSH(directory:destination:...)
        GPS->>Git: git status --porcelain ❌ (missing --no-optional-locks)
        Git-->>GPS: porcelain output
        GPS-->>FES: [String: GitFileStatus]
    end
    FES->>FES: "gitStatusByPath = status (on MainActor)"
Loading

Comments Outside Diff (1)

  1. Sources/FileExplorerStore.swift, line 1200 (link)

    P1 The SSH code path constructs its git status command as a shell string and still omits --no-optional-locks. If a user is working in the same remote repository (e.g., with a local terminal SSH'd into the same host), cmux's background SSH poll can create index.lock on the remote machine and interfere with that session — the same contention the PR set out to fix.

Reviews (1): Last reviewed commit: "fix(git): avoid index.lock contention by..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/FileExplorerStore.swift (1)

1200-1200: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Apply the same --no-optional-locks fix to the SSH git status command.

The local git status invocation at line 1189 now includes --no-optional-locks, but this SSH code path still runs git status --porcelain without the flag. Since refreshGitStatus() (lines 725-738) calls fetchStatusSSH for SSH providers, background polling over SSH can still create .git/index.lock on the remote host and interfere with user git operations, leaving issue #4779 unresolved for SSH workspaces.

🔧 Proposed fix
-        let cmd = "cd '\(escapedDir)' 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null && echo '---GIT_STATUS---' && git status --porcelain 2>/dev/null"
+        let cmd = "cd '\(escapedDir)' 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null && echo '---GIT_STATUS---' && git status --no-optional-locks --porcelain 2>/dev/null"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/FileExplorerStore.swift` at line 1200, The SSH path builds a shell
command string named cmd (in fetchStatusSSH called from refreshGitStatus) that
runs "git status --porcelain" without the --no-optional-locks flag; update that
command to run git with --no-optional-locks (e.g. change "git status
--porcelain" to "git --no-optional-locks status --porcelain") so remote SSH
polling won't create .git/index.lock on the remote host.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@Sources/FileExplorerStore.swift`:
- Line 1200: The SSH path builds a shell command string named cmd (in
fetchStatusSSH called from refreshGitStatus) that runs "git status --porcelain"
without the --no-optional-locks flag; update that command to run git with
--no-optional-locks (e.g. change "git status --porcelain" to "git
--no-optional-locks status --porcelain") so remote SSH polling won't create
.git/index.lock on the remote host.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e0dae918-bb22-49ac-a544-9a93502687fa

📥 Commits

Reviewing files that changed from the base of the PR and between b9f2298 and 73b0bfd.

📒 Files selected for processing (1)
  • Sources/FileExplorerStore.swift

@vercel

vercel Bot commented Jul 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 2, 2026 7:35am

@austinywang

Copy link
Copy Markdown
Contributor

Superseded by #7173 (merged in 553fe35), which applies the same lock-avoidance to the local git status probe and extends it to the SSH remote variant, with regression coverage. Thanks @SpencerJung for the original diagnosis and fix — credited in the merged PR.

@austinywang austinywang closed this Jul 2, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — 73b0bfd5 Deployed Jul 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Git status polling interrupts user's git actions

2 participants