Skip to content

Unbounded concurrent git blame processes pin CPU and freeze the app on large repos (Linux) #368

Open

Description

@ik0zy

Symptom

Installed 0.4.0 Linux build (tryatlas-bin 0.4.0-1, Arch/Omarchy). Opening this repo as the project freezes the app almost instantly: load average 173–332, ~80 processes in D-state, dozens of zombies, tryatlas growing to 4–5.7GB RSS. Had to pkill the app to recover the machine.

Cause

The app spawns unbounded concurrent git blame children. ps during the freeze showed 26+ git processes parented to tryatlas, mostly:

git --no-optional-locks blame -w --line-porcelain -- src/App.tsx   (same file ~20x concurrently)
git --no-optional-locks blame -w --line-porcelain -- index.html
git diff --no-color -U0 -- src/components/CommandPalette.tsx
git ls-files --error-unmatch -- src/App.tsx

Killing the children doesn't help — they respawn immediately, so it's whack-a-mole until the app itself is killed.

Mechanism

Every open editor tab runs refreshBlame → gitBlameFile (src/features/editor/components/editor-panel.tsx:203-216) backed by the native git_blame_file command. There is no dedup across tabs showing related files, no cancellation when tabs close/switch, and no cap on concurrent git children. Each blame on a large monorepo file is slow, so spawns outpace completions and the storm feeds itself. Restored tabs all fire on launch, which is why it freezes on open rather than degrading gradually.

Repro

  1. Fresh 0.4.0 Linux install, defaults (gitBlameInline defaults true, src-tauri/src/state/atlas_config.rs:506).
  2. Open this repo as the project with a few editors/tabs restored.
  3. Watch ps -eo pid,ppid,args | grep blame and load average.

Confirmation

Setting [settings] gitBlameInline = false in ~/.config/dev.atlas.ide/config.toml and reopening a small folder: 0 git children, CPU back to idle. So the blame path is the whole CPU story.

Expected

  • One blame per visible file (dedupe across tabs/views of the same file).
  • Cancel in-flight blame when the view closes or switches file.
  • Cap concurrent git child processes (or serialize blame through a queue).
  • Ideally don't blame files until the editor is actually visible/idle, not all restored tabs at launch.

Env

  • tryatlas-bin 0.4.0-1 (built from 0.4.0 @ e510860), Arch Linux x86-64, 15GB RAM (13GB free at the time — not memory pressure).

Activity

  1. ik0zy commented on Oct 7, 2026

    @ik0zy
    ContributorAuthor

    I found what keeps this respawning: a feedback loop in the git watcher on Linux. Fix in #375.

    notify 8 watches IN_OPEN on Linux, and git_watcher.rs emitted atlas:git-changed for every event, reads included. Every editor tab listens to that event and runs refreshDiffGutter() + refreshBlame() (editor-panel.tsx), and the git store runs status / log / diff HEAD / for-each-ref. Each of those git processes opens .git/HEAD / .git/index / refs, which fires the watcher again 200 ms later, and so on. That's why killing the children didn't help, why the same file got blamed ~20× at once, and why it started the moment restored tabs mounted.

    It's visible without any editor tabs open too: on an idle 0.4.0 Linux install with this repo open, Atlas spawns a steady ~6–7 git processes per second, indefinitely. gitBlameInline = false removes the expensive part, but the loop itself keeps running.

    #375 ignores read-only events in the watcher, the same rule atlas-theme already applies for the same loop. Real changes (commit, checkout, stage, fetch) still fire it. It references this issue rather than closing it: the hardening asks above (dedupe blame per file, cancel on close, cap concurrent git children) are still worth having on their own.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions