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