diff options
Diffstat (limited to 'lisp/vc/vc-git.el')
-rw-r--r-- | lisp/vc/vc-git.el | 215 |
1 files changed, 161 insertions, 54 deletions
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 7072b8e483b..8d8ea33f8b3 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -53,7 +53,8 @@ ;; - responsible-p (file) OK ;; - receive-file (file rev) NOT NEEDED ;; - unregister (file) OK -;; * checkin (files rev comment) OK +;; * checkin (files comment rev) OK +;; - checkin-patch (patch-string comment) OK ;; * find-revision (file rev buffer) OK ;; * checkout (file &optional rev) OK ;; * revert (file &optional contents-done) OK @@ -81,7 +82,7 @@ ;; - annotate-time () OK ;; - annotate-current-time () NOT NEEDED ;; - annotate-extract-revision-at-line () OK -;; TAG SYSTEM +;; TAG/BRANCH SYSTEM ;; - create-tag (dir name branchp) OK ;; - retrieve-tag (dir name update) OK ;; MISCELLANEOUS @@ -118,6 +119,14 @@ If nil, use the value of `vc-diff-switches'. If t, use no switches." (repeat :tag "Argument List" :value ("") string)) :version "23.1") +;;;###autoload +(defun vc-git-annotate-switches-safe-p (switches) + "Check if local value of `vc-git-annotate-switches' is safe. +Currently only \"-w\" (ignore whitespace) is considered safe, but +this list might be extended in the future." + ;; TODO: Probably most options are perfectly safe. + (equal switches "-w")) + (defcustom vc-git-annotate-switches nil "String or list of strings specifying switches for Git blame under VC. If nil, use the value of `vc-annotate-switches'. If t, use no switches." @@ -126,6 +135,7 @@ If nil, use the value of `vc-annotate-switches'. If t, use no switches." (string :tag "Argument String") (repeat :tag "Argument List" :value ("") string)) :version "25.1") +;;;###autoload(put 'vc-git-annotate-switches 'safe-local-variable #'vc-git-annotate-switches-safe-p) (defcustom vc-git-log-switches nil "String or list of strings specifying switches for Git log under VC." @@ -290,12 +300,14 @@ Good example of file name that needs this: \"test[56].xx\".") (vc-git--run-command-string nil "version"))) (setq vc-git--program-version (if (and version-string - ;; Git for Windows appends ".windows.N" to the - ;; numerical version reported by Git. - (string-match - "git version \\([0-9.]+\\)\\(\\.windows\\.[0-9]+\\)?$" - version-string)) - (match-string 1 version-string) + ;; Some Git versions append additional strings + ;; to the numerical version string. E.g., Git + ;; for Windows appends ".windows.N", while Git + ;; for Mac appends " (Apple Git-N)". Capture + ;; numerical version and ignore the rest. + (string-match "git version \\([0-9][0-9.]+\\)" + version-string)) + (string-trim-right (match-string 1 version-string) "\\.") "0"))))) (defun vc-git--git-status-to-vc-state (code-list) @@ -662,32 +674,26 @@ or an empty string if none." :files files :update-function update-function))) -(defvar vc-git-stash-shared-map - (let ((map (make-sparse-keymap))) - (define-key map "S" #'vc-git-stash-snapshot) - (define-key map "C" #'vc-git-stash) - map)) +(defvar-keymap vc-git-stash-shared-map + "S" #'vc-git-stash-snapshot + "C" #'vc-git-stash) -(defvar vc-git-stash-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map vc-git-stash-shared-map) - ;; Turn off vc-dir marking - (define-key map [mouse-2] #'ignore) - - (define-key map [down-mouse-3] #'vc-git-stash-menu) - (define-key map "\C-k" #'vc-git-stash-delete-at-point) - (define-key map "=" #'vc-git-stash-show-at-point) - (define-key map "\C-m" #'vc-git-stash-show-at-point) - (define-key map "A" #'vc-git-stash-apply-at-point) - (define-key map "P" #'vc-git-stash-pop-at-point) - map)) +(defvar-keymap vc-git-stash-map + :parent vc-git-stash-shared-map + ;; Turn off vc-dir marking + "<mouse-2>" #'ignore -(defvar vc-git-stash-button-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map vc-git-stash-shared-map) - (define-key map [mouse-2] #'push-button) - (define-key map "\C-m" #'push-button) - map)) + "<down-mouse-3>" #'vc-git-stash-menu + "C-k" #'vc-git-stash-delete-at-point + "=" #'vc-git-stash-show-at-point + "RET" #'vc-git-stash-show-at-point + "A" #'vc-git-stash-apply-at-point + "P" #'vc-git-stash-pop-at-point) + +(defvar-keymap vc-git-stash-button-map + :parent vc-git-stash-shared-map + "<mouse-2>" #'push-button + "RET" #'push-button) (defconst vc-git-stash-shared-help "\\<vc-git-stash-shared-map>\\[vc-git-stash]: Create named stash\n\\[vc-git-stash-snapshot]: Snapshot stash") @@ -861,6 +867,47 @@ The car of the list is the current branch." ;;; STATE-CHANGING FUNCTIONS +(defcustom vc-git-log-edit-summary-target-len nil + "Target length for Git commit summary lines. +If a number, characters in Summary: lines beyond this length are +displayed in the `vc-git-log-edit-summary-target-warning' face. +A value of any other type means no highlighting. + +By setting this to an integer around 50, you can improve the +compatibility of your commit messages with Git commands that +print the summary line in width-constrained contexts. However, +many commit summaries will need to exceed this length. + +See also `vc-git-log-edit-summary-max-len'." + :type '(choice (const :tag "No target" nil) + (natnum :tag "Target length")) + :safe (lambda (x) (or (not x) (natnump x)))) + +(defface vc-git-log-edit-summary-target-warning + '((t :inherit warning)) + "Face for Git commit summary lines beyond the target length. +See `vc-git-log-edit-summary-target-len'.") + +(defcustom vc-git-log-edit-summary-max-len 68 + "Maximum length for Git commit summary lines. +If a number, characters in summary lines beyond this length are +displayed in the `vc-git-log-edit-summary-max-warning' face. +A value of any other type means no highlighting. + +It is good practice to avoid writing summary lines longer than +this because otherwise the summary line will be truncated in many +contexts in which Git commands display summary lines. + +See also `vc-git-log-edit-summary-target-len'." + :type '(choice (const :tag "No target" nil) + (natnum :tag "Target length")) + :safe (lambda (x) (or (not x) (natnump x)))) + +(defface vc-git-log-edit-summary-max-warning + '((t :inherit error)) + "Face for Git commit summary lines beyond the maximum length. +See `vc-git-log-edit-summary-max-len'.") + (defun vc-git-create-repo () "Create a new Git repository." (vc-git-command nil 0 nil "init")) @@ -908,16 +955,44 @@ If toggling on, also insert its message into the buffer." standard-output 1 nil "log" "--max-count=1" "--pretty=format:%B" "HEAD"))))) -(defvar vc-git-log-edit-mode-map - (let ((map (make-sparse-keymap "Git-Log-Edit"))) - (define-key map "\C-c\C-s" #'vc-git-log-edit-toggle-signoff) - (define-key map "\C-c\C-n" #'vc-git-log-edit-toggle-no-verify) - (define-key map "\C-c\C-e" #'vc-git-log-edit-toggle-amend) - map)) +(defvar-keymap vc-git-log-edit-mode-map + :name "Git-Log-Edit" + "C-c C-s" #'vc-git-log-edit-toggle-signoff + "C-c C-n" #'vc-git-log-edit-toggle-no-verify + "C-c C-e" #'vc-git-log-edit-toggle-amend) + +(defun vc-git--log-edit-summary-check (limit) + (and (re-search-forward "^Summary: " limit t) + (when-let ((regex + (cond ((and (natnump vc-git-log-edit-summary-max-len) + (natnump vc-git-log-edit-summary-target-len)) + (format ".\\{,%d\\}\\(.\\{,%d\\}\\)\\(.*\\)" + vc-git-log-edit-summary-target-len + (- vc-git-log-edit-summary-max-len + vc-git-log-edit-summary-target-len))) + ((natnump vc-git-log-edit-summary-max-len) + (format ".\\{,%d\\}\\(?2:.*\\)" + vc-git-log-edit-summary-max-len)) + ((natnump vc-git-log-edit-summary-target-len) + (format ".\\{,%d\\}\\(.*\\)" + vc-git-log-edit-summary-target-len))))) + (re-search-forward regex limit t)))) (define-derived-mode vc-git-log-edit-mode log-edit-mode "Log-Edit/git" "Major mode for editing Git log messages. -It is based on `log-edit-mode', and has Git-specific extensions.") +It is based on `log-edit-mode', and has Git-specific extensions." + (setq-local + log-edit-font-lock-keywords + (append log-edit-font-lock-keywords + '((vc-git--log-edit-summary-check + (1 'vc-git-log-edit-summary-target-warning prepend t) + (2 'vc-git-log-edit-summary-max-warning prepend t)))))) + +(defvar vc-git-patch-string nil) + +(defun vc-git-checkin-patch (patch-string comment) + (let ((vc-git-patch-string patch-string)) + (vc-git-checkin nil comment))) (defun vc-git-checkin (files comment &optional _rev) (let* ((file1 (or (car files) default-directory)) @@ -941,12 +1016,21 @@ It is based on `log-edit-mode', and has Git-specific extensions.") (if (eq system-type 'windows-nt) (let ((default-directory (file-name-directory file1))) (make-nearby-temp-file "git-msg"))))) + (when vc-git-patch-string + (unless (zerop (vc-git-command nil t nil "diff" "--cached" "--quiet")) + (user-error "Index not empty")) + (let ((patch-file (make-temp-file "git-patch"))) + (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)))) (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 only files) + (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")) @@ -964,7 +1048,8 @@ It is based on `log-edit-mode', and has Git-specific extensions.") (write-region (car args) nil msg-file)) (setq args (cdr args))) args) - (if only (list "--only" "--") '("-a"))))) + (unless vc-git-patch-string + (if only (list "--only" "--") '("-a")))))) (if (and msg-file (file-exists-p msg-file)) (delete-file msg-file)))) (defun vc-git-find-revision (file rev buffer) @@ -1487,13 +1572,25 @@ This requires git 1.8.4 or later, for the \"-L\" option of \"git log\"." (expand-file-name fname (vc-git-root default-directory)))) revision))))) -;;; TAG SYSTEM +;;; TAG/BRANCH SYSTEM + +(declare-function vc-read-revision "vc" + (prompt &optional files backend default initial-input)) (defun vc-git-create-tag (dir name branchp) - (let ((default-directory dir)) - (and (vc-git-command nil 0 nil "update-index" "--refresh") + (let ((default-directory dir) + (start-point (when branchp (vc-read-revision + (format-prompt "Start point" + (car (vc-git-branches))) + (list dir) 'Git)))) + (and (or (zerop (vc-git-command nil t nil "update-index" "--refresh")) + (y-or-n-p "Modified files exist. Proceed? ") + (user-error (format "Can't create %s with modified files" + (if branchp "branch" "tag")))) (if branchp - (vc-git-command nil 0 nil "checkout" "-b" name) + (vc-git-command nil 0 nil "checkout" "-b" name + (when (and start-point (not (eq start-point ""))) + start-point)) (vc-git-command nil 0 nil "tag" name))))) (defun vc-git-retrieve-tag (dir name _update) @@ -1597,7 +1694,7 @@ This requires git 1.8.4 or later, for the \"-L\" option of \"git log\"." (declare-function grep-read-regexp "grep" ()) (declare-function grep-read-files "grep" (regexp)) (declare-function grep-expand-template "grep" - (template &optional regexp files dir excl)) + (template &optional regexp files dir excl more-opts)) (defvar compilation-environment) ;; Derived from `lgrep'. @@ -1680,7 +1777,7 @@ This command shares argument histories with \\[rgrep] and \\[grep]." (let ((stash (completing-read prompt (split-string - (or (vc-git--run-command-string nil "stash" "list") "") "\n") + (or (vc-git--run-command-string nil "stash" "list") "") "\n" t) nil :require-match nil 'vc-git-stash-read-history))) (if (string-equal stash "") (user-error "Not a stash") @@ -1693,8 +1790,8 @@ This command shares argument histories with \\[rgrep] and \\[grep]." (vc-setup-buffer "*vc-git-stash*") (vc-git-command "*vc-git-stash*" 'async nil "stash" "show" "-p" name) (set-buffer "*vc-git-stash*") - (diff-mode) (setq buffer-read-only t) + (diff-mode) (pop-to-buffer (current-buffer))) (defun vc-git-stash-apply (name) @@ -1725,12 +1822,11 @@ This command shares argument histories with \\[rgrep] and \\[grep]." (defun vc-git-stash-list () (when-let ((out (vc-git--run-command-string nil "stash" "list"))) - (delete - "" - (split-string - (replace-regexp-in-string - "^stash@" " " out) - "\n")))) + (split-string + (replace-regexp-in-string + "^stash@" " " out) + "\n" + t))) (defun vc-git-stash-get-at-point (point) (save-excursion @@ -1867,6 +1963,17 @@ Returns nil if not possible." (1- (point-max))))))) (and name (not (string= name "undefined")) name)))) +(defvar-keymap vc-dir-git-mode-map + "z c" #'vc-git-stash + "z s" #'vc-git-stash-snapshot + "z p" #'vc-git-stash-pop) + +(define-minor-mode vc-dir-git-mode + "A minor mode for git-specific commands in `vc-dir-mode' buffers. +Also note that there are git stash commands available in the +\"Stash\" section at the head of the buffer." + :lighter " Git") + (provide 'vc-git) ;;; vc-git.el ends here |