diff options
author | thing1 <thing1@seacrossedlovers.xyz> | 2025-04-01 20:27:39 +0100 |
---|---|---|
committer | thing1 <thing1@seacrossedlovers.xyz> | 2025-04-01 20:27:39 +0100 |
commit | d3a5ddb4189ef7c04df0cc47a0f9642b23292d2d (patch) | |
tree | 14264483e4d2e6481abc74feea6d9cbcae3666d1 /elpa/magit-4.3.1/magit-commit.el | |
parent | dabaff03992c102c395314629f63ce93a2c1bd3a (diff) |
added magit and other general configs
Diffstat (limited to 'elpa/magit-4.3.1/magit-commit.el')
-rw-r--r-- | elpa/magit-4.3.1/magit-commit.el | 816 |
1 files changed, 816 insertions, 0 deletions
diff --git a/elpa/magit-4.3.1/magit-commit.el b/elpa/magit-4.3.1/magit-commit.el new file mode 100644 index 0000000..e8b8438 --- /dev/null +++ b/elpa/magit-4.3.1/magit-commit.el @@ -0,0 +1,816 @@ +;;; magit-commit.el --- Create Git commits -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2025 The Magit Project Contributors + +;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> +;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit is free software: you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Magit is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Magit. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This library implements commands for creating Git commits. These +;; commands just initiate the commit, support for writing the commit +;; messages is implemented in `git-commit.el'. + +;;; Code: + +(require 'magit) +(require 'magit-sequence) + +;;; Options + +(defcustom magit-commit-ask-to-stage 'verbose + "Whether to ask to stage everything when committing and nothing is staged." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type '(choice (const :tag "Ask" t) + (const :tag "Ask showing diff" verbose) + (const :tag "Stage without confirmation" stage) + (const :tag "Don't ask" nil))) + +(defcustom magit-commit-show-diff t + "Whether the relevant diff is automatically shown when committing." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-extend-override-date t + "Whether using `magit-commit-extend' changes the committer date." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-reword-override-date t + "Whether using `magit-commit-reword' changes the committer date." + :package-version '(magit . "2.3.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-commit-squash-confirm t + "Whether the commit targeted by squash and fixup has to be confirmed. +When non-nil then the commit at point (if any) is used as default +choice, otherwise it has to be confirmed. This option only +affects `magit-commit-squash' and `magit-commit-fixup'. The +\"instant\" variants always require confirmation because making +an error while using those is harder to recover from." + :package-version '(magit . "2.1.0") + :group 'magit-commands + :type 'boolean) + +(defcustom magit-post-commit-hook nil + "Hook run after creating a commit without the user editing a message. + +This hook is run by `magit-refresh' if `this-command' is a member +of `magit-post-commit-hook-commands'. This only includes commands +named `magit-commit-*' that do *not* require that the user edits +the commit message in a buffer and then finishes by pressing +\\<with-editor-mode-map>\\[with-editor-finish]. + +Also see `git-commit-post-finish-hook'." + :package-version '(magit . "2.90.0") + :group 'magit-commands + :type 'hook) + +(defcustom magit-commit-diff-inhibit-same-window nil + "Whether to inhibit use of same window when showing diff while committing. + +When writing a commit, then a diff of the changes to be committed +is automatically shown. The idea is that the diff is shown in a +different window of the same frame and for most users that just +works. In other words most users can completely ignore this +option because its value doesn't make a difference for them. + +However for users who configured Emacs to never create a new +window even when the package explicitly tries to do so, then +displaying two new buffers necessarily means that the first is +immediately replaced by the second. In our case the message +buffer is immediately replaced by the diff buffer, which is of +course highly undesirable. + +A workaround is to suppress this user configuration in this +particular case. Users have to explicitly opt-in by toggling +this option. We cannot enable the workaround unconditionally +because that again causes issues for other users: if the frame +is too tiny or the relevant settings too aggressive, then the +diff buffer would end up being displayed in a new frame. + +Also see https://github.com/magit/magit/issues/4132." + :package-version '(magit . "3.3.0") + :group 'magit-commands + :type 'boolean) + +;;; Popup + +;;;###autoload (autoload 'magit-commit "magit-commit" nil t) +(transient-define-prefix magit-commit () + "Create a new commit or replace an existing commit." + :info-manual "(magit)Initiating a Commit" + :man-page "git-commit" + ["Arguments" + ("-a" "Stage all modified and deleted files" ("-a" "--all")) + ("-e" "Allow empty commit" "--allow-empty") + ("-v" "Show diff of changes to be committed" ("-v" "--verbose")) + ("-n" "Disable hooks" ("-n" "--no-verify")) + ("-R" "Claim authorship and reset author date" "--reset-author") + (magit:--author :description "Override the author") + (magit-commit:--date :level 7) + (magit:--gpg-sign :level 5) + (magit:--signoff) + (magit-commit:--reuse-message)] + [["Create" + ("c" "Commit" magit-commit-create)] + ["Edit HEAD" + ("e" "Extend" magit-commit-extend) + "" + ("a" "Amend" magit-commit-amend) + "" + ("w" "Reword" magit-commit-reword) + ("d" "Reshelve" magit-commit-reshelve :level 0)] + ["Edit" + ("f" "Fixup" magit-commit-fixup) + ("s" "Squash" magit-commit-squash) + ("A" "Alter" magit-commit-alter) + ("n" "Augment" magit-commit-augment) + ("W" "Revise" magit-commit-revise)] + ["Edit and rebase" + ("F" "Instant fixup" magit-commit-instant-fixup) + ("S" "Instant squash" magit-commit-instant-squash) + "" + "" + ("R" "Reword past" magit-rebase-reword-commit :level 0)] + ["Spread across commits" + ("x" "Modified files" magit-commit-autofixup :level 6) + ("X" "Updated modules" magit-commit-absorb-modules :level 6)]] + (interactive) + (if-let ((buffer (magit-commit-message-buffer))) + (switch-to-buffer buffer) + (transient-setup 'magit-commit))) + +(defun magit-commit-arguments nil + (transient-args 'magit-commit)) + +(transient-define-argument magit-commit:--date () + :description "Override the author date" + :class 'transient-option + :shortarg "-D" + :argument "--date=" + :reader #'transient-read-date) + +(transient-define-argument magit-commit:--reuse-message () + :description "Reuse commit message" + :class 'transient-option + :shortarg "-C" + :argument "--reuse-message=" + :reader #'magit-read-reuse-message + :history-key 'magit-revision-history) + +(defun magit-read-reuse-message (prompt &optional default history) + (magit-completing-read prompt (magit-list-refnames) + nil nil nil history + (or default + (and (magit-rev-verify "ORIG_HEAD") + "ORIG_HEAD")))) + +;;; Commands +;;;; Create + +;;;###autoload +(defun magit-commit-create (&optional args) + "Create a new commit." + (interactive (list (magit-commit-arguments))) + (cond ((member "--all" args) + (setq this-command 'magit-commit--all)) + ((member "--allow-empty" args) + (setq this-command 'magit-commit--allow-empty))) + (when (setq args (magit-commit-assert args)) + (let ((default-directory (magit-toplevel))) + (magit-run-git-with-editor "commit" args)))) + +;;;; Edit HEAD + +;;;###autoload +(defun magit-commit-extend (&optional args override-date) + "Amend staged changes to the last commit, without editing its message. + +With a prefix argument do not update the committer date; without an +argument update it. The option `magit-commit-extend-override-date' +can be used to inverse the meaning of the prefix argument. Called +non-interactively, the optional OVERRIDE-DATE argument controls this +behavior, and the option is of no relevance." + (interactive (list (magit-commit-arguments) + (if current-prefix-arg + (not magit-commit-extend-override-date) + magit-commit-extend-override-date))) + (when (setq args (magit-commit-assert args)) + (magit-commit-amend-assert) + (if override-date + (magit-run-git-with-editor "commit" "--amend" "--no-edit" args) + (with-environment-variables + (("GIT_COMMITTER_DATE" (magit-rev-format "%cD"))) + (magit-run-git-with-editor "commit" "--amend" "--no-edit" args))))) + +;;;###autoload +(defun magit-commit-amend (&optional args) + "Amend staged changes (if any) to the last commit, and edit its message." + (interactive (list (magit-commit-arguments))) + (magit-commit-amend-assert) + (magit-run-git-with-editor "commit" "--amend" args)) + +;;;###autoload +(defun magit-commit-reword (&optional args override-date) + "Reword the message of the last commit, without amending its tree. + +With a prefix argument do not update the committer date; without an +argument update it. The option `magit-commit-reword-override-date' +can be used to inverse the meaning of the prefix argument. Called +non-interactively, the optional OVERRIDE-DATE argument controls this +behavior, and the option is of no relevance." + (interactive (list (magit-commit-arguments) + (if current-prefix-arg + (not magit-commit-reword-override-date) + magit-commit-reword-override-date))) + (magit-commit-amend-assert) + (cl-pushnew "--allow-empty" args :test #'equal) + (if override-date + (magit-run-git-with-editor "commit" "--amend" "--only" args) + (with-environment-variables + (("GIT_COMMITTER_DATE" (magit-rev-format "%cD"))) + (magit-run-git-with-editor "commit" "--amend" "--only" args)))) + +;;;; Edit + +;;;###autoload +(defun magit-commit-fixup (&optional commit args) + "Create a fixup commit, leaving the original commit message untouched. + +If there is a reachable commit at point, target that. Otherwise prompt +for a commit. If `magit-commit-squash-confirm' is non-nil, always make +the user explicitly select a commit, in a buffer dedicated to that task. + +During a later rebase, when this commit gets squashed into its targeted +commit, the original message of the targeted commit is used as-is. + +In other words, call \"git commit --fixup=COMMIT --no-edit\"." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--fixup=" commit args)) + +;;;###autoload +(defun magit-commit-squash (&optional commit args) + "Create a squash commit, without the user authoring a commit message. + +If there is a reachable commit at point, target that. Otherwise prompt +for a commit. If `magit-commit-squash-confirm' is non-nil, always make +the user explicitly select a commit, in a buffer dedicated to that task. + +During a later rebase, when this commit gets squashed into its targeted +commit, the user is given a chance to edit the original message to take +the changes from the squash commit into account. + +In other words, call \"git commit --squash=COMMIT --no-edit\"." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--squash=" commit args)) + +;;;###autoload +(defun magit-commit-alter (&optional commit args) + "Create a squash commit, authoring the final commit message now. + +If there is a reachable commit at point, target that. Otherwise prompt +for a commit. If `magit-commit-squash-confirm' is non-nil, always make +the user explicitly select a commit, in a buffer dedicated to that task. + +During a later rebase, when this commit gets squashed into its targeted +commit, the original message of the targeted commit is replaced with the +message of this commit, without the user automatically being given a +chance to edit again. + +In other words, call \"git commit --fixup=amend:COMMIT --edit\"." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--fixup=amend:" commit args nil 'edit)) + +;;;###autoload +(defun magit-commit-augment (&optional commit args) + "Create a squash commit, authoring a new temporary commit message. + +If there is a reachable commit at point, target that. Otherwise prompt +for a commit. If `magit-commit-squash-confirm' is non-nil, always make +the user explicitly select a commit, in a buffer dedicated to that task. + +During a later rebase, when this commit gets squashed into its targeted +commit, the user is asked to write a final commit message, in a buffer +that starts out containing both the original commit message, as well as +the temporary commit message of the squash commit. + +In other words, call \"git commit --squash=COMMIT --edit\"." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--squash=" commit args nil 'edit)) + +;;;###autoload +(defun magit-commit-revise (&optional commit args) + "Reword the message of an existing commit, without editing its tree. + +If there is a reachable commit at point, target that. Otherwise prompt +for a commit. If `magit-commit-squash-confirm' is non-nil, always make +the user explicitly select a commit, in a buffer dedicated to that task. + +During a later rebase, when this commit gets squashed into its targeted +commit, a combined commit is created which uses the message of the fixup +commit and the tree of the targeted commit. + +In other words, call \"git commit --fixup=reword:COMMIT --edit\"." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--fixup=reword:" commit args 'nopatch 'edit)) + +;;;; Edit and Rebase + +;;;###autoload +(defun magit-commit-instant-fixup (&optional commit args) + "Create a fixup commit, and immediately combine it with its target. + +If there is a reachable commit at point, target that. Otherwise prompt +for a commit. If `magit-commit-squash-confirm' is non-nil, always make +the user explicitly select a commit, in a buffer dedicated to that task. + +Leave the original commit message of the targeted commit untouched. + +Like `magit-commit-fixup' but also run a `--autofixup' rebase." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--fixup=" commit args nil nil 'rebase)) + +;;;###autoload +(defun magit-commit-instant-squash (&optional commit args) + "Create a squash commit, and immediately combine it with its target. + +If there is a reachable commit at point, target that. Otherwise prompt +for a commit. If `magit-commit-squash-confirm' is non-nil, always make +the user explicitly select a commit, in a buffer dedicated to that task. + +Turing the rebase phase, when the two commits are being squashed, ask +the user to author the final commit message, based on the original +message of the targeted commit. + +Like `magit-commit-squash' but also run a `--autofixup' rebase." + (interactive (list (magit-commit-at-point) + (magit-commit-arguments))) + (magit-commit-squash-internal "--squash=" commit args nil nil 'rebase)) + +;;;; Internal + +(defun magit-commit-squash-internal + (option commit &optional args nopatch edit rebase confirmed) + (when-let ((args (magit-commit-assert args nopatch (not edit)))) + (when (and commit rebase (not (magit-rev-ancestor-p commit "HEAD"))) + (magit-read-char-case + (format "%s isn't an ancestor of HEAD. " commit) nil + (?c "[c]reate without rebasing" (setq rebase nil)) + (?s "[s]elect other" (setq commit nil)) + (?a "[a]bort" (user-error "Quit")))) + (when commit + (setq commit (magit-rebase-interactive-assert commit t))) + (if (and commit + (or confirmed + (not (or rebase + current-prefix-arg + magit-commit-squash-confirm)))) + (let ((magit-commit-show-diff nil)) + (push (concat option commit) args) + (push (if edit "--edit" "--no-edit") args) + (if rebase + (magit-with-editor + (magit-call-git + "commit" "--no-gpg-sign" + (seq-remove (apply-partially #'string-prefix-p "--gpg-sign=") + args))) + (magit-run-git-with-editor "commit" args)) + t) ; The commit was created; used by below lambda. + (let ((winconf (and magit-commit-show-diff + (current-window-configuration)))) + (magit-log-select + (lambda (commit) + (when (and (magit-commit-squash-internal option commit args + nopatch edit rebase t) + rebase) + (magit-commit-amend-assert commit) + (magit-rebase-interactive-1 commit + (list "--autosquash" "--autostash" "--keep-empty") + "" "true" nil t)) + (when winconf + (set-window-configuration winconf))) + (format "Type %%p on a commit to %s into it," + (substring option 2)) + nil nil nil commit)) + (when (and magit-commit-show-diff (not nopatch)) + (let ((magit-display-buffer-noselect t)) + (apply #'magit-diff-staged nil (magit-diff-arguments))))))) + +(defun magit-commit-amend-assert (&optional commit) + (when-let ((branches (magit-list-publishing-branches commit))) + (let ((m1 "This commit has already been published to ") + (m2 ".\nDo you really want to modify it")) + (magit-confirm 'amend-published + (concat m1 "%s" m2) + (concat m1 "%d public branches" m2) + nil branches)))) + +(defun magit-commit-assert (args &optional nopatch strict) + (cond + (nopatch (or args (list "--"))) + ((or (magit-anything-staged-p) + (and (magit-anything-unstaged-p) + ;; ^ Everything of nothing is still nothing. + (member "--all" args)) + (and (not strict) + ;; ^ For amend variants that don't make sense otherwise. + (or (member "--amend" args) + (member "--allow-empty" args) + (member "--reset-author" args) + (member "--signoff" args) + (transient-arg-value "--author=" args) + (transient-arg-value "--date=" args)))) + (or args (list "--"))) + ((and (magit-rebase-in-progress-p) + (not (magit-anything-unstaged-p)) + (y-or-n-p "Nothing staged. Continue in-progress rebase? ")) + (setq this-command #'magit-rebase-continue) + (magit-run-git-sequencer "rebase" "--continue") + nil) + ((file-exists-p (expand-file-name "MERGE_MSG" (magit-gitdir))) + (cond ((magit-anything-unmerged-p) + (user-error "Unresolved conflicts")) + ((and (magit-anything-unstaged-p) + (not (y-or-n-p + "Proceed with merge despite unstaged changes? "))) + (user-error "Abort")) + ((or args (list "--"))))) + ((not (magit-anything-unstaged-p)) + (user-error "Nothing staged (or unstaged)")) + (magit-commit-ask-to-stage + (when (eq magit-commit-ask-to-stage 'verbose) + (apply #'magit-diff-unstaged (magit-diff-arguments))) + (prog1 (when (or (eq magit-commit-ask-to-stage 'stage) + (y-or-n-p + "Nothing staged. Commit all uncommitted changes? ")) + (setq this-command 'magit-commit--all) + (cons "--all" (or args (list "--")))) + (when (and (eq magit-commit-ask-to-stage 'verbose) + (derived-mode-p 'magit-diff-mode)) + (magit-mode-bury-buffer)))) + (t + (user-error "Nothing staged")))) + +;;;; Reshelve + +(defvar magit--reshelve-history nil) + +;;;###autoload +(defun magit-commit-reshelve (date update-author &optional args) + "Change committer (and possibly author) date of the last commit. + +The current time is used as the initial minibuffer input and the +original author or committer date is available as the previous +history element. + +Both the author and the committer dates are changed, unless one +of the following is true, in which case only the committer date +is updated: +- You are not the author of the commit that is being reshelved. +- The command was invoked with a prefix argument. +- Non-interactively if UPDATE-AUTHOR is nil." + (interactive + (let ((update-author (and (magit-rev-author-p "HEAD") + (not current-prefix-arg)))) + (push (magit-rev-format (if update-author "%ad" "%cd") "HEAD" + (concat "--date=format:%F %T %z")) + magit--reshelve-history) + (list (read-string (if update-author + "Change author and committer dates to: " + "Change committer date to: ") + (cons (format-time-string "%F %T %z") 17) + 'magit--reshelve-history) + update-author + (magit-commit-arguments)))) + (with-environment-variables (("GIT_COMMITTER_DATE" date)) + (magit-run-git "commit" "--amend" "--no-edit" + (and update-author (concat "--date=" date)) + args))) + +;;;; Spread + +;;;###autoload +(defun magit-commit-absorb-modules (phase commit) + "Spread modified modules across recent commits." + (interactive (list 'select (magit-get-upstream-branch))) + (let ((modules (magit-list-modified-modules))) + (unless modules + (user-error "There are no modified modules that could be absorbed")) + (when commit + (setq commit (magit-rebase-interactive-assert commit t))) + (if (and commit (eq phase 'run)) + (progn + (dolist (module modules) + (when-let ((msg (magit-git-string + "log" "-1" "--format=%s" + (concat commit "..") "--" module))) + (magit-git "commit" "-m" (concat "fixup! " msg) + "--only" "--" module))) + (magit-refresh) + t) + (magit-log-select + (lambda (commit) + (magit-commit-absorb-modules 'run commit)) + nil nil nil nil commit)))) + +;;;###autoload (autoload 'magit-commit-absorb "magit-commit" nil t) +(transient-define-prefix magit-commit-absorb (phase commit args) + "Spread staged changes across recent commits. +With a prefix argument use a transient command to select infix +arguments. This command requires git-absorb executable, which +is available from https://github.com/tummychow/git-absorb. +See `magit-commit-autofixup' for an alternative implementation." + :value '("-v") + ["Arguments" + ("-f" "Skip safety checks" ("-f" "--force")) + ("-v" "Increase verbosity" ("-v" "--verbose"))] + ["Actions" + ("x" "Absorb" magit-commit-absorb)] + (interactive (if current-prefix-arg + (list 'transient nil nil) + (list 'select + (magit-get-upstream-branch) + (transient-args 'magit-commit-absorb)))) + (if (eq phase 'transient) + (transient-setup 'magit-commit-absorb) + (unless (magit-git-executable-find "git-absorb") + (user-error "This command requires the git-absorb executable, which %s" + "is available from https://github.com/tummychow/git-absorb")) + (unless (magit-anything-staged-p) + (if (magit-anything-unstaged-p) + (if (y-or-n-p "Nothing staged. Absorb all unstaged changes? ") + (magit-with-toplevel + (magit-run-git "add" "-u" ".")) + (user-error "Abort")) + (user-error "There are no changes that could be absorbed"))) + (when commit + (setq commit (magit-rebase-interactive-assert commit t))) + (if (and commit (eq phase 'run)) + (progn (magit-run-git-async "absorb" args "-b" commit) t) + (magit-log-select + (lambda (commit) + (with-no-warnings ; about non-interactive use + (magit-commit-absorb 'run commit args))) + nil nil nil nil commit)))) + +(transient-augment-suffix magit-commit-absorb :transient 'transient--do-exit) + +;;;###autoload (autoload 'magit-commit-autofixup "magit-commit" nil t) +(transient-define-prefix magit-commit-autofixup (phase commit args) + "Spread staged or unstaged changes across recent commits. + +If there are any staged then spread only those, otherwise spread all +unstaged changes. With a prefix argument use a transient command to +select infix arguments. + +This command requires the git-autofixup script, which is available from +https://github.com/torbiak/git-autofixup. See `magit-commit-absorb' for +an alternative implementation." + :value '("-vv") + ["Arguments" + (magit-autofixup:--context) + (magit-autofixup:--strict) + ("-v" "Increase verbosity" "-vv")] + ["Actions" + ("x" "Absorb" magit-commit-autofixup)] + (interactive (if current-prefix-arg + (list 'transient nil nil) + (list 'select + (magit-get-upstream-branch) + (transient-args 'magit-commit-autofixup)))) + (if (eq phase 'transient) + (transient-setup 'magit-commit-autofixup) + (unless (magit-git-executable-find "git-autofixup") + (user-error "This command requires the git-autofixup script, which %s" + "is available from https://github.com/torbiak/git-autofixup")) + (unless (magit-anything-modified-p) + (user-error "There are no changes that could be absorbed")) + (when commit + (setq commit (magit-rebase-interactive-assert commit t))) + (if (and commit (eq phase 'run)) + (progn (magit-run-git-async "autofixup" args commit) t) + (magit-log-select + (lambda (commit) + (with-no-warnings ; about non-interactive use + (magit-commit-autofixup 'run commit args))) + nil nil nil nil commit)))) + +(transient-augment-suffix magit-commit-autofixup :transient 'transient--do-exit) + +(transient-define-argument magit-autofixup:--context () + :description "Diff context lines" + :class 'transient-option + :shortarg "-c" + :argument "--context=" + :reader #'transient-read-number-N0) + +(transient-define-argument magit-autofixup:--strict () + :description "Strictness" + :class 'transient-option + :shortarg "-s" + :argument "--strict=" + :reader #'transient-read-number-N0) + +;;;; Hooks + +(defvar magit-post-commit-hook-commands + (list #'magit-commit-extend + #'magit-commit-fixup + #'magit-commit-augment + #'magit-commit-instant-fixup + #'magit-commit-instant-squash)) + +(defun magit-run-post-commit-hook () + (when (and (not this-command) + (memq last-command magit-post-commit-hook-commands)) + (run-hooks 'magit-post-commit-hook))) + +;;; Pending Diff + +(defun magit-commit-diff () + (magit-repository-local-set 'this-commit-command + (if (eq this-command 'with-editor-finish) + 'magit-commit--rebase + last-command)) + (when (and git-commit-mode magit-commit-show-diff) + (when-let ((diff-buffer (magit-get-mode-buffer 'magit-diff-mode))) + ;; This window just started displaying the commit message + ;; buffer. Without this that buffer would immediately be + ;; replaced with the diff buffer. See #2632. + (unrecord-window-buffer nil diff-buffer)) + (message "Diffing changes to be committed (C-g to abort diffing)") + (let ((inhibit-quit nil)) + (condition-case nil + (magit-commit-diff-1) + (quit))))) + +(defun magit-commit-diff-1 () + (let ((rev nil) + (arg "--cached") + (command (magit-repository-local-get 'this-commit-command)) + (staged (magit-anything-staged-p)) + (unstaged + ;; Escape $GIT_DIR because `magit-anything-unstaged-p' + ;; requires a working tree. + (magit-with-toplevel + (magit-anything-unstaged-p))) + (squash (let ((f (expand-file-name "rebase-merge/rewritten-pending" + (magit-gitdir)))) + (and (file-exists-p f) (length (magit-file-lines f))))) + (noalt nil)) + (pcase (list staged unstaged command) + ((and `(,_ ,_ magit-commit--rebase) + (guard (integerp squash))) + (setq rev (format "HEAD~%s" squash))) + (`(,_ ,_ magit-commit-amend) + (setq rev "HEAD^")) + (`(nil nil magit-commit--allow-empty) + (setq rev "HEAD") + (setq arg nil)) + ((or `(,_ ,_ magit-commit-reword) + `(nil nil ,_)) + (setq rev "HEAD^..HEAD") + (setq arg nil)) + (`(,_ t magit-commit--all) + (setq rev "HEAD") + (setq arg nil)) + (`(nil t handle-switch-frame) + ;; Either --all or --allow-empty. Assume it is the former. + (setq rev "HEAD") + (setq arg nil))) + (cond + ((not + (and (eq this-command 'magit-diff-while-committing) + (and-let* ((buf (magit-get-mode-buffer + 'magit-diff-mode nil 'selected))) + (and (equal rev (buffer-local-value 'magit-buffer-range buf)) + (equal arg (buffer-local-value 'magit-buffer-typearg buf))))))) + ((eq command 'magit-commit-amend) + (setq rev nil)) + ((or squash + (file-exists-p (expand-file-name "rebase-merge/amend" (magit-gitdir)))) + (setq rev "HEAD^")) + (t + (message "No alternative diff while committing") + (setq noalt t))) + (unless noalt + (let ((magit-inhibit-save-previous-winconf 'unset) + (magit-display-buffer-noselect t) + (display-buffer-overriding-action + display-buffer-overriding-action)) + (when magit-commit-diff-inhibit-same-window + (setq display-buffer-overriding-action + '(nil (inhibit-same-window . t)))) + (magit-diff-setup-buffer rev arg (car (magit-diff-arguments)) nil + (cond ((equal rev "HEAD") 'staged) + ((equal rev "HEAD^..HEAD") 'committed) + ('undefined))))))) + +(add-hook 'server-switch-hook #'magit-commit-diff) +(add-hook 'with-editor-filter-visit-hook #'magit-commit-diff) + +(add-to-list 'with-editor-server-window-alist + (cons git-commit-filename-regexp #'switch-to-buffer)) + +(defun magit-commit--reset-command () + (magit-repository-local-delete 'this-commit-command)) + +;;; Message Utilities + +(defun magit-commit-message-buffer () + (let* ((find-file-visit-truename t) ; git uses truename of COMMIT_EDITMSG + (topdir (magit-toplevel))) + (seq-find (##equal topdir (with-current-buffer % + (and git-commit-mode (magit-toplevel)))) + (append (buffer-list (selected-frame)) + (buffer-list))))) + +(defvar magit-commit-add-log-insert-function #'magit-commit-add-log-insert + "Used by `magit-commit-add-log' to insert a single entry.") + +(defun magit-commit-add-log () + "Add a stub for the current change into the commit message buffer. +If no commit is in progress, then initiate it. Use the function +specified by variable `magit-commit-add-log-insert-function' to +actually insert the entry." + (interactive) + (pcase-let* ((hunk (and (magit-section-match 'hunk) + (magit-current-section))) + (log (magit-commit-message-buffer)) + (`(,buf ,pos) (magit-diff-visit-file--noselect))) + (unless log + (unless (magit-commit-assert nil) + (user-error "Abort")) + (magit-commit-create) + (while (not (setq log (magit-commit-message-buffer))) + (sit-for 0.01))) + (magit--with-temp-position buf pos + (funcall magit-commit-add-log-insert-function log + (magit-file-relative-name) + (and hunk (add-log-current-defun)))))) + +(defun magit-commit-add-log-insert (buffer file defun) + (with-current-buffer buffer + (undo-boundary) + (goto-char (point-max)) + (while (re-search-backward (concat "^" comment-start) nil t)) + (save-restriction + (narrow-to-region (point-min) (point)) + (cond ((re-search-backward (format "* %s\\(?: (\\([^)]+\\))\\)?: " file) + nil t) + (when (equal (match-string 1) defun) + (setq defun nil)) + (re-search-forward ": ")) + (t + (when (re-search-backward "^[\\*(].+\n" nil t) + (goto-char (match-end 0))) + (while (re-search-forward "^[^\\*\n].*\n" nil t)) + (if defun + (progn (insert (format "* %s (%s): \n" file defun)) + (setq defun nil)) + (insert (format "* %s: \n" file))) + (backward-char) + (unless (looking-at "\n[\n\\']") + (insert ?\n) + (backward-char)))) + (when defun + (forward-line) + (let ((limit (save-excursion + (and (re-search-forward "^\\*" nil t) + (point))))) + (unless (or (looking-back (format "(%s): " defun) + (line-beginning-position)) + (re-search-forward (format "^(%s): " defun) limit t)) + (while (re-search-forward "^[^\\*\n].*\n" limit t)) + (insert (format "(%s): \n" defun)) + (backward-char))))))) + +;;; _ +(provide 'magit-commit) +;;; magit-commit.el ends here |