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