unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Sean Whitton <spwhitton@spwhitton.name>
To: 64055@debbugs.gnu.org, Eli Zaretskii <eliz@gnu.org>
Cc: Morgan Smith <Morgan.J.Smith@outlook.com>,
	Dmitry Gutov <dgutov@yandex.ru>
Subject: bug#64055: Implementation of modifying VC change comments for Git
Date: Fri, 18 Oct 2024 17:26:28 +0800	[thread overview]
Message-ID: <874j59wym3.fsf@melete.silentflame.com> (raw)
In-Reply-To: <DM5PR03MB3163ADD665E6B3B1D8704A2AC555A@DM5PR03MB3163.namprd03.prod.outlook.com>

[-- Attachment #1: Type: text/plain, Size: 227 bytes --]

Hello,

This patch is the rest of this feature.  Eli, would you kindly test on
MS-Windows, please?  Just try to edit the message for an unpushed git
commit and add some Unicode, and see if it works correctly.

-- 
Sean Whitton

[-- Attachment #2: 0001-Support-modifying-VC-change-comments-for-Git.patch --]
[-- Type: text/x-diff, Size: 10737 bytes --]

From 674f7b7c636a4e2ebcb4955b564f8c9c36bd3ddd Mon Sep 17 00:00:00 2001
From: Sean Whitton <spwhitton@spwhitton.name>
Date: Fri, 18 Oct 2024 17:19:45 +0800
Subject: [PATCH] Support modifying VC change comments for Git

* lisp/vc/vc-git.el (vc-git-allow-rewriting-history): New option.
(vc-git--assert-allowed-rewrite, vc-git-modify-change-comment):
New functions (bug#64055).

(vc-git--current-branch): Factor out of vc-git-dir--branch-headers.
(vc-git--log-edit-extract-headers): Factor out of vc-git-checkin.

* etc/NEWS: Announce the new support and option.
---
 etc/NEWS          |  15 +++++
 lisp/vc/vc-git.el | 147 +++++++++++++++++++++++++++++++++++++---------
 2 files changed, 134 insertions(+), 28 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 10d86173235..d2e72bc537a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -568,6 +568,21 @@ a desktop notification when the song changes, using
 customized using the new user options 'mpc-notifications-title' and
 'mpc-notifications-body'.
 
+** VC
+
+---
+*** Using 'e' from Log View mode to modify change comments now works for Git.
+
+---
+*** New user option 'vc-git-allow-rewriting-history'.
+Many Git commands can change your copy of published change history
+without warning.  If VC commands detect that this could happen, they
+will stop.  You can customize this variable to permit rewriting history
+even though Emacs thinks it is dangerous.
+
+So far, this applies only to the 'log-view-modify-change-comment'
+command.
+
 \f
 * New Modes and Packages in Emacs 31.1
 
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index f77bf0cc5ff..0680d8e7353 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -252,6 +252,27 @@ vc-git-revision-complete-only-branches
   :type 'boolean
   :version "28.1")
 
+;; The default is nil because only a VC user who also possesses a lot of
+;; Git-specific knowledge can know when it is okay to rewrite history,
+;; and we can't convey to a relatively Git-naïve user the potential
+;; risks in only the space of a minibuffer y/n prompt.
+(defcustom vc-git-allow-rewriting-history nil
+  "When non-nil, permit Git operations that may rewrite published history.
+
+Many Git commands can change your copy of published change history
+without warning.  If this occurs, you won't be able to pull and push in
+the ordinary way until you take special action.  See \"Recovering from
+Upstream Rebase\" in the Man page git-rebase(1).
+
+Normally, Emacs refuses to run Git commands that it thinks will rewrite
+published history.  If you customize this variable to a non-nil value,
+Emacs will instead prompt you to confirm that you really want to perform
+the rewrite.  A value of `no-ask' means to proceed with no prompting."
+  :type '(choice (const :tag "Don't allow" nil)
+                 (const :tag "Prompt to allow" t)
+                 (const :tag "Allow without prompting" no-ask))
+  :version "31.1")
+
 ;; History of Git commands.
 (defvar vc-git-history nil)
 
@@ -728,11 +749,13 @@ vc-git-dir-status-files
                                  :files files
                                  :update-function update-function)))
 
+(defun vc-git--current-branch ()
+  (vc-git--out-match '("symbolic-ref" "HEAD")
+                     "^\\(refs/heads/\\)?\\(.+\\)$" 2))
+
 (defun vc-git-dir--branch-headers ()
   "Return headers for branch-related information."
-  (let ((branch (vc-git--out-match
-                 '("symbolic-ref" "HEAD")
-                 "^\\(refs/heads/\\)?\\(.+\\)$" 2))
+  (let ((branch (vc-git--current-branch))
         tracking remote-url)
     (if branch
         (when-let ((branch-merge
@@ -1082,6 +1105,17 @@ vc-git-checkin-patch
 
 (autoload 'vc-switches "vc")
 
+(defun vc-git--log-edit-extract-headers (comment)
+  (cl-flet ((boolean-arg-fn (argument)
+              (lambda (v) (and (equal v "yes") (list argument)))))
+    (log-edit-extract-headers
+     `(("Author" . "--author")
+       ("Date" . "--date")
+       ("Amend" . ,(boolean-arg-fn "--amend"))
+       ("No-Verify" . ,(boolean-arg-fn "--no-verify"))
+       ("Sign-Off" . ,(boolean-arg-fn "--signoff")))
+     comment)))
+
 (defun vc-git-checkin (files comment &optional _rev)
   (let* ((file1 (or (car files) default-directory))
          (root (vc-git-root file1))
@@ -1180,31 +1214,23 @@ vc-git-checkin
               (vc-git-command nil 0 patch-file "apply" "--cached")
             (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)))))
