Function: vc-git-checkin
vc-git-checkin is a byte-compiled function defined in vc-git.el.gz.
Signature
(vc-git-checkin FILES COMMENT &optional REV)
Source Code
;; Defined in /usr/src/emacs/lisp/vc/vc-git.el.gz
(defun vc-git-checkin (files comment &optional _rev)
(let* ((file1 (or (car files) default-directory))
(root (vc-git-root file1))
(default-directory (expand-file-name root))
(only (or (cdr files)
(not (equal root (abbreviate-file-name file1)))))
(pcsw coding-system-for-write)
(coding-system-for-write
;; On MS-Windows, we must encode command-line arguments in
;; the system codepage.
(if (eq system-type 'windows-nt)
locale-coding-system
(or coding-system-for-write vc-git-commits-coding-system)))
(msg-file
;; On MS-Windows, pass the commit log message through a
;; file, to work around the limitation that command-line
;; arguments must be in the system codepage, and therefore
;; might not support the non-ASCII characters in the log
;; message. Handle also remote files.
(if (eq system-type 'windows-nt)
(let ((default-directory (or (file-name-directory file1)
default-directory)))
(make-nearby-temp-file "git-msg"))))
to-stash)
(when vc-git-patch-string
(unless (zerop (vc-git-command nil t nil "diff" "--cached" "--quiet"))
;; Check that what's already staged is compatible with what
;; we want to commit (bug#60126).
;;
;; 1. If the changes to a file in the index are identical to
;; the changes to that file we want to commit, remove the
;; changes from our patch, and let the commit take them
;; from the index. This is necessary for adding and
;; removing files to work.
;;
;; 2. If the changes to a file in the index are different to
;; changes to that file we want to commit, then we have to
;; unstage the changes or abort.
;;
;; 3. If there are changes to a file in the index but we don't
;; want to commit any changes to that file, we need to
;; stash those changes before committing.
(with-temp-buffer
;; If the user has switches like -D, -M etc. in their
;; `vc-git-diff-switches', we must pass them here too, or
;; our string matches will fail.
(if vc-git-diff-switches
(apply #'vc-git-command (current-buffer) t nil
"diff" "--cached" (vc-switches 'git 'diff))
;; Following code doesn't understand plain diff(1) output.
(user-error "Cannot commit patch with nil `vc-git-diff-switches'"))
(goto-char (point-min))
(let ((pos (point)) file-name file-header file-diff file-beg)
(while (not (eobp))
(when (and (looking-at "^diff --git a/\\(.+\\) b/\\(.+\\)")
(string= (match-string 1) (match-string 2)))
(setq file-name (match-string 1)))
(forward-line 1) ; skip current "diff --git" line
(setq file-header (buffer-substring pos (point)))
(search-forward "diff --git" nil 'move)
(move-beginning-of-line 1)
(setq file-diff (buffer-substring pos (point)))
(cond ((and (setq file-beg (string-search
file-diff vc-git-patch-string))
;; Check that file diff ends with an empty string
;; or the beginning of the next file diff.
(string-match-p "\\`\\'\\|\\`diff --git"
(substring
vc-git-patch-string
(+ file-beg (length file-diff)))))
(setq vc-git-patch-string
(string-replace file-diff "" vc-git-patch-string)))
((string-match (format "^%s" (regexp-quote file-header))
vc-git-patch-string)
(if (and file-name
(yes-or-no-p
(format "Unstage already-staged changes to %s?"
file-name)))
(vc-git-command nil 0 file-name "reset" "-q" "--")
(user-error "Index not empty")))
(t (push file-name to-stash)))
(setq pos (point))))))
(unless (string-empty-p vc-git-patch-string)
(let ((patch-file (make-nearby-temp-file "git-patch"))
;; Temporarily countermand the let-binding at the
;; beginning of this function.
(coding-system-for-write
(coding-system-change-eol-conversion
;; On DOS/Windows, it is important for the patch file
;; to have the Unix EOL format, because Git expects
;; that, even on Windows.
(or pcsw vc-git-commits-coding-system) 'unix)))
(with-temp-file patch-file
(insert vc-git-patch-string))
(unwind-protect
(vc-git-command nil 0 patch-file "apply" "--cached")
(delete-file patch-file))))
(when to-stash (vc-git--stash-staged-changes files)))
(cl-flet ((boolean-arg-fn
(argument)
(lambda (value) (when (equal value "yes") (list argument)))))
;; When operating on the whole tree, better pass "-a" than ".", since "."
;; fails when we're committing a merge.
(apply #'vc-git-command nil 0 (if (and only (not vc-git-patch-string)) files)
(nconc (if msg-file (list "commit" "-F"
(file-local-name msg-file))
(list "commit" "-m"))
(let ((args
(log-edit-extract-headers
`(("Author" . "--author")
("Date" . "--date")
("Amend" . ,(boolean-arg-fn "--amend"))
("No-Verify" . ,(boolean-arg-fn "--no-verify"))
("Sign-Off" . ,(boolean-arg-fn "--signoff")))
comment)))
(when msg-file
(let ((coding-system-for-write
(or pcsw vc-git-commits-coding-system)))
(write-region (car args) nil msg-file))
(setq args (cdr args)))
args)
(unless vc-git-patch-string
(if only (list "--only" "--") '("-a"))))))
(if (and msg-file (file-exists-p msg-file)) (delete-file msg-file))
(when to-stash
(let ((cached (make-nearby-temp-file "git-cached")))
(unwind-protect
(progn (with-temp-file cached
(vc-git-command t 0 nil "stash" "show" "-p"))
(vc-git-command nil 0 cached "apply" "--cached"))
(delete-file cached))
(vc-git-command nil 0 nil "stash" "drop")))))