Skip to content

Fix Git repository search root traversal - #4557

Merged
austinywang merged 2 commits into
mainfrom
issue-4520-repro-memory-leak
May 22, 2026
Merged

austinywang merged 2 commits into
mainfrom
issue-4520-repro-memory-leak

Conversation

@austinywang

@austinywang austinywang commented May 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add a regression test for older Foundation behavior where deleting the last path component at / can produce /.. and continue upward
  • stop Git repository search at root-equivalent paths so a restored non-Git workspace cannot spin allocating ever-longer parent paths

Verification

  • Confirmed the regression-only commit failed as intended on cmux-unit x86_64: GitRepositorySearchRootStopTests.testRootParentVariantsStopRepositorySearch failed for / -> /.. and /.. -> /../...
  • After the fix, the same targeted test passed: xcodebuild test -project cmux.xcodeproj -scheme cmux-unit -destination platform=macOS,arch=x86_64 -derivedDataPath /Users/austinwang/Library/Developer/Xcode/DerivedData/cmux-issue-4520-repro-memory-leak-unit-x86_64 -only-testing:cmuxTests/GitRepositorySearchRootStopTests.

Repro evidence

Issue repro details and vmmap/sample evidence were posted at #4520 (comment).

Remote dev build

A fixed x86_64 dev app was copied to the macOS 15.7.5 repro host at /Users/austinywang/Applications/cmux-dev-issue-4520-repro-memory-leak/cmux DEV issue-4520-repro-memory-leak.app.
Zip SHA256: 3752598358b058553bbf339809441ebc218ea94c9cd251cd560382eb6de96933.

Fixes #4520


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


Note

Medium Risk
Adjusts filesystem traversal termination for Git repository detection, which could affect when repos are discovered across different Foundation URL behaviors. Change is small and covered by new unit tests for root//.. edge cases.

Overview
Fixes Git repository discovery to stop traversing when reaching root-equivalent paths (including older Foundation cases where deletingLastPathComponent() can yield /.. and continue upward), preventing unbounded parent-path growth.

Refactors the stop condition into TabManager.shouldStopGitRepositorySearch(...) and adds GitRepositorySearchRootStopTests to lock in the expected behavior at /, /.., and a normal non-root directory.

