From 72022459a90bd68bc6ea4a821ca08ff713c23dd3 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 16 Aug 2025 20:40:50 +0100 Subject: [PATCH] vc-hg-checkin-patch: Fix on MS-Windows, make 'hg import' async * lisp/vc/vc-hg.el (vc-hg--checkin): New function to do the work of vc-hg-checkin and vc-hg-checkin-patch. (vc-hg-checkin): Replace body with call to vc-hg--checkin. (vc-hg-checkin-patch): Likewise. As compared with the old implementation, this change (i) fixes encoding issues when checking in patches on MS-Windows; and (ii) when vc-async-checkin is non-nil, runs 'hg import' asynchronously instead of running 'hg update' asynchronously (bug#79235). --- lisp/vc/vc-hg.el | 109 ++++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el index b0e5a633566..6c13e555836 100644 --- a/lisp/vc/vc-hg.el +++ b/lisp/vc/vc-hg.el @@ -1213,11 +1213,18 @@ It is based on `log-edit-mode', and has Hg-specific extensions.") (defalias 'vc-hg-async-checkins #'always) -(defun vc-hg-checkin (files comment &optional _rev) - "Hg-specific version of `vc-BACKEND-checkin'. -REV is ignored." +(defun vc-hg--checkin (comment &optional files patch-string) + "Workhorse routine for `vc-hg-checkin' and `vc-hg-checkin-patch'. +COMMENT is the commit message. +For a regular checkin, FILES is the list of files to check in. +To check in a patch, PATCH-STRING is the patch text. +It is an error to supply both or neither." + (unless (xor files patch-string) + (error "Invalid call to `vc-hg--checkin'")) (let* ((args (vc-hg--extract-headers comment)) - (file1 (or (car files) default-directory)) + (temps-dir (or (file-name-directory (or (car files) + default-directory)) + default-directory)) (msg-file ;; On MS-Windows, pass the commit log message through a file, ;; to work around the limitation that command-line arguments @@ -1225,30 +1232,53 @@ REV is ignored." ;; support non-ASCII characters in the log message. ;; Also handle remote files. (and (eq system-type 'windows-nt) - (let ((default-directory (or (file-name-directory file1) - default-directory))) - (make-nearby-temp-file "hg-msg"))))) - (when msg-file - (let ((coding-system-for-write 'utf-8)) - (write-region (car args) nil msg-file))) - (let ((coding-system-for-write - ;; On MS-Windows, we must encode command-line arguments in - ;; the system codepage. - (if (eq system-type 'windows-nt) - locale-coding-system - coding-system-for-write)) - (args (if msg-file - (cl-list* "commit" "-A" "-l" (file-local-name msg-file) - (cdr args)) - (cl-list* "commit" "-A" "-m" args))) - (post (lambda () - (when (and msg-file (file-exists-p msg-file)) - (delete-file msg-file))))) + (let ((default-directory temps-dir)) + (make-nearby-temp-file "hg-msg")))) + (patch-file (and patch-string + (let ((default-directory temps-dir)) + (make-nearby-temp-file "hg-patch"))))) + (let ((coding-system-for-write 'utf-8)) + (when msg-file + (write-region (car args) nil msg-file)) + (when patch-file + (write-region patch-string nil patch-file))) + (let* ((coding-system-for-write + ;; On MS-Windows, we must encode command-line arguments in + ;; the system codepage. + (if (eq system-type 'windows-nt) + locale-coding-system + coding-system-for-write)) + (args + (nconc (if patch-file + (list "import" "--bypass" patch-file) + (list "commit" "-A")) + (if msg-file + (cl-list* "-l" (file-local-name msg-file) (cdr args)) + (cl-list* "-m" args)))) + (post (lambda () + (when (and msg-file (file-exists-p msg-file)) + (delete-file msg-file)) + (when (and patch-file (file-exists-p patch-file)) + (delete-file patch-file)) + ;; When committing a patch we run 'hg import' and + ;; then 'hg update'. We have 'hg update' here in the + ;; always-synchronous `post' function because we + ;; assume that 'hg import' is the one that might be + ;; slow and so benefits most from `vc-async-checkin'. + ;; If in fact both the 'hg import' and the 'hg + ;; update' can be slow, then we need to make both of + ;; them part of the async command, possibly by + ;; writing out a tiny shell script (bug#79235). + (when patch-file + (vc-hg-command nil 0 nil "update" "--merge" + "--tool" "internal:local" "tip"))))) (if vc-async-checkin (let ((buffer (vc-hg--async-buffer))) (vc-wait-for-process-before-save (apply #'vc-hg--async-command buffer (nconc args files)) - "Finishing checking in files...") + (if patch-file + "Finishing checking in patch...." + "Finishing checking in files...")) (with-current-buffer buffer (vc-run-delayed (vc-compilation-mode 'hg) @@ -1258,31 +1288,14 @@ REV is ignored." (apply #'vc-hg-command nil 0 files args) (funcall post))))) -;; FIXME: Needs MS-Windows encoding issues handling. -;; Possibly we want fix this by merging this function into the preceeding one. -;; Figure out resolution of #79235 first. +(defun vc-hg-checkin (files comment &optional _rev) + "Hg-specific version of `vc-BACKEND-checkin'. +REV is ignored." + (vc-hg--checkin comment files nil)) + (defun vc-hg-checkin-patch (patch-string comment) - (let ((patch-file (make-nearby-temp-file "hg-patch"))) - (write-region patch-string nil patch-file) - (unwind-protect - (let ((args (list "update" - "--merge" "--tool" "internal:local" - "tip"))) - (apply #'vc-hg-command nil 0 nil - (nconc (list "import" "--bypass" patch-file "-m") - (vc-hg--extract-headers comment))) - (if vc-async-checkin - (let ((buffer (vc-hg--async-buffer))) - (vc-wait-for-process-before-save - (apply #'vc-hg--async-command buffer args) - "Finishing checking in patch....") - (with-current-buffer buffer - (vc-run-delayed - (vc-compilation-mode 'hg))) - (vc-set-async-update buffer) - (list 'async (get-buffer-process buffer))) - (apply #'vc-hg-command nil 0 nil args))) - (delete-file patch-file)))) + "Hg-specific version of `vc-BACKEND-checkin-patch'." + (vc-hg--checkin comment nil patch-string)) (defun vc-hg--extract-headers (comment) (log-edit-extract-headers `(("Author" . "--user")