James Thomas <jim...@gmx.net> writes:

> Suhail Singh writes:
>
>> emacs-git-email
>
> This is only tangential, but...
>
> I'd come up with an equivalent (?) hack that I'd posted here:
>
>   (gnus-summary-goto-article "<868qypm5c5....@gmx.net>")
>
> Basically:
>
>> M-&
>> git send-email --sendmail-cmd=echo HEAD^..HEAD
>> RET
>>

FWIW, I've submitted patches to Guix using a version of the built-in
vc.el command 'vc-prepare-patch' modified to act like 'git send-email'.

Am I missing something, or would the code below do the same thing as
'emacs-git-email'? Could we submit a patch upstream to Emacs to make
sending patches to projects like Guix easier?

--8<---------------cut here---------------start------------->8---
(require 'vc)
(require 'seq)

(defun fern--git-variable (variable &optional file)
  "Return the value of the Git VARIABLE for the repo containing FILE.

Return nil if the variable is undefined or if FILE is not version
controlled by Git."
  (when-let* (((memq (ignore-errors (vc-responsible-backend
                                    (or file default-directory)))
                    '(Git)))
             (result (string-chop-newline
                      (shell-command-to-string
                       (format "git config --get %s"
                               (shell-quote-argument variable)))))
             ((not (string-empty-p result))))
    result))

(defun fern-prepare-patch (addressee subject revisions
                                     &optional other-headers
                                     no-self-bcc)
  "Compose an email sending patches for REVISIONS to ADDRESSEE.

Interactively, prompt for REVISIONS and prompt for ADDRESSEE if it
cannot be determined automatically (see below).

When invoked with a numerical prefix argument, use the last N revisions.

If `vc-prepare-patches-separately' is nil, use SUBJECT as the subject,
or prompt for a subject when invoked interactively.  Otherwise, compose
a separate message for each revision, with the subject automatically
derived from each revision subject.

Optional argument OTHER-HEADERS is as with `compose-mail', which see.
When invoked interactively, OTHER-HEADERS is automatically populated by
running the command specified by the sendemail.headerCmd.  That command,
if found, will be called with one argument, a patch file for the first
revision in REVISIONS.

Unless optional argument NO-SELF-BCC is non-nil, add a Bcc header with
the user's mail address, specified by the value of `user-mail-address'.

This command is like `vc-prepare-patch', but differs in that it
automatically determines ADDRESSEE according to the sendemail.to Git
variable, and it adds extra headers according to the sendemail.headerCmd
Git variable (see above).  Additionally, it ensures that no signatures
are inserted at the end of the message.  This makes the behavior more
similar to what one would expect from `git send-email' command."
  ;; Modified from `vc-prepare-patch'.
  (interactive
   (let* ((revs (vc-prepare-patch-prompt-revisions))
          (rev (car revs))
          (patch (vc-call-backend
                  (vc-responsible-backend default-directory)
                  'prepare-patch rev))
          (subject (when (length= revs 1)
                     (plist-get patch :subject)))
          (to (or (fern--git-variable "sendemail.to")
                  (progn
                    (require 'message)
                    (completing-read
                     (format-prompt
                      "Addressee" vc-default-patch-addressee)
                     (message--name-table "") nil nil nil nil
                     vc-default-patch-addressee))))
          (other (when-let* ((buf (plist-get patch :buffer))
                            (patch-file (make-temp-file
                                         (format "vc-patch-for-%s" rev)
                                         nil nil
                                         (with-current-buffer buf
                                           (buffer-substring-no-properties
                                            (point-min) (point-max)))))
                            (cmd (fern--git-variable
                                  "sendemail.headerCmd"))
                            ;; XXX: Give headerCmd a patch file argument.
                            (cmd-1 (concat cmd " "
                                           (shell-quote-argument
                                            patch-file)))
                            ;; XXX: Prompt before running the command
                            ;; as a security precaution!
                            (headers (when (yes-or-no-p
                                            (format
                                             "Run header command: %s?"
                                             cmd-1))
                                       (shell-command-to-string
                                        cmd-1))))
                   (let (result)
                     (dolist (header (string-lines headers))
                       ;; XXX: Is this regexp enough to parse header
                       ;; names and values?
                       (when (string-match
                              "^\\([^:]+\\):[[:blank:]]*\\(.*\\)$"
                              header)
                         (push (cons (match-string 1 header)
                                     (match-string 2 header))
                               result)))
                     (nreverse result)))))
     (list to
           (and (not vc-prepare-patches-separately)
                (read-string "Subect: " (or subject "[PATCH] ")
                             nil nil t))
           revs
           other)))
  (save-current-buffer
    (let ((patches (mapcar (lambda (rev)
                             (vc-call-backend
                              (vc-responsible-backend default-directory)
                              'prepare-patch rev))
                           revisions))
          ;; XXX: Disable signatures
          (mail-signature nil)
          (message-signature nil))
      (unless no-self-bcc
        (let* ((val (alist-get "bcc" other-headers nil nil
                               #'string-equal-ignore-case))
               (val-1 (if (stringp val)
                          (string-join
                           (seq-uniq (append (list user-mail-address)
                                             (split-string val ",[[:blank:]]*"))
                                     #'string-equal-ignore-case)
                           ", ")
                        user-mail-address)))
          (setq other-headers (copy-tree other-headers))
          (setf (alist-get "bcc" other-headers nil nil
                           #'string-equal-ignore-case)
                val-1)))
      (if vc-prepare-patches-separately
          (dolist (patch (reverse patches))
            (compose-mail addressee
                          (plist-get patch :subject)
                          other-headers nil nil nil
                          `((kill-buffer ,(plist-get patch :buffer))))
            (rfc822-goto-eoh)
            (forward-line)
            (save-excursion
              (insert-buffer-substring
               (plist-get patch :buffer)
               (plist-get patch :body-start)
               (plist-get patch :body-end))))
        (compose-mail addressee subject other-headers
                      nil nil nil
                      (mapcar
                       (lambda (p)
                         (list #'kill-buffer (plist-get p :buffer)))
                       patches))
        (rfc822-goto-eoh)
        (forward-line)
        (save-excursion
          (let ((i 0))
            (dolist (patch patches)
              (let* ((patch-subject (plist-get patch :subject))
                     (file (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" (setq i (1+ i)) file))))))
        (open-line 2)))))
--8<---------------cut here---------------end--------------->8---

-- 
Kierin Bell

Reply via email to