Reviewed by Cursor Bugbot for commit 3640031. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Stop Git repository search at root-equivalent paths to prevent walking to /.. and beyond. This fixes the memory growth loop in non‑Git workspaces (fixes #4520).

  • Bug Fixes
    • Added TabManager.shouldStopGitRepositorySearch(...) to stop at /, when the standardized parent equals the current path, or when Foundation returns /..-style parents.
    • Added tests to guard against / -> /.. -> /../.. traversal and to ensure normal parent traversal still works.

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

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced Git repository detection by improving termination logic for directory search operations to properly recognize and stop at filesystem boundaries, preventing unnecessary traversal.
  • Tests

    • Added validation tests to ensure Git repository search correctly terminates at filesystem boundaries.

Review Change Stack

@vercel

vercel Bot commented May 22, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Building Building Preview, Comment May 22, 2026 6:22am
cmux-staging Building Building Preview, Comment May 22, 2026 6:22am

@coderabbitai

coderabbitai Bot commented May 22, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d0a95bd3-d2e6-4db6-87f4-42411bb072d6

📥 Commits

Reviewing files that changed from the base of the PR and between c677a46 and 3640031.

📒 Files selected for processing (2)
  • Sources/TabManager.swift
  • cmuxTests/WorkspacePullRequestSidebarTests.swift

📝 Walkthrough

Walkthrough

The PR refactors Git repository search termination logic in TabManager by extracting a new shouldStopGitRepositorySearch helper function that centralizes conditions for stopping upward .git directory traversal at filesystem boundaries, with test coverage validating root-stopping and non-root continuation behavior.

Changes

Git Repository Search Termination Centralization

Layer / File(s) Summary
Git repository search termination helper
Sources/TabManager.swift
The new shouldStopGitRepositorySearch(currentURL:parentURL:) helper consolidates termination logic using raw-path comparison, standardized-path root checks, and canonical-path equivalence. The search loop integrates the helper by delegating the stop decision before advancing to the parent URL.
Git repository search root termination tests
cmuxTests/WorkspacePullRequestSidebarTests.swift
New GitRepositorySearchRootStopTests class validates stopping behavior when parent resolution reaches / or /.. scenarios, and confirms non-root /tmp directories do not trigger early termination.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

A rabbit hops through directory trees,
Searching for .git files with ease—
But when it reaches the root with care,
A helper now stops it right there! 🐰✨

🚥 Pre-merge checks | ✅ 16 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (16 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: fixing Git repository search to stop at root directory traversal.
Description check ✅ Passed The description covers the key aspects including what changed (regression test and fix), verification steps, and repro evidence, though it does not fully follow the provided template structure.
Linked Issues check ✅ Passed The PR addresses the core memory leak issue #4520 by implementing the fix to stop Git repository search at root-equivalent paths, preventing unbounded parent-path growth.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the Git repository traversal issue: adding the helper function and corresponding unit tests with no extraneous modifications.
Cmux Swift Actor Isolation ✅ Passed Production code changes are properly marked 'nonisolated static': shouldStopGitRepositorySearch and resolveGitRepository both explicitly opt out of MainActor isolation. Test code is exempt per rules.
Cmux Swift Blocking Runtime ✅ Passed The new shouldStopGitRepositorySearch function uses only synchronous URL path comparisons without blocking operations like semaphores, sleeps, waits, or polling.
Cmux No Hacky Sleeps ✅ Passed Check not applicable: PR only changes Swift files. Rule explicitly excludes Swift code, covered by separate "cmux Swift blocking runtime" checks.
Cmux Swift Concurrency ✅ Passed Changes add a synchronous helper function shouldStopGitRepositorySearch and tests for it. No Dispatch queues, Combine, completion handlers, or fire-and-forget Tasks are introduced.
Cmux Swift @Concurrent ✅ Passed The PR adds a new synchronous nonisolated helper function with only lightweight URL path operations; no async/await, @concurrent, or actor isolation issues present.
Cmux Swift File And Package Boundaries ✅ Passed Small focused bugfix (+13 lines) to existing oversized file with regression tests; qualifies as allowed case preserving clear extraction path.
Cmux Swift Logging ✅ Passed No logging violations detected. The PR adds production code with pure path-comparison logic and tests, with no print/debug/NSLog statements or diagnostics logging.
Cmux User-Facing Error Privacy ✅ Passed Production code changes contain only internal utility methods with path comparisons; no user-facing errors, localized strings, NSAlert dialogs, or sensitive data exposed. Test code is exempt per rule.
Cmux Full Internationalization ✅ Passed PR adds internal Git search logic fix and tests only; no user-facing text, localization APIs, or string catalog changes required.
Cmux Swiftui State Layout ✅ Passed PR contains no SwiftUI state changes—only Git repository search logic refactoring with a new static helper function and unit tests.
Cmux Architecture Rethink ✅ Passed Small correctness fix with clear owner (shouldStopGitRepositorySearch) and invariant (stop at root). No timing repairs, locks, observers, or architectural violations found.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds Git repository search helper function and unit tests, with no NSWindow, NSPanel, NSWindowController, SwiftUI Window, or WindowGroup changes. Check is not applicable.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-4520-repro-memory-leak

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 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes an infinite-loop memory leak in the Git repository search upward traversal (TabManager.gitRepository(startURL:)). On older Foundation versions, deletingLastPathComponent() on / can return /.. rather than /, causing the loop's existing root-equality guard to miss the termination condition and spin forever allocating ever-longer /../.. paths.

  • TabManager.swift: Extracts the root-termination check into shouldStopGitRepositorySearch, adding two new guards: stop if the standardized current path is already /, and stop if the standardized parent equals the standardized current.
  • WorkspacePullRequestSidebarTests.swift: Adds GitRepositorySearchRootStopTests with both a regression case (verifying / → /.. and /.. → /../.. stop search) and a sanity-check that a normal parent path (/tmp/cmux-4520-nongit → /tmp) does not stop prematurely.

Confidence Score: 4/5

Safe to merge; the fix is minimal, empirically verified on the repro machine, and backed by a targeted regression test.

The traversal termination logic is correct for all tested root-escape variants. The only open item is a missing comment explaining why shouldStopGitRepositorySearch is internal rather than private, which could cause accidental access-level drift on future edits.

Sources/TabManager.swift — the new helper's access level should be annotated to explain testability intent.

Important Files Changed

Filename Overview
Sources/TabManager.swift Adds shouldStopGitRepositorySearch static helper to guard the upward traversal loop against older Foundation's /.. root-escape; logic is correct and well-scoped, but the helper is internal (not private) solely for testability with no explanatory comment, slightly widening TabManager's API surface.
cmuxTests/WorkspacePullRequestSidebarTests.swift Adds GitRepositorySearchRootStopTests covering the root-stop regression and a non-root sanity case; tests are correct and directly invoke the new helper via @testable import.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[startURL] --> B{Is directory?}
    B -- No --> C[deleteLastPathComponent]
    B -- Yes --> D
    C --> D[Loop: check for .git]
    D --> E{.git exists?}
    E -- Yes --> F[Return ResolvedGitRepository]
    E -- No --> G[parentURL = deletingLastPathComponent]
    G --> H{shouldStopGitRepositorySearch?}
    H --> I{rawParent == rawCurrent?}
    I -- Yes --> J[return nil — stop]
    I -- No --> K{standardize current == slash?}
    K -- Yes --> J
    K -- No --> L{standardize parent == standardize current?}
    L -- Yes --> J
    L -- No --> M[currentURL = parentURL]
    M --> D
Loading

Reviews (1): Last reviewed commit: "Stop Git search at root-equivalent paths" | Re-trigger Greptile

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 2 files

Re-trigger cubic

This branch was successfully deployed

1 active deployment
Preview – cmux — 3640031e Deployed May 22, 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.

v0.64.8 severe memory leak - RSS grows from 400MB to 8GB within minutes, triggers OOM killer

1 participant