From aaca72806ecd60f28384fe839cdfe6a28a2b5d1f Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 16 Dec 2022 22:34:52 -0700 Subject: vc-prepare-patch: Number the attached patches * lisp/gnus/mml.el (mml-attach-buffer): New FILENAME argument. * lisp/vc/vc.el (vc--subject-to-file-name): New function. (vc-prepare-patch): When vc-prepare-patches-separately is nil, generate file names for the attached patches. Call vc--subject-to-file-name, and then prepend numbers indicating the ordering of the patches (bug#60147). --- lisp/vc/vc.el | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'lisp/vc') diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 690c907c77e..b40bb31b603 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -3369,7 +3369,7 @@ If nil, no default will be used. This option may be set locally." (declare-function message--name-table "message" (orig-string)) (declare-function mml-attach-buffer "mml" - (buffer &optional type description disposition)) + (buffer &optional type description disposition filename)) (declare-function log-view-get-marked "log-view" ()) (defun vc-default-prepare-patch (_backend rev) @@ -3410,6 +3410,19 @@ of the current file." (and-let* ((file (buffer-file-name))) (vc-working-revision file))))) +(defun vc--subject-to-file-name (subject) + "Generate a file name for a patch with subject line SUBJECT." + (let* ((stripped + (replace-regexp-in-string "\\`\\[.*PATCH.*\\]\\s-*" "" + subject)) + (truncated (if (length> stripped 50) + (substring stripped 0 50) + stripped))) + (concat + (string-trim (replace-regexp-in-string "\\W" "-" truncated) + "-+" "-+") + ".patch"))) + ;;;###autoload (defun vc-prepare-patch (addressee subject revisions) "Compose an Email sending patches for REVISIONS to ADDRESSEE. @@ -3466,11 +3479,17 @@ marked revisions, use those these." (rfc822-goto-eoh) (forward-line) (save-excursion - (dolist (patch patches) - (mml-attach-buffer (buffer-name (plist-get patch :buffer)) - "text/x-patch" - (plist-get patch :subject) - "attachment"))) + (let ((i 0)) + (dolist (patch patches) + (let* ((patch-subject (plist-get patch :subject)) + (filename + (vc--subject-to-file-name patch-subject))) + (mml-attach-buffer + (buffer-name (plist-get patch :buffer)) + "text/x-patch" + patch-subject + "attachment" + (format "%04d-%s" (cl-incf i) filename)))))) (open-line 2))))) (defun vc-default-responsible-p (_backend _file) -- cgit v1.2.1 From 8739cba1ee0336cef444ec07f170879e67f68202 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Mon, 19 Dec 2022 15:15:48 -0700 Subject: ; * lisp/vc/vc.el (vc-prepare-patch): Fix typo. --- lisp/vc/vc.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/vc') diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index b40bb31b603..130214b840a 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -3433,7 +3433,7 @@ revision, with SUBJECT derived from each revision subject. When invoked with a numerical prefix argument, use the last N revisions. When invoked interactively in a Log View buffer with -marked revisions, use those these." +marked revisions, use those." (interactive (let ((revs (vc-prepare-patch-prompt-revisions)) to) (require 'message) -- cgit v1.2.1 From 962bdfcdfe7e27687021c7dbaf0bb292afe9483c Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 16 Dec 2022 11:28:20 -0700 Subject: vc-git-checkin: Offer to unstage conflicting changes * lisp/vc/vc-git.el (vc-git-checkin): When committing a patch, if conflicting changes are already staged, offer to clear them, instead of just immediately failing with "Index not empty" (bug#60126). --- lisp/vc/vc-git.el | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'lisp/vc') diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 9f27f759d35..8f995021dcc 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -1030,23 +1030,31 @@ It is based on `log-edit-mode', and has Git-specific extensions." (with-temp-buffer (vc-git-command (current-buffer) t nil "diff" "--cached") (goto-char (point-min)) - (let ((pos (point)) file-diff file-beg) + (let ((pos (point)) file-name file-diff file-beg) (while (not (eobp)) + (when (and (looking-at "^diff --git a/\\(.+\\) b/\\(.+\\)") + (string= (match-string 1) (match-string 2))) + (setq file-name (match-string 1))) (forward-line 1) ; skip current "diff --git" line (search-forward "diff --git" nil 'move) (move-beginning-of-line 1) (setq file-diff (buffer-substring pos (point))) - (if (and (setq file-beg (string-search - file-diff vc-git-patch-string)) - ;; Check that file diff ends with an empty string - ;; or the beginning of the next file diff. - (string-match-p "\\`\\'\\|\\`diff --git" - (substring - vc-git-patch-string - (+ file-beg (length file-diff))))) - (setq vc-git-patch-string - (string-replace file-diff "" vc-git-patch-string)) - (user-error "Index not empty")) + (cond ((and (setq file-beg (string-search + file-diff vc-git-patch-string)) + ;; Check that file diff ends with an empty string + ;; or the beginning of the next file diff. + (string-match-p "\\`\\'\\|\\`diff --git" + (substring + vc-git-patch-string + (+ file-beg (length file-diff))))) + (setq vc-git-patch-string + (string-replace file-diff "" vc-git-patch-string))) + ((and file-name + (yes-or-no-p + (format "Unstage already-staged changes to %s?" + file-name))) + (vc-git-command nil 0 file-name "reset" "-q" "--")) + (t (user-error "Index not empty"))) (setq pos (point)))))) (let ((patch-file (make-nearby-temp-file "git-patch"))) (with-temp-file patch-file -- cgit v1.2.1 From 1424342225ef5b18c630364dd88e004f4ebb1c7f Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Tue, 20 Dec 2022 15:53:19 -0700 Subject: vc-git-checkin: Don't try to apply an empty patch * lisp/vc/vc-git.el (vc-git-checkin): Don't try to apply an empty patch to the index, because in that case 'git apply' fails. --- lisp/vc/vc-git.el | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lisp/vc') diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 8f995021dcc..0a4e9caa614 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -1056,12 +1056,13 @@ It is based on `log-edit-mode', and has Git-specific extensions." (vc-git-command nil 0 file-name "reset" "-q" "--")) (t (user-error "Index not empty"))) (setq pos (point)))))) - (let ((patch-file (make-nearby-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)))) + (unless (string-empty-p vc-git-patch-string) + (let ((patch-file (make-nearby-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))))) -- cgit v1.2.1 From baaa9f42e574aa5eceeb4b9354a42ccb8ff1969a Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Tue, 20 Dec 2022 15:53:19 -0700 Subject: vc-git-checkin: Don't try to apply an empty patch * lisp/vc/vc-git.el (vc-git-checkin): Don't try to apply an empty patch to the index, because in that case 'git apply' fails. (cherry picked from commit 1424342225ef5b18c630364dd88e004f4ebb1c7f) --- lisp/vc/vc-git.el | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lisp/vc') diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index b5959d535c0..afaaa44e908 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -1041,12 +1041,13 @@ It is based on `log-edit-mode', and has Git-specific extensions." (string-replace file-diff "" vc-git-patch-string)) (user-error "Index not empty")) (setq pos (point)))))) - (let ((patch-file (make-nearby-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)))) + (unless (string-empty-p vc-git-patch-string) + (let ((patch-file (make-nearby-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))))) -- cgit v1.2.1 From 666c24a6269c35f27145350206cbbc057863e557 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Thu, 22 Dec 2022 20:54:08 -0700 Subject: vc-git-checkin: Stash other staged changes * lisp/vc/vc-git.el (vc-git--stash-staged-changes): New function. (vc-git-checkin): Use new function to avoid needing to unstage changes unrelated to the patch we want to commit (bug#60126). --- lisp/vc/vc-git.el | 97 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 14 deletions(-) (limited to 'lisp/vc') diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 0a4e9caa614..671be66bbef 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -1020,22 +1020,36 @@ It is based on `log-edit-mode', and has Git-specific extensions." ;; message. Handle also remote files. (if (eq system-type 'windows-nt) (let ((default-directory (file-name-directory file1))) - (make-nearby-temp-file "git-msg"))))) + (make-nearby-temp-file "git-msg")))) + to-stash) (when vc-git-patch-string (unless (zerop (vc-git-command nil t nil "diff" "--cached" "--quiet")) - ;; Check that all staged changes also exist in the patch. - ;; This is needed to allow adding/removing files that are - ;; currently staged to the index. So remove the whole file diff - ;; from the patch because commit will take it from the index. + ;; Check that what's already staged is compatible with what + ;; we want to commit (bug#60126). + ;; + ;; 1. If the changes to a file in the index are identical to + ;; the changes to that file we want to commit, remove the + ;; changes from our patch, and let the commit take them + ;; from the index. This is necessary for adding and + ;; removing files to work. + ;; + ;; 2. If the changes to a file in the index are different to + ;; changes to that file we want to commit, then we have to + ;; unstage the changes or abort. + ;; + ;; 3. If there are changes to a file in the index but we don't + ;; want to commit any changes to that file, we need to + ;; stash those changes before committing. (with-temp-buffer (vc-git-command (current-buffer) t nil "diff" "--cached") (goto-char (point-min)) - (let ((pos (point)) file-name file-diff file-beg) + (let ((pos (point)) file-name file-header file-diff file-beg) (while (not (eobp)) (when (and (looking-at "^diff --git a/\\(.+\\) b/\\(.+\\)") (string= (match-string 1) (match-string 2))) (setq file-name (match-string 1))) (forward-line 1) ; skip current "diff --git" line + (setq file-header (buffer-substring pos (point))) (search-forward "diff --git" nil 'move) (move-beginning-of-line 1) (setq file-diff (buffer-substring pos (point))) @@ -1049,12 +1063,15 @@ It is based on `log-edit-mode', and has Git-specific extensions." (+ file-beg (length file-diff))))) (setq vc-git-patch-string (string-replace file-diff "" vc-git-patch-string))) - ((and file-name - (yes-or-no-p - (format "Unstage already-staged changes to %s?" - file-name))) - (vc-git-command nil 0 file-name "reset" "-q" "--")) - (t (user-error "Index not empty"))) + ((string-match (format "^%s" (regexp-quote file-header)) + vc-git-patch-string) + (if (and file-name + (yes-or-no-p + (format "Unstage already-staged changes to %s?" + file-name))) + (vc-git-command nil 0 file-name "reset" "-q" "--") + (user-error "Index not empty"))) + (t (push file-name to-stash))) (setq pos (point)))))) (unless (string-empty-p vc-git-patch-string) (let ((patch-file (make-nearby-temp-file "git-patch"))) @@ -1062,7 +1079,8 @@ It is based on `log-edit-mode', and has Git-specific extensions." (insert vc-git-patch-string)) (unwind-protect (vc-git-command nil 0 patch-file "apply" "--cached") - (delete-file patch-file))))) + (delete-file patch-file)))) + (when to-stash (vc-git--stash-staged-changes files))) (cl-flet ((boolean-arg-fn (argument) (lambda (value) (when (equal value "yes") (list argument))))) @@ -1088,7 +1106,58 @@ It is based on `log-edit-mode', and has Git-specific extensions." args) (unless vc-git-patch-string (if only (list "--only" "--") '("-a")))))) - (if (and msg-file (file-exists-p msg-file)) (delete-file msg-file)))) + (if (and msg-file (file-exists-p msg-file)) (delete-file msg-file)) + (when to-stash + (let ((cached (make-nearby-temp-file "git-cached"))) + (unwind-protect + (progn (with-temp-file cached + (vc-git-command t 0 nil "stash" "show" "-p")) + (vc-git-command nil 0 cached "apply" "--cached")) + (delete-file cached)) + (vc-git-command nil 0 nil "stash" "drop"))))) + +(defun vc-git--stash-staged-changes (files) + "Stash only the staged changes to FILES." + ;; This is necessary because even if you pass a list of file names + ;; to 'git stash push', it will stash any and all staged changes. + (unless (zerop + (vc-git-command nil t files "diff" "--cached" "--quiet")) + (cl-flet + ((git-string (&rest args) + (string-trim-right + (with-output-to-string + (apply #'vc-git-command standard-output 0 nil args))))) + (let ((cached (make-nearby-temp-file "git-cached")) + (message "Previously staged changes") + tree) + ;; Use a temporary index to create a tree object corresponding + ;; to the staged changes to FILES. + (unwind-protect + (progn + (with-temp-file cached + (vc-git-command t 0 files "diff" "--cached" "--")) + (let* ((index (make-nearby-temp-file "git-index")) + (process-environment + (cons (format "GIT_INDEX_FILE=%s" index) + process-environment))) + (unwind-protect + (progn + (vc-git-command nil 0 nil "read-tree" "HEAD") + (vc-git-command nil 0 cached "apply" "--cached") + (setq tree (git-string "write-tree"))) + (delete-file index)))) + (delete-file cached)) + ;; Prepare stash commit object, which has a special structure. + (let* ((tree-commit (git-string "commit-tree" "-m" message + "-p" "HEAD" tree)) + (stash-commit (git-string "commit-tree" "-m" message + "-p" "HEAD" "-p" tree-commit + tree))) + ;; Push the new stash entry. + (vc-git-command nil 0 nil "update-ref" "--create-reflog" + "-m" message "refs/stash" stash-commit) + ;; Unstage the changes we've now stashed. + (vc-git-command nil 0 files "reset" "--")))))) (defun vc-git-find-revision (file rev buffer) (let* (process-file-side-effects -- cgit v1.2.1 From b69bffeec05302529209559dfb2ab24d9e711192 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Wed, 28 Dec 2022 20:14:43 +0200 Subject: * lisp/vc/diff-mode.el (diff-minor-mode-prefix): Replace "ESC" with "\e". "ESC" looks like an attempt to use kbd syntax in customization. But actually now 'key-description' is used in 'diff-minor-mode-map' to convert "\e" to "ESC". --- lisp/vc/diff-mode.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lisp/vc') diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index 357ce001b3c..b80337eb742 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el @@ -272,8 +272,7 @@ and hunk-based syntax highlighting otherwise as a fallback." (defcustom diff-minor-mode-prefix "\C-c=" "Prefix key for `diff-minor-mode' commands." - :type '(choice (string "ESC") - (string "\C-c=") string)) + :type '(choice (string "\e") (string "\C-c=") string)) (defvar-keymap diff-minor-mode-map :doc "Keymap for `diff-minor-mode'. See also `diff-mode-shared-map'." -- cgit v1.2.1