bug#81527: Prevent vc-hg-diff from blocking when called with non-nil ASYNC argument

Aaron Zeng via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <[email protected]> Fri, 31 Jul 2026 15:47:48 -0400
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
Tags: patch

Hello,

At my site we use the diff-hl package to show inline diffs and
highlight changed parts of a visited file in the margin.  The
diff-hl-update-async=t option makes updates call the VC `diff' backend
function with a non-nil ASYNC argument, which attempts to alleviate
most of the undesirable pauses during typing that this might otherwise
cause.

However, I noticed that vc-hg-diff can sometimes cause a brief
noticeable pause during this background update if
vc-hg-working-revision takes some time to complete.  I believe calling
this function is actually not necessary, so the second patch removes
that call.  This changes the arguments passed to "hg diff" slightly if
OLDVERS is "." or the actual hash of the working revision, but should
not change behavior.

The first patch contains a small bugfix for a discrepancy between
vc-hg-diff and the `diff' VC backend function's description in the
vc.el Commentary, which I happened to notice as I was drafting my
original patch.  (I compared with vc-git-diff).

Thanks,
Aaron Zeng

In GNU Emacs 31.0.91 (build 1, x86_64-pc-linux-gnu, X toolkit, cairo
 version 1.15.12, Xaw scroll bars) of 2026-07-29 built on
 vdc-qws-i40813a
Repository revision: 112645541ac2e8781080e722ba6756227606160c
System Description: Rocky Linux 8.10 (Green Obsidian)

Configured using:
 'configure --with-x-toolkit=lucid --without-gpm --without-gconf
 --without-selinux --without-imagemagick --with-modules --with-gif=no
 --with-cairo --with-rsvg --without-compress-install
 --with-tree-sitter --with-native-compilation=aot
 --prefix=/usr/local/home/garnish/raw-emacs/31-20260729_110947'
0001-Fix-vc-hg-diff-arguments-to-diff-vs-empty-tree.patch (text/x-patch, 2.3 KB)
From 9bc8fe4f9d84c618b78c00ebdc1eda17f4b09b81 Mon Sep 17 00:00:00 2001
From: "Aaron L. Zeng" <[email protected]>
Date: Fri, 31 Jul 2026 15:01:19 -0400
Subject: [PATCH 1/2] Fix vc-hg-diff arguments to diff vs empty tree

* lisp/vc/vc-hg.el (vc-hg-diff): Fix behavior according to
backend function specification.

* test/lisp/vc/vc-hg-tests.el (vc-hg-diff-revision-arguments):
Add tests for vc-hg-diff arguments.
---
 lisp/vc/vc-hg.el            |  2 +-
 test/lisp/vc/vc-hg-tests.el | 17 +++++++++++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el
index a34ed82fa23..acc257dab2f 100644
--- a/lisp/vc/vc-hg.el
+++ b/lisp/vc/vc-hg.el
@@ -565,7 +565,7 @@ vc-hg-diff
     (when (and (not newvers) (member oldvers (list working ".")))
       (setq oldvers nil))
     (when (and newvers (not oldvers))
-      (setq oldvers working))
+      (setq oldvers "null"))
     (apply #'vc-hg-command
 	   (or buffer "*vc-diff*")
            (if async 'async 1)
diff --git a/test/lisp/vc/vc-hg-tests.el b/test/lisp/vc/vc-hg-tests.el
index 0216c23eebe..09891f10b22 100644
--- a/test/lisp/vc/vc-hg-tests.el
+++ b/test/lisp/vc/vc-hg-tests.el
@@ -88,4 +88,21 @@ vc-hg-after-dir-status
      ("foo2" added #s(vc-hg-extra-fileinfo renamed-from "foo"))
      ("bar2" added #s(vc-hg-extra-fileinfo renamed-from "bar")))))
 
+(ert-deftest vc-hg-diff-revision-arguments ()
+  "Test `vc-hg-diff' revision arguments."
+  (cl-letf (((symbol-function 'vc-hg-command) #'list)
+            (vc-hg-diff-switches t))
+    ;; REV1 REV2 both nil: diff file against working revision
+    (should (equal (vc-hg-diff '("foo") nil nil)
+                   '("*vc-diff*" 1 ("foo") "diff")))
+    ;; REV1 nil and REV2 non-nil: diff REV2 against empty tree
+    (should (equal (vc-hg-diff '("foo") nil "22222")
+                   '("*vc-diff*" 1 ("foo") "diff" "-r" "null" "-r" "22222")))
+    ;; REV1 non-nil and REV2 nil: diff working copy against older revision
+    (should (equal (vc-hg-diff '("foo") "11111" nil)
+                   '("*vc-diff*" 1 ("foo") "diff" "-r" "11111")))
+    ;; REV1 REV2 both non-nil: diff two revisions
+    (should (equal (vc-hg-diff '("foo") "11111" "22222")
+                   '("*vc-diff*" 1 ("foo") "diff" "-r" "11111" "-r" "22222")))))
+
 ;;; vc-hg-tests.el ends here
-- 
2.43.7
0002-lisp-vc-vc-hg.el-vc-hg-diff-Skip-unnecessary-call-to.patch (text/x-patch, 1.7 KB)
From 9d72a02fb55e862d4d072afdc8f2bf6efb970d26 Mon Sep 17 00:00:00 2001
From: "Aaron L. Zeng" <[email protected]>
Date: Fri, 31 Jul 2026 15:13:22 -0400
Subject: [PATCH 2/2] * lisp/vc/vc-hg.el (vc-hg-diff): Skip unnecessary call to
 vc-working-revision.

---
 lisp/vc/vc-hg.el | 28 ++++++++++++----------------
 1 file changed, 12 insertions(+), 16 deletions(-)

diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el
index acc257dab2f..6916c56c141 100644
--- a/lisp/vc/vc-hg.el
+++ b/lisp/vc/vc-hg.el
@@ -560,22 +560,18 @@ vc-hg-region-history-mode
 
 (defun vc-hg-diff (files &optional oldvers newvers buffer async)
   "Get a difference report using hg between two revisions of FILES."
-  (let* ((firstfile (car files))
-         (working (and firstfile (vc-working-revision firstfile 'Hg))))
-    (when (and (not newvers) (member oldvers (list working ".")))
-      (setq oldvers nil))
-    (when (and newvers (not oldvers))
-      (setq oldvers "null"))
-    (apply #'vc-hg-command
-	   (or buffer "*vc-diff*")
-           (if async 'async 1)
-           files "diff"
-           (append
-            (vc-switches 'hg 'diff)
-            (when oldvers
-              (if newvers
-                  (list "-r" oldvers "-r" newvers)
-                (list "-r" oldvers)))))))
+  (when (and newvers (not oldvers))
+    (setq oldvers "null"))
+  (apply #'vc-hg-command
+	 (or buffer "*vc-diff*")
+         (if async 'async 1)
+         files "diff"
+         (append
+          (vc-switches 'hg 'diff)
+          (when oldvers
+            (if newvers
+                (list "-r" oldvers "-r" newvers)
+              (list "-r" oldvers))))))
 
 (defun vc-hg-expanded-log-entry (revision)
   (with-temp-buffer
-- 
2.43.7