From: Nikolaj Schumacher <n_schumacher@web.de>
To: git@vger.kernel.org
Cc: Alexandre Julliard <julliard@winehq.org>
Subject: [PATCH v2/RFC] git.el: Commands for committing patches
Date: Mon, 23 Jun 2008 09:41:37 +0200 [thread overview]
Message-ID: <m263s0vbri.fsf_-_@nschum.de> (raw)
In-Reply-To: <m2abhcvcil.fsf@nschum.de> (Nikolaj Schumacher's message of "Mon\, 23 Jun 2008 09\:25\:22 +0200")
>From 609f6fca6c70919036d41e1e4034b6e4de2e7ea2 Mon Sep 17 00:00:00 2001
From: Nikolaj Schumacher <git@nschum.de>
Date: Mon, 23 Jun 2008 09:34:14 +0200
Subject: [PATCH] git.el: Added command for committing patches.
This adds commands for committing patches from files, buffers and email
buffers.
In order to minimize code duplication, git-start-log-edit and
git-prepare-log-buffer have been extracted from git-commit-file.
Signed-off-by: Nikolaj Schumacher <git@nschum.de>
---
contrib/emacs/git.el | 200 +++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 175 insertions(+), 25 deletions(-)
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index 4fa853f..a6c776d 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -189,6 +189,13 @@ if there is already one that displays the same directory."
process-environment)))
(apply #'call-process "git" nil buffer nil args)))
+(defun git-call-process-env-on-region (buffer env beg end &rest args)
+ "Wrapper for `call-process-region' that sets environment strings."
+ (let ((process-environment (append (git-get-env-strings env)
+ process-environment)))
+ (buffer-string)
+ (apply #'call-process-region beg end "git" nil buffer nil args)))
+
(defun git-call-process-display-error (&rest args)
"Wrapper for call-process that displays error messages."
(let* ((dir default-directory)
@@ -315,6 +322,42 @@ and returns the process output as a string, or nil if the git failed."
"\"")
name))
+(defun git-parse-email-address (email-address)
+ "Split the EMAIL-ADDRESS string into a cons of address and name."
+ (if (string-match "\\`\"?\\([^\n<,\"]+\\)\"?[ \t]<\\([^ @]+@[^ \n\t]+\\)>\\'"
+ email-address)
+ (cons (match-string-no-properties 2 email-address)
+ (match-string-no-properties 1 email-address))
+ (when (string-match
+ "\\`\\([^ \t\n@]+@[^ \t\n,]+\\)\\([ \t](\\([^)]*\\))\\)?"
+ email-address)
+ (cons (match-string-no-properties 1 email-address)
+ (match-string-no-properties 3 email-address)))))
+
+(defun git-find-patch (&optional start)
+ "Find the patch start in the current buffer."
+ (save-excursion
+ (goto-char (or start (point-min)))
+ (when (re-search-forward "^\\(---$\\|diff -\\|Index: \\)" nil t)
+ (match-beginning 0))))
+
+(defun git-find-message ()
+ "Find the start of the commit message in an email buffer."
+ (require 'message)
+ (save-excursion
+ (message-goto-body)
+ (search-forward-regexp "[^:]+:\\([^\n]\\|\n[ \t]\\)+\n\n" nil t)
+ (point)))
+
+(defun git-fetch-header (header limit)
+ (save-excursion
+ (goto-char limit)
+ (let ((case-fold-search t))
+ (when (re-search-backward (concat "^" (regexp-quote header)
+ "[ \t]*:[ \t]*")
+ nil t)
+ (buffer-substring-no-properties (match-end 0) (point-at-eol))))))
+
(defun git-success-message (text files)
"Print a success message after having handled FILES."
(let ((n (length files)))
@@ -891,6 +934,58 @@ Return the list of files that haven't been handled."
(message "No files to commit.")))
(delete-file index-file))))))
+(defun git-apply-patch-to-index (index-file patch &optional beg end)
+ "Run git-apply on a patch."
+ (with-temp-buffer
+ (let ((env (and index-file `(("GIT_INDEX_FILE" . ,index-file))))
+ (temp-buffer (current-buffer))
+ res)
+ (if (stringp patch)
+ (if (file-exists-p patch)
+ (setq res (git-call-process-env temp-buffer env "apply"
+ "--cached"
+ (expand-file-name patch)))
+ (error "Patch file disappeared"))
+ (if (buffer-live-p patch)
+ (setq res (with-current-buffer patch
+ (git-call-process-env-on-region
+ temp-buffer env (or beg (point-min))
+ (or end (point-max)) "apply" "--cached" "-")))
+ (error "Patch buffer disappeared")))
+ (unless (= 0 res)
+ (error "Applying patch failed:\n%s" (buffer-string))))))
+
+(defun git-do-commit-patch (patch &optional beg end)
+ "Actually commit the patch using the current buffer as log message."
+ (interactive)
+ (let ((buffer (current-buffer))
+ (index-file (make-temp-file "gitidx")))
+ (with-current-buffer log-edit-parent-buffer
+ (unwind-protect
+ (let (head parent head-tree)
+ (unless (git-empty-db-p)
+ (setq head (git-rev-parse "HEAD")
+ head-tree (git-rev-parse "HEAD^{tree}")))
+ (message "Running git commit...")
+ (git-read-tree head-tree index-file)
+ ;; Update both the default index and the temporary one.
+ (git-apply-patch-to-index index-file patch beg end)
+ (git-apply-patch-to-index nil patch beg end)
+ (let* ((tree (git-write-tree index-file))
+ (commit (git-commit-tree buffer tree head)))
+ (when commit
+ (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)
+ (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
+ (with-current-buffer buffer (erase-buffer))
+ (git-call-process-env nil nil "rerere")
+ (git-call-process-env nil nil "gc" "--auto")
+ (git-refresh-status)
+ (git-refresh-ewoc-hf git-status)
+ (message "Committed %s." commit)
+ (git-run-hook "post-commit" nil)))
+ t)
+ (delete-file index-file)
+ nil)))))
;;;; Interactive functions
;;;; ------------------------------------------------------------
@@ -1263,36 +1358,41 @@ Return the list of files that haven't been handled."
(when sign-off (git-append-sign-off committer-name committer-email)))
buffer))
+(defun git-start-log-edit (buffer action)
+ (if (boundp 'log-edit-diff-function)
+ (log-edit action nil '((log-edit-listfun . git-log-edit-files)
+ (log-edit-diff-function . git-log-edit-diff)) buffer)
+ (log-edit action nil 'git-log-edit-files buffer))
+ (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
+ (setq buffer-file-coding-system (git-get-commits-coding-system))
+ (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))
+
+(defun git-prepare-log-buffer ()
+ (let ((buffer (get-buffer-create "*git-commit*"))
+ author-name author-email subject date)
+ (when (eq 0 (buffer-size buffer))
+ (when (file-readable-p ".dotest/info")
+ (with-temp-buffer
+ (insert-file-contents ".dotest/info")
+ (goto-char (point-min))
+ (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
+ (setq author-name (match-string 1))
+ (setq author-email (match-string 2)))
+ (goto-char (point-min))
+ (when (re-search-forward "^Subject: \\(.*\\)$" nil t)
+ (setq subject (match-string 1)))
+ (goto-char (point-min))
+ (when (re-search-forward "^Date: \\(.*\\)$" nil t)
+ (setq date (match-string 1)))))
+ (git-setup-log-buffer buffer author-name author-email subject date))
+ buffer))
+
(defun git-commit-file ()
"Commit the marked file(s), asking for a commit message."
(interactive)
(unless git-status (error "Not in git-status buffer."))
(when (git-run-pre-commit-hook)
- (let ((buffer (get-buffer-create "*git-commit*"))
- (coding-system (git-get-commits-coding-system))
- author-name author-email subject date)
- (when (eq 0 (buffer-size buffer))
- (when (file-readable-p ".dotest/info")
- (with-temp-buffer
- (insert-file-contents ".dotest/info")
- (goto-char (point-min))
- (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t)
- (setq author-name (match-string 1))
- (setq author-email (match-string 2)))
- (goto-char (point-min))
- (when (re-search-forward "^Subject: \\(.*\\)$" nil t)
- (setq subject (match-string 1)))
- (goto-char (point-min))
- (when (re-search-forward "^Date: \\(.*\\)$" nil t)
- (setq date (match-string 1)))))
- (git-setup-log-buffer buffer author-name author-email subject date))
- (if (boundp 'log-edit-diff-function)
- (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files)
- (log-edit-diff-function . git-log-edit-diff)) buffer)
- (log-edit 'git-do-commit nil 'git-log-edit-files buffer))
- (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
- (setq buffer-file-coding-system coding-system)
- (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
+ (git-start-log-edit (git-prepare-log-buffer) 'git-do-commit)))
(defun git-setup-commit-buffer (commit)
"Setup the commit buffer with the contents of COMMIT."
@@ -1340,6 +1440,56 @@ amended version of it."
(git-setup-commit-buffer commit)
(git-commit-file))))
+(defun git-commit-patch (patch)
+ "Commit the patch in file PATCH, asking for a commit message."
+ (interactive "fPatch file: ")
+ (unless git-status (error "Not in git-status buffer."))
+ (when (git-run-pre-commit-hook)
+ (git-start-log-edit (git-prepare-log-buffer)
+ `(lambda ()
+ (interactive)
+ (git-do-commit-patch ,patch)))))
+
+(defun git-commit-patch-buffer (patch)
+ "Commit the patch in buffer PATCH, asking for a commit message."
+ (interactive "bPatch buffer")
+ (when (stringp patch)
+ (setq patch (get-buffer patch)))
+ (unless git-status (error "Not in git-status buffer."))
+ (when (git-run-pre-commit-hook)
+ (git-start-log-edit (git-prepare-log-buffer)
+ `(lambda ()
+ (interactive)
+ (git-do-commit-patch ,patch)))))
+
+(defun git-commit-email-patch (email)
+ "Commit the patch in the email in buffer EMAIL."
+ (interactive "bEmail buffer")
+ (when (stringp email)
+ (setq email (get-buffer email)))
+ (let ((buffer (get-buffer-create "*git-commit-patch*"))
+ author-name author-email email-subject subject date
+ body-start patch-start)
+ (with-current-buffer email
+ (setq body-start (git-find-message)
+ patch-start (git-find-patch body-start))
+ (let ((address (git-parse-email-address
+ (git-fetch-header "From" patch-start)))
+ (email-subject (git-fetch-header "Subject" patch-start)))
+ (setq author-name (car address)
+ author-email (cdr address)
+ subject (when (string-match "\\`\\[PATCH[^]]*\\]\s *"
+ email-subject)
+ (substring email-subject (match-end 0)))
+ date (git-fetch-header "Date" patch-start)
+ msg (buffer-substring body-start patch-start))))
+ (when (git-run-pre-commit-hook)
+ (git-setup-log-buffer buffer author-name author-email subject date msg)
+ (git-start-log-edit buffer
+ `(lambda ()
+ (interactive)
+ (git-do-commit-patch ,email ,patch-start))))))
+
(defun git-find-file ()
"Visit the current file in its own buffer."
(interactive)
--
1.5.5.3
next prev parent reply other threads:[~2008-06-23 7:42 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-06-22 23:35 [PATCH/RFC] git.el: Commands for committing patches Nikolaj Schumacher
2008-06-23 1:59 ` Edward Z. Yang
2008-06-23 7:25 ` Nikolaj Schumacher
2008-06-23 7:41 ` Nikolaj Schumacher [this message]
2008-06-23 21:27 ` [PATCH v2/RFC] " Junio C Hamano
2008-06-24 20:44 ` Nikolaj Schumacher
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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=m263s0vbri.fsf_-_@nschum.de \
--to=n_schumacher@web.de \
--cc=git@vger.kernel.org \
--cc=julliard@winehq.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.
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 NNTP newsgroup(s).