config

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

magit-commit.el (29478B)


      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 (and commit rebase (not (magit-rev-ancestor-p commit "HEAD")))
    296       (magit-read-char-case
    297           (format "%s isn't an ancestor of HEAD.  " commit) nil
    298         (?c "[c]reate without rebasing" (setq rebase nil))
    299         (?s "[s]elect other"            (setq commit nil))
    300         (?a "[a]bort"                   (user-error "Quit"))))
    301     (when commit
    302       (setq commit (magit-rebase-interactive-assert commit t)))
    303     (if (and commit
    304              (or confirmed
    305                  (not (or rebase
    306                           current-prefix-arg
    307                           magit-commit-squash-confirm))))
    308         (let ((magit-commit-show-diff nil))
    309           (push (concat option "=" commit) args)
    310           (unless edit
    311             (push "--no-edit" args))
    312           (if rebase
    313               (magit-with-editor
    314                 (magit-call-git
    315                  "commit" "--no-gpg-sign"
    316                  (seq-remove (apply-partially #'string-prefix-p "--gpg-sign=")
    317                              args)))
    318             (magit-run-git-with-editor "commit" args))
    319           t) ; The commit was created; used by below lambda.
    320       (let ((winconf (and magit-commit-show-diff
    321                           (current-window-configuration))))
    322         (magit-log-select
    323           (lambda (commit)
    324             (when (and (magit-commit-squash-internal option commit args
    325                                                      rebase edit t)
    326                        rebase)
    327               (magit-commit-amend-assert commit)
    328               (magit-rebase-interactive-1 commit
    329                   (list "--autosquash" "--autostash" "--keep-empty")
    330                 "" "true" nil t))
    331             (when winconf
    332               (set-window-configuration winconf)))
    333           (format "Type %%p on a commit to %s into it,"
    334                   (substring option 2))
    335           nil nil nil commit))
    336       (when magit-commit-show-diff
    337         (let ((magit-display-buffer-noselect t))
    338           (apply #'magit-diff-staged nil (magit-diff-arguments)))))))
    339 
    340 (defun magit-commit-amend-assert (&optional commit)
    341   (when-let ((branches (magit-list-publishing-branches commit)))
    342     (let ((m1 "This commit has already been published to ")
    343           (m2 ".\nDo you really want to modify it"))
    344       (magit-confirm 'amend-published
    345         (concat m1 "%s" m2)
    346         (concat m1 "%d public branches" m2)
    347         nil branches))))
    348 
    349 (defun magit-commit-assert (args &optional strict)
    350   (cond
    351    ((or (magit-anything-staged-p)
    352         (and (magit-anything-unstaged-p)
    353              ;; ^ Everything of nothing is still nothing.
    354              (member "--all" args))
    355         (and (not strict)
    356              ;; ^ For amend variants that don't make sense otherwise.
    357              (or (member "--amend" args)
    358                  (member "--allow-empty" args)
    359                  (member "--reset-author" args)
    360                  (member "--signoff" args)
    361                  (transient-arg-value "--author=" args)
    362                  (transient-arg-value "--date=" args))))
    363     (or args (list "--")))
    364    ((and (magit-rebase-in-progress-p)
    365          (not (magit-anything-unstaged-p))
    366          (y-or-n-p "Nothing staged.  Continue in-progress rebase? "))
    367     (setq this-command #'magit-rebase-continue)
    368     (magit-run-git-sequencer "rebase" "--continue")
    369     nil)
    370    ((file-exists-p (expand-file-name "MERGE_MSG" (magit-gitdir)))
    371     (cond ((magit-anything-unmerged-p)
    372            (user-error "Unresolved conflicts"))
    373           ((and (magit-anything-unstaged-p)
    374                 (not (y-or-n-p
    375                       "Proceed with merge despite unstaged changes? ")))
    376            (user-error "Abort"))
    377           ((or args (list "--")))))
    378    ((not (magit-anything-unstaged-p))
    379     (user-error "Nothing staged (or unstaged)"))
    380    (magit-commit-ask-to-stage
    381     (when (eq magit-commit-ask-to-stage 'verbose)
    382       (magit-diff-unstaged))
    383     (prog1 (when (or (eq magit-commit-ask-to-stage 'stage)
    384                      (y-or-n-p
    385                       "Nothing staged.  Commit all uncommitted changes? "))
    386              (setq this-command 'magit-commit--all)
    387              (cons "--all" (or args (list "--"))))
    388       (when (and (eq magit-commit-ask-to-stage 'verbose)
    389                  (derived-mode-p 'magit-diff-mode))
    390         (magit-mode-bury-buffer))))
    391    (t
    392     (user-error "Nothing staged"))))
    393 
    394 (defvar magit--reshelve-history nil)
    395 
    396 ;;;###autoload
    397 (defun magit-commit-reshelve (date update-author &optional args)
    398   "Change the committer date and possibly the author date of `HEAD'.
    399 
    400 The current time is used as the initial minibuffer input and the
    401 original author or committer date is available as the previous
    402 history element.
    403 
    404 Both the author and the committer dates are changed, unless one
    405 of the following is true, in which case only the committer date
    406 is updated:
    407 - You are not the author of the commit that is being reshelved.
    408 - The command was invoked with a prefix argument.
    409 - Non-interactively if UPDATE-AUTHOR is nil."
    410   (interactive
    411    (let ((update-author (and (magit-rev-author-p "HEAD")
    412                              (not current-prefix-arg))))
    413      (push (magit-rev-format (if update-author "%ad" "%cd") "HEAD"
    414                              (concat "--date=format:%F %T %z"))
    415            magit--reshelve-history)
    416      (list (read-string (if update-author
    417                             "Change author and committer dates to: "
    418                           "Change committer date to: ")
    419                         (cons (format-time-string "%F %T %z") 17)
    420                         'magit--reshelve-history)
    421            update-author
    422            (magit-commit-arguments))))
    423   (with-environment-variables (("GIT_COMMITTER_DATE" date))
    424     (magit-run-git "commit" "--amend" "--no-edit"
    425                    (and update-author (concat "--date=" date))
    426                    args)))
    427 
    428 ;;;###autoload
    429 (defun magit-commit-absorb-modules (phase commit)
    430   "Spread modified modules across recent commits."
    431   (interactive (list 'select (magit-get-upstream-branch)))
    432   (let ((modules (magit-list-modified-modules)))
    433     (unless modules
    434       (user-error "There are no modified modules that could be absorbed"))
    435     (when commit
    436       (setq commit (magit-rebase-interactive-assert commit t)))
    437     (if (and commit (eq phase 'run))
    438         (progn
    439           (dolist (module modules)
    440             (when-let ((msg (magit-git-string
    441                              "log" "-1" "--format=%s"
    442                              (concat commit "..") "--" module)))
    443               (magit-git "commit" "-m" (concat "fixup! " msg)
    444                          "--only" "--" module)))
    445           (magit-refresh)
    446           t)
    447       (magit-log-select
    448         (lambda (commit)
    449           (magit-commit-absorb-modules 'run commit))
    450         nil nil nil nil commit))))
    451 
    452 ;;;###autoload (autoload 'magit-commit-absorb "magit-commit" nil t)
    453 (transient-define-prefix magit-commit-absorb (phase commit args)
    454   "Spread staged changes across recent commits.
    455 With a prefix argument use a transient command to select infix
    456 arguments.  This command requires git-absorb executable, which
    457 is available from https://github.com/tummychow/git-absorb.
    458 See `magit-commit-autofixup' for an alternative implementation."
    459   ["Arguments"
    460    ("-f" "Skip safety checks"       ("-f" "--force"))
    461    ("-v" "Display more output"      ("-v" "--verbose"))]
    462   ["Actions"
    463    ("x"  "Absorb" magit-commit-absorb)]
    464   (interactive (if current-prefix-arg
    465                    (list 'transient nil nil)
    466                  (list 'select
    467                        (magit-get-upstream-branch)
    468                        (transient-args 'magit-commit-absorb))))
    469   (if (eq phase 'transient)
    470       (transient-setup 'magit-commit-absorb)
    471     (unless (magit-git-executable-find "git-absorb")
    472       (user-error "This command requires the git-absorb executable, which %s"
    473                   "is available from https://github.com/tummychow/git-absorb"))
    474     (unless (magit-anything-staged-p)
    475       (if (magit-anything-unstaged-p)
    476           (if (y-or-n-p "Nothing staged.  Absorb all unstaged changes? ")
    477               (magit-with-toplevel
    478                 (magit-run-git "add" "-u" "."))
    479             (user-error "Abort"))
    480         (user-error "There are no changes that could be absorbed")))
    481     (when commit
    482       (setq commit (magit-rebase-interactive-assert commit t)))
    483     (if (and commit (eq phase 'run))
    484         (progn (magit-run-git-async "absorb" "-v" args "-b" commit) t)
    485       (magit-log-select
    486         (lambda (commit)
    487           (with-no-warnings ; about non-interactive use
    488             (magit-commit-absorb 'run commit args)))
    489         nil nil nil nil commit))))
    490 
    491 ;;;###autoload (autoload 'magit-commit-autofixup "magit-commit" nil t)
    492 (transient-define-prefix magit-commit-autofixup (phase commit args)
    493   "Spread staged or unstaged changes across recent commits.
    494 
    495 If there are any staged then spread only those, otherwise
    496 spread all unstaged changes. With a prefix argument use a
    497 transient command to select infix arguments.
    498 
    499 This command requires the git-autofixup script, which is
    500 available from https://github.com/torbiak/git-autofixup.
    501 See `magit-commit-absorb' for an alternative implementation."
    502   ["Arguments"
    503    (magit-autofixup:--context)
    504    (magit-autofixup:--strict)]
    505   ["Actions"
    506    ("x"  "Absorb" magit-commit-autofixup)]
    507   (interactive (if current-prefix-arg
    508                    (list 'transient nil nil)
    509                  (list 'select
    510                        (magit-get-upstream-branch)
    511                        (transient-args 'magit-commit-autofixup))))
    512   (if (eq phase 'transient)
    513       (transient-setup 'magit-commit-autofixup)
    514     (unless (magit-git-executable-find "git-autofixup")
    515       (user-error "This command requires the git-autofixup script, which %s"
    516                   "is available from https://github.com/torbiak/git-autofixup"))
    517     (unless (magit-anything-modified-p)
    518       (user-error "There are no changes that could be absorbed"))
    519     (when commit
    520       (setq commit (magit-rebase-interactive-assert commit t)))
    521     (if (and commit (eq phase 'run))
    522         (progn (magit-run-git-async "autofixup" "-vv" args commit) t)
    523       (magit-log-select
    524         (lambda (commit)
    525           (with-no-warnings ; about non-interactive use
    526             (magit-commit-autofixup 'run commit args)))
    527         nil nil nil nil commit))))
    528 
    529 (transient-define-argument magit-autofixup:--context ()
    530   :description "Diff context lines"
    531   :class 'transient-option
    532   :shortarg "-c"
    533   :argument "--context="
    534   :reader #'transient-read-number-N0)
    535 
    536 (transient-define-argument magit-autofixup:--strict ()
    537   :description "Strictness"
    538   :class 'transient-option
    539   :shortarg "-s"
    540   :argument "--strict="
    541   :reader #'transient-read-number-N0)
    542 
    543 (defvar magit-post-commit-hook-commands
    544   '(magit-commit-extend
    545     magit-commit-fixup
    546     magit-commit-augment
    547     magit-commit-instant-fixup
    548     magit-commit-instant-squash))
    549 
    550 (defun magit-run-post-commit-hook ()
    551   (when (and (not this-command)
    552              (memq last-command magit-post-commit-hook-commands))
    553     (run-hooks 'magit-post-commit-hook)))
    554 
    555 ;;; Pending Diff
    556 
    557 (defun magit-commit-diff ()
    558   (magit-repository-local-set 'this-commit-command
    559                               (if (eq this-command 'with-editor-finish)
    560                                   'magit-commit--rebase
    561                                 last-command))
    562   (when (and git-commit-mode magit-commit-show-diff)
    563     (when-let ((diff-buffer (magit-get-mode-buffer 'magit-diff-mode)))
    564       ;; This window just started displaying the commit message
    565       ;; buffer.  Without this that buffer would immediately be
    566       ;; replaced with the diff buffer.  See #2632.
    567       (unrecord-window-buffer nil diff-buffer))
    568     (message "Diffing changes to be committed (C-g to abort diffing)")
    569     (let ((inhibit-quit nil))
    570       (condition-case nil
    571           (magit-commit-diff-1)
    572         (quit)))))
    573 
    574 (defun magit-commit-diff-1 ()
    575   (let ((rev nil)
    576         (arg "--cached")
    577         (command (magit-repository-local-get 'this-commit-command))
    578         (staged (magit-anything-staged-p))
    579         (unstaged
    580          ;; Escape $GIT_DIR because `magit-anything-unstaged-p'
    581          ;; requires a working tree.
    582          (magit-with-toplevel
    583            (magit-anything-unstaged-p)))
    584         (squash (let ((f (expand-file-name "rebase-merge/rewritten-pending"
    585                                            (magit-gitdir))))
    586                   (and (file-exists-p f) (length (magit-file-lines f)))))
    587         (noalt nil))
    588     (pcase (list staged unstaged command)
    589       ((and `(,_ ,_ magit-commit--rebase)
    590             (guard (integerp squash)))
    591        (setq rev (format "HEAD~%s" squash)))
    592       (`(,_ ,_ magit-commit-amend)
    593        (setq rev "HEAD^"))
    594       (`(nil nil magit-commit--allow-empty)
    595        (setq rev "HEAD")
    596        (setq arg nil))
    597       ((or `(,_ ,_ magit-commit-reword)
    598            `(nil nil ,_))
    599        (setq rev "HEAD^..HEAD")
    600        (setq arg nil))
    601       (`(,_ t magit-commit--all)
    602        (setq rev "HEAD")
    603        (setq arg nil))
    604       (`(nil t handle-switch-frame)
    605        ;; Either --all or --allow-empty. Assume it is the former.
    606        (setq rev "HEAD")
    607        (setq arg nil)))
    608     (cond
    609      ((not
    610        (and (eq this-command 'magit-diff-while-committing)
    611             (and-let* ((buf (magit-get-mode-buffer
    612                              'magit-diff-mode nil 'selected)))
    613               (and (equal rev (buffer-local-value 'magit-buffer-range buf))
    614                    (equal arg (buffer-local-value 'magit-buffer-typearg buf)))))))
    615      ((eq command 'magit-commit-amend)
    616       (setq rev nil))
    617      ((or squash
    618           (file-exists-p (expand-file-name "rebase-merge/amend" (magit-gitdir))))
    619       (setq rev "HEAD^"))
    620      (t
    621       (message "No alternative diff while committing")
    622       (setq noalt t)))
    623     (unless noalt
    624       (let ((magit-inhibit-save-previous-winconf 'unset)
    625             (magit-display-buffer-noselect t)
    626             (display-buffer-overriding-action
    627              display-buffer-overriding-action))
    628         (when magit-commit-diff-inhibit-same-window
    629           (setq display-buffer-overriding-action
    630                 '(nil (inhibit-same-window . t))))
    631         (magit-diff-setup-buffer rev arg (car (magit-diff-arguments)) nil
    632                                  (cond ((equal rev "HEAD") 'staged)
    633                                        ((equal rev "HEAD^..HEAD") 'committed)
    634                                        ('undefined)))))))
    635 
    636 (add-hook 'server-switch-hook #'magit-commit-diff)
    637 (add-hook 'with-editor-filter-visit-hook #'magit-commit-diff)
    638 
    639 (add-to-list 'with-editor-server-window-alist
    640              (cons git-commit-filename-regexp #'switch-to-buffer))
    641 
    642 (defun magit-commit--reset-command ()
    643   (magit-repository-local-delete 'this-commit-command))
    644 
    645 ;;; Message Utilities
    646 
    647 (defun magit-commit-message-buffer ()
    648   (let* ((find-file-visit-truename t) ; git uses truename of COMMIT_EDITMSG
    649          (topdir (magit-toplevel)))
    650     (--first (equal topdir (with-current-buffer it
    651                              (and git-commit-mode (magit-toplevel))))
    652              (append (buffer-list (selected-frame))
    653                      (buffer-list)))))
    654 
    655 (defvar magit-commit-add-log-insert-function #'magit-commit-add-log-insert
    656   "Used by `magit-commit-add-log' to insert a single entry.")
    657 
    658 (defun magit-commit-add-log ()
    659   "Add a stub for the current change into the commit message buffer.
    660 If no commit is in progress, then initiate it.  Use the function
    661 specified by variable `magit-commit-add-log-insert-function' to
    662 actually insert the entry."
    663   (interactive)
    664   (pcase-let* ((hunk (and (magit-section-match 'hunk)
    665                           (magit-current-section)))
    666                (log  (magit-commit-message-buffer))
    667                (`(,buf ,pos) (magit-diff-visit-file--noselect)))
    668     (unless log
    669       (unless (magit-commit-assert nil)
    670         (user-error "Abort"))
    671       (magit-commit-create)
    672       (while (not (setq log (magit-commit-message-buffer)))
    673         (sit-for 0.01)))
    674     (magit--with-temp-position buf pos
    675       (funcall magit-commit-add-log-insert-function log
    676                (magit-file-relative-name)
    677                (and hunk (add-log-current-defun))))))
    678 
    679 (defun magit-commit-add-log-insert (buffer file defun)
    680   (with-current-buffer buffer
    681     (undo-boundary)
    682     (goto-char (point-max))
    683     (while (re-search-backward (concat "^" comment-start) nil t))
    684     (save-restriction
    685       (narrow-to-region (point-min) (point))
    686       (cond ((re-search-backward (format "* %s\\(?: (\\([^)]+\\))\\)?: " file)
    687                                  nil t)
    688              (when (equal (match-string 1) defun)
    689                (setq defun nil))
    690              (re-search-forward ": "))
    691             (t
    692              (when (re-search-backward "^[\\*(].+\n" nil t)
    693                (goto-char (match-end 0)))
    694              (while (re-search-forward "^[^\\*\n].*\n" nil t))
    695              (if defun
    696                  (progn (insert (format "* %s (%s): \n" file defun))
    697                         (setq defun nil))
    698                (insert (format "* %s: \n" file)))
    699              (backward-char)
    700              (unless (looking-at "\n[\n\\']")
    701                (insert ?\n)
    702                (backward-char))))
    703       (when defun
    704         (forward-line)
    705         (let ((limit (save-excursion
    706                        (and (re-search-forward "^\\*" nil t)
    707                             (point)))))
    708           (unless (or (looking-back (format "(%s): " defun)
    709                                     (line-beginning-position))
    710                       (re-search-forward (format "^(%s): " defun) limit t))
    711             (while (re-search-forward "^[^\\*\n].*\n" limit t))
    712             (insert (format "(%s): \n" defun))
    713             (backward-char)))))))
    714 
    715 ;;; _
    716 (provide 'magit-commit)
    717 ;;; magit-commit.el ends here