From 9f7b98f812674d2824d713b460973842e6e0943b Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Tue, 2 Oct 2012 04:24:18 +0400 Subject: [PATCH] Support git commit --amend/--signoff * lisp/vc/log-edit.el (log-edit-font-lock-keywords): Allow hyphens in header names. (log-edit-toggle-header): New function. (log-edit-extract-headers): Accept function values in HEADERS alist. * lisp/vc/vc-git.el (vc-git-log-edit-toggle-signoff): New function. (vc-git-log-edit-toggle-amend): New function. (vc-git-log-edit-toggle-signoff): New function. (vc-git-log-edit-mode): New major mode. (vc-git-log-edit-mode-map): Keymap for it. (vc-git-checkin): Handle "Amend" and "Sign-Off" headers. --- lisp/ChangeLog | 14 ++++++++++++++ lisp/vc/log-edit.el | 50 +++++++++++++++++++++++++++++++++++++++++--------- lisp/vc/vc-git.el | 48 ++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 97 insertions(+), 15 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index b324dce5164..bb5e79e4ac2 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,17 @@ +2012-10-01 Dmitry Gutov + + * vc/vc-git.el (vc-git-log-edit-toggle-signoff): New function. + (vc-git-log-edit-toggle-amend): New function. + (vc-git-log-edit-toggle-signoff): New function. + (vc-git-log-edit-mode): New major mode. + (vc-git-log-edit-mode-map): Keymap for it. + (vc-git-checkin): Handle "Amend" and "Sign-Off" headers. + + * vc/log-edit.el (log-edit-font-lock-keywords): Allow hyphens in + header names. + (log-edit-toggle-header): New function. + (log-edit-extract-headers): Accept function values in HEADERS alist. + 2012-10-01 David Engster * emacs-lisp/eieio-opt.el (eieio-describe-class): Add filename diff --git a/lisp/vc/log-edit.el b/lisp/vc/log-edit.el index 932abb9818c..3c34a762a1b 100644 --- a/lisp/vc/log-edit.el +++ b/lisp/vc/log-edit.el @@ -341,7 +341,7 @@ automatically." (defvar log-edit-font-lock-keywords ;; Copied/inspired by message-font-lock-keywords. `((log-edit-match-to-eoh - (,(concat "^\\(\\([[:alpha:]]+\\):\\)" log-edit-header-contents-regexp) + (,(concat "^\\(\\([[:alpha:]-]+\\):\\)" log-edit-header-contents-regexp) (progn (goto-char (match-beginning 0)) (match-end 0)) nil (1 (if (assoc-string (match-string 2) log-edit-headers-alist t) 'log-edit-header @@ -900,14 +900,44 @@ Rename relative filenames in the ChangeLog entry as FILES." (insert "\n")) log-edit-author)) +(defun log-edit-toggle-header (header value) + "Toggle a boolean-type header in the current buffer. +If the value of HEADER is VALUE, clear it. Otherwise, add the +header if it's not present and set it to VALUE. Then make sure +there is an empty line after the headers. Return t if toggled +on, otherwise nil." + (let ((val t) + (line (concat header ": " value "\n"))) + (save-excursion + (save-restriction + (rfc822-goto-eoh) + (narrow-to-region (point-min) (point)) + (goto-char (point-min)) + (if (re-search-forward (concat "^" header ":" + log-edit-header-contents-regexp) + nil t) + (if (setq val (not (string= (match-string 1) value))) + (replace-match line t t) + (replace-match "" t t nil 1)) + (insert line))) + (rfc822-goto-eoh) + (delete-horizontal-space) + (unless (looking-at "\n") + (insert "\n"))) + val)) + (defun log-edit-extract-headers (headers comment) "Extract headers from COMMENT to form command line arguments. -HEADERS should be an alist with elements of the form (HEADER . CMDARG) -associating header names to the corresponding cmdline option name and the -result is then a list of the form (MSG CMDARG1 HDRTEXT1 CMDARG2 HDRTEXT2...). -where MSG is the remaining text from STRING. -If \"Summary\" is not in HEADERS, then the \"Summary\" header is extracted -anyway and put back as the first line of MSG." +HEADERS should be an alist with elements (HEADER . CMDARG) +or (HEADER . FUNCTION) associating headers to command line +options and the result is then a list of the form (MSG ARGUMENTS...) +where MSG is the remaining text from COMMENT. +FUNCTION should be a function of one argument that takes the +header value and returns the list of strings to be appended to +ARGUMENTS. CMDARG will be added to ARGUMENTS followed by the +header value. If \"Summary\" is not in HEADERS, then the +\"Summary\" header is extracted anyway and put back as the first +line of MSG." (with-temp-buffer (insert comment) (rfc822-goto-eoh) @@ -923,8 +953,10 @@ anyway and put back as the first line of MSG." nil t) (if (eq t (cdr header)) (setq summary (match-string 1)) - (push (match-string 1) res) - (push (or (cdr header) (car header)) res)) + (if (functionp (cdr header)) + (setq res (nconc res (funcall (cdr header) (match-string 1)))) + (push (match-string 1) res) + (push (or (cdr header) (car header)) res))) (replace-match "" t t))) ;; Remove header separator if the header is empty. (widen) diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index ea9ce949ccb..5d7cb366e82 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -608,16 +608,52 @@ The car of the list is the current branch." (defun vc-git-unregister (file) (vc-git-command nil 0 file "rm" "-f" "--cached" "--")) +(declare-function log-edit-mode "log-edit" ()) +(declare-function log-edit-toggle-header "log-edit" (header value)) (declare-function log-edit-extract-headers "log-edit" (headers string)) +(defun vc-git-log-edit-toggle-signoff () + "Toggle whether to add the \"Signed-off-by\" line at the end of +the commit message." + (interactive) + (log-edit-toggle-header "Sign-Off" "yes")) + +(defun vc-git-log-edit-toggle-amend () + "Toggle whether this will amend the previous commit. +If toggling on, also insert its message into the buffer." + (interactive) + (when (log-edit-toggle-header "Amend" "yes") + (goto-char (point-max)) + (unless (bolp) (insert "\n")) + (insert (with-output-to-string + (vc-git-command + standard-output 1 nil + "log" "--max-count=1" "--pretty=format:%B" "HEAD"))))) + +(defvar vc-git-log-edit-mode-map + (let ((map (make-sparse-keymap "Git-Log-Edit"))) + (define-key map "\C-c\C-s" 'vc-git-log-edit-toggle-signoff) + (define-key map "\C-c\C-e" 'vc-git-log-edit-toggle-amend) + map)) + +(define-derived-mode vc-git-log-edit-mode log-edit-mode "Log-Edit/git" + "Major mode for editing Git log messages. +It is based on `log-edit-mode', and has Git-specific extensions.") + (defun vc-git-checkin (files _rev comment) (let ((coding-system-for-write vc-git-commits-coding-system)) - (apply 'vc-git-command nil 0 files - (nconc (list "commit" "-m") - (log-edit-extract-headers '(("Author" . "--author") - ("Date" . "--date")) - comment) - (list "--only" "--"))))) + (cl-flet ((boolean-arg-fn + (argument) + (lambda (value) (when (equal value "yes") (list argument))))) + (apply 'vc-git-command nil 0 files + (nconc (list "commit" "-m") + (log-edit-extract-headers + `(("Author" . "--author") + ("Date" . "--date") + ("Amend" . ,(boolean-arg-fn "--amend")) + ("Sign-Off" . ,(boolean-arg-fn "--signoff"))) + comment) + (list "--only" "--")))))) (defun vc-git-find-revision (file rev buffer) (let* (process-file-side-effects -- 2.11.4.GIT