config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

magit-commit.el (29501B)


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