When diff-hl-mode is enabled, revert-buffer issues 3 calls to vc-diff
tomfitzhenry opened this issue · 5 comments
Steps to reproduce
- Modify a file tracked by vc, and save that file.
revert-buffer
Expected
revert-buffer
triggers 1 call to vc-git-diff
.
Actual
revert-buffer
triggers 3 calls to vc-git-diff
(observed via debug-on-entry
)
This is a problem if vc-git-diff
is slow (vc-diff can take ~200ms for me, which I know is atypical). It's especially a problem if many files are reverted (e.g. in a vc-retrieve-tag). I can find a vc-oriented
Stack traces
* vc-git-diff(("/home/tom/git-repo/foo") nil nil " *diff-hl* ")
apply(vc-git-diff (("/home/tom/git-repo/foo") nil nil " *diff-hl* "))
vc-call-backend(Git diff ("/home/tom/git-repo/foo") nil nil " *diff-hl* ")
diff-hl-changes-buffer("/home/tom/git-repo/foo" Git)
diff-hl-changes()
diff-hl-update()
run-hooks(diff-hl-mode-hook diff-hl-mode-on-hook)
diff-hl-mode(1)
turn-on-diff-hl-mode()
diff-hl--global-turn-on()
global-diff-hl-mode-enable-in-buffers()
run-hooks(change-major-mode-after-body-hook after-change-major-mode-hook)
normal-mode(t)
after-find-file(nil nil t nil nil)
revert-buffer--default(t nil)
revert-buffer(t)
funcall-interactively(revert-buffer t)
call-interactively(revert-buffer record nil)
command-execute(revert-buffer record)
apply(vc-git-diff (("/home/tom/git-repo/foo") nil nil " *diff-hl* "))
vc-call-backend(Git diff ("/home/tom/git-repo/foo") nil nil " *diff-hl* ")
diff-hl-changes-buffer("/home/tom/git-repo/foo" Git)
diff-hl-changes()
diff-hl-update()
run-hooks(diff-hl-mode-hook diff-hl-mode-on-hook)
diff-hl-mode(1)
turn-on-diff-hl-mode()
diff-hl--global-turn-on()
global-diff-hl-mode-enable-in-buffers()
run-hooks(after-change-major-mode-hook)
run-mode-hooks()
fundamental-mode()
set-buffer-major-mode(#<buffer foo>)
set-auto-mode()
normal-mode(t)
after-find-file(nil nil t nil nil)
revert-buffer--default(t nil)
revert-buffer(t)
funcall-interactively(revert-buffer t)
call-interactively(revert-buffer record nil)
command-execute(revert-buffer record)
Debugger entered--entering a function:
* vc-git-diff(("/home/tom/git-repo/foo") nil nil " *diff-hl* ")
apply(vc-git-diff (("/home/tom/git-repo/foo") nil nil " *diff-hl* "))
vc-call-backend(Git diff ("/home/tom/git-repo/foo") nil nil " *diff-hl* ")
diff-hl-changes-buffer("/home/tom/git-repo/foo" Git)
diff-hl-changes()
diff-hl-update()
run-hooks(revert-buffer-internal-hook)
revert-buffer--default(t nil)
revert-buffer(t)
funcall-interactively(revert-buffer t)
call-interactively(revert-buffer record nil)
command-execute(revert-buffer record)
Thanks for the report. I just pushed a change that should eliminate the last one.
Another extra call it a bit trickier because the minor mode does get enabled twice (by normal-mode
), so a fix will need some kind of debounce.
Unless we come up with something more elegant, here's a patch to try:
diff --git a/diff-hl.el b/diff-hl.el
index 0f59263..a28d463 100644
--- a/diff-hl.el
+++ b/diff-hl.el
@@ -358,6 +358,15 @@ performance when viewing such files in certain conditions."
(overlay-put h 'insert-in-front-hooks hook)
(overlay-put h 'insert-behind-hooks hook)))))))))
+(defvar-local diff-hl--modified-tick nil)
+
+(put 'diff-hl--modified-tick 'permanent-local t)
+
+(defun diff-hl-update-once ()
+ (unless (equal diff-hl--modified-tick (buffer-modified-tick))
+ (diff-hl-update)
+ (setq diff-hl--modified-tick (buffer-modified-tick))))
+
(defun diff-hl-add-highlighting (type shape)
(let ((o (make-overlay (point) (point))))
(overlay-put o 'diff-hl t)
@@ -571,7 +580,7 @@ The value of this variable is a mode line template as in
;; let's wait until the state information is
;; saved, in order not to fetch it twice.
'find-file-hook)
- 'diff-hl-update t t)
+ 'diff-hl-update-once t t)
(add-hook 'vc-checkin-hook 'diff-hl-update nil t)
;; https://github.com/magit/magit/issues/603
(add-hook 'magit-revert-buffer-hook 'diff-hl-update nil t)
This works great, thanks!
OK, regarding the previous fix, note to self: after-revert-hook
is still needed for auto-revert-mode
, or other modes that call revert-buffer
with non-nil preserve-modes
.
diff-hl
should look up whether revert-buffer-preserve-modes
is non-nil in its hook function.
This should do it.
Hopefully it won't slow things down too much for you now that auto-revert-mode
reverts trigger diff-hl-update
again.