-      ;; 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 (and only (not vc-git-patch-string)) files)
-             (nconc (if msg-file (list "commit" "-F"
-                                       (file-local-name msg-file))
-                      (list "commit" "-m"))
-                    (let ((args
-                           (log-edit-extract-headers
-                            `(("Author" . "--author")
-                              ("Date" . "--date")
-                              ("Amend" . ,(boolean-arg-fn "--amend"))
-                              ("No-Verify" . ,(boolean-arg-fn "--no-verify"))
-                              ("Sign-Off" . ,(boolean-arg-fn "--signoff")))
-                            comment)))
-                      (when msg-file
-                        (let ((coding-system-for-write
-                               (or pcsw vc-git-commits-coding-system)))
-                          (write-region (car args) nil msg-file))
-                        (setq args (cdr args)))
-                      args)
-                    (unless vc-git-patch-string
-                      (if only (list "--only" "--") '("-a"))))))
+    ;; 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 (and only (not vc-git-patch-string)) files)
+           (nconc (if msg-file (list "commit" "-F"
+                                     (file-local-name msg-file))
+                    (list "commit" "-m"))
+                  (let ((args
+                         (vc-git--log-edit-extract-headers comment)))
+                    (when msg-file
+                      (let ((coding-system-for-write
+                             (or pcsw vc-git-commits-coding-system)))
+                        (write-region (car args) nil msg-file))
+                      (setq args (cdr args)))
+                    args)
+                  (unless vc-git-patch-string
+                    (if only (list "--only" "--") '("-a")))))
     (if (and msg-file (file-exists-p msg-file)) (delete-file msg-file))
     (when to-stash
       (let ((cached (make-nearby-temp-file "git-cached")))
@@ -1960,6 +1986,71 @@ vc-git-get-change-comment
     (vc-git-command standard-output 1 nil
                     "log" "--max-count=1" "--pretty=format:%B" rev)))
 
+(defun vc-git--assert-allowed-rewrite (rev)
+  (when (and (not (eq vc-git-allow-rewriting-history 'no-ask))
+             ;; Check there is an upstream.
+             (with-temp-buffer
+               (vc-git--out-ok "config" "--get"
+                               (format "branch.%s.merge"
+                                       (vc-git--current-branch)))))
+    (let ((outgoing (split-string
+                     (with-output-to-string
+                       (vc-git-command standard-output 0 nil "log"
+                                       "--pretty=format:%H"
+                                       "@{upstream}..HEAD")))))
+      (unless (or (cl-member rev outgoing :test #'string-prefix-p)
+                  (and vc-git-allow-rewriting-history
+                       (yes-or-no-p
+                        (format
+"Commit %s looks to be published; are you sure you want to rewrite history?"
+                         rev))))
+        (user-error "Will not rewrite likely-public Git history")))))
+
+(defun vc-git-modify-change-comment (files rev comment)
+  (vc-git--assert-allowed-rewrite rev)
+  (let* ((args (vc-git--log-edit-extract-headers comment))
+         (message (format "amend! %s\n\n%s" rev (pop args)))
+         (msg-file
+          ;; On MS-Windows, pass the message through a file, to work
+          ;; around how command line arguments must be in the system
+          ;; codepage, and therefore might not support non-ASCII.
+          ;;
+          ;; As our other arguments are static, we need not be concerned
+          ;; about the encoding of command line arguments in general.
+          ;; See `vc-git-checkin' for the more complex case.
+          (and (eq system-type 'windows-nt)
+               (let ((default-directory
+                      (or (file-name-directory (or (car files)
+                                                   default-directory))
+                          default-directory)))
+                 (make-nearby-temp-file "git-msg")))))
+    (unwind-protect
+        (progn
+          (when (cl-intersection '("--author" "--date") args
+                                 :test #'string=)
+            ;; 'git rebase --autosquash' cannot alter authorship.
+            ;; See the description of --fixup in git-commit(1).
+            (error
+"Author: and Date: not supported when modifying existing commits"))
+          (when msg-file
+            (let ((coding-system-for-write
+                   (or coding-system-for-write
+                       vc-git-commits-coding-system)))
+              (write-region message nil msg-file)))
+          ;; Regardless of the state of the index and working tree, this
+          ;; will always create an empty commit, thanks to --only.
+          (apply #'vc-git-command nil 0 nil
+                 "commit" "--only" "--allow-empty"
+                 (nconc (if msg-file
+                            (list "-F" (file-local-name msg-file))
+                          (list "-m" message))
+                        args)))
+      (when (and msg-file (file-exists-p msg-file))
+        (delete-file msg-file))))
+  (with-environment-variables (("GIT_SEQUENCE_EDITOR" "true"))
+    (vc-git-command nil 0 nil "rebase" "--autostash" "--autosquash" "-i"
+                    (format "%s~1" rev))))
+
 (defvar vc-git-extra-menu-map
   (let ((map (make-sparse-keymap)))
     (define-key map [git-grep]
-- 
2.45.2


      parent reply	other threads:[~2024-10-18  9:26 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-13 22:59 bug#64055: [WIP Patch] Enable editing commit messages - vc-git-modify-change-comment Morgan Smith
2023-06-14  8:00 ` Robert Pluim
2023-06-14 11:41   ` Morgan Smith
2023-06-14 13:13     ` Robert Pluim
2023-06-14 13:54       ` Morgan Smith
2023-06-14 15:30         ` Robert Pluim
2024-10-01  2:38       ` Sean Whitton
2024-10-01 19:32         ` Dmitry Gutov
2024-10-02  0:01           ` Sean Whitton
2024-10-02 23:20             ` Dmitry Gutov
2024-10-10  2:39               ` Sean Whitton
2024-10-10  2:48                 ` Sean Whitton
2024-10-17 13:27                   ` Sean Whitton
2024-10-18  5:26                     ` Eli Zaretskii
2024-10-18  6:20                       ` Sean Whitton
2024-10-18  9:14                         ` Eli Zaretskii
2024-10-18  9:30                           ` Sean Whitton
2024-10-18 12:18                             ` Eli Zaretskii
2024-10-18  0:46                 ` Dmitry Gutov
2024-10-18  4:50                   ` Sean Whitton
2023-06-17  2:40 ` Dmitry Gutov
2024-10-01  2:37   ` Sean Whitton
2024-10-01 13:35     ` Dmitry Gutov
2024-10-10  2:45 ` Sean Whitton
2024-10-10  6:12   ` Eli Zaretskii
2024-10-10  6:23     ` Sean Whitton
2024-10-10  7:36       ` Eli Zaretskii
2024-10-10  7:46         ` Sean Whitton
2024-10-18  9:26 ` Sean Whitton [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=874j59wym3.fsf@melete.silentflame.com \
    --to=spwhitton@spwhitton.name \
    --cc=64055@debbugs.gnu.org \
    --cc=Morgan.J.Smith@outlook.com \
    --cc=dgutov@yandex.ru \
    --cc=eliz@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).