Merge branch 'jc/checkout' (early part)
[gitweb.git] / contrib / emacs / git.el
index db87a37895f952a4e9b90de4b426e5c2ca7c0c0d..2f9995ea3979350c7cd3883dfc6364e90b8d9bb2 100644 (file)
@@ -1,6 +1,6 @@
 ;;; git.el --- A user interface for git
 
-;; Copyright (C) 2005, 2006 Alexandre Julliard <julliard@winehq.org>
+;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard <julliard@winehq.org>
 
 ;; Version: 1.0
 
@@ -213,6 +213,23 @@ and returns the process output as a string."
     (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))
   (message "Running git %s...done" (car args)))
 
+(defun git-run-hook (hook env &rest args)
+  "Run a git hook and display its output if any."
+  (let ((dir default-directory)
+        (hook-name (expand-file-name (concat ".git/hooks/" hook))))
+    (or (not (file-executable-p hook-name))
+        (let (status (buffer (get-buffer-create "*Git Hook Output*")))
+          (with-current-buffer buffer
+            (erase-buffer)
+            (cd dir)
+            (setq status
+                  (if env
+                      (apply #'call-process "env" nil (list buffer t) nil
+                             (append (git-get-env-strings env) (list hook-name) args))
+                    (apply #'call-process hook-name nil (list buffer t) nil args))))
+          (display-message-or-buffer buffer)
+          (eq 0 status)))))
+
 (defun git-get-string-sha1 (string)
   "Read a SHA1 from the specified string."
   (and string
@@ -246,6 +263,16 @@ and returns the process output as a string."
              (locale-charset-to-coding-system repo-config))
       'utf-8)))
 
+(defun git-get-logoutput-coding-system ()
+  "Return the coding system used for git-log output."
+  (let ((repo-config (or (git-config "i18n.logoutputencoding")
+                         (git-config "i18n.commitencoding"))))
+    (or git-commits-coding-system
+        (and repo-config
+             (fboundp 'locale-charset-to-coding-system)
+             (locale-charset-to-coding-system repo-config))
+      'utf-8)))
+
 (defun git-escape-file-name (name)
   "Escape a file name if necessary."
   (if (string-match "[\n\t\"\\]" name)
@@ -389,6 +416,14 @@ and returns the process output as a string."
           (push (match-string 0) heads))))
     (nreverse heads)))
 
+(defun git-get-commit-description (commit)
+  "Get a one-line description of COMMIT."
+  (let ((coding-system-for-read (git-get-logoutput-coding-system)))
+    (let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit)))
+      (if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr))
+          (concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr))
+        descr))))
+
 ;;;; File info structure
 ;;;; ------------------------------------------------------------
 
@@ -556,7 +591,7 @@ and returns the process output as a string."
   "Refresh the ewoc header and footer."
   (let ((branch (git-symbolic-ref "HEAD"))
         (head (if (git-empty-db-p) "Nothing committed yet"
-                (substring (git-rev-parse "HEAD") 0 10)))
+                (git-get-commit-description "HEAD")))
         (merge-heads (git-get-merge-heads)))
     (ewoc-set-hf status
                  (format "Directory:  %s\nBranch:     %s\nHead:       %s%s\n"
@@ -567,7 +602,7 @@ and returns the process output as a string."
                          head
                          (if merge-heads
                              (concat "\nMerging:    "
-                                     (mapconcat (lambda (str) (substring str 0 10)) merge-heads " "))
+                                     (mapconcat (lambda (str) (git-get-commit-description str)) merge-heads "\n            "))
                            ""))
                  (if (ewoc-nth status 0) "" "    No changes."))))
 
@@ -590,6 +625,20 @@ and returns the process output as a string."
     (when modified
       (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified)))))
 
+(defun git-run-pre-commit-hook ()
+  "Run the pre-commit hook if any."
+  (unless git-status (error "Not in git-status buffer."))
+  (let ((files (git-marked-files-state 'added 'deleted 'modified)))
+    (or (not files)
+        (not (file-executable-p ".git/hooks/pre-commit"))
+        (let ((index-file (make-temp-file "gitidx")))
+          (unwind-protect
+            (let ((head-tree (unless (git-empty-db-p) (git-rev-parse "HEAD^{tree}"))))
+              (git-read-tree head-tree index-file)
+              (git-update-index index-file files)
+              (git-run-hook "pre-commit" `(("GIT_INDEX_FILE" . ,index-file))))
+          (delete-file index-file))))))
+
 (defun git-do-commit ()
   "Perform the actual commit using the current buffer as log message."
   (interactive)
@@ -622,7 +671,8 @@ and returns the process output as a string."
                               (git-run-command nil nil "rerere"))
                             (git-refresh-files)
                             (git-refresh-ewoc-hf git-status)
-                            (message "Committed %s." commit))
+                            (message "Committed %s." commit)
+                            (git-run-hook "post-commit" nil))
                         (message "Commit aborted."))))
                 (message "No files to commit.")))
           (delete-file index-file))))))
@@ -944,28 +994,29 @@ and returns the process output as a string."
   "Commit the marked file(s), asking for a commit message."
   (interactive)
   (unless git-status (error "Not in git-status buffer."))
-  (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))
-    (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)))
+  (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))
+      (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))))
 
 (defun git-find-file ()
   "Visit the current file in its own buffer."