From 8ba358543666bd6f01e531a33aac92fefb002f0b Mon Sep 17 00:00:00 2001 From: kozabaa Date: Wed, 29 Apr 2026 10:14:37 +1000 Subject: [PATCH] fix(diff): remove stale git sentinel after failed attach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # fix(diff): remove stale git sentinel after failed attach ## Problem Opening a buffer in a directory that is **not yet a git repository** permanently blocks `mini.diff` from ever attaching to that buffer, even after `git init` is run later in the same session. Symptoms: - Gutter signs never appear after `git init` + commit - `MiniDiff.get_buf_data()` always returns `nil` - `MiniDiff.enable()` called manually has no effect - No errors are logged anywhere ## Root cause In `H.git_start_watching_index`, when the initial `git rev-parse --git-dir` returns a non-zero exit code, `on_not_in_git` fires: ```lua -- lua/mini/diff.lua ~line 1696 local on_not_in_git = vim.schedule_wrap(function() if not vim.api.nvim_buf_is_valid(buf_id) then H.cache[buf_id] = nil return end MiniDiff.fail_attach(buf_id) H.git_cache[buf_id] = {} -- ← stale sentinel end) ``` The call chain is: ``` fail_attach(buf_id) → disable(buf_id) → source.detach(buf_id) → H.git_cache[buf_id] = nil ✓ invariant restored → H.cache[buf_id] = nil ✓ buffer marked as not attached ← returns H.git_cache[buf_id] = {} ✗ invariant violated ``` After `detach` correctly clears the entry to `nil`, `on_not_in_git` immediately overwrites it with an empty table `{}`. The `attach` guard then blocks every future re-attachment attempt for that buffer ID: ```lua local attach = function(buf_id) if H.git_cache[buf_id] ~= nil then return false end -- {} ~= nil → returns false ... end ``` Because `attach` returns `false`, `enable` calls `fail_attach` again, which calls `disable`, which clears `H.cache[buf_id]`. As a result, `get_buf_data()` always returns `nil` and the buffer is permanently wedged for the rest of the Neovim session under that buffer ID. ## Fix Remove the one line that re-poisons the cache after `detach` has already cleaned it up: ```diff local on_not_in_git = vim.schedule_wrap(function() if not vim.api.nvim_buf_is_valid(buf_id) then H.cache[buf_id] = nil return end MiniDiff.fail_attach(buf_id) - H.git_cache[buf_id] = {} end) ``` ## Why this is safe The sentinel's apparent purpose is to prevent re-attach storms after a failed attempt. That concern is already handled elsewhere: 1. **`detach` already cleaned up.** `fail_attach → disable → source.detach` sets `H.git_cache[buf_id] = nil` before the removed line ever ran. The extra assignment was redundant on top of being harmful. 2. **No re-attach loop is possible.** `auto_enable` is gated on `H.is_buf_enabled(buf_id)` and fires only on `BufEnter`. After `fail_attach`, `H.cache[buf_id]` is `nil` (buffer disabled); nothing schedules another enable until the user switches to the buffer again — which is exactly when a retry is wanted (the user may have just run `git init`). 3. **The in-flight guard is unaffected.** The `attach` function sets `H.git_cache[buf_id] = {}` *synchronously* before launching the async `git rev-parse` job (line 663). That assignment is what prevents a second concurrent attach during a live job. Removing the post-failure assignment does not touch this path. ## Reproduction ``` # 1. Open Neovim in a non-git directory cd /tmp/testdir && nvim hello.md # 2. In another pane, while Neovim stays open: git init /tmp/testdir git -C /tmp/testdir add . git -C /tmp/testdir commit -m "init" # 3. Edit hello.md, then in Neovim: :lua print(vim.inspect(MiniDiff.get_buf_data())) -- Before fix: nil -- After fix: table with config/hunks/summary ``` ## Tested on - Neovim 0.12.1 - Debian trixie-slim (Docker container, `HOME=/tmp`) - mini.nvim `main` (confirmed by reading the source) --- lua/mini/diff.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lua/mini/diff.lua b/lua/mini/diff.lua index 726a58765..4f710e13f 100644 --- a/lua/mini/diff.lua +++ b/lua/mini/diff.lua @@ -1699,7 +1699,6 @@ H.git_start_watching_index = function(buf_id, path) return end MiniDiff.fail_attach(buf_id) - H.git_cache[buf_id] = {} end) local process, stdout_feed = nil, {}