commit 78698e9211ce642fb0ddeb63ce7d26339863d557 (HEAD, refs/remotes/origin/master) Author: Juri Linkov Date: Sun Aug 28 22:44:52 2022 +0300 'C-c C-d' on vc-log buffer shows the diff from 'vc-log-fileset' (bug#52349) * lisp/vc/log-edit.el (log-edit-diff-fileset): New function. * lisp/vc/vc-dispatcher.el (vc-log-edit): Set log-edit-diff-function to log-edit-diff-fileset instead of vc-diff. * lisp/vc/vc.el (vc-diff): New optional arg ‘fileset’. If non-nil, use instead of vc-deduce-fileset. diff --git a/lisp/vc/log-edit.el b/lisp/vc/log-edit.el index 4a94553b21..5290616302 100644 --- a/lisp/vc/log-edit.el +++ b/lisp/vc/log-edit.el @@ -670,6 +670,13 @@ comment history, see `log-edit-comment-ring', and hides `log-edit-files-buf'." (defun log-edit-diff-patch () (vc-diff-patch-string vc-patch-string)) +(defvar vc-log-fileset) + +(defun log-edit-diff-fileset () + "Display diffs for the files to be committed." + (interactive) + (vc-diff nil nil (list log-edit-vc-backend vc-log-fileset))) + (defun log-edit-show-diff () "Show the diff for the files to be committed." (interactive) diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el index df5bf1cfa6..36a6f27891 100644 --- a/lisp/vc/vc-dispatcher.el +++ b/lisp/vc/vc-dispatcher.el @@ -656,7 +656,7 @@ NOT-URGENT means it is ok to continue if the user says not to save." (lambda (file) (file-relative-name file root)) fileset)))) (log-edit-diff-function - . ,(if vc-patch-string 'log-edit-diff-patch 'vc-diff)) + . ,(if vc-patch-string 'log-edit-diff-patch 'log-edit-diff-fileset)) (log-edit-vc-backend . ,backend) (vc-log-fileset . ,fileset) (vc-patch-string . ,vc-patch-string)) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 88139fe13d..d93be951a3 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -2001,19 +2001,20 @@ state of each file in the fileset." (when buffer-file-name (vc-buffer-sync not-urgent)))) ;;;###autoload -(defun vc-diff (&optional historic not-urgent) +(defun vc-diff (&optional historic not-urgent fileset) "Display diffs between file revisions. Normally this compares the currently selected fileset with their working revisions. With a prefix argument HISTORIC, it reads two revision designators specifying which revisions to compare. The optional argument NOT-URGENT non-nil means it is ok to say no to -saving the buffer." +saving the buffer. The optional argument FILESET can override the +deduced fileset." (interactive (list current-prefix-arg t)) (if historic (call-interactively 'vc-version-diff) (vc-maybe-buffer-sync not-urgent) - (let ((fileset (vc-deduce-fileset t))) + (let ((fileset (or fileset (vc-deduce-fileset t)))) (vc-buffer-sync-fileset fileset not-urgent) (vc-diff-internal t fileset nil nil (called-interactively-p 'interactive))))) commit 4803fba487d41f0817feab48b5095ef4b4940ff6 Author: Juri Linkov Date: Sun Aug 28 22:38:51 2022 +0300 'C-x v v' on a diff buffer commits it as a patch (bug#52349) * lisp/vc/diff-mode.el (diff-vc-deduce-fileset): New function. * lisp/vc/log-edit.el (log-edit-diff-patch): New function. * lisp/vc/vc-dispatcher.el (vc-log-edit): Set log-edit-diff-function to log-edit-diff-patch when vc-patch-string is non-nil. (vc-start-logentry): New optional arg 'patch-string'. Set buffer-local 'vc-patch-string' to 'patch-string'. (vc-dispatcher-browsing): Add (derived-mode-p 'diff-mode). * lisp/vc/vc-git.el (vc-git-checkin-patch): New function. (vc-git-checkin): When vc-git-patch-string is non-nil, use `git apply --cached` to add the patch to the index, then commit the staged changes. * lisp/vc/vc.el: New backend function 'checkin-patch'. (vc-deduce-fileset-1): Call diff-vc-deduce-fileset in diff-mode. (vc-next-action): For model 'patch' call vc-checkin with the diff buffer string. (vc-checkin): New optional arg 'patch-string'. Call backend function 'checkin-patch' when 'patch-string' is non-nil. Call vc-start-logentry with 'patch-string'. (vc-diff-patch-string): New function. diff --git a/etc/NEWS b/etc/NEWS index f337381dd4..b27f0760d1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1580,6 +1580,12 @@ info node. This command only works for the Emacs and Emacs Lisp manuals. This command marks files based on a regexp. If given a prefix argument, unmark instead. +*** 'C-x v v' on a diff buffer commits it as a patch. +You can create a diff buffer by e.g. 'C-x v D' ('vc-root-diff'), +then remove unnecessary hunks, and commit only part of your changes +by typing 'C-x v v' in that diff buffer. Currently this works only +with Git. + --- *** 'C-x v v' on an unregistered file will now use the most specific backend. Previously, if you had an SVN-covered "~/" directory, and a Git-covered diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index 6b30de3cb3..a01943437c 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el @@ -2928,6 +2928,15 @@ hunk text is not found in the source file." (forward-line 1))) (nreverse props))) +;;;###autoload +(defun diff-vc-deduce-fileset () + (let ((backend (vc-responsible-backend default-directory)) + files) + (save-excursion + (goto-char (point-min)) + (while (progn (diff-file-next) (not (eobp))) + (push (diff-find-file-name nil t) files))) + (list backend (nreverse files) nil nil 'patch))) (defun diff--filter-substring (str) (when diff-font-lock-prettify diff --git a/lisp/vc/log-edit.el b/lisp/vc/log-edit.el index e958673fea..4a94553b21 100644 --- a/lisp/vc/log-edit.el +++ b/lisp/vc/log-edit.el @@ -664,6 +664,12 @@ comment history, see `log-edit-comment-ring', and hides `log-edit-files-buf'." (indent-rigidly (point) (point-max) (- log-edit-common-indent common))))) +(defvar vc-patch-string) + +(autoload 'vc-diff-patch-string "vc") +(defun log-edit-diff-patch () + (vc-diff-patch-string vc-patch-string)) + (defun log-edit-show-diff () "Show the diff for the files to be committed." (interactive) diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el index e2a490092b..df5bf1cfa6 100644 --- a/lisp/vc/vc-dispatcher.el +++ b/lisp/vc/vc-dispatcher.el @@ -624,6 +624,8 @@ NOT-URGENT means it is ok to continue if the user says not to save." (declare-function log-edit-empty-buffer-p "log-edit" ()) +(defvar vc-patch-string) + (defun vc-log-edit (fileset mode backend) "Set up `log-edit' for use on FILE." (setq default-directory @@ -653,15 +655,17 @@ NOT-URGENT means it is ok to continue if the user says not to save." (mapcar (lambda (file) (file-relative-name file root)) fileset)))) - (log-edit-diff-function . vc-diff) + (log-edit-diff-function + . ,(if vc-patch-string 'log-edit-diff-patch 'vc-diff)) (log-edit-vc-backend . ,backend) - (vc-log-fileset . ,fileset)) + (vc-log-fileset . ,fileset) + (vc-patch-string . ,vc-patch-string)) nil mode) (set-buffer-modified-p nil) (setq buffer-file-name nil)) -(defun vc-start-logentry (files comment initial-contents msg logbuf mode action &optional after-hook backend) +(defun vc-start-logentry (files comment initial-contents msg logbuf mode action &optional after-hook backend patch-string) "Accept a comment for an operation on FILES. If COMMENT is nil, pop up a LOGBUF buffer, emit MSG, and set the action on close to ACTION. If COMMENT is a string and @@ -673,7 +677,8 @@ empty comment. Remember the file's buffer in `vc-parent-buffer' \(current one if no file). Puts the log-entry buffer in major mode MODE, defaulting to `log-edit-mode' if MODE is nil. AFTER-HOOK specifies the local value for `vc-log-after-operation-hook'. -BACKEND, if non-nil, specifies a VC backend for the Log Edit buffer." +BACKEND, if non-nil, specifies a VC backend for the Log Edit buffer. +PATCH-STRING is a patch to check in." (let ((parent (if (vc-dispatcher-browsing) ;; If we are called from a directory browser, the parent buffer is @@ -688,6 +693,8 @@ BACKEND, if non-nil, specifies a VC backend for the Log Edit buffer." (setq-local vc-parent-buffer parent) (setq-local vc-parent-buffer-name (concat " from " (buffer-name vc-parent-buffer))) + (when patch-string + (setq-local vc-patch-string patch-string)) (vc-log-edit files mode backend) (make-local-variable 'vc-log-after-operation-hook) (when after-hook @@ -753,7 +760,8 @@ the buffer contents as a comment." (defun vc-dispatcher-browsing () "Are we in a directory browser buffer?" (or (derived-mode-p 'vc-dir-mode) - (derived-mode-p 'dired-mode))) + (derived-mode-p 'dired-mode) + (derived-mode-p 'diff-mode))) ;; These are unused. ;; (defun vc-dispatcher-in-fileset-p (fileset) diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 46a486a46c..7395253745 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -53,7 +53,8 @@ ;; - responsible-p (file) OK ;; - receive-file (file rev) NOT NEEDED ;; - unregister (file) OK -;; * checkin (files rev comment) OK +;; * checkin (files comment rev) OK +;; - checkin-patch (patch-string comment) OK ;; * find-revision (file rev buffer) OK ;; * checkout (file &optional rev) OK ;; * revert (file &optional contents-done) OK @@ -914,6 +915,12 @@ If toggling on, also insert its message into the buffer." "Major mode for editing Git log messages. It is based on `log-edit-mode', and has Git-specific extensions.") +(defvar vc-git-patch-string nil) + +(defun vc-git-checkin-patch (patch-string comment) + (let ((vc-git-patch-string patch-string)) + (vc-git-checkin nil comment))) + (defun vc-git-checkin (files comment &optional _rev) (let* ((file1 (or (car files) default-directory)) (root (vc-git-root file1)) @@ -936,12 +943,21 @@ It is based on `log-edit-mode', and has Git-specific extensions.") (if (eq system-type 'windows-nt) (let ((default-directory (file-name-directory file1))) (make-nearby-temp-file "git-msg"))))) + (when vc-git-patch-string + (unless (zerop (vc-git-command nil t nil "diff" "--cached" "--quiet")) + (user-error "Index not empty")) + (let ((patch-file (make-temp-file "git-patch"))) + (with-temp-file patch-file + (insert vc-git-patch-string)) + (unwind-protect + (vc-git-command nil 0 patch-file "apply" "--cached") + (delete-file patch-file)))) (cl-flet ((boolean-arg-fn (argument) (lambda (value) (when (equal value "yes") (list argument))))) ;; When operating on the whole tree, better pass "-a" than ".", since "." ;; fails when we're committing a merge. - (apply #'vc-git-command nil 0 (if only files) + (apply #'vc-git-command nil 0 (if (and only (not vc-git-patch-string)) files) (nconc (if msg-file (list "commit" "-F" (file-local-name msg-file)) (list "commit" "-m")) @@ -959,7 +975,8 @@ It is based on `log-edit-mode', and has Git-specific extensions.") (write-region (car args) nil msg-file)) (setq args (cdr args))) args) - (if only (list "--only" "--") '("-a"))))) + (unless vc-git-patch-string + (if only (list "--only" "--") '("-a")))))) (if (and msg-file (file-exists-p msg-file)) (delete-file msg-file)))) (defun vc-git-find-revision (file rev buffer) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 85a96a29fa..88139fe13d 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -247,6 +247,11 @@ ;; revision argument is only supported with some older VCSes, like ;; RCS and CVS, and is otherwise silently ignored. ;; +;; - checkin-patch (patch-string comment) +;; +;; Commit a single patch PATCH-STRING to this backend, bypassing +;; the changes in filesets. COMMENT is used as a check-in comment. +;; ;; * find-revision (file rev buffer) ;; ;; Fetch revision REV of file FILE and put it into BUFFER. @@ -1102,6 +1107,8 @@ BEWARE: this function may change the current buffer." (vc-dir-deduce-fileset state-model-only-files)) ((derived-mode-p 'dired-mode) (dired-vc-deduce-fileset state-model-only-files not-state-changing)) + ((derived-mode-p 'diff-mode) + (diff-vc-deduce-fileset)) ((setq backend (vc-backend buffer-file-name)) (if state-model-only-files (list backend (list buffer-file-name) @@ -1114,7 +1121,8 @@ BEWARE: this function may change the current buffer." (or (buffer-file-name vc-parent-buffer) (with-current-buffer vc-parent-buffer (or (derived-mode-p 'vc-dir-mode) - (derived-mode-p 'dired-mode))))) + (derived-mode-p 'dired-mode) + (derived-mode-p 'diff-mode))))) (progn ;FIXME: Why not `with-current-buffer'? --Stef. (set-buffer vc-parent-buffer) (vc-deduce-fileset-1 not-state-changing allow-unregistered state-model-only-files))) @@ -1230,6 +1238,8 @@ with, using the most specific one." (error "Fileset files are missing, so cannot be operated on")) ((eq state 'ignored) (error "Fileset files are ignored by the version-control system")) + ((eq model 'patch) + (vc-checkin files backend nil nil nil (buffer-string))) ((or (null state) (eq state 'unregistered)) (cond (verbose (let ((backend (vc-read-backend "Backend to register to: "))) @@ -1615,13 +1625,14 @@ Type \\[vc-next-action] to check in changes.") ".\n") (message "Please explain why you stole the lock. Type C-c C-c when done."))) -(defun vc-checkin (files backend &optional comment initial-contents rev) +(defun vc-checkin (files backend &optional comment initial-contents rev patch-string) "Check in FILES. COMMENT is a comment string; if omitted, a buffer is popped up to accept a comment. If INITIAL-CONTENTS is non-nil, then COMMENT is used as the initial contents of the log entry buffer. The optional argument REV may be a string specifying the new revision level (only supported for some older VCSes, like RCS and CVS). +The optional argument PATCH-STRING is a string to check in as a patch. Runs the normal hooks `vc-before-checkin-hook' and `vc-checkin-hook'." (run-hooks 'vc-before-checkin-hook) @@ -1643,7 +1654,9 @@ Runs the normal hooks `vc-before-checkin-hook' and `vc-checkin-hook'." ;; vc-checkin-switches, but 'the' local buffer is ;; not a well-defined concept for filesets. (progn - (vc-call-backend backend 'checkin files comment rev) + (if patch-string + (vc-call-backend backend 'checkin-patch patch-string comment) + (vc-call-backend backend 'checkin files comment rev)) (mapc #'vc-delete-automatic-version-backups files)) `((vc-state . up-to-date) (vc-checkout-time . ,(file-attribute-modification-time @@ -1651,7 +1664,8 @@ Runs the normal hooks `vc-before-checkin-hook' and `vc-checkin-hook'." (vc-working-revision . nil))) (message "Checking in %s...done" (vc-delistify files))) 'vc-checkin-hook - backend)) + backend + patch-string)) ;;; Additional entry points for examining version histories @@ -1779,6 +1793,25 @@ objects, and finally killing buffer ORIGINAL." (defvar vc-diff-added-files nil "If non-nil, diff added files by comparing them to /dev/null.") +(defvar vc-patch-string nil) + +(defun vc-diff-patch-string (patch-string) + "Report diffs to be committed from the patch. +Like `vc-diff-internal' but uses PATCH-STRING to display +in the output buffer." + (let ((buffer "*vc-diff*")) + (vc-setup-buffer buffer) + (let ((buffer-undo-list t) + (inhibit-read-only t)) + (insert patch-string)) + (setq buffer-read-only t) + (diff-mode) + (setq-local diff-vc-backend (vc-responsible-backend default-directory)) + (setq-local revert-buffer-function (lambda (_ _) (vc-diff-patch-string))) + (setq-local vc-patch-string patch-string) + (pop-to-buffer (current-buffer)) + (vc-run-delayed (vc-diff-finish (current-buffer) nil)))) + (defun vc-diff-internal (async vc-fileset rev1 rev2 &optional verbose buffer) "Report diffs between two revisions of a fileset. Output goes to the buffer BUFFER, which defaults to *vc-diff*. commit 35af917f187719fecadde278a51fd10bf47eed07 Author: Juri Linkov Date: Sun Aug 28 22:13:45 2022 +0300 Revert parts of 3f076a8e44 that caused test failures * lisp/simple.el (line-move-finish): * lisp/window.el (scroll-command--goto-goal-column): Revert back to using truncate-partial-width-windows instead of truncated-partial-width-window-p. This change caused test failures in lisp/ls-lisp-tests.log and lisp/emacs-lisp/edebug-tests.log. diff --git a/lisp/simple.el b/lisp/simple.el index ceb29b1e30..daacf697ff 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -7973,7 +7973,7 @@ If NOERROR, don't signal an error if we can't move that many lines." ;; Move to the desired column. (if (and line-move-visual - (not (or truncate-lines (truncated-partial-width-window-p)))) + (not (or truncate-lines truncate-partial-width-windows))) ;; Under line-move-visual, goal-column should be ;; interpreted in units of the frame's canonical character ;; width, which is exactly what vertical-motion does. diff --git a/lisp/window.el b/lisp/window.el index db69379e69..ec2b0a6930 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -10137,7 +10137,7 @@ semipermanent goal column for this command." (when goal-column ;; Move to the desired column. (if (and line-move-visual - (not (or truncate-lines (truncated-partial-width-window-p)))) + (not (or truncate-lines truncate-partial-width-windows))) ;; Under line-move-visual, goal-column should be ;; interpreted in units of the frame's canonical character ;; width, which is exactly what vertical-motion does. commit 3edee9a6a352d0c352a29d6590233b412bd1ab04 Author: Ken Brown Date: Sat Aug 27 17:42:42 2022 -0400 Fix data for files-tests-bug-21454 on Cygwin * test/lisp/files-tests.el (files-tests-bug-21454): Fix test data to reflect the fact that Cygwin supports UNC paths. diff --git a/test/lisp/files-tests.el b/test/lisp/files-tests.el index 20c712226e..682b5cdb44 100644 --- a/test/lisp/files-tests.el +++ b/test/lisp/files-tests.el @@ -223,20 +223,39 @@ form.") ("x:/foo/bar/baz/" "z:/qux/foo/")) ("///foo/bar/" "$FOO/baz/;/qux/foo/" ("//foo/bar//baz/" "/qux/foo/"))) - '(("/foo/bar//baz/:/bar/foo/baz//" nil - ("/foo/bar//baz/" "/bar/foo/baz//")) - ("/foo/bar/:/bar/qux/:/qux/foo" nil - ("/foo/bar/" "/bar/qux/" "/qux/foo/")) - ("//foo/bar/:/bar/qux/:/qux/foo/" nil - ("/foo/bar/" "/bar/qux/" "/qux/foo/")) - ("/foo/bar/:/bar/qux/:/qux/foo/" nil - ("/foo/bar/" "/bar/qux/" "/qux/foo/")) - ("/foo//bar/:/bar/qux/:/qux/foo/" nil - ("/foo//bar/" "/bar/qux/" "/qux/foo/")) - ("/foo//bar/:/bar/qux/:/qux/foo" nil - ("/foo//bar/" "/bar/qux/" "/qux/foo/")) - ("/foo/bar" "$FOO/baz/:/qux/foo/" ("/foo/bar/baz/" "/qux/foo/")) - ("//foo/bar/" "$FOO/baz/:/qux/foo/" ("/foo/bar//baz/" "/qux/foo/"))))) + (if (eq system-type 'cygwin) + '(("/foo/bar//baz/:/bar/foo/baz//" nil + ("/foo/bar//baz/" "/bar/foo/baz//")) + ("/foo/bar/:/bar/qux/:/qux/foo" nil + ("/foo/bar/" "/bar/qux/" "/qux/foo/")) + ("//foo/bar/:/bar/qux/:/qux/foo/" nil + ("//foo/bar/" "/bar/qux/" "/qux/foo/")) + ("/foo/bar/:/bar/qux/:/qux/foo/" nil + ("/foo/bar/" "/bar/qux/" "/qux/foo/")) + ("/foo//bar/:/bar/qux/:/qux/foo/" nil + ("/foo//bar/" "/bar/qux/" "/qux/foo/")) + ("/foo//bar/:/bar/qux/:/qux/foo" nil + ("/foo//bar/" "/bar/qux/" "/qux/foo/")) + ("/foo/bar" "$FOO/baz/:/qux/foo/" + ("/foo/bar/baz/" "/qux/foo/")) + ("///foo/bar/" "$FOO/baz/:/qux/foo/" + ("//foo/bar//baz/" "/qux/foo/"))) + '(("/foo/bar//baz/:/bar/foo/baz//" nil + ("/foo/bar//baz/" "/bar/foo/baz//")) + ("/foo/bar/:/bar/qux/:/qux/foo" nil + ("/foo/bar/" "/bar/qux/" "/qux/foo/")) + ("//foo/bar/:/bar/qux/:/qux/foo/" nil + ("/foo/bar/" "/bar/qux/" "/qux/foo/")) + ("/foo/bar/:/bar/qux/:/qux/foo/" nil + ("/foo/bar/" "/bar/qux/" "/qux/foo/")) + ("/foo//bar/:/bar/qux/:/qux/foo/" nil + ("/foo//bar/" "/bar/qux/" "/qux/foo/")) + ("/foo//bar/:/bar/qux/:/qux/foo" nil + ("/foo//bar/" "/bar/qux/" "/qux/foo/")) + ("/foo/bar" "$FOO/baz/:/qux/foo/" + ("/foo/bar/baz/" "/qux/foo/")) + ("//foo/bar/" "$FOO/baz/:/qux/foo/" + ("/foo/bar//baz/" "/qux/foo/")))))) (foo-env (getenv "FOO")) (bar-env (getenv "BAR"))) (unwind-protect commit c8c3cd3b08c468a8bc2c93fefaaf1c6f819a0d88 Author: Eli Zaretskii Date: Sun Aug 28 17:40:49 2022 +0300 ; Fix docs of glyphless-character display * etc/NEWS: Update "glyphless character" entries. * doc/lispref/display.texi (Glyphless Chars): The extra slot of the char-table can now use a cons cell. diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index a56f467e0b..db58cd14c6 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -8563,7 +8563,7 @@ square brackets, @samp{[]}. The char-table has one extra slot, which determines how to display any character that cannot be displayed with any available font, or cannot be encoded by the terminal's coding system. Its value should be one -of the above display methods, except @code{zero-width} or a cons cell. +of the above display methods, except @code{zero-width}. If a character has a non-@code{nil} entry in an active display table, the display table takes effect; in this case, Emacs does not consult diff --git a/etc/NEWS b/etc/NEWS index 1317cd0128..f337381dd4 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1532,11 +1532,13 @@ completion, and adds the Emoji into the search string. This allows an easy way to toggle seeing all glyphless characters in the current buffer. ++++ *** The extra slot of 'glyphless-char-display' can now have cons values. The extra slot of the 'glyphless-char-display' char-table can now have values that are cons cells, specifying separate values for text-mode and GUI terminals. +--- *** "Replacement character" feature for undisplayable characters on TTYs. The 'acronym' method of displaying glyphless characters on text-mode frames treats single-character acronyms specially: they are displayed