git.el: More robust handling of subprocess errors when returning strings.
[git.git] / contrib / emacs / git.el
index 0b24b4d..cf650da 100644 (file)
@@ -44,7 +44,6 @@
 ;;  - switching branches
 ;;  - revlist browser
 ;;  - git-show-branch browser
-;;  - customize support
 ;;  - menus
 ;;
 
 (require 'ewoc)
 
 
-;;;; Faces
+;;;; Customizations
 ;;;; ------------------------------------------------------------
 
+(defgroup git nil
+  "Git user interface")
+
+(defcustom git-committer-name nil
+  "User name to use for commits.
+The default is to fall back to `add-log-full-name' and then `user-full-name'."
+  :group 'git
+  :type '(choice (const :tag "Default" nil)
+                 (string :tag "Name")))
+
+(defcustom git-committer-email nil
+  "Email address to use for commits.
+The default is to fall back to `add-log-mailing-address' and then `user-mail-address'."
+  :group 'git
+  :type '(choice (const :tag "Default" nil)
+                 (string :tag "Email")))
+
+(defcustom git-commits-coding-system 'utf-8
+  "Default coding system for the log message of git commits."
+  :group 'git
+  :type 'coding-system)
+
+(defcustom git-append-signed-off-by nil
+  "Whether to append a Signed-off-by line to the commit message before editing."
+  :group 'git
+  :type 'boolean)
+
+(defcustom git-per-dir-ignore-file ".gitignore"
+  "Name of the per-directory ignore file."
+  :group 'git
+  :type 'string)
+
 (defface git-status-face
   '((((class color) (background light)) (:foreground "purple")))
-  "Git mode face used to highlight added and modified files.")
+  "Git mode face used to highlight added and modified files."
+  :group 'git)
 
 (defface git-unmerged-face
   '((((class color) (background light)) (:foreground "red" :bold t)))
-  "Git mode face used to highlight unmerged files.")
+  "Git mode face used to highlight unmerged files."
+  :group 'git)
 
 (defface git-unknown-face
   '((((class color) (background light)) (:foreground "goldenrod" :bold t)))
-  "Git mode face used to highlight unknown files.")
+  "Git mode face used to highlight unknown files."
+  :group 'git)
 
 (defface git-uptodate-face
   '((((class color) (background light)) (:foreground "grey60")))
-  "Git mode face used to highlight up-to-date files.")
+  "Git mode face used to highlight up-to-date files."
+  :group 'git)
 
 (defface git-ignored-face
   '((((class color) (background light)) (:foreground "grey60")))
-  "Git mode face used to highlight ignored files.")
+  "Git mode face used to highlight ignored files."
+  :group 'git)
 
 (defface git-mark-face
   '((((class color) (background light)) (:foreground "red" :bold t)))
-  "Git mode face used for the file marks.")
+  "Git mode face used for the file marks."
+  :group 'git)
 
 (defface git-header-face
   '((((class color) (background light)) (:foreground "blue")))
-  "Git mode face used for commit headers.")
+  "Git mode face used for commit headers."
+  :group 'git)
 
 (defface git-separator-face
   '((((class color) (background light)) (:foreground "brown")))
-  "Git mode face used for commit separator.")
+  "Git mode face used for commit separator."
+  :group 'git)
 
 (defface git-permission-face
   '((((class color) (background light)) (:foreground "green" :bold t)))
-  "Git mode face used for permission changes.")
-
-(defvar git-committer-name nil
-  "*User name to use for commits.
-If not set, fall back to `add-log-full-name' and then `user-full-name'.")
-
-(defvar git-committer-email nil
-  "*Email address to use for commits.
-If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.")
-
-(defvar git-commits-coding-system 'utf-8
-  "Default coding system for git commits.")
-
-(defvar git-append-signed-off-by nil
-  "Whether to append a Signed-off-by line to the commit message.")
-
-(defconst git-log-msg-separator "--- log message follows this line ---")
-
-(defconst git-per-dir-ignore-file ".gitignore"
-  "Name of the per-directory ignore file.")
+  "Git mode face used for permission changes."
+  :group 'git)
 
 
 ;;;; Utilities
 ;;;; ------------------------------------------------------------
 
+(defconst git-log-msg-separator "--- log message follows this line ---")
+
 (defun git-get-env-strings (env)
   "Build a list of NAME=VALUE strings from a list of environment strings."
   (mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env))
@@ -125,6 +148,12 @@ If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.
              (append (git-get-env-strings env) (list "git") args))
     (apply #'call-process "git" nil buffer nil args)))
 
+(defun git-call-process-env-string (env &rest args)
+  "Wrapper for call-process that sets environment strings, and returns the process output as a string."
+  (with-temp-buffer
+    (and (eq 0 (apply #' git-call-process-env t env args))
+         (buffer-string))))
+
 (defun git-run-process-region (buffer start end program args)
   "Run a git process with a buffer region as input."
   (let ((output-buffer (current-buffer))
@@ -166,8 +195,9 @@ If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.
 
 (defun git-get-string-sha1 (string)
   "Read a SHA1 from the specified string."
-  (let ((pos (string-match "[0-9a-f]\\{40\\}" string)))
-    (and pos (substring string pos (match-end 0)))))
+  (and string
+       (string-match "[0-9a-f]\\{40\\}" string)
+       (match-string 0 string)))
 
 (defun git-get-committer-name ()
   "Return the name to use as GIT_COMMITTER_NAME."
@@ -236,18 +266,12 @@ If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.
 (defun git-rev-parse (rev)
   "Parse a revision name and return its SHA1."
   (git-get-string-sha1
-   (with-output-to-string
-     (with-current-buffer standard-output
-       (git-call-process-env t nil "rev-parse" rev)))))
+   (git-call-process-env-string nil "rev-parse" rev)))
 
 (defun git-symbolic-ref (ref)
   "Wrapper for the git-symbolic-ref command."
-  (car
-   (split-string
-    (with-output-to-string
-      (with-current-buffer standard-output
-        (git-call-process-env t nil "symbolic-ref" ref)))
-    "\n")))
+  (let ((str (git-call-process-env-string nil "symbolic-ref" ref)))
+    (and str (car (split-string str "\n")))))
 
 (defun git-update-ref (ref val &optional oldval)
   "Update a reference by calling git-update-ref."
@@ -262,11 +286,7 @@ If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.
 (defun git-write-tree (&optional index-file)
   "Call git-write-tree and return the resulting tree SHA1 as a string."
   (git-get-string-sha1
-   (with-output-to-string
-     (with-current-buffer standard-output
-       (git-call-process-env t
-        (if index-file `(("GIT_INDEX_FILE" . ,index-file)) nil)
-        "write-tree")))))
+   (git-call-process-env-string (and index-file `(("GIT_INDEX_FILE" . ,index-file))) "write-tree")))
 
 (defun git-commit-tree (buffer tree head)
   "Call git-commit-tree with buffer as input and return the resulting commit SHA1."
@@ -279,7 +299,7 @@ If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.
     (with-current-buffer buffer
       (goto-char (point-min))
       (if
-          (setq log-start (re-search-forward (concat "^" git-log-msg-separator "\n") nil t))
+          (setq log-start (re-search-forward (concat "^" (regexp-quote git-log-msg-separator) "\n") nil t))
           (save-restriction
             (narrow-to-region (point-min) log-start)
             (goto-char (point-min))