Merge branch 'master' into next

* master:
  AsciiDoc fix for tutorial
  git.el: Added customize support for all parameters.
  git.el: Added support for Signed-off-by.
  git.el: Automatically update .gitignore status.
  git.el: Set default directory before running the status mode setup hooks.
  git.el: Portability fixes for XEmacs and Emacs CVS.
  contrib/emacs: Add an Emacs VC backend.
This commit is contained in:
Junio C Hamano 2006-03-04 13:53:27 -08:00
commit b463ceb885
3 changed files with 217 additions and 41 deletions

View File

@ -309,7 +309,7 @@ git diff HEAD^^ HEAD^
-------------------------------------
shows the difference between that previous state and the state two
commits ago. Also, HEAD~5 can be used as a shorthand for HEAD^^^^^,
commits ago. Also, HEAD~5 can be used as a shorthand for HEAD{caret}{caret}{caret}{caret}{caret},
and more generally HEAD~n can refer to the nth previous commit.
Commits representing merges have more than one parent, and you can
specify which parent to follow in that case; see

View File

@ -39,13 +39,11 @@
;; - hook into file save (after-save-hook)
;; - diff against other branch
;; - renaming files from the status buffer
;; - support for appending signed-off-by
;; - creating tags
;; - fetch/pull
;; - switching branches
;; - revlist browser
;; - git-show-branch browser
;; - customize support
;; - menus
;;
@ -53,65 +51,92 @@
(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.")
(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))
@ -213,14 +238,19 @@ If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.
"Add a file name to the ignore file in its directory."
(let* ((fullname (expand-file-name file))
(dir (file-name-directory fullname))
(name (file-name-nondirectory fullname)))
(name (file-name-nondirectory fullname))
(ignore-name (expand-file-name git-per-dir-ignore-file dir))
(created (not (file-exists-p ignore-name))))
(save-window-excursion
(set-buffer (find-file-noselect (expand-file-name git-per-dir-ignore-file dir)))
(set-buffer (find-file-noselect ignore-name))
(goto-char (point-max))
(unless (zerop (current-column)) (insert "\n"))
(insert name "\n")
(sort-lines nil (point-min) (point-max))
(save-buffer))))
(save-buffer))
(when created
(git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name)))
(git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name))))
;;;; Wrappers for basic git commands
@ -272,7 +302,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))
@ -388,9 +418,9 @@ If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.
(propertize
(if (or (not old-perm)
(not new-perm)
(eq 0 (logand #O111 (logxor old-perm new-perm))))
(eq 0 (logand ?\111 (logxor old-perm new-perm))))
" "
(if (eq 0 (logand #O111 old-perm)) "+x" "-x"))
(if (eq 0 (logand ?\111 old-perm)) "+x" "-x"))
'face 'git-permission-face))
(defun git-fileinfo-prettyprint (info)
@ -787,7 +817,8 @@ If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.
(unless git-status (error "Not in git-status buffer."))
(let ((buffer (get-buffer-create "*git-commit*"))
(merge-heads (git-get-merge-heads))
(dir default-directory))
(dir default-directory)
(sign-off git-append-signed-off-by))
(with-current-buffer buffer
(when (eq 0 (buffer-size))
(cd dir)
@ -804,9 +835,18 @@ If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.
'face 'git-header-face)
(propertize git-log-msg-separator 'face 'git-separator-face)
"\n")
(when (and merge-heads (file-readable-p ".git/MERGE_MSG"))
(insert-file-contents ".git/MERGE_MSG"))))
(log-edit #'git-do-commit nil #'git-log-edit-files buffer)))
(cond ((and merge-heads (file-readable-p ".git/MERGE_MSG"))
(insert-file-contents ".git/MERGE_MSG"))
(sign-off
(insert (format "\n\nSigned-off-by: %s <%s>\n"
(git-get-committer-name) (git-get-committer-email)))))))
(let ((log-edit-font-lock-keywords
`(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)"
(1 font-lock-keyword-face)
(2 font-lock-function-name-face))
(,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$")
(1 font-lock-comment-face)))))
(log-edit #'git-do-commit nil #'git-log-edit-files buffer))))
(defun git-find-file ()
"Visit the current file in its own buffer."
@ -891,7 +931,7 @@ If not set, fall back to `add-log-mailing-address' and then `user-mail-address'.
(define-key map "d" diff-map)
(define-key map "=" 'git-diff-file)
(define-key map "f" 'git-find-file)
(define-key map [RET] 'git-find-file)
(define-key map "\r" 'git-find-file)
(define-key map "g" 'git-refresh-status)
(define-key map "i" 'git-ignore-file)
(define-key map "l" 'git-log-file)
@ -937,6 +977,7 @@ Commands:
(erase-buffer)
(let ((status (ewoc-create 'git-fileinfo-prettyprint "" "")))
(set (make-local-variable 'git-status) status))
(set (make-local-variable 'list-buffers-directory) default-directory)
(run-hooks 'git-status-mode-hook)))
(defun git-status (dir)
@ -946,8 +987,8 @@ Commands:
(if (file-directory-p (concat (file-name-as-directory dir) ".git"))
(let ((buffer (create-file-buffer (expand-file-name "*git-status*" dir))))
(switch-to-buffer buffer)
(git-status-mode)
(cd dir)
(git-status-mode)
(git-refresh-status)
(goto-char (point-min)))
(message "%s is not a git working tree." dir)))

135
contrib/emacs/vc-git.el Normal file
View File

@ -0,0 +1,135 @@
;;; vc-git.el --- VC backend for the git version control system
;; Copyright (C) 2006 Alexandre Julliard
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2 of
;; the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be
;; useful, but WITHOUT ANY WARRANTY; without even the implied
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE. See the GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public
;; License along with this program; if not, write to the Free
;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
;; MA 02111-1307 USA
;;; Commentary:
;; This file contains a VC backend for the git version control
;; system.
;;
;; To install: put this file on the load-path and add GIT to the list
;; of supported backends in `vc-handled-backends'.
;;
;; TODO
;; - changelog generation
;; - working with revisions other than HEAD
;;
(defvar git-commits-coding-system 'utf-8
"Default coding system for git commits.")
(defun vc-git--run-command-string (file &rest args)
"Run a git command on FILE and return its output as string."
(let* ((ok t)
(str (with-output-to-string
(with-current-buffer standard-output
(unless (eq 0 (apply #'call-process "git" nil '(t nil) nil
(append args (list (file-relative-name file)))))
(setq ok nil))))))
(and ok str)))
(defun vc-git--run-command (file &rest args)
"Run a git command on FILE, discarding any output."
(let ((name (file-relative-name file)))
(eq 0 (apply #'call-process "git" nil (get-buffer "*Messages") nil (append args (list name))))))
(defun vc-git-registered (file)
"Check whether FILE is registered with git."
(with-temp-buffer
(let* ((dir (file-name-directory file))
(name (file-relative-name file dir)))
(when dir (cd dir))
(and (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name))
(let ((str (buffer-string)))
(and (> (length str) (length name))
(string= (substring str 0 (1+ (length name))) (concat name "\0"))))))))
(defun vc-git-state (file)
"git-specific version of `vc-state'."
(let ((diff (vc-git--run-command-string file "diff-index" "-z" "HEAD" "--")))
(if (and diff (string-match ":[0-7]\\{6\\} [0-7]\\{6\\} [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} [ADMU]\0[^\0]+\0" diff))
'edited
'up-to-date)))
(defun vc-git-workfile-version (file)
"git-specific version of `vc-workfile-version'."
(let ((str (with-output-to-string
(with-current-buffer standard-output
(call-process "git" nil '(t nil) nil "symbolic-ref" "HEAD")))))
(if (string-match "^\\(refs/heads/\\)?\\(.+\\)$" str)
(match-string 2 str)
str)))
(defun vc-git-revert (file &optional contents-done)
"Revert FILE to the version stored in the git repository."
(if contents-done
(vc-git--run-command file "update-index" "--")
(vc-git--run-command file "checkout" "HEAD")))
(defun vc-git-checkout-model (file)
'implicit)
(defun vc-git-workfile-unchanged-p (file)
(let ((sha1 (vc-git--run-command-string file "hash-object" "--"))
(head (vc-git--run-command-string file "ls-tree" "-z" "HEAD" "--")))
(and head
(string-match "[0-7]\\{6\\} blob \\([0-9a-f]\\{40\\}\\)\t[^\0]+\0" head)
(string= (car (split-string sha1 "\n")) (match-string 1 head)))))
(defun vc-git-register (file &optional rev comment)
"Register FILE into the git version-control system."
(vc-git--run-command file "update-index" "--add" "--"))
(defun vc-git-print-log (file)
(let ((name (file-relative-name file))
(coding-system-for-read git-commits-coding-system))
(vc-do-command nil 'async "git" name "rev-list" "--pretty" "HEAD" "--")))
(defun vc-git-diff (file &optional rev1 rev2)
(let ((name (file-relative-name file)))
(if (and rev1 rev2)
(vc-do-command "*vc-diff*" 0 "git" name "diff-tree" "-p" rev1 rev2 "--")
(vc-do-command "*vc-diff*" 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--"))
; git-diff-index doesn't set exit status like diff does
(if (vc-git-workfile-unchanged-p file) 0 1)))
(defun vc-git-checkin (file rev comment)
(let ((coding-system-for-write git-commits-coding-system))
(vc-git--run-command file "commit" "-m" comment "--only" "--")))
(defun vc-git-checkout (file &optional editable rev destfile)
(vc-git--run-command file "checkout" (or rev "HEAD")))
(defun vc-git-annotate-command (file buf &optional rev)
; FIXME: rev is ignored
(let ((name (file-relative-name file)))
(call-process "git" nil buf nil "annotate" name)))
(defun vc-git-annotate-time ()
(and (re-search-forward "[0-9a-f]+\t(.*\t\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\)\t[0-9]+)" nil t)
(vc-annotate-convert-time
(apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7))))))
;; Not really useful since we can't do anything with the revision yet
;;(defun vc-annotate-extract-revision-at-line ()
;; (save-excursion
;; (move-beginning-of-line 1)
;; (and (looking-at "[0-9a-f]+")
;; (buffer-substring (match-beginning 0) (match-end 0)))))
(provide 'vc-git)