fix(diff): remove stale git sentinel after failed attach#1
Open
kozabaa wants to merge 1 commit into
Open
Conversation
# 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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.difffrom ever attaching to that buffer, even aftergit initis run later in the same session.Symptoms:
git init+ commitMiniDiff.get_buf_data()always returnsnilMiniDiff.enable()called manually has no effectRoot cause
In
H.git_start_watching_index, when the initialgit rev-parse --git-dirreturns a non-zero exit code,on_not_in_gitfires:The call chain is:
After
detachcorrectly clears the entry tonil,on_not_in_gitimmediately overwrites it with an empty table{}. Theattachguard then blocks every future re-attachment attempt for that buffer ID:Because
attachreturnsfalse,enablecallsfail_attachagain, which callsdisable, which clearsH.cache[buf_id]. As a result,get_buf_data()always returnsniland 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
detachhas already cleaned it up: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:
detachalready cleaned up.fail_attach → disable → source.detachsetsH.git_cache[buf_id] = nilbefore the removed line ever ran. The extra assignment was redundant on top of being harmful.No re-attach loop is possible.
auto_enableis gated onH.is_buf_enabled(buf_id)and fires only onBufEnter. Afterfail_attach,H.cache[buf_id]isnil(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 rungit init).The in-flight guard is unaffected. The
attachfunction setsH.git_cache[buf_id] = {}synchronously before launching the asyncgit rev-parsejob (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
Tested on
Neovim 0.12.1
Debian trixie-slim (Docker container,
HOME=/tmp)mini.nvim
main(confirmed by reading the source)I have read CONTRIBUTING.md
I have read CODE_OF_CONDUCT.md