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