{"thread":{"id":"14095","subject":"[PATCH/RFC] git.el: Commands for committing patches","startedAt":"2008-06-22T23:35:29Z","lastAt":"2008-06-24T20:44:56Z","messageCount":6,"participants":["Nikolaj Schumacher","Edward Z. Yang","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"80657","messageId":"m2prq9ujpa.fsf@nschum.de","threadId":"14095","inReplyTo":null,"subject":"[PATCH/RFC] git.el: Commands for committing patches","fromName":"Nikolaj Schumacher","fromEmail":"n_schumacher@web.de","sentAt":"2008-06-22T23:35:29Z","receivedAt":"2008-06-22T23:35:29Z","isPatch":true,"sender":{"key":"n_schumacher@web.de","avatar":null},"body":">From 8fb3c0b6c431bbe7c145882811b1419add34527b Mon Sep 17 00:00:00 2001\nFrom: Nikolaj Schumacher <git@nschum.de>\nDate: Mon, 23 Jun 2008 00:24:36 +0200\nSubject: [PATCH] git.el: Added command for committing patches.\n\nThis adds commands for committing patches from files, buffers and email\nbuffers.\n\nIn order to minimize code duplication, git-start-log-edit and\ngit-prepare-log-buffer have been extracted from git-commit-file.\n\nSigned-off-by: Nikolaj Schumacher <git@nschum.de>\n---\n contrib/emacs/git.el |  192 +++++++++++++++++++++++++++++++++++++++++++-------\n 1 files changed, 167 insertions(+), 25 deletions(-)\n\ndiff --git a/contrib/emacs/git.el b/contrib/emacs/git.el\nindex 4fa853f..c6df5aa 100644\n--- a/contrib/emacs/git.el\n+++ b/contrib/emacs/git.el\n@@ -189,6 +189,13 @@ if there is already one that displays the same directory.\"\n                                      process-environment)))\n     (apply #'call-process \"git\" nil buffer nil args)))\n \n+(defun git-call-process-env-on-region (buffer env beg end &rest args)\n+  \"Wrapper for `call-process-region' that sets environment strings.\"\n+  (let ((process-environment (append (git-get-env-strings env)\n+                                     process-environment)))\n+    (buffer-string)\n+    (apply #'call-process-region beg end \"git\" nil buffer nil args)))\n+\n (defun git-call-process-display-error (&rest args)\n   \"Wrapper for call-process that displays error messages.\"\n   (let* ((dir default-directory)\n@@ -315,6 +322,34 @@ and returns the process output as a string, or nil if the git failed.\"\n               \"\\\"\")\n     name))\n \n+(defun git-parse-email-address (email-address)\n+  \"Split the EMAIL-ADDRESS string into a cons of address and name.\"\n+  (if (string-match \"\\\\`\\\"?\\\\([^\\n<,\\\"]+\\\\)\\\"?[ \\t]<\\\\([^ @]+@[^ \\n\\t]+\\\\)>\\\\'\"\n+                    email-address)\n+      (cons (match-string-no-properties 2 email-address)\n+            (match-string-no-properties 1 email-address))\n+    (when (string-match\n+           \"\\\\`\\\\([^ \\t\\n@]+@[^ \\t\\n,]+\\\\)\\\\([ \\t](\\\\([^)]*\\\\))\\\\)?\"\n+           email-address)\n+      (cons (match-string-no-properties 1 email-address)\n+            (match-string-no-properties 3 email-address)))))\n+\n+(defun git-find-patch (&optional start)\n+  \"Find the patch in the current buffer.\"\n+  (save-excursion\n+    (goto-char (or start (point-min)))\n+    (when (re-search-forward \"^\\\\(---$\\\\|diff -\\\\|Index: \\\\)\" nil t)\n+      (match-beginning 0))))\n+\n+(defun git-fetch-header (header limit)\n+  (save-excursion\n+    (goto-char limit)\n+    (let ((case-fold-search t))\n+      (when (re-search-backward (concat \"^\" (regexp-quote header)\n+                                        \"[ \\t]*:[ \\t]*\")\n+                                nil t)\n+        (buffer-substring-no-properties (match-end 0) (point-at-eol))))))\n+\n (defun git-success-message (text files)\n   \"Print a success message after having handled FILES.\"\n   (let ((n (length files)))\n@@ -891,6 +926,58 @@ Return the list of files that haven't been handled.\"\n                 (message \"No files to commit.\")))\n           (delete-file index-file))))))\n \n+(defun git-apply-patch-to-index (index-file patch &optional beg end)\n+  \"Run git-apply on a patch.\"\n+  (with-temp-buffer\n+    (let ((env (and index-file `((\"GIT_INDEX_FILE\" . ,index-file))))\n+          (temp-buffer (current-buffer))\n+          res)\n+      (if (stringp patch)\n+          (if (file-exists-p patch)\n+              (setq res (git-call-process-env temp-buffer env \"apply\"\n+                                              \"--cached\"\n+                                              (expand-file-name patch)))\n+            (error \"Patch file disappeared\"))\n+        (if (buffer-live-p patch)\n+            (setq res (with-current-buffer patch\n+                        (git-call-process-env-on-region\n+                         temp-buffer env (or beg (point-min))\n+                         (or end (point-max)) \"apply\" \"--cached\" \"-\")))\n+          (error \"Patch buffer disappeared\")))\n+      (unless (= 0 res)\n+        (error \"Applying patch failed:\\n%s\" (buffer-string))))))\n+\n+(defun git-do-commit-patch (patch &optional beg end)\n+  \"Actually commit the patch using the current buffer as log message.\"\n+  (interactive)\n+  (let ((buffer (current-buffer))\n+        (index-file (make-temp-file \"gitidx\")))\n+    (with-current-buffer log-edit-parent-buffer\n+      (unwind-protect\n+          (let (head parent head-tree)\n+            (unless (git-empty-db-p)\n+              (setq head (git-rev-parse \"HEAD\")\n+                    head-tree (git-rev-parse \"HEAD^{tree}\")))\n+            (message \"Running git commit...\")\n+            (git-read-tree head-tree index-file)\n+            ;; Update both the default index and the temporary one.\n+            (git-apply-patch-to-index index-file patch beg end)\n+            (git-apply-patch-to-index nil patch beg end)\n+            (let* ((tree (git-write-tree index-file))\n+                   (commit (git-commit-tree buffer tree head)))\n+              (when commit\n+                (condition-case nil (delete-file \".git/MERGE_HEAD\") (error nil)\n+                (condition-case nil (delete-file \".git/MERGE_MSG\") (error nil))\n+                (with-current-buffer buffer (erase-buffer))\n+                (git-call-process-env nil nil \"rerere\")\n+                (git-call-process-env nil nil \"gc\" \"--auto\")\n+                (git-refresh-status)\n+                (git-refresh-ewoc-hf git-status)\n+                (message \"Committed %s.\" commit)\n+                (git-run-hook \"post-commit\" nil)))\n+            t)\n+        (delete-file index-file)\n+        nil)))))\n \n ;;;; Interactive functions\n ;;;; ------------------------------------------------------------\n@@ -1263,36 +1350,41 @@ Return the list of files that haven't been handled.\"\n       (when sign-off (git-append-sign-off committer-name committer-email)))\n     buffer))\n \n+(defun git-start-log-edit (buffer action)\n+  (if (boundp 'log-edit-diff-function)\n+      (log-edit action nil '((log-edit-listfun . git-log-edit-files)\n+                             (log-edit-diff-function . git-log-edit-diff)) buffer)\n+    (log-edit action nil 'git-log-edit-files buffer))\n+  (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))\n+  (setq buffer-file-coding-system (git-get-commits-coding-system))\n+  (re-search-forward (regexp-quote (concat git-log-msg-separator \"\\n\")) nil t))\n+\n+(defun git-prepare-log-buffer ()\n+  (let ((buffer (get-buffer-create \"*git-commit*\"))\n+        author-name author-email subject date)\n+    (when (eq 0 (buffer-size buffer))\n+      (when (file-readable-p \".dotest/info\")\n+        (with-temp-buffer\n+          (insert-file-contents \".dotest/info\")\n+          (goto-char (point-min))\n+          (when (re-search-forward \"^Author: \\\\(.*\\\\)\\nEmail: \\\\(.*\\\\)$\" nil t)\n+            (setq author-name (match-string 1))\n+            (setq author-email (match-string 2)))\n+          (goto-char (point-min))\n+          (when (re-search-forward \"^Subject: \\\\(.*\\\\)$\" nil t)\n+            (setq subject (match-string 1)))\n+          (goto-char (point-min))\n+          (when (re-search-forward \"^Date: \\\\(.*\\\\)$\" nil t)\n+            (setq date (match-string 1)))))\n+      (git-setup-log-buffer buffer author-name author-email subject date))\n+    buffer))\n+\n (defun git-commit-file ()\n   \"Commit the marked file(s), asking for a commit message.\"\n   (interactive)\n   (unless git-status (error \"Not in git-status buffer.\"))\n   (when (git-run-pre-commit-hook)\n-    (let ((buffer (get-buffer-create \"*git-commit*\"))\n-          (coding-system (git-get-commits-coding-system))\n-          author-name author-email subject date)\n-      (when (eq 0 (buffer-size buffer))\n-        (when (file-readable-p \".dotest/info\")\n-          (with-temp-buffer\n-            (insert-file-contents \".dotest/info\")\n-            (goto-char (point-min))\n-            (when (re-search-forward \"^Author: \\\\(.*\\\\)\\nEmail: \\\\(.*\\\\)$\" nil t)\n-              (setq author-name (match-string 1))\n-              (setq author-email (match-string 2)))\n-            (goto-char (point-min))\n-            (when (re-search-forward \"^Subject: \\\\(.*\\\\)$\" nil t)\n-              (setq subject (match-string 1)))\n-            (goto-char (point-min))\n-            (when (re-search-forward \"^Date: \\\\(.*\\\\)$\" nil t)\n-              (setq date (match-string 1)))))\n-        (git-setup-log-buffer buffer author-name author-email subject date))\n-      (if (boundp 'log-edit-diff-function)\n-\t  (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files)\n-\t\t\t\t\t (log-edit-diff-function . git-log-edit-diff)) buffer)\n-\t(log-edit 'git-do-commit nil 'git-log-edit-files buffer))\n-      (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))\n-      (setq buffer-file-coding-system coding-system)\n-      (re-search-forward (regexp-quote (concat git-log-msg-separator \"\\n\")) nil t))))\n+    (git-start-log-edit (git-prepare-log-buffer) 'git-do-commit)))\n \n (defun git-setup-commit-buffer (commit)\n   \"Setup the commit buffer with the contents of COMMIT.\"\n@@ -1340,6 +1432,56 @@ amended version of it.\"\n       (git-setup-commit-buffer commit)\n       (git-commit-file))))\n \n+(defun git-commit-patch (patch)\n+  \"Commit the patch in file PATCH, asking for a commit message.\"\n+  (interactive \"fPatch file: \")\n+  (unless git-status (error \"Not in git-status buffer.\"))\n+  (when (git-run-pre-commit-hook)\n+    (git-start-log-edit (git-prepare-log-buffer)\n+                        `(lambda ()\n+                           (interactive)\n+                           (git-do-commit-patch ,patch)))))\n+\n+(defun git-commit-patch-buffer (patch)\n+  \"Commit the patch in buffer PATCH, asking for a commit message.\"\n+  (interactive \"bPatch buffer\")\n+  (when (stringp patch)\n+    (setq patch (get-buffer patch)))\n+  (unless git-status (error \"Not in git-status buffer.\"))\n+  (when (git-run-pre-commit-hook)\n+    (git-start-log-edit (git-prepare-log-buffer)\n+                        `(lambda ()\n+                           (interactive)\n+                           (git-do-commit-patch ,patch)))))\n+\n+(defun git-commit-email-patch (email)\n+  \"Commit the patch in the email in buffer EMAIL.\"\n+  (interactive \"bEmail buffer\")\n+  (require 'message)\n+  (when (stringp email)\n+    (setq email (get-buffer email)))\n+  (let ((buffer (get-buffer-create \"*git-commit-patch*\"))\n+        author-name author-email email-subject subject date\n+        body-start patch-start)\n+    (with-current-buffer email\n+      (setq body-start (save-excursion (message-goto-body))\n+            patch-start (git-find-patch body-start))\n+      (let ((address (git-parse-email-address (message-fetch-field \"From\")))\n+            (email-subject (git-fetch-header \"Subject\" patch-start)))\n+        (setq author-name (car address)\n+              author-email (cdr address)\n+              subject (when (string-match \"\\\\`\\\\[PATCH[^]]*\\\\]\\s *\"\n+                                          email-subject)\n+                        (substring email-subject (match-end 0)))\n+              date (message-fetch-field \"Date\")\n+              msg (buffer-substring body-start patch-start))))\n+    (when (git-run-pre-commit-hook)\n+      (git-setup-log-buffer buffer author-name author-email subject date msg)\n+      (git-start-log-edit buffer\n+                          `(lambda ()\n+                             (interactive)\n+                             (git-do-commit-patch ,email ,patch-start))))))\n+\n (defun git-find-file ()\n   \"Visit the current file in its own buffer.\"\n   (interactive)\n-- \n1.5.5.3\n"},{"id":"80666","messageId":"485F0388.4080907@thewritingpot.com","threadId":"14095","inReplyTo":"m2prq9ujpa.fsf@nschum.de","subject":"Re: [PATCH/RFC] git.el: Commands for committing patches","fromName":"Edward Z. Yang","fromEmail":"edwardzyang@thewritingpot.com","sentAt":"2008-06-23T01:59:36Z","receivedAt":"2008-06-23T01:59:36Z","isPatch":true,"sender":{"key":"edwardzyang@thewritingpot.com","avatar":"https://gravatar.com/avatar/a805a0a3c1d7d36e7fe22270596e4d812723652933c59cac267e67c79126fdd0?d=mp&s=160"},"body":"-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA1\n\nNikolaj Schumacher wrote:\n[blank]\n\nAs per Documentation/SubmittingPatches, we'd appreciate it if you\nsubmitted the patch inline. Thanks!\n\n- From SubmittingPatches:\n> People on the git mailing list need to be able to read and\n> comment on the changes you are submitting.  It is important for\n> a developer to be able to \"quote\" your changes, using standard\n> e-mail tools, so that they may comment on specific portions of\n> your code.  For this reason, all patches should be submitted\n> \"inline\".  WARNING: Be wary of your MUAs word-wrap\n> corrupting your patch.  Do not cut-n-paste your patch; you can\n> lose tabs that way if you are not careful.\n-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1.4.7 (MingW32)\nComment: Using GnuPG with Mozilla - http://enigmail.mozdev.org\n\niD8DBQFIXwOIqTO+fYacSNoRAt+kAJ9AF8D560SV9liqQ1XTPM0g1i56PgCfVP19\nJ5FXtNyDj/CEYxolhO0rKSk=\n=RNRN\n-----END PGP SIGNATURE-----\n"},{"id":"80682","messageId":"m2abhcvcil.fsf@nschum.de","threadId":"14095","inReplyTo":"485F0388.4080907@thewritingpot.com","subject":"Re: [PATCH/RFC] git.el: Commands for committing patches","fromName":"Nikolaj Schumacher","fromEmail":"n_schumacher@web.de","sentAt":"2008-06-23T07:25:22Z","receivedAt":"2008-06-23T07:25:22Z","isPatch":true,"sender":{"key":"n_schumacher@web.de","avatar":null},"body":"\"Edward Z. Yang\" <edwardzyang@thewritingpot.com> wrote:\n\n> As per Documentation/SubmittingPatches, we'd appreciate it if you\n> submitted the patch inline. Thanks!\n\nI'm sorry, I fully intended to.  I was mistaken what disposition inline\nmeant in Gnus.  It looked as intended in my tests.\n\nI hope v2 is correct in that regard.\n\n\nregards,\nNikolaj Schumacher\n"},{"id":"80684","messageId":"m263s0vbri.fsf_-_@nschum.de","threadId":"14095","inReplyTo":"m2abhcvcil.fsf@nschum.de","subject":"[PATCH v2/RFC] git.el: Commands for committing patches","fromName":"Nikolaj Schumacher","fromEmail":"n_schumacher@web.de","sentAt":"2008-06-23T07:41:37Z","receivedAt":"2008-06-23T07:41:37Z","isPatch":true,"sender":{"key":"n_schumacher@web.de","avatar":null},"body":">From 609f6fca6c70919036d41e1e4034b6e4de2e7ea2 Mon Sep 17 00:00:00 2001\nFrom: Nikolaj Schumacher <git@nschum.de>\nDate: Mon, 23 Jun 2008 09:34:14 +0200\nSubject: [PATCH] git.el: Added command for committing patches.\n\nThis adds commands for committing patches from files, buffers and email\nbuffers.\n\nIn order to minimize code duplication, git-start-log-edit and\ngit-prepare-log-buffer have been extracted from git-commit-file.\n\nSigned-off-by: Nikolaj Schumacher <git@nschum.de>\n---\n contrib/emacs/git.el |  200 +++++++++++++++++++++++++++++++++++++++++++------\n 1 files changed, 175 insertions(+), 25 deletions(-)\n\ndiff --git a/contrib/emacs/git.el b/contrib/emacs/git.el\nindex 4fa853f..a6c776d 100644\n--- a/contrib/emacs/git.el\n+++ b/contrib/emacs/git.el\n@@ -189,6 +189,13 @@ if there is already one that displays the same directory.\"\n                                      process-environment)))\n     (apply #'call-process \"git\" nil buffer nil args)))\n \n+(defun git-call-process-env-on-region (buffer env beg end &rest args)\n+  \"Wrapper for `call-process-region' that sets environment strings.\"\n+  (let ((process-environment (append (git-get-env-strings env)\n+                                     process-environment)))\n+    (buffer-string)\n+    (apply #'call-process-region beg end \"git\" nil buffer nil args)))\n+\n (defun git-call-process-display-error (&rest args)\n   \"Wrapper for call-process that displays error messages.\"\n   (let* ((dir default-directory)\n@@ -315,6 +322,42 @@ and returns the process output as a string, or nil if the git failed.\"\n               \"\\\"\")\n     name))\n \n+(defun git-parse-email-address (email-address)\n+  \"Split the EMAIL-ADDRESS string into a cons of address and name.\"\n+  (if (string-match \"\\\\`\\\"?\\\\([^\\n<,\\\"]+\\\\)\\\"?[ \\t]<\\\\([^ @]+@[^ \\n\\t]+\\\\)>\\\\'\"\n+                    email-address)\n+      (cons (match-string-no-properties 2 email-address)\n+            (match-string-no-properties 1 email-address))\n+    (when (string-match\n+           \"\\\\`\\\\([^ \\t\\n@]+@[^ \\t\\n,]+\\\\)\\\\([ \\t](\\\\([^)]*\\\\))\\\\)?\"\n+           email-address)\n+      (cons (match-string-no-properties 1 email-address)\n+            (match-string-no-properties 3 email-address)))))\n+\n+(defun git-find-patch (&optional start)\n+  \"Find the patch start in the current buffer.\"\n+  (save-excursion\n+    (goto-char (or start (point-min)))\n+    (when (re-search-forward \"^\\\\(---$\\\\|diff -\\\\|Index: \\\\)\" nil t)\n+      (match-beginning 0))))\n+\n+(defun git-find-message ()\n+  \"Find the start of the commit message in an email buffer.\"\n+  (require 'message)\n+  (save-excursion\n+    (message-goto-body)\n+    (search-forward-regexp \"[^:]+:\\\\([^\\n]\\\\|\\n[ \\t]\\\\)+\\n\\n\" nil t)\n+    (point)))\n+\n+(defun git-fetch-header (header limit)\n+  (save-excursion\n+    (goto-char limit)\n+    (let ((case-fold-search t))\n+      (when (re-search-backward (concat \"^\" (regexp-quote header)\n+                                        \"[ \\t]*:[ \\t]*\")\n+                                nil t)\n+        (buffer-substring-no-properties (match-end 0) (point-at-eol))))))\n+\n (defun git-success-message (text files)\n   \"Print a success message after having handled FILES.\"\n   (let ((n (length files)))\n@@ -891,6 +934,58 @@ Return the list of files that haven't been handled.\"\n                 (message \"No files to commit.\")))\n           (delete-file index-file))))))\n \n+(defun git-apply-patch-to-index (index-file patch &optional beg end)\n+  \"Run git-apply on a patch.\"\n+  (with-temp-buffer\n+    (let ((env (and index-file `((\"GIT_INDEX_FILE\" . ,index-file))))\n+          (temp-buffer (current-buffer))\n+          res)\n+      (if (stringp patch)\n+          (if (file-exists-p patch)\n+              (setq res (git-call-process-env temp-buffer env \"apply\"\n+                                              \"--cached\"\n+                                              (expand-file-name patch)))\n+            (error \"Patch file disappeared\"))\n+        (if (buffer-live-p patch)\n+            (setq res (with-current-buffer patch\n+                        (git-call-process-env-on-region\n+                         temp-buffer env (or beg (point-min))\n+                         (or end (point-max)) \"apply\" \"--cached\" \"-\")))\n+          (error \"Patch buffer disappeared\")))\n+      (unless (= 0 res)\n+        (error \"Applying patch failed:\\n%s\" (buffer-string))))))\n+\n+(defun git-do-commit-patch (patch &optional beg end)\n+  \"Actually commit the patch using the current buffer as log message.\"\n+  (interactive)\n+  (let ((buffer (current-buffer))\n+        (index-file (make-temp-file \"gitidx\")))\n+    (with-current-buffer log-edit-parent-buffer\n+      (unwind-protect\n+          (let (head parent head-tree)\n+            (unless (git-empty-db-p)\n+              (setq head (git-rev-parse \"HEAD\")\n+                    head-tree (git-rev-parse \"HEAD^{tree}\")))\n+            (message \"Running git commit...\")\n+            (git-read-tree head-tree index-file)\n+            ;; Update both the default index and the temporary one.\n+            (git-apply-patch-to-index index-file patch beg end)\n+            (git-apply-patch-to-index nil patch beg end)\n+            (let* ((tree (git-write-tree index-file))\n+                   (commit (git-commit-tree buffer tree head)))\n+              (when commit\n+                (condition-case nil (delete-file \".git/MERGE_HEAD\") (error nil)\n+                (condition-case nil (delete-file \".git/MERGE_MSG\") (error nil))\n+                (with-current-buffer buffer (erase-buffer))\n+                (git-call-process-env nil nil \"rerere\")\n+                (git-call-process-env nil nil \"gc\" \"--auto\")\n+                (git-refresh-status)\n+                (git-refresh-ewoc-hf git-status)\n+                (message \"Committed %s.\" commit)\n+                (git-run-hook \"post-commit\" nil)))\n+            t)\n+        (delete-file index-file)\n+        nil)))))\n \n ;;;; Interactive functions\n ;;;; ------------------------------------------------------------\n@@ -1263,36 +1358,41 @@ Return the list of files that haven't been handled.\"\n       (when sign-off (git-append-sign-off committer-name committer-email)))\n     buffer))\n \n+(defun git-start-log-edit (buffer action)\n+  (if (boundp 'log-edit-diff-function)\n+      (log-edit action nil '((log-edit-listfun . git-log-edit-files)\n+                             (log-edit-diff-function . git-log-edit-diff)) buffer)\n+    (log-edit action nil 'git-log-edit-files buffer))\n+  (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))\n+  (setq buffer-file-coding-system (git-get-commits-coding-system))\n+  (re-search-forward (regexp-quote (concat git-log-msg-separator \"\\n\")) nil t))\n+\n+(defun git-prepare-log-buffer ()\n+  (let ((buffer (get-buffer-create \"*git-commit*\"))\n+        author-name author-email subject date)\n+    (when (eq 0 (buffer-size buffer))\n+      (when (file-readable-p \".dotest/info\")\n+        (with-temp-buffer\n+          (insert-file-contents \".dotest/info\")\n+          (goto-char (point-min))\n+          (when (re-search-forward \"^Author: \\\\(.*\\\\)\\nEmail: \\\\(.*\\\\)$\" nil t)\n+            (setq author-name (match-string 1))\n+            (setq author-email (match-string 2)))\n+          (goto-char (point-min))\n+          (when (re-search-forward \"^Subject: \\\\(.*\\\\)$\" nil t)\n+            (setq subject (match-string 1)))\n+          (goto-char (point-min))\n+          (when (re-search-forward \"^Date: \\\\(.*\\\\)$\" nil t)\n+            (setq date (match-string 1)))))\n+      (git-setup-log-buffer buffer author-name author-email subject date))\n+    buffer))\n+\n (defun git-commit-file ()\n   \"Commit the marked file(s), asking for a commit message.\"\n   (interactive)\n   (unless git-status (error \"Not in git-status buffer.\"))\n   (when (git-run-pre-commit-hook)\n-    (let ((buffer (get-buffer-create \"*git-commit*\"))\n-          (coding-system (git-get-commits-coding-system))\n-          author-name author-email subject date)\n-      (when (eq 0 (buffer-size buffer))\n-        (when (file-readable-p \".dotest/info\")\n-          (with-temp-buffer\n-            (insert-file-contents \".dotest/info\")\n-            (goto-char (point-min))\n-            (when (re-search-forward \"^Author: \\\\(.*\\\\)\\nEmail: \\\\(.*\\\\)$\" nil t)\n-              (setq author-name (match-string 1))\n-              (setq author-email (match-string 2)))\n-            (goto-char (point-min))\n-            (when (re-search-forward \"^Subject: \\\\(.*\\\\)$\" nil t)\n-              (setq subject (match-string 1)))\n-            (goto-char (point-min))\n-            (when (re-search-forward \"^Date: \\\\(.*\\\\)$\" nil t)\n-              (setq date (match-string 1)))))\n-        (git-setup-log-buffer buffer author-name author-email subject date))\n-      (if (boundp 'log-edit-diff-function)\n-\t  (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files)\n-\t\t\t\t\t (log-edit-diff-function . git-log-edit-diff)) buffer)\n-\t(log-edit 'git-do-commit nil 'git-log-edit-files buffer))\n-      (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))\n-      (setq buffer-file-coding-system coding-system)\n-      (re-search-forward (regexp-quote (concat git-log-msg-separator \"\\n\")) nil t))))\n+    (git-start-log-edit (git-prepare-log-buffer) 'git-do-commit)))\n \n (defun git-setup-commit-buffer (commit)\n   \"Setup the commit buffer with the contents of COMMIT.\"\n@@ -1340,6 +1440,56 @@ amended version of it.\"\n       (git-setup-commit-buffer commit)\n       (git-commit-file))))\n \n+(defun git-commit-patch (patch)\n+  \"Commit the patch in file PATCH, asking for a commit message.\"\n+  (interactive \"fPatch file: \")\n+  (unless git-status (error \"Not in git-status buffer.\"))\n+  (when (git-run-pre-commit-hook)\n+    (git-start-log-edit (git-prepare-log-buffer)\n+                        `(lambda ()\n+                           (interactive)\n+                           (git-do-commit-patch ,patch)))))\n+\n+(defun git-commit-patch-buffer (patch)\n+  \"Commit the patch in buffer PATCH, asking for a commit message.\"\n+  (interactive \"bPatch buffer\")\n+  (when (stringp patch)\n+    (setq patch (get-buffer patch)))\n+  (unless git-status (error \"Not in git-status buffer.\"))\n+  (when (git-run-pre-commit-hook)\n+    (git-start-log-edit (git-prepare-log-buffer)\n+                        `(lambda ()\n+                           (interactive)\n+                           (git-do-commit-patch ,patch)))))\n+\n+(defun git-commit-email-patch (email)\n+  \"Commit the patch in the email in buffer EMAIL.\"\n+  (interactive \"bEmail buffer\")\n+  (when (stringp email)\n+    (setq email (get-buffer email)))\n+  (let ((buffer (get-buffer-create \"*git-commit-patch*\"))\n+        author-name author-email email-subject subject date\n+        body-start patch-start)\n+    (with-current-buffer email\n+      (setq body-start (git-find-message)\n+            patch-start (git-find-patch body-start))\n+      (let ((address (git-parse-email-address\n+                      (git-fetch-header \"From\" patch-start)))\n+            (email-subject (git-fetch-header \"Subject\" patch-start)))\n+        (setq author-name (car address)\n+              author-email (cdr address)\n+              subject (when (string-match \"\\\\`\\\\[PATCH[^]]*\\\\]\\s *\"\n+                                          email-subject)\n+                        (substring email-subject (match-end 0)))\n+              date (git-fetch-header \"Date\" patch-start)\n+              msg (buffer-substring body-start patch-start))))\n+    (when (git-run-pre-commit-hook)\n+      (git-setup-log-buffer buffer author-name author-email subject date msg)\n+      (git-start-log-edit buffer\n+                          `(lambda ()\n+                             (interactive)\n+                             (git-do-commit-patch ,email ,patch-start))))))\n+\n (defun git-find-file ()\n   \"Visit the current file in its own buffer.\"\n   (interactive)\n-- \n1.5.5.3\n"},{"id":"80774","messageId":"7vlk0vhmeb.fsf@gitster.siamese.dyndns.org","threadId":"14095","inReplyTo":"m263s0vbri.fsf_-_@nschum.de","subject":"Re: [PATCH v2/RFC] git.el: Commands for committing patches","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-23T21:27:56Z","receivedAt":"2008-06-23T21:27:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nikolaj Schumacher <n_schumacher@web.de> writes:\n\n> From 609f6fca6c70919036d41e1e4034b6e4de2e7ea2 Mon Sep 17 00:00:00 2001\n> From: Nikolaj Schumacher <git@nschum.de>\n> Date: Mon, 23 Jun 2008 09:34:14 +0200\n> Subject: [PATCH] git.el: Added command for committing patches.\n>\n> This adds commands for committing patches from files, buffers and email\n> buffers.\n>\n> In order to minimize code duplication, git-start-log-edit and\n> git-prepare-log-buffer have been extracted from git-commit-file.\n>\n> Signed-off-by: Nikolaj Schumacher <git@nschum.de>\n> ---\n>  contrib/emacs/git.el |  200 +++++++++++++++++++++++++++++++++++++++++++------\n>  1 files changed, 175 insertions(+), 25 deletions(-)\n\nI do not use things in contrib/emacs/git.el myself, but this looks like a\nlot of code to do what \"M-| git am <Enter>\" already does...\n"},{"id":"80982","messageId":"m2r6amtvef.fsf@nschum.de","threadId":"14095","inReplyTo":"7vlk0vhmeb.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v2/RFC] git.el: Commands for committing patches","fromName":"Nikolaj Schumacher","fromEmail":"n_schumacher@web.de","sentAt":"2008-06-24T20:44:56Z","receivedAt":"2008-06-24T20:44:56Z","isPatch":true,"sender":{"key":"n_schumacher@web.de","avatar":null},"body":"Junio C Hamano <gitster@pobox.com> wrote:\n\n> I do not use things in contrib/emacs/git.el myself, but this looks like a\n> lot of code to do what \"M-| git am <Enter>\" already does...\n\nI admit committing email buffers might be unnecessary.  I consider it\nmore of a cherry-on-top.  The important part is committing patches,\nbecause it gives way to partial commits (below file level).\n\n\nregards,\nNikolaj Schumacher\n"}]}