Summary
On Emacs 31 I get a Buffer ... has a running process; kill it? style
confirmation while refreshing a Dired buffer with diff-hl-dired-mode on.
Setting diff-hl-dired-extra-indicators to nil makes it stop.
I want to be upfront: I do not have a reliable recipe. I tried hard to
build one and failed (details below). I am filing this because the code path
that I believe is involved looks genuinely unguarded, and you will judge that
much faster than I can.
Environment
- Emacs 31.0.50
- diff-hl 20260723.238 (MELPA)
- git 2.50.1
- Backend: Git. The same config runs on macOS and on Windows, and I am not
certain which of the two first showed the prompt.
Workaround that works
(with-eval-after-load 'diff-hl-dired
(setq diff-hl-dired-extra-indicators nil))
That skips diff-hl-dir-status-ignored-files, so only one Git process per
refresh instead of two. The prompt has not come back since.
The code path I suspect
diff-hl-dired-update keeps one reusable buffer per Dired buffer in
diff-hl-dired-process-buffer, and the final callback kills it:
https://github.com/dgutov/diff-hl/blob/master/diff-hl-dired.el#L139
Two things combine there:
- VC's async processes keep
process-query-on-exit-flag at t.
vc-dispatcher.el never clears it, so process-kill-buffer-query-function
will ask if kill-buffer meets a live process.
- The buffer is shared across runs. When a process exits normally but its
queued continuation has not yet reached the kill-buffer call, a new
refresh can find the buffer still live, erase it, and start a second
process in it. The older continuation then kills a buffer that owns a
younger, live process.
With diff-hl-dired-extra-indicators non-nil there are two chained Git
processes per refresh instead of one, which widens that window. That fits the
fact that nil makes the symptom go away.
What I could not reproduce
I built a repo with ~2,300 directory entries and ~6,000 ignored files, put a
sleep 0.4 wrapper in front of git via vc-git-program, and drove 8
overlapping revert-buffer calls in batch, recording every case where
kill-buffer met a live process with the query flag set. The overlap
definitely happened (each refresh found the previous process in run state),
but the recorded count stayed at 0.
The reason looks like vc-exec-after (vc-dispatcher.el:233): given an
okstatus, it skips the continuation for any process that died from a signal.
So the process that diff-hl-dired-update explicitly kills never reaches
kill-buffer. That rules out the obvious race and leaves only the narrower
"exited normally, continuation not yet run" window above, which I could not
hit on demand.
Possible fixes
Both look cheap, if you agree the window is real:
(set-process-query-on-exit-flag proc nil) on the status process, since this
buffer is internal and the user has no reason to be asked about it; or
- have the continuation kill the buffer only when the process it is holding is
still the buffer's current process, so a stale run cannot kill a live one.
For context, the kill-buffer call came from #89.
Happy to test any patch, or to run further instrumentation if you tell me what
would be useful to capture.
Summary
On Emacs 31 I get a
Buffer ... has a running process; kill it?styleconfirmation while refreshing a Dired buffer with
diff-hl-dired-modeon.Setting
diff-hl-dired-extra-indicatorsto nil makes it stop.I want to be upfront: I do not have a reliable recipe. I tried hard to
build one and failed (details below). I am filing this because the code path
that I believe is involved looks genuinely unguarded, and you will judge that
much faster than I can.
Environment
certain which of the two first showed the prompt.
Workaround that works
That skips
diff-hl-dir-status-ignored-files, so only one Git process perrefresh instead of two. The prompt has not come back since.
The code path I suspect
diff-hl-dired-updatekeeps one reusable buffer per Dired buffer indiff-hl-dired-process-buffer, and the final callback kills it:https://github.com/dgutov/diff-hl/blob/master/diff-hl-dired.el#L139
Two things combine there:
process-query-on-exit-flagat t.vc-dispatcher.elnever clears it, soprocess-kill-buffer-query-functionwill ask if
kill-buffermeets a live process.queued continuation has not yet reached the
kill-buffercall, a newrefresh can find the buffer still live, erase it, and start a second
process in it. The older continuation then kills a buffer that owns a
younger, live process.
With
diff-hl-dired-extra-indicatorsnon-nil there are two chained Gitprocesses per refresh instead of one, which widens that window. That fits the
fact that nil makes the symptom go away.
What I could not reproduce
I built a repo with ~2,300 directory entries and ~6,000 ignored files, put a
sleep 0.4wrapper in front ofgitviavc-git-program, and drove 8overlapping
revert-buffercalls in batch, recording every case wherekill-buffermet a live process with the query flag set. The overlapdefinitely happened (each refresh found the previous process in
runstate),but the recorded count stayed at 0.
The reason looks like
vc-exec-after(vc-dispatcher.el:233): given anokstatus, it skips the continuation for any process that died from a signal.So the process that
diff-hl-dired-updateexplicitly kills never reacheskill-buffer. That rules out the obvious race and leaves only the narrower"exited normally, continuation not yet run" window above, which I could not
hit on demand.
Possible fixes
Both look cheap, if you agree the window is real:
(set-process-query-on-exit-flag proc nil)on the status process, since thisbuffer is internal and the user has no reason to be asked about it; or
still the buffer's current process, so a stale run cannot kill a live one.
For context, the
kill-buffercall came from #89.Happy to test any patch, or to run further instrumentation if you tell me what
would be useful to capture.