Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

565 line
22 KiB

4 年之前
  1. ;;; magit-commit.el --- create Git commits -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2008-2019 The Magit Project Contributors
  3. ;;
  4. ;; You should have received a copy of the AUTHORS.md file which
  5. ;; lists all contributors. If not, see http://magit.vc/authors.
  6. ;; Author: Jonas Bernoulli <jonas@bernoul.li>
  7. ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
  8. ;; Magit is free software; you can redistribute it and/or modify it
  9. ;; under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation; either version 3, or (at your option)
  11. ;; any later version.
  12. ;;
  13. ;; Magit is distributed in the hope that it will be useful, but WITHOUT
  14. ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  15. ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  16. ;; License for more details.
  17. ;;
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with Magit. If not, see http://www.gnu.org/licenses.
  20. ;;; Commentary:
  21. ;; This library implements commands for creating Git commits. These
  22. ;; commands just initiate the commit, support for writing the commit
  23. ;; messages is implemented in `git-commit.el'.
  24. ;;; Code:
  25. (require 'magit)
  26. (require 'magit-sequence)
  27. (eval-when-compile (require 'epa)) ; for `epa-protocol'
  28. (eval-when-compile (require 'epg))
  29. (eval-when-compile (require 'subr-x))
  30. ;;; Options
  31. (defcustom magit-commit-ask-to-stage 'verbose
  32. "Whether to ask to stage all unstaged changes when committing and nothing is staged."
  33. :package-version '(magit . "2.3.0")
  34. :group 'magit-commands
  35. :type '(choice (const :tag "Ask showing diff" verbose)
  36. (const :tag "Ask" t)
  37. (const :tag "Don't ask" nil)))
  38. (defcustom magit-commit-show-diff t
  39. "Whether the relevant diff is automatically shown when committing."
  40. :package-version '(magit . "2.3.0")
  41. :group 'magit-commands
  42. :type 'boolean)
  43. (defcustom magit-commit-extend-override-date t
  44. "Whether using `magit-commit-extend' changes the committer date."
  45. :package-version '(magit . "2.3.0")
  46. :group 'magit-commands
  47. :type 'boolean)
  48. (defcustom magit-commit-reword-override-date t
  49. "Whether using `magit-commit-reword' changes the committer date."
  50. :package-version '(magit . "2.3.0")
  51. :group 'magit-commands
  52. :type 'boolean)
  53. (defcustom magit-commit-squash-confirm t
  54. "Whether the commit targeted by squash and fixup has to be confirmed.
  55. When non-nil then the commit at point (if any) is used as default
  56. choice, otherwise it has to be confirmed. This option only
  57. affects `magit-commit-squash' and `magit-commit-fixup'. The
  58. \"instant\" variants always require confirmation because making
  59. an error while using those is harder to recover from."
  60. :package-version '(magit . "2.1.0")
  61. :group 'magit-commands
  62. :type 'boolean)
  63. (defcustom magit-post-commit-hook nil
  64. "Hook run after creating a commit without the user editing a message.
  65. This hook is run by `magit-refresh' if `this-command' is a member
  66. of `magit-post-stage-hook-commands'. This only includes commands
  67. named `magit-commit-*' that do *not* require that the user edits
  68. the commit message in a buffer and then finishes by pressing
  69. \\<with-editor-mode-map>\\[with-editor-finish].
  70. Also see `git-commit-post-finish-hook'."
  71. :package-version '(magit . "2.90.0")
  72. :group 'magit-commands
  73. :type 'hook)
  74. (defvar magit-post-commit-hook-commands
  75. '(magit-commit-extend
  76. magit-commit-fixup
  77. magit-commit-augment
  78. magit-commit-instant-fixup
  79. magit-commit-instant-squash))
  80. ;;; Popup
  81. ;;;###autoload (autoload 'magit-commit "magit-commit" nil t)
  82. (define-transient-command magit-commit ()
  83. "Create a new commit or replace an existing commit."
  84. :info-manual "(magit)Initiating a Commit"
  85. :man-page "git-commit"
  86. ["Arguments"
  87. ("-a" "Stage all modified and deleted files" ("-a" "--all"))
  88. ("-e" "Allow empty commit" "--allow-empty")
  89. ("-v" "Show diff of changes to be committed" ("-v" "--verbose"))
  90. ("-n" "Disable hooks" ("-n" "--no-verify"))
  91. ("-R" "Claim authorship and reset author date" "--reset-author")
  92. (magit:--author :description "Override the author")
  93. (7 "-D" "Override the author date" "--date=" transient-read-date)
  94. ("-s" "Add Signed-off-by line" ("-s" "--signoff"))
  95. (5 magit:--gpg-sign)
  96. (magit-commit:--reuse-message)]
  97. [["Create"
  98. ("c" "Commit" magit-commit-create)]
  99. ["Edit HEAD"
  100. ("e" "Extend" magit-commit-extend)
  101. ("w" "Reword" magit-commit-reword)
  102. ("a" "Amend" magit-commit-amend)
  103. (6 "n" "Reshelve" magit-commit-reshelve)]
  104. ["Edit"
  105. ("f" "Fixup" magit-commit-fixup)
  106. ("s" "Squash" magit-commit-squash)
  107. ("A" "Augment" magit-commit-augment)
  108. (6 "x" "Absorb changes" magit-commit-absorb)]
  109. [""
  110. ("F" "Instant fixup" magit-commit-instant-fixup)
  111. ("S" "Instant squash" magit-commit-instant-squash)]]
  112. (interactive)
  113. (if-let ((buffer (magit-commit-message-buffer)))
  114. (switch-to-buffer buffer)
  115. (transient-setup 'magit-commit)))
  116. (defun magit-commit-arguments nil
  117. (transient-args 'magit-commit))
  118. (define-infix-argument magit:--gpg-sign ()
  119. :description "Sign using gpg"
  120. :class 'transient-option
  121. :shortarg "-S"
  122. :argument "--gpg-sign="
  123. :allow-empty t
  124. :reader 'magit-read-gpg-secret-key)
  125. (defvar magit-gpg-secret-key-hist nil)
  126. (defun magit-read-gpg-secret-key (prompt &optional _initial-input history)
  127. (require 'epa)
  128. (let ((keys (--map (concat (epg-sub-key-id (car (epg-key-sub-key-list it)))
  129. " "
  130. (when-let ((id-obj (car (epg-key-user-id-list it))))
  131. (let ((id-str (epg-user-id-string id-obj)))
  132. (if (stringp id-str)
  133. id-str
  134. (epg-decode-dn id-obj)))))
  135. (epg-list-keys (epg-make-context epa-protocol) nil t))))
  136. (car (split-string (magit-completing-read
  137. prompt keys nil nil nil history
  138. (car (or history keys)))
  139. " "))))
  140. (define-infix-argument magit-commit:--reuse-message ()
  141. :description "Reuse commit message"
  142. :class 'transient-option
  143. :shortarg "-C"
  144. :argument "--reuse-message="
  145. :reader 'magit-read-reuse-message
  146. :history-key 'magit-revision-history)
  147. (defun magit-read-reuse-message (prompt &optional default history)
  148. (magit-completing-read prompt (magit-list-refnames)
  149. nil nil nil history
  150. (or default
  151. (and (magit-rev-verify "ORIG_HEAD")
  152. "ORIG_HEAD"))))
  153. ;;; Commands
  154. ;;;###autoload
  155. (defun magit-commit-create (&optional args)
  156. "Create a new commit on `HEAD'.
  157. With a prefix argument, amend to the commit at `HEAD' instead.
  158. \n(git commit [--amend] ARGS)"
  159. (interactive (if current-prefix-arg
  160. (list (cons "--amend" (magit-commit-arguments)))
  161. (list (magit-commit-arguments))))
  162. (when (member "--all" args)
  163. (setq this-command 'magit-commit-all))
  164. (when (setq args (magit-commit-assert args))
  165. (let ((default-directory (magit-toplevel)))
  166. (magit-run-git-with-editor "commit" args))))
  167. ;;;###autoload
  168. (defun magit-commit-amend (&optional args)
  169. "Amend the last commit.
  170. \n(git commit --amend ARGS)"
  171. (interactive (list (magit-commit-arguments)))
  172. (magit-commit-amend-assert)
  173. (magit-run-git-with-editor "commit" "--amend" args))
  174. ;;;###autoload
  175. (defun magit-commit-extend (&optional args override-date)
  176. "Amend the last commit, without editing the message.
  177. With a prefix argument keep the committer date, otherwise change
  178. it. The option `magit-commit-extend-override-date' can be used
  179. to inverse the meaning of the prefix argument. \n(git commit
  180. --amend --no-edit)"
  181. (interactive (list (magit-commit-arguments)
  182. (if current-prefix-arg
  183. (not magit-commit-extend-override-date)
  184. magit-commit-extend-override-date)))
  185. (when (setq args (magit-commit-assert args (not override-date)))
  186. (magit-commit-amend-assert)
  187. (let ((process-environment process-environment))
  188. (unless override-date
  189. (push (magit-rev-format "GIT_COMMITTER_DATE=%cD") process-environment))
  190. (magit-run-git-with-editor "commit" "--amend" "--no-edit" args))))
  191. ;;;###autoload
  192. (defun magit-commit-reword (&optional args override-date)
  193. "Reword the last commit, ignoring staged changes.
  194. With a prefix argument keep the committer date, otherwise change
  195. it. The option `magit-commit-reword-override-date' can be used
  196. to inverse the meaning of the prefix argument.
  197. Non-interactively respect the optional OVERRIDE-DATE argument
  198. and ignore the option.
  199. \n(git commit --amend --only)"
  200. (interactive (list (magit-commit-arguments)
  201. (if current-prefix-arg
  202. (not magit-commit-reword-override-date)
  203. magit-commit-reword-override-date)))
  204. (magit-commit-amend-assert)
  205. (let ((process-environment process-environment))
  206. (unless override-date
  207. (push (magit-rev-format "GIT_COMMITTER_DATE=%cD") process-environment))
  208. (cl-pushnew "--allow-empty" args :test #'equal)
  209. (magit-run-git-with-editor "commit" "--amend" "--only" args)))
  210. ;;;###autoload
  211. (defun magit-commit-fixup (&optional commit args)
  212. "Create a fixup commit.
  213. With a prefix argument the target COMMIT has to be confirmed.
  214. Otherwise the commit at point may be used without confirmation
  215. depending on the value of option `magit-commit-squash-confirm'."
  216. (interactive (list (magit-commit-at-point)
  217. (magit-commit-arguments)))
  218. (magit-commit-squash-internal "--fixup" commit args))
  219. ;;;###autoload
  220. (defun magit-commit-squash (&optional commit args)
  221. "Create a squash commit, without editing the squash message.
  222. With a prefix argument the target COMMIT has to be confirmed.
  223. Otherwise the commit at point may be used without confirmation
  224. depending on the value of option `magit-commit-squash-confirm'."
  225. (interactive (list (magit-commit-at-point)
  226. (magit-commit-arguments)))
  227. (magit-commit-squash-internal "--squash" commit args))
  228. ;;;###autoload
  229. (defun magit-commit-augment (&optional commit args)
  230. "Create a squash commit, editing the squash message.
  231. With a prefix argument the target COMMIT has to be confirmed.
  232. Otherwise the commit at point may be used without confirmation
  233. depending on the value of option `magit-commit-squash-confirm'."
  234. (interactive (list (magit-commit-at-point)
  235. (magit-commit-arguments)))
  236. (magit-commit-squash-internal "--squash" commit args nil t))
  237. ;;;###autoload
  238. (defun magit-commit-instant-fixup (&optional commit args)
  239. "Create a fixup commit targeting COMMIT and instantly rebase."
  240. (interactive (list (magit-commit-at-point)
  241. (magit-commit-arguments)))
  242. (magit-commit-squash-internal "--fixup" commit args t))
  243. ;;;###autoload
  244. (defun magit-commit-instant-squash (&optional commit args)
  245. "Create a squash commit targeting COMMIT and instantly rebase."
  246. (interactive (list (magit-commit-at-point)
  247. (magit-commit-arguments)))
  248. (magit-commit-squash-internal "--squash" commit args t))
  249. (defun magit-commit-squash-internal
  250. (option commit &optional args rebase edit confirmed)
  251. (when-let ((args (magit-commit-assert args t)))
  252. (when commit
  253. (when (and rebase (not (magit-rev-ancestor-p commit "HEAD")))
  254. (magit-read-char-case
  255. (format "%s isn't an ancestor of HEAD. " commit) nil
  256. (?c "[c]reate without rebasing" (setq rebase nil))
  257. (?s "[s]elect other" (setq commit nil))
  258. (?a "[a]bort" (user-error "Quit")))))
  259. (when commit
  260. (setq commit (magit-rebase-interactive-assert commit t)))
  261. (if (and commit
  262. (or confirmed
  263. (not (or rebase
  264. current-prefix-arg
  265. magit-commit-squash-confirm))))
  266. (let ((magit-commit-show-diff nil))
  267. (push (concat option "=" commit) args)
  268. (unless edit
  269. (push "--no-edit" args))
  270. (if rebase
  271. (magit-with-editor
  272. (magit-call-git
  273. "commit" "--no-gpg-sign"
  274. (-remove-first
  275. (apply-partially #'string-match-p "\\`--gpg-sign=")
  276. args)))
  277. (magit-run-git-with-editor "commit" args))
  278. t) ; The commit was created; used by below lambda.
  279. (magit-log-select
  280. (lambda (commit)
  281. (when (and (magit-commit-squash-internal option commit args
  282. rebase edit t)
  283. rebase)
  284. (magit-commit-amend-assert commit)
  285. (magit-rebase-interactive-1 commit
  286. (list "--autosquash" "--autostash" "--keep-empty")
  287. "" "true" nil t)))
  288. (format "Type %%p on a commit to %s into it,"
  289. (substring option 2))
  290. nil nil nil commit)
  291. (when magit-commit-show-diff
  292. (let ((magit-display-buffer-noselect t))
  293. (apply #'magit-diff-staged nil (magit-diff-arguments)))))))
  294. (defun magit-commit-amend-assert (&optional commit)
  295. (--when-let (magit-list-publishing-branches commit)
  296. (let ((m1 "This commit has already been published to ")
  297. (m2 ".\nDo you really want to modify it"))
  298. (magit-confirm 'amend-published
  299. (concat m1 "%s" m2)
  300. (concat m1 "%i public branches" m2)
  301. nil it))))
  302. (defun magit-commit-assert (args &optional strict)
  303. (cond
  304. ((or (magit-anything-staged-p)
  305. (and (magit-anything-unstaged-p)
  306. ;; ^ Everything of nothing is still nothing.
  307. (member "--all" args))
  308. (and (not strict)
  309. ;; ^ For amend variants that don't make sense otherwise.
  310. (or (member "--amend" args)
  311. (member "--allow-empty" args))))
  312. (or args (list "--")))
  313. ((and (magit-rebase-in-progress-p)
  314. (not (magit-anything-unstaged-p))
  315. (y-or-n-p "Nothing staged. Continue in-progress rebase? "))
  316. (setq this-command 'magit-rebase-continue)
  317. (magit-run-git-sequencer "rebase" "--continue")
  318. nil)
  319. ((and (file-exists-p (magit-git-dir "MERGE_MSG"))
  320. (not (magit-anything-unstaged-p)))
  321. (or args (list "--")))
  322. ((not (magit-anything-unstaged-p))
  323. (user-error "Nothing staged (or unstaged)"))
  324. (magit-commit-ask-to-stage
  325. (when (eq magit-commit-ask-to-stage 'verbose)
  326. (magit-diff-unstaged))
  327. (prog1 (when (y-or-n-p "Nothing staged. Stage and commit all unstaged changes? ")
  328. (magit-run-git "add" "-u" ".")
  329. (or args (list "--")))
  330. (when (and (eq magit-commit-ask-to-stage 'verbose)
  331. (derived-mode-p 'magit-diff-mode))
  332. (magit-mode-bury-buffer))))
  333. (t
  334. (user-error "Nothing staged"))))
  335. (defvar magit--reshelve-history nil)
  336. ;;;###autoload
  337. (defun magit-commit-reshelve (date)
  338. "Change the committer date and possibly the author date of `HEAD'.
  339. If you are the author of `HEAD', then both dates are changed,
  340. otherwise only the committer date. The current time is used
  341. as the initial minibuffer input and the original author (if
  342. that is you) or committer date is available as the previous
  343. history element."
  344. (interactive
  345. (let ((author-p (magit-rev-author-p "HEAD")))
  346. (push (magit-rev-format (if author-p "%ad" "%cd") "HEAD"
  347. (concat "--date=format:%F %T %z"))
  348. magit--reshelve-history)
  349. (list (read-string (if author-p
  350. "Change author and committer dates to: "
  351. "Change committer date to: ")
  352. (cons (format-time-string "%F %T %z") 17)
  353. 'magit--reshelve-history))))
  354. (let ((process-environment process-environment))
  355. (push (concat "GIT_COMMITTER_DATE=" date) process-environment)
  356. (magit-run-git "commit" "--amend" "--no-edit"
  357. (and (magit-rev-author-p "HEAD")
  358. (concat "--date=" date)))))
  359. ;;;###autoload (autoload 'magit-commit-absorb "magit-commit" nil t)
  360. (define-transient-command magit-commit-absorb (phase commit args)
  361. "Spread unstaged changes across recent commits.
  362. With a prefix argument use a transient command to select infix
  363. arguments. This command requires the git-autofixup script, which
  364. is available from https://github.com/torbiak/git-autofixup."
  365. ["Arguments"
  366. (magit-autofixup:--context)
  367. (magit-autofixup:--strict)]
  368. ["Actions"
  369. ("x" "Absorb" magit-commit-absorb)]
  370. (interactive (if current-prefix-arg
  371. (list 'transient nil nil)
  372. (list 'select
  373. (magit-get-upstream-branch)
  374. (transient-args 'magit-commit-absorb))))
  375. (if (eq phase 'transient)
  376. (transient-setup 'magit-commit-absorb)
  377. (unless (executable-find "git-autofixup")
  378. (user-error "This command requires the git-autofixup script, which %s"
  379. "is available from https://github.com/torbiak/git-autofixup"))
  380. (when (magit-anything-staged-p)
  381. (user-error "Cannot absorb when there are staged changes"))
  382. (unless (magit-anything-unstaged-p)
  383. (user-error "There are no unstaged changes that could be absorbed"))
  384. (when commit
  385. (setq commit (magit-rebase-interactive-assert commit t)))
  386. (if (and commit (eq phase 'run))
  387. (progn (magit-run-git-async "autofixup" "-vv" args commit) t)
  388. (magit-log-select
  389. (lambda (commit)
  390. (with-no-warnings ; about non-interactive use
  391. (magit-commit-absorb 'run commit args)))
  392. nil nil nil nil commit))))
  393. (define-infix-argument magit-autofixup:--context ()
  394. :description "Diff context lines"
  395. :class 'transient-option
  396. :shortarg "-c"
  397. :argument "--context="
  398. :reader 'transient-read-number-N0)
  399. (define-infix-argument magit-autofixup:--strict ()
  400. :description "Strictness"
  401. :class 'transient-option
  402. :shortarg "-s"
  403. :argument "--strict="
  404. :reader 'transient-read-number-N0)
  405. ;;; Pending Diff
  406. (defun magit-commit-diff ()
  407. (when (and git-commit-mode magit-commit-show-diff)
  408. (when-let ((diff-buffer (magit-get-mode-buffer 'magit-diff-mode)))
  409. ;; This window just started displaying the commit message
  410. ;; buffer. Without this that buffer would immediately be
  411. ;; replaced with the diff buffer. See #2632.
  412. (unrecord-window-buffer nil diff-buffer))
  413. (condition-case nil
  414. (let ((args (car (magit-diff-arguments)))
  415. (magit-inhibit-save-previous-winconf 'unset)
  416. (magit-display-buffer-noselect t)
  417. (inhibit-quit nil))
  418. (message "Diffing changes to be committed (C-g to abort diffing)")
  419. (cl-case last-command
  420. (magit-commit
  421. (magit-diff-staged nil args))
  422. (magit-commit-all
  423. (magit-diff-working-tree nil args))
  424. ((magit-commit-amend
  425. magit-commit-reword
  426. magit-rebase-reword-commit)
  427. (magit-diff-while-amending args))
  428. (t (if (magit-anything-staged-p)
  429. (magit-diff-staged nil args)
  430. (magit-diff-while-amending args)))))
  431. (quit))))
  432. ;; Mention `magit-diff-while-committing' because that's
  433. ;; always what I search for when I try to find this line.
  434. (add-hook 'server-switch-hook 'magit-commit-diff)
  435. (add-to-list 'with-editor-server-window-alist
  436. (cons git-commit-filename-regexp 'switch-to-buffer))
  437. ;;; Message Utilities
  438. (defun magit-commit-message-buffer ()
  439. (let* ((find-file-visit-truename t) ; git uses truename of COMMIT_EDITMSG
  440. (topdir (magit-toplevel)))
  441. (--first (equal topdir (with-current-buffer it
  442. (and git-commit-mode (magit-toplevel))))
  443. (append (buffer-list (selected-frame))
  444. (buffer-list)))))
  445. (defvar magit-commit-add-log-insert-function 'magit-commit-add-log-insert
  446. "Used by `magit-commit-add-log' to insert a single entry.")
  447. (defun magit-commit-add-log ()
  448. "Add a stub for the current change into the commit message buffer.
  449. If no commit is in progress, then initiate it. Use the function
  450. specified by variable `magit-commit-add-log-insert-function' to
  451. actually insert the entry."
  452. (interactive)
  453. (pcase-let* ((hunk (and (magit-section-match 'hunk)
  454. (magit-current-section)))
  455. (log (magit-commit-message-buffer))
  456. (`(,buf ,pos) (magit-diff-visit-file--noselect)))
  457. (unless log
  458. (unless (magit-commit-assert nil)
  459. (user-error "Abort"))
  460. (magit-commit-create)
  461. (while (not (setq log (magit-commit-message-buffer)))
  462. (sit-for 0.01)))
  463. (magit--with-temp-position buf pos
  464. (funcall magit-commit-add-log-insert-function log
  465. (magit-file-relative-name)
  466. (and hunk (add-log-current-defun))))))
  467. (defun magit-commit-add-log-insert (buffer file defun)
  468. (with-current-buffer buffer
  469. (undo-boundary)
  470. (goto-char (point-max))
  471. (while (re-search-backward (concat "^" comment-start) nil t))
  472. (save-restriction
  473. (narrow-to-region (point-min) (point))
  474. (cond ((re-search-backward (format "* %s\\(?: (\\([^)]+\\))\\)?: " file)
  475. nil t)
  476. (when (equal (match-string 1) defun)
  477. (setq defun nil))
  478. (re-search-forward ": "))
  479. (t
  480. (when (re-search-backward "^[\\*(].+\n" nil t)
  481. (goto-char (match-end 0)))
  482. (while (re-search-forward "^[^\\*\n].*\n" nil t))
  483. (if defun
  484. (progn (insert (format "* %s (%s): \n" file defun))
  485. (setq defun nil))
  486. (insert (format "* %s: \n" file)))
  487. (backward-char)
  488. (unless (looking-at "\n[\n\\']")
  489. (insert ?\n)
  490. (backward-char))))
  491. (when defun
  492. (forward-line)
  493. (let ((limit (save-excursion
  494. (and (re-search-forward "^\\*" nil t)
  495. (point)))))
  496. (unless (or (looking-back (format "(%s): " defun)
  497. (line-beginning-position))
  498. (re-search-forward (format "^(%s): " defun) limit t))
  499. (while (re-search-forward "^[^\\*\n].*\n" limit t))
  500. (insert (format "(%s): \n" defun))
  501. (backward-char)))))))
  502. ;;; _
  503. (provide 'magit-commit)
  504. ;;; magit-commit.el ends here