dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

magit-commit.el (28940B)


      1 ;;; magit-commit.el --- create Git commits  -*- lexical-binding: t -*-
      2 
      3 ;; Copyright (C) 2008-2021  The Magit Project Contributors
      4 ;;
      5 ;; You should have received a copy of the AUTHORS.md file which
      6 ;; lists all contributors.  If not, see http://magit.vc/authors.
      7 
      8 ;; Author: Jonas Bernoulli <jonas@bernoul.li>
      9 ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
     10 
     11 ;; SPDX-License-Identifier: GPL-3.0-or-later
     12 
     13 ;; Magit is free software; you can redistribute it and/or modify it
     14 ;; under the terms of the GNU General Public License as published by
     15 ;; the Free Software Foundation; either version 3, or (at your option)
     16 ;; any later version.
     17 ;;
     18 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
     19 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     20 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     21 ;; License for more details.
     22 ;;
     23 ;; You should have received a copy of the GNU General Public License
     24 ;; along with Magit.  If not, see http://www.gnu.org/licenses.
     25 
     26 ;;; Commentary:
     27 
     28 ;; This library implements commands for creating Git commits.  These
     29 ;; commands just initiate the commit, support for writing the commit
     30 ;; messages is implemented in `git-commit.el'.
     31 
     32 ;;; Code:
     33 
     34 (require 'magit)
     35 (require 'magit-sequence)
     36 
     37 (eval-when-compile (require 'epa)) ; for `epa-protocol'
     38 (eval-when-compile (require 'epg))
     39 
     40 ;;; Options
     41 
     42 (defcustom magit-commit-ask-to-stage 'verbose
     43   "Whether to ask to stage everything when committing and nothing is staged."
     44   :package-version '(magit . "2.3.0")
     45   :group 'magit-commands
     46   :type '(choice (const :tag "Ask" t)
     47                  (const :tag "Ask showing diff" verbose)
     48                  (const :tag "Stage without confirmation" stage)
     49                  (const :tag "Don't ask" nil)))
     50 
     51 (defcustom magit-commit-show-diff t
     52   "Whether the relevant diff is automatically shown when committing."
     53   :package-version '(magit . "2.3.0")
     54   :group 'magit-commands
     55   :type 'boolean)
     56 
     57 (defcustom magit-commit-extend-override-date t
     58   "Whether using `magit-commit-extend' changes the committer date."
     59   :package-version '(magit . "2.3.0")
     60   :group 'magit-commands
     61   :type 'boolean)
     62 
     63 (defcustom magit-commit-reword-override-date t
     64   "Whether using `magit-commit-reword' changes the committer date."
     65   :package-version '(magit . "2.3.0")
     66   :group 'magit-commands
     67   :type 'boolean)
     68 
     69 (defcustom magit-commit-squash-confirm t
     70   "Whether the commit targeted by squash and fixup has to be confirmed.
     71 When non-nil then the commit at point (if any) is used as default
     72 choice, otherwise it has to be confirmed.  This option only
     73 affects `magit-commit-squash' and `magit-commit-fixup'.  The
     74 \"instant\" variants always require confirmation because making
     75 an error while using those is harder to recover from."
     76   :package-version '(magit . "2.1.0")
     77   :group 'magit-commands
     78   :type 'boolean)
     79 
     80 (defcustom magit-post-commit-hook nil
     81   "Hook run after creating a commit without the user editing a message.
     82 
     83 This hook is run by `magit-refresh' if `this-command' is a member
     84 of `magit-post-stage-hook-commands'.  This only includes commands
     85 named `magit-commit-*' that do *not* require that the user edits
     86 the commit message in a buffer and then finishes by pressing
     87 \\<with-editor-mode-map>\\[with-editor-finish].
     88 
     89 Also see `git-commit-post-finish-hook'."
     90   :package-version '(magit . "2.90.0")
     91   :group 'magit-commands
     92   :type 'hook)
     93 
     94 (defcustom magit-commit-diff-inhibit-same-window nil
     95   "Whether to inhibit use of same window when showing diff while committing.
     96 
     97 When writing a commit, then a diff of the changes to be committed
     98 is automatically shown.  The idea is that the diff is shown in a
     99 different window of the same frame and for most users that just
    100 works.  In other words most users can completely ignore this
    101 option because its value doesn't make a difference for them.
    102 
    103 However for users who configured Emacs to never create a new
    104 window even when the package explicitly tries to do so, then
    105 displaying two new buffers necessarily means that the first is
    106 immediately replaced by the second.  In our case the message
    107 buffer is immediately replaced by the diff buffer, which is of
    108 course highly undesirable.
    109 
    110 A workaround is to suppress this user configuration in this
    111 particular case.  Users have to explicitly opt-in by toggling
    112 this option.  We cannot enable the workaround unconditionally
    113 because that again causes issues for other users: if the frame
    114 is too tiny or the relevant settings too aggressive, then the
    115 diff buffer would end up being displayed in a new frame.
    116 
    117 Also see https://github.com/magit/magit/issues/4132."
    118   :package-version '(magit . "3.3.0")
    119   :group 'magit-commands
    120   :type 'boolean)
    121 
    122 (defvar magit-post-commit-hook-commands
    123   '(magit-commit-extend
    124     magit-commit-fixup
    125     magit-commit-augment
    126     magit-commit-instant-fixup
    127     magit-commit-instant-squash))
    128 
    129 ;;; Popup
    130 
    131 ;;;###autoload (autoload 'magit-commit "magit-commit" nil t)
    132 (transient-define-prefix magit-commit ()
    133   "Create a new commit or replace an existing commit."
    134   :info-manual "(magit)Initiating a Commit"
    135   :man-page "git-commit"
    136   ["Arguments"
    137    ("-a" "Stage all modified and deleted files"   ("-a" "--all"))
    138    ("-e" "Allow empty commit"                     "--allow-empty")
    139    ("-v" "Show diff of changes to be committed"   ("-v" "--verbose"))
    140    ("-n" "Disable hooks"                          ("-n" "--no-verify"))
    141    ("-R" "Claim authorship and reset author date" "--reset-author")
    142    (magit:--author :description "Override the author")
    143    (7 "-D" "Override the author date" "--date=" transient-read-date)
    144    ("-s" "Add Signed-off-by line"                 ("-s" "--signoff"))
    145    (5 magit:--gpg-sign)
    146    (magit-commit:--reuse-message)]
    147   [["Create"
    148     ("c" "Commit"         magit-commit-create)]
    149    ["Edit HEAD"
    150     ("e" "Extend"         magit-commit-extend)
    151     ("w" "Reword"         magit-commit-reword)
    152     ("a" "Amend"          magit-commit-amend)
    153     (6 "n" "Reshelve"     magit-commit-reshelve)]
    154    ["Edit"
    155     ("f" "Fixup"          magit-commit-fixup)
    156     ("s" "Squash"         magit-commit-squash)
    157     ("A" "Augment"        magit-commit-augment)
    158     (6 "x" "Absorb changes" magit-commit-autofixup)
    159     (6 "X" "Absorb modules" magit-commit-absorb-modules)]
    160    [""
    161     ("F" "Instant fixup"  magit-commit-instant-fixup)
    162     ("S" "Instant squash" magit-commit-instant-squash)]]
    163   (interactive)
    164   (if-let ((buffer (magit-commit-message-buffer)))
    165       (switch-to-buffer buffer)
    166     (transient-setup 'magit-commit)))
    167 
    168 (defun magit-commit-arguments nil
    169   (transient-args 'magit-commit))
    170 
    171 (transient-define-argument magit:--gpg-sign ()
    172   :description "Sign using gpg"
    173   :class 'transient-option
    174   :shortarg "-S"
    175   :argument "--gpg-sign="
    176   :allow-empty t
    177   :reader 'magit-read-gpg-signing-key)
    178 
    179 (defvar magit-gpg-secret-key-hist nil)
    180 
    181 (defun magit-read-gpg-secret-key
    182     (prompt &optional initial-input history predicate)
    183   (require 'epa)
    184   (let* ((keys (mapcan
    185                 (lambda (cert)
    186                   (and (or (not predicate)
    187                            (funcall predicate cert))
    188                        (let* ((key (car (epg-key-sub-key-list cert)))
    189                               (fpr (epg-sub-key-fingerprint key))
    190                               (id  (epg-sub-key-id key))
    191                               (author
    192                                (when-let ((id-obj
    193                                            (car (epg-key-user-id-list cert))))
    194                                  (let ((id-str (epg-user-id-string id-obj)))
    195                                    (if (stringp id-str)
    196                                        id-str
    197                                      (epg-decode-dn id-obj))))))
    198                          (list
    199                           (propertize fpr 'display
    200                                       (concat (substring fpr 0 (- (length id)))
    201                                               (propertize id 'face 'highlight)
    202                                               " " author))))))
    203                 (epg-list-keys (epg-make-context epa-protocol) nil t)))
    204          (choice (completing-read prompt keys nil nil nil
    205                                   history nil initial-input)))
    206     (set-text-properties 0 (length choice) nil choice)
    207     choice))
    208 
    209 (defun magit-read-gpg-signing-key (prompt &optional initial-input history)
    210   (magit-read-gpg-secret-key
    211    prompt initial-input history
    212    (lambda (cert)
    213      (cl-some (lambda (key)
    214                 (memq 'sign (epg-sub-key-capability key)))
    215               (epg-key-sub-key-list cert)))))
    216 
    217 (transient-define-argument magit-commit:--reuse-message ()
    218   :description "Reuse commit message"
    219   :class 'transient-option
    220   :shortarg "-C"
    221   :argument "--reuse-message="
    222   :reader 'magit-read-reuse-message
    223   :history-key 'magit-revision-history)
    224 
    225 (defun magit-read-reuse-message (prompt &optional default history)
    226   (magit-completing-read prompt (magit-list-refnames)
    227                          nil nil nil history
    228                          (or default
    229                              (and (magit-rev-verify "ORIG_HEAD")
    230                                   "ORIG_HEAD"))))
    231 
    232 ;;; Commands
    233 
    234 ;;;###autoload
    235 (defun magit-commit-create (&optional args)
    236   "Create a new commit on `HEAD'.
    237 With a prefix argument, amend to the commit at `HEAD' instead.
    238 \n(git commit [--amend] ARGS)"
    239   (interactive (if current-prefix-arg
    240                    (list (cons "--amend" (magit-commit-arguments)))
    241                  (list (magit-commit-arguments))))
    242   (when (member "--all" args)
    243     (setq this-command 'magit-commit-all))
    244   (when (setq args (magit-commit-assert args))
    245     (let ((default-directory (magit-toplevel)))
    246       (magit-run-git-with-editor "commit" args))))
    247 
    248 ;;;###autoload
    249 (defun magit-commit-amend (&optional args)
    250   "Amend the last commit.
    251 \n(git commit --amend ARGS)"
    252   (interactive (list (magit-commit-arguments)))
    253   (magit-commit-amend-assert)
    254   (magit-run-git-with-editor "commit" "--amend" args))
    255 
    256 ;;;###autoload
    257 (defun magit-commit-extend (&optional args override-date)
    258   "Amend the last commit, without editing the message.
    259 
    260 With a prefix argument keep the committer date, otherwise change
    261 it.  The option `magit-commit-extend-override-date' can be used
    262 to inverse the meaning of the prefix argument.  \n(git commit
    263 --amend --no-edit)"
    264   (interactive (list (magit-commit-arguments)
    265                      (if current-prefix-arg
    266                          (not magit-commit-extend-override-date)
    267                        magit-commit-extend-override-date)))
    268   (when (setq args (magit-commit-assert args))
    269     (magit-commit-amend-assert)
    270     (let ((process-environment process-environment))
    271       (unless override-date
    272         (push (magit-rev-format "GIT_COMMITTER_DATE=%cD") process-environment))
    273       (magit-run-git-with-editor "commit" "--amend" "--no-edit" args))))
    274 
    275 ;;;###autoload
    276 (defun magit-commit-reword (&optional args override-date)
    277   "Reword the last commit, ignoring staged changes.
    278 
    279 With a prefix argument keep the committer date, otherwise change
    280 it.  The option `magit-commit-reword-override-date' can be used
    281 to inverse the meaning of the prefix argument.
    282 
    283 Non-interactively respect the optional OVERRIDE-DATE argument
    284 and ignore the option.
    285 \n(git commit --amend --only)"
    286   (interactive (list (magit-commit-arguments)
    287                      (if current-prefix-arg
    288                          (not magit-commit-reword-override-date)
    289                        magit-commit-reword-override-date)))
    290   (magit-commit-amend-assert)
    291   (let ((process-environment process-environment))
    292     (unless override-date
    293       (push (magit-rev-format "GIT_COMMITTER_DATE=%cD") process-environment))
    294     (cl-pushnew "--allow-empty" args :test #'equal)
    295     (magit-run-git-with-editor "commit" "--amend" "--only" args)))
    296 
    297 ;;;###autoload
    298 (defun magit-commit-fixup (&optional commit args)
    299   "Create a fixup commit.
    300 
    301 With a prefix argument the target COMMIT has to be confirmed.
    302 Otherwise the commit at point may be used without confirmation
    303 depending on the value of option `magit-commit-squash-confirm'."
    304   (interactive (list (magit-commit-at-point)
    305                      (magit-commit-arguments)))
    306   (magit-commit-squash-internal "--fixup" commit args))
    307 
    308 ;;;###autoload
    309 (defun magit-commit-squash (&optional commit args)
    310   "Create a squash commit, without editing the squash message.
    311 
    312 With a prefix argument the target COMMIT has to be confirmed.
    313 Otherwise the commit at point may be used without confirmation
    314 depending on the value of option `magit-commit-squash-confirm'.
    315 
    316 If you want to immediately add a message to the squash commit,
    317 then use `magit-commit-augment' instead of this command."
    318   (interactive (list (magit-commit-at-point)
    319                      (magit-commit-arguments)))
    320   (magit-commit-squash-internal "--squash" commit args))
    321 
    322 ;;;###autoload
    323 (defun magit-commit-augment (&optional commit args)
    324   "Create a squash commit, editing the squash message.
    325 
    326 With a prefix argument the target COMMIT has to be confirmed.
    327 Otherwise the commit at point may be used without confirmation
    328 depending on the value of option `magit-commit-squash-confirm'."
    329   (interactive (list (magit-commit-at-point)
    330                      (magit-commit-arguments)))
    331   (magit-commit-squash-internal "--squash" commit args nil t))
    332 
    333 ;;;###autoload
    334 (defun magit-commit-instant-fixup (&optional commit args)
    335   "Create a fixup commit targeting COMMIT and instantly rebase."
    336   (interactive (list (magit-commit-at-point)
    337                      (magit-commit-arguments)))
    338   (magit-commit-squash-internal "--fixup" commit args t))
    339 
    340 ;;;###autoload
    341 (defun magit-commit-instant-squash (&optional commit args)
    342   "Create a squash commit targeting COMMIT and instantly rebase."
    343   (interactive (list (magit-commit-at-point)
    344                      (magit-commit-arguments)))
    345   (magit-commit-squash-internal "--squash" commit args t))
    346 
    347 (defun magit-commit-squash-internal
    348     (option commit &optional args rebase edit confirmed)
    349   (when-let ((args (magit-commit-assert args (not edit))))
    350     (when commit
    351       (when (and rebase (not (magit-rev-ancestor-p commit "HEAD")))
    352         (magit-read-char-case
    353             (format "%s isn't an ancestor of HEAD.  " commit) nil
    354           (?c "[c]reate without rebasing" (setq rebase nil))
    355           (?s "[s]elect other"            (setq commit nil))
    356           (?a "[a]bort"                   (user-error "Quit")))))
    357     (when commit
    358       (setq commit (magit-rebase-interactive-assert commit t)))
    359     (if (and commit
    360              (or confirmed
    361                  (not (or rebase
    362                           current-prefix-arg
    363                           magit-commit-squash-confirm))))
    364         (let ((magit-commit-show-diff nil))
    365           (push (concat option "=" commit) args)
    366           (unless edit
    367             (push "--no-edit" args))
    368           (if rebase
    369               (magit-with-editor
    370                 (magit-call-git
    371                  "commit" "--no-gpg-sign"
    372                  (-remove-first
    373                   (apply-partially #'string-match-p "\\`--gpg-sign=")
    374                   args)))
    375             (magit-run-git-with-editor "commit" args))
    376           t) ; The commit was created; used by below lambda.
    377       (magit-log-select
    378         (lambda (commit)
    379           (when (and (magit-commit-squash-internal option commit args
    380                                                    rebase edit t)
    381                      rebase)
    382             (magit-commit-amend-assert commit)
    383             (magit-rebase-interactive-1 commit
    384                 (list "--autosquash" "--autostash" "--keep-empty")
    385               "" "true" nil t)))
    386         (format "Type %%p on a commit to %s into it,"
    387                 (substring option 2))
    388         nil nil nil commit)
    389       (when magit-commit-show-diff
    390         (let ((magit-display-buffer-noselect t))
    391           (apply #'magit-diff-staged nil (magit-diff-arguments)))))))
    392 
    393 (defun magit-commit-amend-assert (&optional commit)
    394   (--when-let (magit-list-publishing-branches commit)
    395     (let ((m1 "This commit has already been published to ")
    396           (m2 ".\nDo you really want to modify it"))
    397       (magit-confirm 'amend-published
    398         (concat m1 "%s" m2)
    399         (concat m1 "%i public branches" m2)
    400         nil it))))
    401 
    402 (defun magit-commit-assert (args &optional strict)
    403   (cond
    404    ((or (magit-anything-staged-p)
    405         (and (magit-anything-unstaged-p)
    406              ;; ^ Everything of nothing is still nothing.
    407              (member "--all" args))
    408         (and (not strict)
    409              ;; ^ For amend variants that don't make sense otherwise.
    410              (or (member "--amend" args)
    411                  (member "--allow-empty" args)
    412                  (member "--reset-author" args)
    413                  (member "--signoff" args)
    414                  (transient-arg-value "--author=" args)
    415                  (transient-arg-value "--date=" args))))
    416     (or args (list "--")))
    417    ((and (magit-rebase-in-progress-p)
    418          (not (magit-anything-unstaged-p))
    419          (y-or-n-p "Nothing staged.  Continue in-progress rebase? "))
    420     (setq this-command 'magit-rebase-continue)
    421     (magit-run-git-sequencer "rebase" "--continue")
    422     nil)
    423    ((and (file-exists-p (magit-git-dir "MERGE_MSG"))
    424          (not (magit-anything-unstaged-p)))
    425     (or args (list "--")))
    426    ((not (magit-anything-unstaged-p))
    427     (user-error "Nothing staged (or unstaged)"))
    428    (magit-commit-ask-to-stage
    429     (when (eq magit-commit-ask-to-stage 'verbose)
    430       (magit-diff-unstaged))
    431     (prog1 (when (or (eq magit-commit-ask-to-stage 'stage)
    432                      (y-or-n-p "Nothing staged.  Stage and commit all unstaged changes? "))
    433              (magit-run-git "add" "-u" ".")
    434              (or args (list "--")))
    435       (when (and (eq magit-commit-ask-to-stage 'verbose)
    436                  (derived-mode-p 'magit-diff-mode))
    437         (magit-mode-bury-buffer))))
    438    (t
    439     (user-error "Nothing staged"))))
    440 
    441 (defvar magit--reshelve-history nil)
    442 
    443 ;;;###autoload
    444 (defun magit-commit-reshelve (date update-author &optional args)
    445   "Change the committer date and possibly the author date of `HEAD'.
    446 
    447 The current time is used as the initial minibuffer input and the
    448 original author or committer date is available as the previous
    449 history element.
    450 
    451 Both the author and the committer dates are changes, unless one
    452 of the following is true, in which case only the committer date
    453 is updated:
    454 - You are not the author of the commit that is being reshelved.
    455 - The command was invoked with a prefix argument.
    456 - Non-interactively if UPDATE-AUTHOR is nil."
    457   (interactive
    458    (let ((update-author (and (magit-rev-author-p "HEAD")
    459                              (not current-prefix-arg))))
    460      (push (magit-rev-format (if update-author "%ad" "%cd") "HEAD"
    461                              (concat "--date=format:%F %T %z"))
    462            magit--reshelve-history)
    463      (list (read-string (if update-author
    464                             "Change author and committer dates to: "
    465                           "Change committer date to: ")
    466                         (cons (format-time-string "%F %T %z") 17)
    467                         'magit--reshelve-history)
    468            update-author
    469            (magit-commit-arguments))))
    470   (let ((process-environment process-environment))
    471     (push (concat "GIT_COMMITTER_DATE=" date) process-environment)
    472     (magit-run-git "commit" "--amend" "--no-edit"
    473                    (and update-author (concat "--date=" date))
    474                    args)))
    475 
    476 ;;;###autoload
    477 (defun magit-commit-absorb-modules (phase commit)
    478   "Spread modified modules across recent commits."
    479   (interactive (list 'select (magit-get-upstream-branch)))
    480   (let ((modules (magit-list-modified-modules)))
    481     (unless modules
    482       (user-error "There are no modified modules that could be absorbed"))
    483     (when commit
    484       (setq commit (magit-rebase-interactive-assert commit t)))
    485     (if (and commit (eq phase 'run))
    486         (progn
    487           (dolist (module modules)
    488             (when-let ((msg (magit-git-string
    489                              "log" "-1" "--format=%s"
    490                              (concat commit "..") "--" module)))
    491               (magit-git "commit" "-m" (concat "fixup! " msg)
    492                          "--only" "--" module)))
    493           (magit-refresh)
    494           t)
    495       (magit-log-select
    496         (lambda (commit)
    497           (magit-commit-absorb-modules 'run commit))
    498         nil nil nil nil commit))))
    499 
    500 ;;;###autoload (autoload 'magit-commit-absorb "magit-commit" nil t)
    501 (transient-define-prefix magit-commit-absorb (phase commit args)
    502   "Spread staged changes across recent commits.
    503 With a prefix argument use a transient command to select infix
    504 arguments.  This command requires git-absorb executable, which
    505 is available from https://github.com/tummychow/git-absorb.
    506 See `magit-commit-autofixup' for an alternative implementation."
    507   ["Arguments"
    508    ("-f" "Skip safety checks"       ("-f" "--force"))
    509    ("-v" "Display more output"      ("-v" "--verbose"))]
    510   ["Actions"
    511    ("x"  "Absorb" magit-commit-absorb)]
    512   (interactive (if current-prefix-arg
    513                    (list 'transient nil nil)
    514                  (list 'select
    515                        (magit-get-upstream-branch)
    516                        (transient-args 'magit-commit-absorb))))
    517   (if (eq phase 'transient)
    518       (transient-setup 'magit-commit-absorb)
    519     (unless (executable-find "git-absorb")
    520       (user-error "This command requires the git-absorb executable, which %s"
    521                   "is available from https://github.com/tummychow/git-absorb"))
    522     (unless (magit-anything-staged-p)
    523       (if (magit-anything-unstaged-p)
    524           (if (y-or-n-p "Nothing staged.  Absorb all unstaged changes? ")
    525               (magit-with-toplevel
    526                 (magit-run-git "add" "-u" "."))
    527             (user-error "Abort"))
    528         (user-error "There are no changes that could be absorbed")))
    529     (when commit
    530       (setq commit (magit-rebase-interactive-assert commit t)))
    531     (if (and commit (eq phase 'run))
    532         (progn (magit-run-git-async "absorb" "-v" args "-b" commit) t)
    533       (magit-log-select
    534         (lambda (commit)
    535           (with-no-warnings ; about non-interactive use
    536             (magit-commit-absorb 'run commit args)))
    537         nil nil nil nil commit))))
    538 
    539 ;;;###autoload (autoload 'magit-commit-autofixup "magit-commit" nil t)
    540 (transient-define-prefix magit-commit-autofixup (phase commit args)
    541   "Spread staged or unstaged changes across recent commits.
    542 
    543 If there are any staged then spread only those, otherwise
    544 spread all unstaged changes. With a prefix argument use a
    545 transient command to select infix arguments.
    546 
    547 This command requires the git-autofixup script, which is
    548 available from https://github.com/torbiak/git-autofixup.
    549 See `magit-commit-absorb' for an alternative implementation."
    550   ["Arguments"
    551    (magit-autofixup:--context)
    552    (magit-autofixup:--strict)]
    553   ["Actions"
    554    ("x"  "Absorb" magit-commit-autofixup)]
    555   (interactive (if current-prefix-arg
    556                    (list 'transient nil nil)
    557                  (list 'select
    558                        (magit-get-upstream-branch)
    559                        (transient-args 'magit-commit-autofixup))))
    560   (if (eq phase 'transient)
    561       (transient-setup 'magit-commit-autofixup)
    562     (unless (executable-find "git-autofixup")
    563       (user-error "This command requires the git-autofixup script, which %s"
    564                   "is available from https://github.com/torbiak/git-autofixup"))
    565     (unless (magit-anything-modified-p)
    566       (user-error "There are no changes that could be absorbed"))
    567     (when commit
    568       (setq commit (magit-rebase-interactive-assert commit t)))
    569     (if (and commit (eq phase 'run))
    570         (progn (magit-run-git-async "autofixup" "-vv" args commit) t)
    571       (magit-log-select
    572         (lambda (commit)
    573           (with-no-warnings ; about non-interactive use
    574             (magit-commit-autofixup 'run commit args)))
    575         nil nil nil nil commit))))
    576 
    577 (transient-define-argument magit-autofixup:--context ()
    578   :description "Diff context lines"
    579   :class 'transient-option
    580   :shortarg "-c"
    581   :argument "--context="
    582   :reader 'transient-read-number-N0)
    583 
    584 (transient-define-argument magit-autofixup:--strict ()
    585   :description "Strictness"
    586   :class 'transient-option
    587   :shortarg "-s"
    588   :argument "--strict="
    589   :reader 'transient-read-number-N0)
    590 
    591 ;;; Pending Diff
    592 
    593 (defun magit-commit-diff ()
    594   (when (and git-commit-mode magit-commit-show-diff)
    595     (when-let ((diff-buffer (magit-get-mode-buffer 'magit-diff-mode)))
    596       ;; This window just started displaying the commit message
    597       ;; buffer.  Without this that buffer would immediately be
    598       ;; replaced with the diff buffer.  See #2632.
    599       (unrecord-window-buffer nil diff-buffer))
    600     (condition-case nil
    601         (let ((args (car (magit-diff-arguments)))
    602               (magit-inhibit-save-previous-winconf 'unset)
    603               (magit-display-buffer-noselect t)
    604               (inhibit-quit nil)
    605               (display-buffer-overriding-action
    606                display-buffer-overriding-action))
    607           (when magit-commit-diff-inhibit-same-window
    608             (setq display-buffer-overriding-action
    609                   '(nil (inhibit-same-window t))))
    610           (message "Diffing changes to be committed (C-g to abort diffing)")
    611           (cl-case last-command
    612             (magit-commit
    613              (magit-diff-staged nil args))
    614             (magit-commit-all
    615              (magit-diff-working-tree nil args))
    616             ((magit-commit-amend
    617               magit-commit-reword
    618               magit-rebase-reword-commit)
    619              (magit-diff-while-amending args))
    620             (t (if (magit-anything-staged-p)
    621                    (magit-diff-staged nil args)
    622                  (magit-diff-while-amending args)))))
    623       (quit))))
    624 
    625 ;; Mention `magit-diff-while-committing' because that's
    626 ;; always what I search for when I try to find this line.
    627 (add-hook 'server-switch-hook 'magit-commit-diff)
    628 (add-hook 'with-editor-filter-visit-hook 'magit-commit-diff)
    629 
    630 (add-to-list 'with-editor-server-window-alist
    631              (cons git-commit-filename-regexp 'switch-to-buffer))
    632 
    633 ;;; Message Utilities
    634 
    635 (defun magit-commit-message-buffer ()
    636   (let* ((find-file-visit-truename t) ; git uses truename of COMMIT_EDITMSG
    637          (topdir (magit-toplevel)))
    638     (--first (equal topdir (with-current-buffer it
    639                              (and git-commit-mode (magit-toplevel))))
    640              (append (buffer-list (selected-frame))
    641                      (buffer-list)))))
    642 
    643 (defvar magit-commit-add-log-insert-function 'magit-commit-add-log-insert
    644   "Used by `magit-commit-add-log' to insert a single entry.")
    645 
    646 (defun magit-commit-add-log ()
    647   "Add a stub for the current change into the commit message buffer.
    648 If no commit is in progress, then initiate it.  Use the function
    649 specified by variable `magit-commit-add-log-insert-function' to
    650 actually insert the entry."
    651   (interactive)
    652   (pcase-let* ((hunk (and (magit-section-match 'hunk)
    653                           (magit-current-section)))
    654                (log  (magit-commit-message-buffer))
    655                (`(,buf ,pos) (magit-diff-visit-file--noselect)))
    656     (unless log
    657       (unless (magit-commit-assert nil)
    658         (user-error "Abort"))
    659       (magit-commit-create)
    660       (while (not (setq log (magit-commit-message-buffer)))
    661         (sit-for 0.01)))
    662     (magit--with-temp-position buf pos
    663       (funcall magit-commit-add-log-insert-function log
    664                (magit-file-relative-name)
    665                (and hunk (add-log-current-defun))))))
    666 
    667 (defun magit-commit-add-log-insert (buffer file defun)
    668   (with-current-buffer buffer
    669     (undo-boundary)
    670     (goto-char (point-max))
    671     (while (re-search-backward (concat "^" comment-start) nil t))
    672     (save-restriction
    673       (narrow-to-region (point-min) (point))
    674       (cond ((re-search-backward (format "* %s\\(?: (\\([^)]+\\))\\)?: " file)
    675                                  nil t)
    676              (when (equal (match-string 1) defun)
    677                (setq defun nil))
    678              (re-search-forward ": "))
    679             (t
    680              (when (re-search-backward "^[\\*(].+\n" nil t)
    681                (goto-char (match-end 0)))
    682              (while (re-search-forward "^[^\\*\n].*\n" nil t))
    683              (if defun
    684                  (progn (insert (format "* %s (%s): \n" file defun))
    685                         (setq defun nil))
    686                (insert (format "* %s: \n" file)))
    687              (backward-char)
    688              (unless (looking-at "\n[\n\\']")
    689                (insert ?\n)
    690                (backward-char))))
    691       (when defun
    692         (forward-line)
    693         (let ((limit (save-excursion
    694                        (and (re-search-forward "^\\*" nil t)
    695                             (point)))))
    696           (unless (or (looking-back (format "(%s): " defun)
    697                                     (line-beginning-position))
    698                       (re-search-forward (format "^(%s): " defun) limit t))
    699             (while (re-search-forward "^[^\\*\n].*\n" limit t))
    700             (insert (format "(%s): \n" defun))
    701             (backward-char)))))))
    702 
    703 ;;; _
    704 (provide 'magit-commit)
    705 ;;; magit-commit.el ends here