update packages and add valign
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
((nil
|
||||
(indent-tabs-mode . nil))
|
||||
(emacs-lisp-mode
|
||||
(checkdoc-allow-quoting-nil-and-t . t)
|
||||
(lisp-indent-local-overrides . ((cond . 0) (interactive . 0))))
|
||||
(makefile-mode
|
||||
(indent-tabs-mode . t)
|
||||
(outline-regexp . "#\\(#+\\)")
|
||||
(mode . outline-minor))
|
||||
(emacs-lisp-mode
|
||||
(checkdoc-allow-quoting-nil-and-t . t))
|
||||
(mode . outline-minor)
|
||||
(outline-regexp . "#\\(#+\\)"))
|
||||
(git-commit-mode
|
||||
(git-commit-major-mode . git-commit-elisp-text-mode))
|
||||
(".github/PULL_REQUEST_TEMPLATE"
|
||||
|
||||
@@ -49,6 +49,7 @@ All Contributors
|
||||
- Alex Kreisher
|
||||
- Alex Ott
|
||||
- Allen Li
|
||||
- Andrea Alberti
|
||||
- Andreas Fuchs
|
||||
- Andreas Liljeqvist
|
||||
- Andreas Rottmann
|
||||
@@ -230,6 +231,7 @@ All Contributors
|
||||
- Lele Gaifax
|
||||
- Lénaïc Huard
|
||||
- Leo Liu
|
||||
- Leonard Lausen
|
||||
- Leonardo Etcheverry
|
||||
- Leo Vivier
|
||||
- Li Chen
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; git-commit.el --- Edit Git commit messages -*- lexical-binding:t; coding:utf-8 -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Sebastian Wiesner <lunaryorn@gmail.com>
|
||||
@@ -110,6 +110,7 @@
|
||||
(require 'transient)
|
||||
(require 'with-editor)
|
||||
|
||||
(defvar dabbrev--abbrev-char-regexp)
|
||||
(defvar diff-default-read-only)
|
||||
(defvar flyspell-generic-check-word-predicate)
|
||||
(defvar font-lock-beg)
|
||||
@@ -118,6 +119,12 @@
|
||||
|
||||
(defvar git-commit-need-summary-line)
|
||||
|
||||
(declare-function dabbrev--reset-global-variables "dabbrev" ())
|
||||
(declare-function dabbrev-capf "dabbrev" ())
|
||||
(declare-function magit-commit-diff--args "magit-commit" ())
|
||||
(declare-function magit-diff--modified-defuns "magit-diff" ())
|
||||
(declare-function magit-diff-arguments "magit-diff" (&optional mode))
|
||||
|
||||
(define-obsolete-variable-alias
|
||||
'git-commit-known-pseudo-headers
|
||||
'git-commit-trailers
|
||||
@@ -132,42 +139,6 @@
|
||||
:link '(info-link "(magit)Editing Commit Messages")
|
||||
:group 'tools)
|
||||
|
||||
(define-minor-mode global-git-commit-mode
|
||||
"Edit Git commit messages.
|
||||
|
||||
This global mode arranges for `git-commit-setup' to be called
|
||||
when a Git commit message file is opened. That usually happens
|
||||
when Git uses the Emacsclient as $GIT_EDITOR to have the user
|
||||
provide such a commit message.
|
||||
|
||||
Loading the library `git-commit' by default enables this mode,
|
||||
but the library is not automatically loaded because doing that
|
||||
would pull in many dependencies and increase startup time too
|
||||
much. You can either rely on `magit' loading this library or
|
||||
you can load it explicitly. Autoloading is not an alternative
|
||||
because in this case autoloading would immediately trigger
|
||||
full loading."
|
||||
:group 'git-commit
|
||||
:type 'boolean
|
||||
:global t
|
||||
:init-value t
|
||||
:initialize
|
||||
(lambda (symbol exp)
|
||||
(custom-initialize-default symbol exp)
|
||||
(when global-git-commit-mode
|
||||
(add-hook 'find-file-hook #'git-commit-setup-check-buffer)
|
||||
(remove-hook 'after-change-major-mode-hook
|
||||
#'git-commit-setup-font-lock-in-buffer)))
|
||||
(cond
|
||||
(global-git-commit-mode
|
||||
(add-hook 'find-file-hook #'git-commit-setup-check-buffer)
|
||||
(add-hook 'after-change-major-mode-hook
|
||||
#'git-commit-setup-font-lock-in-buffer))
|
||||
(t
|
||||
(remove-hook 'find-file-hook #'git-commit-setup-check-buffer)
|
||||
(remove-hook 'after-change-major-mode-hook
|
||||
#'git-commit-setup-font-lock-in-buffer))))
|
||||
|
||||
(defcustom git-commit-major-mode #'text-mode
|
||||
"Major mode used to edit Git commit messages.
|
||||
|
||||
@@ -198,9 +169,11 @@ Also note that `git-commit-mode' (which see) is not a major-mode.")
|
||||
(defcustom git-commit-setup-hook
|
||||
(list #'git-commit-ensure-comment-gap
|
||||
#'git-commit-save-message
|
||||
#'git-commit-setup-capf
|
||||
#'git-commit-setup-changelog-support
|
||||
#'git-commit-turn-on-auto-fill
|
||||
#'git-commit-setup-auto-fill
|
||||
#'git-commit-propertize-diff
|
||||
#'git-commit-collapse-diff
|
||||
#'bug-reference-mode)
|
||||
"Hook run at the end of `git-commit-setup'."
|
||||
:group 'git-commit
|
||||
@@ -208,12 +181,14 @@ Also note that `git-commit-mode' (which see) is not a major-mode.")
|
||||
:get #'magit-hook-custom-get
|
||||
:options '(git-commit-ensure-comment-gap
|
||||
git-commit-save-message
|
||||
git-commit-setup-capf
|
||||
git-commit-setup-changelog-support
|
||||
magit-generate-changelog
|
||||
git-commit-turn-on-auto-fill
|
||||
git-commit-turn-on-orglink
|
||||
git-commit-turn-on-flyspell
|
||||
git-commit-setup-auto-fill
|
||||
git-commit-setup-orglink
|
||||
git-commit-setup-flyspell
|
||||
git-commit-propertize-diff
|
||||
git-commit-collapse-diff
|
||||
bug-reference-mode))
|
||||
|
||||
(defcustom git-commit-finish-query-functions
|
||||
@@ -363,6 +338,11 @@ In this context a \"keyword\" is text surrounded by brackets."
|
||||
"Face used for headings in commit message comments."
|
||||
:group 'git-commit-faces)
|
||||
|
||||
(defface git-commit-comment-button
|
||||
'((t :inherit git-commit-comment-heading :underline t))
|
||||
"Face used for buttons in commit message comments."
|
||||
:group 'git-commit-faces)
|
||||
|
||||
(defface git-commit-comment-file
|
||||
'((t :inherit git-commit-trailer-value))
|
||||
"Face used for file names in commit message comments."
|
||||
@@ -437,7 +417,43 @@ the redundant bindings, then set this to nil, before loading
|
||||
["Cancel" with-editor-cancel t]
|
||||
["Commit" with-editor-finish t]))
|
||||
|
||||
;;; Hooks
|
||||
;;; Global Mode
|
||||
|
||||
(define-minor-mode global-git-commit-mode
|
||||
"Edit Git commit messages.
|
||||
|
||||
This global mode arranges for `git-commit-setup' to be called
|
||||
when a Git commit message file is opened. That usually happens
|
||||
when Git uses the Emacsclient as $GIT_EDITOR to have the user
|
||||
provide such a commit message.
|
||||
|
||||
Loading the library `git-commit' by default enables this mode,
|
||||
but the library is not automatically loaded because doing that
|
||||
would pull in many dependencies and increase startup time too
|
||||
much. You can either rely on `magit' loading this library or
|
||||
you can load it explicitly. Autoloading is not an alternative
|
||||
because in this case autoloading would immediately trigger
|
||||
full loading."
|
||||
:group 'git-commit
|
||||
:type 'boolean
|
||||
:global t
|
||||
:init-value t
|
||||
:initialize
|
||||
(lambda (symbol exp)
|
||||
(custom-initialize-default symbol exp)
|
||||
(when global-git-commit-mode
|
||||
(add-hook 'find-file-hook #'git-commit-setup-check-buffer)
|
||||
(remove-hook 'after-change-major-mode-hook
|
||||
#'git-commit-setup-font-lock-in-buffer)))
|
||||
(cond
|
||||
(global-git-commit-mode
|
||||
(add-hook 'find-file-hook #'git-commit-setup-check-buffer)
|
||||
(add-hook 'after-change-major-mode-hook
|
||||
#'git-commit-setup-font-lock-in-buffer))
|
||||
(t
|
||||
(remove-hook 'find-file-hook #'git-commit-setup-check-buffer)
|
||||
(remove-hook 'after-change-major-mode-hook
|
||||
#'git-commit-setup-font-lock-in-buffer))))
|
||||
|
||||
(defconst git-commit-filename-regexp "/\\(\
|
||||
\\(\\(COMMIT\\|NOTES\\|PULLREQ\\|MERGEREQ\\|TAG\\)_EDIT\\|MERGE_\\|\\)MSG\
|
||||
@@ -458,8 +474,6 @@ the redundant bindings, then set this to nil, before loading
|
||||
(string-match-p git-commit-filename-regexp buffer-file-name))
|
||||
(git-commit-setup)))
|
||||
|
||||
(defvar git-commit-mode)
|
||||
|
||||
(defun git-commit-file-not-found ()
|
||||
;; cygwin git will pass a cygwin path (/cygdrive/c/foo/.git/...),
|
||||
;; try to handle this in window-nt Emacs.
|
||||
@@ -480,6 +494,10 @@ the redundant bindings, then set this to nil, before loading
|
||||
(when (eq system-type 'windows-nt)
|
||||
(add-hook 'find-file-not-found-functions #'git-commit-file-not-found))
|
||||
|
||||
;;; Local Mode
|
||||
|
||||
(defvar git-commit-mode)
|
||||
|
||||
(defconst git-commit-default-usage-message "\
|
||||
Type \\[with-editor-finish] to finish, \
|
||||
\\[with-editor-cancel] to cancel, and \
|
||||
@@ -504,6 +522,12 @@ Used as the local value of `header-line-format', in buffer using
|
||||
(setq git-commit-usage-message nil) ; show a shorter message")
|
||||
|
||||
(defun git-commit-setup ()
|
||||
;; If an error occurs when `emacsclient' is used, and it turns out
|
||||
;; to not be triggered by something in this function, then another
|
||||
;; likely source are functions on `server-switch-hook'. Debugging
|
||||
;; is suppressed while running that hook, so it may be necessary to
|
||||
;; force debugging by modifying those functions directly. Enabling
|
||||
;; `magit-process-record-invocations' may also help.
|
||||
(let ((gitdir default-directory)
|
||||
(cd (and git-commit-cd-to-toplevel
|
||||
(or (car (rassoc default-directory magit--separated-gitdirs))
|
||||
@@ -568,9 +592,9 @@ Used as the local value of `header-line-format', in buffer using
|
||||
(with-demoted-errors "Error running git-commit-setup-hook: %S"
|
||||
(run-hooks 'git-commit-setup-hook))
|
||||
(set-buffer-modified-p nil)
|
||||
(when-let ((format git-commit-header-line-format))
|
||||
(when$ git-commit-header-line-format
|
||||
(setq header-line-format
|
||||
(if (stringp format) (substitute-command-keys format) format)))
|
||||
(if (stringp $) (substitute-command-keys $) $)))
|
||||
(when git-commit-usage-message
|
||||
(setq with-editor-usage-message git-commit-usage-message))
|
||||
(with-editor-usage-message))
|
||||
@@ -585,6 +609,8 @@ used."
|
||||
|
||||
(put 'git-commit-mode 'permanent-local t)
|
||||
|
||||
;;; Setup
|
||||
|
||||
(defun git-commit-ensure-comment-gap ()
|
||||
"Separate initial empty line from initial comment.
|
||||
If the buffer begins with an empty line followed by a comment, insert
|
||||
@@ -595,25 +621,36 @@ the input isn't tacked to the comment."
|
||||
(when (looking-at (format "\\`\n%s" comment-start))
|
||||
(open-line 1))))
|
||||
|
||||
(defun git-commit-setup-capf ()
|
||||
"Teach `complete-symbol' about `dabbrev-capf'.
|
||||
When \"git commit\"'s \"--verbose\" argument is used, this allows
|
||||
completing modified symbols and other text appearing in the diff."
|
||||
(require 'dabbrev)
|
||||
(unless dabbrev--abbrev-char-regexp
|
||||
;; Initialize (not "reset") variables. See #5545.
|
||||
(dabbrev--reset-global-variables))
|
||||
(add-hook 'completion-at-point-functions #'dabbrev-capf -90 t))
|
||||
|
||||
(defun git-commit-setup-changelog-support ()
|
||||
"Treat ChangeLog entries as unindented paragraphs."
|
||||
(setq-local fill-paragraph-function #'log-edit-fill-entry)
|
||||
(setq-local fill-indent-according-to-mode t)
|
||||
(setq-local paragraph-start (concat paragraph-start "\\|\\*\\|(")))
|
||||
|
||||
(defun git-commit-turn-on-auto-fill ()
|
||||
(defun git-commit-setup-auto-fill ()
|
||||
"Unconditionally turn on Auto Fill mode.
|
||||
Ensure auto filling happens everywhere, except in the summary line."
|
||||
(auto-fill-mode 1)
|
||||
(setq-local comment-auto-fill-only-comments nil)
|
||||
(when git-commit-need-summary-line
|
||||
(setq-local auto-fill-function #'git-commit-auto-fill-except-summary)))
|
||||
(setq-local auto-fill-function #'git-commit--auto-fill-except-summary)))
|
||||
|
||||
(defun git-commit-auto-fill-except-summary ()
|
||||
(defun git-commit--auto-fill-except-summary ()
|
||||
"Do not fill summary line."
|
||||
(unless (eq (line-beginning-position) 1)
|
||||
(do-auto-fill)))
|
||||
|
||||
(defun git-commit-turn-on-orglink ()
|
||||
(defun git-commit-setup-orglink ()
|
||||
"Turn on Orglink mode if it is available.
|
||||
If `git-commit-major-mode' is `org-mode', then silently forgo
|
||||
turning on `orglink-mode'."
|
||||
@@ -623,15 +660,14 @@ turning on `orglink-mode'."
|
||||
(setq-local orglink-match-anywhere t)
|
||||
(orglink-mode 1)))
|
||||
|
||||
(defun git-commit-turn-on-flyspell ()
|
||||
(defun git-commit-setup-flyspell ()
|
||||
"Unconditionally turn on Flyspell mode.
|
||||
Also check text that is already in the buffer, while avoiding to check
|
||||
most text that Git will strip from the final message, such as the last
|
||||
comment and anything below the cut line (\"--- >8 ---\")."
|
||||
(require 'flyspell)
|
||||
(flyspell-mode 1)
|
||||
(setq flyspell-generic-check-word-predicate
|
||||
#'git-commit-flyspell-verify)
|
||||
(setq flyspell-generic-check-word-predicate #'git-commit--flyspell-verify)
|
||||
(let ((end nil)
|
||||
;; The "cut line" is defined in "git/wt-status.c". It appears
|
||||
;; in the commit message when `commit.verbose' is set to true.
|
||||
@@ -647,10 +683,37 @@ comment and anything below the cut line (\"--- >8 ---\")."
|
||||
(setq end (point)))
|
||||
(flyspell-region (point-min) end)))
|
||||
|
||||
(defun git-commit-flyspell-verify ()
|
||||
(defun git-commit--flyspell-verify ()
|
||||
"Do not check spelling in comments."
|
||||
(not (= (char-after (line-beginning-position))
|
||||
(aref comment-start 0))))
|
||||
|
||||
(defun git-commit-collapse-diff ()
|
||||
"Collapse inline diff and add button to allow expanding it."
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(when (re-search-forward (format "%s -+ >8 -+" comment-start) nil t)
|
||||
(let ((elt '(git-commit-diff t)))
|
||||
(add-to-invisibility-spec elt)
|
||||
(make-button (line-beginning-position) (point)
|
||||
'face 'git-commit-comment-button
|
||||
'keymap (define-keymap :parent button-map
|
||||
"<return>" #'push-button
|
||||
"<tab>" #'push-button)
|
||||
'action (lambda (_)
|
||||
(if (memq elt buffer-invisibility-spec)
|
||||
(remove-from-invisibility-spec elt)
|
||||
(add-to-invisibility-spec elt))
|
||||
;; KLUDGE Force "redisplay".
|
||||
(when-let ((w1 (selected-window))
|
||||
(w2 (next-window)))
|
||||
(select-window w2)
|
||||
(select-window w1)))))
|
||||
(let ((ov (make-overlay (point) (point-max))))
|
||||
(overlay-put ov 'invisible 'git-commit-diff)))))
|
||||
|
||||
;;; Finish
|
||||
|
||||
(defun git-commit-finish-query-functions (force)
|
||||
(run-hook-with-args-until-failure
|
||||
'git-commit-finish-query-functions force))
|
||||
@@ -722,9 +785,9 @@ With a numeric prefix ARG, go forward ARG messages."
|
||||
"Search backward through message history for a match for STRING.
|
||||
Save current message first."
|
||||
(interactive
|
||||
(list (read-string (format-prompt "Comment substring"
|
||||
log-edit-last-comment-match)
|
||||
nil nil log-edit-last-comment-match)))
|
||||
(list (read-string (format-prompt "Comment substring"
|
||||
log-edit-last-comment-match)
|
||||
nil nil log-edit-last-comment-match)))
|
||||
(cl-letf (((symbol-function #'log-edit-previous-comment)
|
||||
(symbol-function #'git-commit-prev-message)))
|
||||
(log-edit-comment-search-backward string)))
|
||||
@@ -733,9 +796,9 @@ Save current message first."
|
||||
"Search forward through message history for a match for STRING.
|
||||
Save current message first."
|
||||
(interactive
|
||||
(list (read-string (format-prompt "Comment substring"
|
||||
log-edit-last-comment-match)
|
||||
nil nil log-edit-last-comment-match)))
|
||||
(list (read-string (format-prompt "Comment substring"
|
||||
log-edit-last-comment-match)
|
||||
nil nil log-edit-last-comment-match)))
|
||||
(cl-letf (((symbol-function #'log-edit-previous-comment)
|
||||
(symbol-function #'git-commit-prev-message)))
|
||||
(log-edit-comment-search-forward string)))
|
||||
@@ -784,6 +847,55 @@ Save current message first."
|
||||
(setq str (replace-match "\n" t t str)))
|
||||
str))))
|
||||
|
||||
;;; Changelog
|
||||
|
||||
(defun git-commit--modified-defuns ()
|
||||
(if (save-excursion
|
||||
(goto-char (point-min))
|
||||
(re-search-forward "^diff --git" nil t))
|
||||
(magit-diff--modified-defuns)
|
||||
(with-temp-buffer
|
||||
(pcase-let ((`(,rev ,arg) (magit-commit-diff--args)))
|
||||
(save-excursion
|
||||
(magit-git-insert "diff" "-p" arg (car (magit-diff-arguments)) rev)))
|
||||
(magit-diff--modified-defuns))))
|
||||
|
||||
;;;###autoload
|
||||
(defun git-commit-insert-changelog-gnu ()
|
||||
"Insert a GNU-style changelog at point while authorig a commit message.
|
||||
|
||||
The modified definitions are extracted from the diff in the message
|
||||
buffer, which is only available if \"git commit\" was invoked with
|
||||
\"--verbose\"."
|
||||
(interactive)
|
||||
(unless git-commit-mode
|
||||
(user-error "Not in a commit message buffer"))
|
||||
;; Like `change-log-insert-entries'.
|
||||
(pcase-dolist (`(,file . ,defuns) (git-commit--modified-defuns))
|
||||
(if (not defuns)
|
||||
(insert "* " file ":\n")
|
||||
(insert "* " file " ")
|
||||
(dolist (def defuns)
|
||||
(insert "(" def "):\n")))))
|
||||
|
||||
;;;###autoload
|
||||
(defun git-commit-insert-changelog-plain ()
|
||||
"Insert a simple changelog at point while authorig a commit message.
|
||||
|
||||
Defuns are slightly indented and quoted like in elisp docstrings.
|
||||
The exact format is still subject to change.
|
||||
|
||||
The modified definitions are extracted from the diff in the message
|
||||
buffer, which is only available if \"git commit\" was invoked with
|
||||
\"--verbose\"."
|
||||
(interactive)
|
||||
(unless git-commit-mode
|
||||
(user-error "Not in a commit message buffer"))
|
||||
(pcase-dolist (`(,file . ,defuns) (git-commit--modified-defuns))
|
||||
(insert file ":\n")
|
||||
(dolist (def defuns)
|
||||
(insert " `" def "'\n"))))
|
||||
|
||||
;;; Trailers
|
||||
|
||||
(transient-define-prefix git-commit-insert-trailer ()
|
||||
@@ -793,19 +905,22 @@ See also manpage git-interpret-trailer(1). This command does
|
||||
not use that Git command, but the initial description still
|
||||
serves as a good introduction."
|
||||
[[:description (##cond (prefix-arg
|
||||
"Insert ... by someone ")
|
||||
("Insert ... by yourself"))
|
||||
"Insert trailer ... by someone ")
|
||||
("Insert trailer ... by yourself"))
|
||||
("a" "Ack" git-commit-ack)
|
||||
("m" "Modified" git-commit-modified)
|
||||
("r" "Reviewed" git-commit-review)
|
||||
("s" "Signed-off" git-commit-signoff)
|
||||
("t" "Tested" git-commit-test)]
|
||||
["Insert ... by someone"
|
||||
["Insert trailer ... by someone"
|
||||
("C-c" "Cc" git-commit-cc)
|
||||
("C-r" "Reported" git-commit-reported)
|
||||
("C-i" "Suggested" git-commit-suggested)
|
||||
("C-a" "Co-authored" git-commit-co-authored)
|
||||
("C-d" "Co-developed" git-commit-co-developed)]])
|
||||
("C-d" "Co-developed" git-commit-co-developed)]]
|
||||
["Insert changelog"
|
||||
("l g" "GNU-style" git-commit-insert-changelog-gnu)
|
||||
("l p" "plain" git-commit-insert-changelog-plain)])
|
||||
|
||||
(defun git-commit-ack (name mail)
|
||||
"Insert a trailer acknowledging that you have looked at the commit."
|
||||
@@ -915,20 +1030,20 @@ completion candidates. The input must have the form \"NAME <EMAIL>\"."
|
||||
(setq leading-comment-end (point))
|
||||
(goto-char (point-max))
|
||||
(cond
|
||||
;; Look backwards for existing trailers.
|
||||
((re-search-backward (git-commit--trailer-regexp) nil t)
|
||||
(end-of-line)
|
||||
(insert ?\n string)
|
||||
(unless (= (char-after) ?\n)
|
||||
(insert ?\n)))
|
||||
;; Or place the new trailer right before the first non-leading
|
||||
;; comments.
|
||||
(t
|
||||
(while (re-search-backward (concat "^" comment-start)
|
||||
leading-comment-end t))
|
||||
(unless (looking-back "\n\n" nil)
|
||||
(insert ?\n))
|
||||
(insert string ?\n))))
|
||||
;; Look backwards for existing trailers.
|
||||
((re-search-backward (git-commit--trailer-regexp) nil t)
|
||||
(end-of-line)
|
||||
(insert ?\n string)
|
||||
(unless (= (char-after) ?\n)
|
||||
(insert ?\n)))
|
||||
;; Or place the new trailer right before the first non-leading
|
||||
;; comments.
|
||||
(t
|
||||
(while (re-search-backward (concat "^" comment-start)
|
||||
leading-comment-end t))
|
||||
(unless (looking-back "\n\n" nil)
|
||||
(insert ?\n))
|
||||
(insert string ?\n))))
|
||||
(unless (or (eobp) (= (char-after) ?\n))
|
||||
(insert ?\n))))
|
||||
|
||||
@@ -1225,6 +1340,19 @@ commit, then the hook is not run at all."
|
||||
'git-commit-trailer-token
|
||||
"git-commit 4.0.0")
|
||||
|
||||
(define-obsolete-function-alias
|
||||
'git-commit-turn-on-auto-fill
|
||||
'git-commit-setup-auto-fill
|
||||
"git-commit 4.6.0")
|
||||
(define-obsolete-function-alias
|
||||
'git-commit-turn-on-flyspell
|
||||
'git-commit-setup-flyspell
|
||||
"git-commit 4.6.0")
|
||||
(define-obsolete-function-alias
|
||||
'git-commit-turn-on-orglink
|
||||
'git-commit-setup-orglink
|
||||
"git-commit 4.6.0")
|
||||
|
||||
(provide 'git-commit)
|
||||
;; Local Variables:
|
||||
;; read-symbol-shorthands: (
|
||||
@@ -1232,6 +1360,7 @@ commit, then the hook is not run at all."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; git-rebase.el --- Edit Git rebase files -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Phil Jackson <phil@shellarchive.co.uk>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -371,14 +371,14 @@ BATCH also ignores commented lines."
|
||||
(and (looking-at (concat re-start re)) type)))
|
||||
git-rebase-line-regexps)]
|
||||
(git-rebase-action
|
||||
:action-type type
|
||||
:action (and-let ((action (match-str 1)))
|
||||
(or (cdr (assoc action git-rebase-short-options))
|
||||
action))
|
||||
:action-options (match-str 2)
|
||||
:target (match-str 3)
|
||||
:trailer (match-str 5)
|
||||
:comment-p (and (match-str 99) t)))
|
||||
:action-type type
|
||||
:action (and-let ((action (match-str 1)))
|
||||
(or (cdr (assoc action git-rebase-short-options))
|
||||
action))
|
||||
:action-options (match-str 2)
|
||||
:target (match-str 3)
|
||||
:trailer (match-str 5)
|
||||
:comment-p (and (match-str 99) t)))
|
||||
((not batch)
|
||||
;; Use empty object rather than nil to ease handling.
|
||||
(git-rebase-action)))))
|
||||
@@ -399,23 +399,23 @@ of its action type."
|
||||
(with-slots (action-type target trailer comment-p)
|
||||
(git-rebase-current-line)
|
||||
(cond
|
||||
((and action (eq action-type 'commit))
|
||||
(let ((inhibit-read-only t))
|
||||
(magit-delete-line)
|
||||
(insert (concat action " " target " "))
|
||||
(when (magit-git-version>= "2.50.0")
|
||||
(insert "# "))
|
||||
(insert (concat trailer "\n"))))
|
||||
((and (not action) action-type)
|
||||
(let ((inhibit-read-only t))
|
||||
(if comment-p
|
||||
(delete-region beg (+ beg 2))
|
||||
(insert comment-start " ")))
|
||||
(forward-line))
|
||||
;; In the case of --rebase-merges, commit lines may have
|
||||
;; other lines with other action types, empty lines, and
|
||||
;; "Branch" comments interspersed. Move along.
|
||||
((forward-line)))))
|
||||
((and action (eq action-type 'commit))
|
||||
(let ((inhibit-read-only t))
|
||||
(magit-delete-line)
|
||||
(insert (concat action " " target " "))
|
||||
(when (magit-git-version>= "2.50.0")
|
||||
(insert "# "))
|
||||
(insert (concat trailer "\n"))))
|
||||
((and (not action) action-type)
|
||||
(let ((inhibit-read-only t))
|
||||
(if comment-p
|
||||
(delete-region beg (+ beg 2))
|
||||
(insert comment-start " ")))
|
||||
(forward-line))
|
||||
;; In the case of --rebase-merges, commit lines may have
|
||||
;; other lines with other action types, empty lines, and
|
||||
;; "Branch" comments interspersed. Move along.
|
||||
((forward-line)))))
|
||||
(goto-char (cond (git-rebase-auto-advance end-marker)
|
||||
(pt-below-p (1- end-marker))
|
||||
(beg)))
|
||||
@@ -435,15 +435,15 @@ point or mark. If the region isn't active and FALLBACK is
|
||||
non-nil, return the beginning and end of the current rebase line,
|
||||
if any."
|
||||
(cond
|
||||
((use-region-p)
|
||||
(let ((beg (magit--bol-position (region-beginning)))
|
||||
(end (magit--eol-position (region-end))))
|
||||
(and (git-rebase-line-p beg)
|
||||
(git-rebase-line-p end)
|
||||
(list beg (1+ end)))))
|
||||
((and fallback (git-rebase-line-p))
|
||||
(list (line-beginning-position)
|
||||
(1+ (line-end-position))))))
|
||||
((use-region-p)
|
||||
(let ((beg (magit--bol-position (region-beginning)))
|
||||
(end (magit--eol-position (region-end))))
|
||||
(and (git-rebase-line-p beg)
|
||||
(git-rebase-line-p end)
|
||||
(list beg (1+ end)))))
|
||||
((and fallback (git-rebase-line-p))
|
||||
(list (line-beginning-position)
|
||||
(1+ (line-end-position))))))
|
||||
|
||||
(defun git-rebase-move-line-down (n)
|
||||
"Move the current commit (or command) N lines down.
|
||||
@@ -875,22 +875,22 @@ except for the \"pick\" command."
|
||||
(replace-match (make-string 10 ?\s) t t nil 1))
|
||||
(setq cmd (intern (concat "git-rebase-" (match-str 4))))
|
||||
(cond
|
||||
((not (fboundp cmd))
|
||||
(delete-line))
|
||||
((eq cmd 'git-rebase-fixup)
|
||||
(delete-line)
|
||||
(git-rebase--insert-descriptions git-rebase-fixup-descriptions))
|
||||
(t
|
||||
(add-text-properties (line-beginning-position)
|
||||
(1+ (line-end-position))
|
||||
'(font-lock-face font-lock-comment-face))
|
||||
(replace-match " " t t nil 2)
|
||||
(replace-match
|
||||
(string-pad
|
||||
(save-match-data
|
||||
(substitute-command-keys (format "\\[%s]" cmd)))
|
||||
8)
|
||||
t t nil 3))))))))))
|
||||
((not (fboundp cmd))
|
||||
(delete-line))
|
||||
((eq cmd 'git-rebase-fixup)
|
||||
(delete-line)
|
||||
(git-rebase--insert-descriptions git-rebase-fixup-descriptions))
|
||||
(t
|
||||
(add-text-properties (line-beginning-position)
|
||||
(1+ (line-end-position))
|
||||
'(font-lock-face font-lock-comment-face))
|
||||
(replace-match " " t t nil 2)
|
||||
(replace-match
|
||||
(string-pad
|
||||
(save-match-data
|
||||
(substitute-command-keys (format "\\[%s]" cmd)))
|
||||
8)
|
||||
t t nil 3))))))))))
|
||||
|
||||
(defun git-rebase--insert-descriptions (alist)
|
||||
(pcase-dolist (`(,cmd . ,desc) alist)
|
||||
@@ -948,6 +948,7 @@ is used as a value for `imenu-extract-index-name-function'."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-apply.el --- Apply Git diffs -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -115,19 +115,19 @@ is a member of `magit-post-unstage-hook-commands'."
|
||||
With a prefix argument fallback to a 3-way merge. Doing
|
||||
so causes the change to be applied to the index as well."
|
||||
(interactive (and current-prefix-arg (list "--3way")))
|
||||
(when-let ((s (magit-apply--get-selection)))
|
||||
(when$ (magit-apply--get-selection)
|
||||
(pcase (list (magit-diff-type) (magit-diff-scope))
|
||||
(`(,(or 'unstaged 'staged) ,_)
|
||||
(user-error "Change is already in the working tree"))
|
||||
(`(untracked ,(or 'file 'files))
|
||||
(call-interactively #'magit-am))
|
||||
(`(,_ region) (magit-apply-region s args))
|
||||
(`(,_ hunk) (magit-apply-hunk s args))
|
||||
(`(,_ hunks) (magit-apply-hunks s args))
|
||||
(`(,_ region) (magit-apply-region $ args))
|
||||
(`(,_ hunk) (magit-apply-hunk $ args))
|
||||
(`(,_ hunks) (magit-apply-hunks $ args))
|
||||
(`(rebase-sequence file)
|
||||
(call-interactively #'magit-patch-apply))
|
||||
(`(,_ file) (magit-apply-diff s args))
|
||||
(`(,_ files) (magit-apply-diffs s args)))))
|
||||
(`(,_ file) (magit-apply-diff $ args))
|
||||
(`(,_ files) (magit-apply-diffs $ args)))))
|
||||
|
||||
(defun magit-apply--section-content (section)
|
||||
(buffer-substring-no-properties (if (magit-hunk-section-p section)
|
||||
@@ -310,17 +310,17 @@ at point, stage the file but not its content."
|
||||
"Read one or more files and stage all changes in those files.
|
||||
With prefix argument FORCE, offer ignored files for completion."
|
||||
(interactive
|
||||
(let* ((choices (if current-prefix-arg
|
||||
(magit-ignored-files)
|
||||
(nconc (magit-unstaged-files)
|
||||
(magit-untracked-files))))
|
||||
(default (or (magit-section-value-if 'file)
|
||||
(magit-file-relative-name)))
|
||||
(default (car (member default choices))))
|
||||
(list (magit-completing-read-multiple
|
||||
(if current-prefix-arg "Stage ignored file,s: " "Stage file,s: ")
|
||||
choices nil t nil nil default)
|
||||
current-prefix-arg)))
|
||||
(let* ((choices (if current-prefix-arg
|
||||
(magit-ignored-files)
|
||||
(nconc (magit-unstaged-files)
|
||||
(magit-untracked-files))))
|
||||
(default (or (magit-section-value-if 'file)
|
||||
(magit-file-relative-name)))
|
||||
(default (car (member default choices))))
|
||||
(list (magit-completing-read-multiple
|
||||
(if current-prefix-arg "Stage ignored file,s: " "Stage file,s: ")
|
||||
choices nil t nil nil default)
|
||||
current-prefix-arg)))
|
||||
(magit-with-toplevel
|
||||
(magit-stage-1 (and force "--force") files)))
|
||||
|
||||
@@ -433,12 +433,12 @@ ignored) files."
|
||||
(defun magit-unstage-files (files)
|
||||
"Read one or more files and unstage all changes to those files."
|
||||
(interactive
|
||||
(let* ((choices (magit-staged-files))
|
||||
(default (or (magit-section-value-if 'file)
|
||||
(magit-file-relative-name)))
|
||||
(default (car (member default choices))))
|
||||
(list (magit-completing-read-multiple "Unstage file,s: " choices
|
||||
nil t nil nil default))))
|
||||
(let* ((choices (magit-staged-files))
|
||||
(default (or (magit-section-value-if 'file)
|
||||
(magit-file-relative-name)))
|
||||
(default (car (member default choices))))
|
||||
(list (magit-completing-read-multiple "Unstage file,s: " choices
|
||||
nil t nil nil default))))
|
||||
(magit-with-toplevel
|
||||
(magit-unstage-1 files)))
|
||||
|
||||
@@ -488,17 +488,17 @@ On a hunk or file with unresolved conflicts prompt which side to
|
||||
keep (while discarding the other). If point is within the text
|
||||
of a side, then keep that side without prompting."
|
||||
(interactive)
|
||||
(when-let ((s (magit-apply--get-selection)))
|
||||
(when$ (magit-apply--get-selection)
|
||||
(pcase (list (magit-diff-type) (magit-diff-scope))
|
||||
(`(committed ,_) (user-error "Cannot discard committed changes"))
|
||||
(`(undefined ,_) (user-error "Cannot discard this change"))
|
||||
(`(untracked list) (magit-discard-untracked))
|
||||
(`(,_ region) (magit-discard-region s))
|
||||
(`(,_ hunk) (magit-discard-hunk s))
|
||||
(`(,_ hunks) (magit-discard-hunks s))
|
||||
(`(,_ file) (magit-discard-file s))
|
||||
(`(,_ files) (magit-discard-files s))
|
||||
(`(,_ list) (magit-discard-files s)))))
|
||||
(`(,_ region) (magit-discard-region $))
|
||||
(`(,_ hunk) (magit-discard-hunk $))
|
||||
(`(,_ hunks) (magit-discard-hunks $))
|
||||
(`(,_ file) (magit-discard-file $))
|
||||
(`(,_ files) (magit-discard-files $))
|
||||
(`(,_ list) (magit-discard-files $)))))
|
||||
|
||||
(defun magit-discard-untracked ()
|
||||
(magit-discard-files--delete
|
||||
@@ -642,13 +642,13 @@ of a side, then keep that side without prompting."
|
||||
files))
|
||||
(dolist (file files)
|
||||
(let ((orig (cadr (assoc file status))))
|
||||
(if (file-exists-p file)
|
||||
(progn
|
||||
(when-let ((path (file-name-directory orig)))
|
||||
(make-directory path t))
|
||||
(magit-call-git "mv" file orig))
|
||||
(magit-call-git "rm" "--cached" "--" file)
|
||||
(magit-call-git "reset" "--" orig)))))
|
||||
(cond ((file-exists-p file)
|
||||
(when$ (file-name-directory orig)
|
||||
(make-directory $ t))
|
||||
(magit-call-git "mv" file orig))
|
||||
(t
|
||||
(magit-call-git "rm" "--cached" "--" file)
|
||||
(magit-call-git "reset" "--" orig))))))
|
||||
|
||||
(defun magit-discard-files--discard (sections new-files)
|
||||
(let ((files (mapcar (##oref % value) sections)))
|
||||
@@ -684,16 +684,16 @@ of a side, then keep that side without prompting."
|
||||
With a prefix argument fallback to a 3-way merge. Doing
|
||||
so causes the change to be applied to the index as well."
|
||||
(interactive (and current-prefix-arg (list "--3way")))
|
||||
(when-let ((s (magit-apply--get-selection)))
|
||||
(when$ (magit-apply--get-selection)
|
||||
(pcase (list (magit-diff-type) (magit-diff-scope))
|
||||
(`(untracked ,_) (user-error "Cannot reverse untracked changes"))
|
||||
(`(unstaged ,_) (user-error "Cannot reverse unstaged changes"))
|
||||
(`(,_ region) (magit-reverse-region s args))
|
||||
(`(,_ hunk) (magit-reverse-hunk s args))
|
||||
(`(,_ hunks) (magit-reverse-hunks s args))
|
||||
(`(,_ file) (magit-reverse-file s args))
|
||||
(`(,_ files) (magit-reverse-files s args))
|
||||
(`(,_ list) (magit-reverse-files s args)))))
|
||||
(`(,_ region) (magit-reverse-region $ args))
|
||||
(`(,_ hunk) (magit-reverse-hunk $ args))
|
||||
(`(,_ hunks) (magit-reverse-hunks $ args))
|
||||
(`(,_ file) (magit-reverse-file $ args))
|
||||
(`(,_ files) (magit-reverse-files $ args))
|
||||
(`(,_ list) (magit-reverse-files $ args)))))
|
||||
|
||||
(defun magit-reverse-region (section args)
|
||||
(magit-confirm 'reverse "Reverse region")
|
||||
@@ -717,9 +717,9 @@ so causes the change to be applied to the index as well."
|
||||
(pcase-let ((`(,binaries ,sections)
|
||||
(let ((bs (magit-binary-files
|
||||
(cond ((derived-mode-p 'magit-revision-mode)
|
||||
magit-buffer-range)
|
||||
magit-buffer-diff-range)
|
||||
((derived-mode-p 'magit-diff-mode)
|
||||
magit-buffer-range)
|
||||
magit-buffer-diff-range)
|
||||
("--cached")))))
|
||||
(magit--separate (##member (oref % value) bs)
|
||||
sections))))
|
||||
@@ -822,6 +822,7 @@ a separate commit. A typical workflow would be:
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-autorevert.el --- Revert buffers when files in repository change -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-base.el --- Early birds -*- lexical-binding:t; coding:utf-8 -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -494,7 +494,7 @@ and delay of your graphical environment or operating system."
|
||||
(defclass magit-hunk-section (magit-diff-section)
|
||||
((keymap :initform 'magit-hunk-section-map)
|
||||
(painted :initform nil)
|
||||
(fontified :initform nil) ;TODO
|
||||
(fontified :initform nil)
|
||||
(refined :initform nil)
|
||||
(combined :initform nil :initarg :combined)
|
||||
(from-range :initform nil :initarg :from-range)
|
||||
@@ -824,13 +824,13 @@ ACTION is a member of option `magit-slow-confirm'."
|
||||
(or (cond ((and (not (eq action t))
|
||||
(or (eq magit-no-confirm t)
|
||||
(memq action magit-no-confirm)
|
||||
(cl-member-if (pcase-lambda (`(,key ,var . ,sub))
|
||||
(and (memq key magit-no-confirm)
|
||||
(memq action sub)
|
||||
(or (not var)
|
||||
(and (boundp var)
|
||||
(symbol-value var)))))
|
||||
magit--no-confirm-alist)))
|
||||
(magit--any (pcase-lambda (`(,key ,var . ,sub))
|
||||
(and (memq key magit-no-confirm)
|
||||
(memq action sub)
|
||||
(or (not var)
|
||||
(and (boundp var)
|
||||
(symbol-value var)))))
|
||||
magit--no-confirm-alist)))
|
||||
(or (not sitems) items))
|
||||
((not sitems)
|
||||
(magit-y-or-n-p prompt action))
|
||||
@@ -963,32 +963,32 @@ Pad the left side of STRING so that it aligns with the text area."
|
||||
(goto-char (point-min))
|
||||
(while (search-forward "%" nil t)
|
||||
(cond
|
||||
;; Quoted percent sign.
|
||||
((eq (char-after) ?%)
|
||||
(delete-char 1))
|
||||
;; Valid format spec.
|
||||
((looking-at "\\([-0-9.]*\\)\\([a-zA-Z]\\)")
|
||||
(let* ((num (match-str 1))
|
||||
(spec (string-to-char (match-str 2)))
|
||||
(val (assq spec specification)))
|
||||
(unless val
|
||||
(error "Invalid format character: `%%%c'" spec))
|
||||
(setq val (cdr val))
|
||||
;; Pad result to desired length.
|
||||
(let ((text (format (concat "%" num "s") val)))
|
||||
;; Insert first, to preserve text properties.
|
||||
(if (next-property-change 0 (concat " " text))
|
||||
;; If the inserted text has properties, then preserve those.
|
||||
(insert text)
|
||||
;; Otherwise preserve FORMAT's properties, like `format-spec'.
|
||||
(insert-and-inherit text))
|
||||
;; Delete the specifier body.
|
||||
(delete-region (+ (match-beginning 0) (length text))
|
||||
(+ (match-end 0) (length text)))
|
||||
;; Delete the percent sign.
|
||||
(delete-region (1- (match-beginning 0)) (match-beginning 0)))))
|
||||
;; Signal an error on bogus format strings.
|
||||
((error "Invalid format string"))))
|
||||
;; Quoted percent sign.
|
||||
((eq (char-after) ?%)
|
||||
(delete-char 1))
|
||||
;; Valid format spec.
|
||||
((looking-at "\\([-0-9.]*\\)\\([a-zA-Z]\\)")
|
||||
(let* ((num (match-str 1))
|
||||
(spec (string-to-char (match-str 2)))
|
||||
(val (assq spec specification)))
|
||||
(unless val
|
||||
(error "Invalid format character: `%%%c'" spec))
|
||||
(setq val (cdr val))
|
||||
;; Pad result to desired length.
|
||||
(let ((text (format (concat "%" num "s") val)))
|
||||
;; Insert first, to preserve text properties.
|
||||
(if (next-property-change 0 (concat " " text))
|
||||
;; If the inserted text has properties, then preserve those.
|
||||
(insert text)
|
||||
;; Otherwise preserve FORMAT's properties, like `format-spec'.
|
||||
(insert-and-inherit text))
|
||||
;; Delete the specifier body.
|
||||
(delete-region (+ (match-beginning 0) (length text))
|
||||
(+ (match-end 0) (length text)))
|
||||
;; Delete the percent sign.
|
||||
(delete-region (1- (match-beginning 0)) (match-beginning 0)))))
|
||||
;; Signal an error on bogus format strings.
|
||||
((error "Invalid format string"))))
|
||||
(buffer-string)))
|
||||
|
||||
;;; Missing from Emacs
|
||||
@@ -1013,6 +1013,24 @@ This function should be named `version>' and be part of Emacs."
|
||||
This function should be named `version>=' and be part of Emacs."
|
||||
(version-list-<= (version-to-list v2) (version-to-list v1)))
|
||||
|
||||
(defun magit--delete-text-properties (string &optional props)
|
||||
"Delete text properties PROPS from STRING and return it.
|
||||
If PROPS is nil, remove all properties. To leave STRING unchanged
|
||||
and return a new string, instead use `magit--remove-text-properties'."
|
||||
(set-text-properties 0 (length string) props string)
|
||||
string)
|
||||
|
||||
(defun magit--remove-text-properties (string &optional props)
|
||||
"Return a copy of STRING with text properties PROPS removed.
|
||||
If PROPS is nil, remove all properties."
|
||||
(magit--delete-text-properties (copy-sequence string) props))
|
||||
|
||||
;;; Emacs Compatibility
|
||||
|
||||
(static-if (fboundp 'member-if) ; Emacs 31.1
|
||||
(defalias 'magit--any 'member-if)
|
||||
(defalias 'magit--any 'cl-member-if))
|
||||
|
||||
;;; Kludges for Emacs Bugs
|
||||
|
||||
(defun magit-which-function ()
|
||||
@@ -1187,6 +1205,16 @@ Like `message', except that `message-log-max' is bound to nil."
|
||||
(push char quoted))
|
||||
(concat (nreverse quoted))))
|
||||
|
||||
(defun magit--find-buffer (&rest plist)
|
||||
"Like `find-buffer' but take multiple VARIABLE-VALUE pairs."
|
||||
(seq-find (lambda (buf)
|
||||
(while (and plist
|
||||
(equal (buffer-local-value (car plist) buf)
|
||||
(cadr plist)))
|
||||
(setq plist (cddr plist)))
|
||||
(not plist))
|
||||
(buffer-list)))
|
||||
|
||||
;;; _
|
||||
(provide 'magit-base)
|
||||
;; Local Variables:
|
||||
@@ -1195,6 +1223,7 @@ Like `message', except that `message-log-max' is bound to nil."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-bisect.el --- Bisect support for Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -321,6 +321,7 @@ bisect run'."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-blame.el --- Blame support for Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -261,7 +261,7 @@ Also see option `magit-blame-styles'."
|
||||
(or (and (not (and type (not (eq type magit-blame-type))))
|
||||
(magit-blame-chunk-at (point)))
|
||||
(and type
|
||||
(let ((rev (or magit-buffer-refname magit-buffer-revision))
|
||||
(let ((rev magit-buffer-revision)
|
||||
(file (and (not (derived-mode-p 'dired-mode))
|
||||
(magit-file-relative-name
|
||||
nil (not magit-buffer-file-name))))
|
||||
@@ -418,8 +418,8 @@ modes is toggled, then this mode also gets toggled automatically.
|
||||
(magit-blame-mode 1))
|
||||
(message "Blaming...")
|
||||
(magit-blame-run-process
|
||||
(and$ (or magit-buffer-refname magit-buffer-revision)
|
||||
(and (not (equal $ "{index}")) $))
|
||||
(and (not (equal magit-buffer-revision "{index}"))
|
||||
magit-buffer-revision)
|
||||
(magit-file-relative-name nil (not magit-buffer-file-name))
|
||||
(if (memq magit-blame-type '(final removal))
|
||||
(cons "--reverse" args)
|
||||
@@ -1006,6 +1006,7 @@ instead of the hash, like `kill-ring-save' would."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-bookmark.el --- Bookmarks for Magit buffers -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Inspired by an earlier implementation by Yuri Khan.
|
||||
|
||||
@@ -56,8 +56,8 @@
|
||||
;;;; Diff
|
||||
|
||||
(put 'magit-diff-mode 'magit-bookmark-variables
|
||||
'(magit-buffer-range-hashed
|
||||
magit-buffer-typearg
|
||||
'(magit-buffer-diff-range-oids
|
||||
magit-buffer-diff-typearg
|
||||
magit-buffer-diff-args
|
||||
magit-buffer-diff-files))
|
||||
|
||||
@@ -66,9 +66,10 @@
|
||||
(pcase (magit-diff-type)
|
||||
('staged "staged")
|
||||
('unstaged "unstaged")
|
||||
('committed magit-buffer-range)
|
||||
('committed magit-buffer-diff-range)
|
||||
('undefined
|
||||
(delq nil (list magit-buffer-typearg magit-buffer-range-hashed))))
|
||||
(delq nil
|
||||
(list magit-buffer-diff-typearg magit-buffer-diff-range-oids))))
|
||||
(if magit-buffer-diff-files
|
||||
(concat " -- " (string-join magit-buffer-diff-files " "))
|
||||
"")))
|
||||
@@ -76,7 +77,7 @@
|
||||
;;;; Revision
|
||||
|
||||
(put 'magit-revision-mode 'magit-bookmark-variables
|
||||
'(magit-buffer-revision-hash
|
||||
'(magit-buffer-revision-oid
|
||||
magit-buffer-diff-args
|
||||
magit-buffer-diff-files))
|
||||
|
||||
@@ -90,7 +91,7 @@
|
||||
;;;; Stash
|
||||
|
||||
(put 'magit-stash-mode 'magit-bookmark-variables
|
||||
'(magit-buffer-revision-hash
|
||||
'(magit-buffer-revision-oid
|
||||
magit-buffer-diff-args
|
||||
magit-buffer-diff-files))
|
||||
|
||||
@@ -104,20 +105,20 @@
|
||||
(cl-defmethod magit-bookmark--get-child-value
|
||||
(section &context (major-mode magit-stash-mode))
|
||||
(string-replace magit-buffer-revision
|
||||
magit-buffer-revision-hash
|
||||
magit-buffer-revision-oid
|
||||
(oref section value)))
|
||||
|
||||
;;; Log
|
||||
;;;; Log
|
||||
|
||||
(put 'magit-log-mode 'magit-bookmark-variables
|
||||
'(magit-buffer-revisions
|
||||
'(magit-buffer-log-revisions
|
||||
magit-buffer-log-args
|
||||
magit-buffer-log-files))
|
||||
|
||||
(cl-defmethod magit-bookmark-name (&context (major-mode magit-log-mode))
|
||||
(format "magit-log(%s%s)"
|
||||
(string-join magit-buffer-revisions " ")
|
||||
(string-join magit-buffer-log-revisions " ")
|
||||
(if magit-buffer-log-files
|
||||
(concat " -- " (string-join magit-buffer-log-files " "))
|
||||
"")))
|
||||
@@ -126,12 +127,12 @@
|
||||
|
||||
(put 'magit-cherry-mode 'magit-bookmark-variables
|
||||
'(magit-buffer-refname
|
||||
magit-buffer-upstream))
|
||||
magit-buffer-cherry-upstream))
|
||||
|
||||
(cl-defmethod magit-bookmark-name (&context (major-mode magit-cherry-mode))
|
||||
(format "magit-cherry(%s > %s)"
|
||||
magit-buffer-refname
|
||||
magit-buffer-upstream))
|
||||
magit-buffer-cherry-upstream))
|
||||
|
||||
;;;; Reflog
|
||||
|
||||
@@ -146,8 +147,8 @@
|
||||
(put 'magit-status-mode 'magit-bookmark-variables nil)
|
||||
|
||||
(put 'magit-refs-mode 'magit-bookmark-variables
|
||||
'(magit-buffer-upstream
|
||||
magit-buffer-arguments))
|
||||
'(magit-buffer-refs-upstream
|
||||
magit-buffer-refs-args))
|
||||
|
||||
(put 'magit-stashes-mode 'magit-bookmark-variables nil)
|
||||
|
||||
@@ -162,6 +163,7 @@
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-branch.el --- Branch support -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -153,11 +153,11 @@ to set `magit-branch-prefer-remote-upstream' to a non-nil value.
|
||||
However, I recommend that you use local branches as UPSTREAM."
|
||||
:package-version '(magit . "2.9.0")
|
||||
:group 'magit-commands
|
||||
:type '(repeat (cons (string :tag "Use upstream")
|
||||
(choice :tag "For branches" ;???
|
||||
(regexp :tag "Matching")
|
||||
(repeat :tag "Except"
|
||||
(string :tag "Branch"))))))
|
||||
:type '(alist :key-type (string :tag "Use upstream")
|
||||
:value-type (choice :tag "For branches" ;???
|
||||
(regexp :tag "Matching")
|
||||
(repeat :tag "Except"
|
||||
(string :tag "Branch")))))
|
||||
|
||||
(defcustom magit-branch-rename-push-target t
|
||||
"Whether the push-remote setup is preserved when renaming a branch.
|
||||
@@ -314,10 +314,10 @@ branch. This is similar to what `magit-branch-and-checkout'
|
||||
does."
|
||||
(declare (interactive-only magit-call-git))
|
||||
(interactive
|
||||
(let ((arg (magit-read-other-branch-or-commit "Checkout")))
|
||||
(list arg
|
||||
(and (not (magit-commit-p arg))
|
||||
(magit-read-starting-point "Create and checkout branch" arg)))))
|
||||
(let ((arg (magit-read-other-branch-or-commit "Checkout")))
|
||||
(list arg
|
||||
(and (not (magit-commit-p arg))
|
||||
(magit-read-starting-point "Create and checkout branch" arg)))))
|
||||
(when (string-match "\\`heads/\\(.+\\)" arg)
|
||||
(setq arg (match-str 1 arg)))
|
||||
(if start-point
|
||||
@@ -352,50 +352,50 @@ value of `magit-branch-adjust-remote-upstream-alist', just like
|
||||
when using `magit-branch-and-checkout'."
|
||||
(declare (interactive-only magit-call-git))
|
||||
(interactive
|
||||
(let* ((current (magit-get-current-branch))
|
||||
(local (magit-list-local-branch-names))
|
||||
(remote (seq-filter (##and (string-match "[^/]+/" %)
|
||||
(not (member (substring % (match-end 0))
|
||||
(cons "HEAD" local))))
|
||||
(magit-list-remote-branch-names)))
|
||||
(choices (nconc (delete current local) remote))
|
||||
(atpoint (magit-branch-at-point))
|
||||
(choice (magit-completing-read
|
||||
"Checkout branch" choices
|
||||
nil nil nil 'magit-revision-history
|
||||
(or (car (member atpoint choices))
|
||||
(and atpoint
|
||||
(car (member (and (string-match "[^/]+/" atpoint)
|
||||
(substring atpoint (match-end 0)))
|
||||
choices)))))))
|
||||
(cond ((member choice remote)
|
||||
(list (and (string-match "[^/]+/" choice)
|
||||
(substring choice (match-end 0)))
|
||||
choice))
|
||||
((member choice local)
|
||||
(list choice))
|
||||
((list choice (magit-read-starting-point "Create" choice))))))
|
||||
(let* ((current (magit-get-current-branch))
|
||||
(local (magit-list-local-branch-names))
|
||||
(remote (seq-filter (##and (string-match "[^/]+/" %)
|
||||
(not (member (substring % (match-end 0))
|
||||
(cons "HEAD" local))))
|
||||
(magit-list-remote-branch-names)))
|
||||
(choices (nconc (delete current local) remote))
|
||||
(atpoint (magit-branch-at-point))
|
||||
(choice (magit-completing-read
|
||||
"Checkout branch" choices
|
||||
nil nil nil 'magit-revision-history
|
||||
(or (car (member atpoint choices))
|
||||
(and atpoint
|
||||
(car (member (and (string-match "[^/]+/" atpoint)
|
||||
(substring atpoint (match-end 0)))
|
||||
choices)))))))
|
||||
(cond ((member choice remote)
|
||||
(list (and (string-match "[^/]+/" choice)
|
||||
(substring choice (match-end 0)))
|
||||
choice))
|
||||
((member choice local)
|
||||
(list choice))
|
||||
((list choice (magit-read-starting-point "Create" choice))))))
|
||||
(cond
|
||||
((not start-point)
|
||||
(magit--checkout branch (magit-branch-arguments))
|
||||
(magit-refresh))
|
||||
(t
|
||||
(when (magit-anything-modified-p t)
|
||||
(user-error "Cannot checkout when there are uncommitted changes"))
|
||||
(magit-run-git-async "checkout" (magit-branch-arguments)
|
||||
"-b" branch start-point)
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
(lambda (process event)
|
||||
(when (memq (process-status process) '(exit signal))
|
||||
(magit-branch-maybe-adjust-upstream branch start-point)
|
||||
(when (magit-remote-branch-p start-point)
|
||||
(pcase-let ((`(,remote . ,remote-branch)
|
||||
(magit-split-branch-name start-point)))
|
||||
(when (and (equal branch remote-branch)
|
||||
(not (equal remote (magit-get "remote.pushDefault"))))
|
||||
(magit-set remote "branch" branch "pushRemote"))))
|
||||
(magit-process-sentinel process event)))))))
|
||||
((not start-point)
|
||||
(magit--checkout branch (magit-branch-arguments))
|
||||
(magit-refresh))
|
||||
(t
|
||||
(when (magit-anything-modified-p t)
|
||||
(user-error "Cannot checkout when there are uncommitted changes"))
|
||||
(magit-run-git-async "checkout" (magit-branch-arguments)
|
||||
"-b" branch start-point)
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
(lambda (process event)
|
||||
(when (memq (process-status process) '(exit signal))
|
||||
(magit-branch-maybe-adjust-upstream branch start-point)
|
||||
(when (magit-remote-branch-p start-point)
|
||||
(pcase-let ((`(,remote . ,remote-branch)
|
||||
(magit-split-branch-name start-point)))
|
||||
(when (and (equal branch remote-branch)
|
||||
(not (equal remote (magit-get "remote.pushDefault"))))
|
||||
(magit-set remote "branch" branch "pushRemote"))))
|
||||
(magit-process-sentinel process event)))))))
|
||||
|
||||
(defun magit-branch-maybe-adjust-upstream (branch start-point)
|
||||
(when-let ((upstream
|
||||
@@ -423,20 +423,20 @@ when using `magit-branch-and-checkout'."
|
||||
(if magit-branch-read-upstream-first
|
||||
(let ((choice (magit-read-starting-point prompt nil default-start)))
|
||||
(cond
|
||||
((magit-rev-verify choice)
|
||||
(list (magit-read-string-ns
|
||||
(if magit-completing-read--silent-default
|
||||
(format "%s (starting at `%s')" prompt choice)
|
||||
"Name for new branch")
|
||||
(let ((def (string-join (cdr (split-string choice "/")) "/")))
|
||||
(and (member choice (magit-list-remote-branch-names))
|
||||
(not (member def (magit-list-local-branch-names)))
|
||||
def)))
|
||||
choice))
|
||||
((eq magit-branch-read-upstream-first 'fallback)
|
||||
(list choice
|
||||
(magit-read-starting-point prompt choice default-start)))
|
||||
((user-error "Not a valid starting-point: %s" choice))))
|
||||
((magit-rev-verify choice)
|
||||
(list (magit-read-string-ns
|
||||
(if magit-completing-read--silent-default
|
||||
(format "%s (starting at `%s')" prompt choice)
|
||||
"Name for new branch")
|
||||
(let ((def (string-join (cdr (split-string choice "/")) "/")))
|
||||
(and (member choice (magit-list-remote-branch-names))
|
||||
(not (member def (magit-list-local-branch-names)))
|
||||
def)))
|
||||
choice))
|
||||
((eq magit-branch-read-upstream-first 'fallback)
|
||||
(list choice
|
||||
(magit-read-starting-point prompt choice default-start)))
|
||||
((user-error "Not a valid starting-point: %s" choice))))
|
||||
(let ((branch (magit-read-string-ns (concat prompt " named"))))
|
||||
(if (magit-branch-p branch)
|
||||
(magit-branch-read-args
|
||||
@@ -511,8 +511,8 @@ from the source branch's upstream, then an error is raised."
|
||||
(if checkout
|
||||
(magit-call-git "checkout" "-b" branch current)
|
||||
(magit-call-git "branch" branch current)))
|
||||
(when-let ((upstream (magit-get-indirect-upstream-branch current)))
|
||||
(magit-call-git "branch" "--set-upstream-to" upstream branch))
|
||||
(when$ (magit-get-indirect-upstream-branch current)
|
||||
(magit-call-git "branch" "--set-upstream-to" $ branch))
|
||||
(when (and tracked
|
||||
(setq base
|
||||
(if from
|
||||
@@ -544,13 +544,13 @@ When resetting to another branch and a prefix argument is used,
|
||||
then also set the target branch as the upstream of the branch
|
||||
that is being reset."
|
||||
(interactive
|
||||
(let ((branch (magit-read-local-branch "Reset branch"
|
||||
(magit-local-branch-at-point))))
|
||||
(list branch
|
||||
(magit-read-branch-or-commit (format "Reset %s to" branch)
|
||||
(magit-get-upstream-branch branch)
|
||||
branch)
|
||||
current-prefix-arg)))
|
||||
(let ((branch (magit-read-local-branch "Reset branch"
|
||||
(magit-local-branch-at-point))))
|
||||
(list branch
|
||||
(magit-read-branch-or-commit (format "Reset %s to" branch)
|
||||
(magit-get-upstream-branch branch)
|
||||
branch)
|
||||
current-prefix-arg)))
|
||||
(let ((magit-inhibit-refresh t))
|
||||
(if (equal branch (magit-get-current-branch))
|
||||
(if (and (magit-anything-modified-p)
|
||||
@@ -560,8 +560,7 @@ that is being reset."
|
||||
(magit-reset-hard to))
|
||||
(magit-call-git "update-ref"
|
||||
"-m" (format "reset: moving to %s" to)
|
||||
(magit-git-string "rev-parse" "--symbolic-full-name"
|
||||
branch)
|
||||
(magit-ref-fullname branch)
|
||||
to))
|
||||
(when (and set-upstream (magit-branch-p to))
|
||||
(magit-set-upstream-branch branch to)
|
||||
@@ -588,24 +587,24 @@ prompt is confusing."
|
||||
;; `magit-branch-rename'; but it turns out everyone wants to squeeze
|
||||
;; a bit of extra functionality into this one, including myself.
|
||||
(interactive
|
||||
(let ((branches (magit-region-values 'branch t))
|
||||
(force current-prefix-arg))
|
||||
(if (length> branches 1)
|
||||
(magit-confirm t nil "Delete %d branches" nil branches)
|
||||
(setq branches
|
||||
(list (magit-read-branch-prefer-other
|
||||
(if force "Force delete branch" "Delete branch")))))
|
||||
(cond-let
|
||||
(force)
|
||||
[[unmerged (seq-remove #'magit-branch-merged-p branches)]]
|
||||
((magit-confirm 'delete-unmerged-branch
|
||||
"Delete unmerged branch %s"
|
||||
"Delete %d unmerged branches"
|
||||
'noabort unmerged)
|
||||
(setq force branches))
|
||||
((setq branches (cl-set-difference branches unmerged :test #'equal)))
|
||||
((user-error "Abort")))
|
||||
(list branches force)))
|
||||
(let ((branches (magit-region-values 'branch t))
|
||||
(force current-prefix-arg))
|
||||
(if (length> branches 1)
|
||||
(magit-confirm t nil "Delete %d branches" nil branches)
|
||||
(setq branches
|
||||
(list (magit-read-branch-prefer-other
|
||||
(if force "Force delete branch" "Delete branch")))))
|
||||
(cond-let
|
||||
(force)
|
||||
[[unmerged (seq-remove #'magit-branch-merged-p branches)]]
|
||||
((magit-confirm 'delete-unmerged-branch
|
||||
"Delete unmerged branch %s"
|
||||
"Delete %d unmerged branches"
|
||||
'noabort unmerged)
|
||||
(setq force branches))
|
||||
((setq branches (cl-set-difference branches unmerged :test #'equal)))
|
||||
((user-error "Abort")))
|
||||
(list branches force)))
|
||||
(let ((refs (mapcar #'magit-ref-fullname branches)))
|
||||
;; If a member of refs is nil, that means that
|
||||
;; the respective branch name is ambiguous.
|
||||
@@ -614,86 +613,86 @@ prompt is confusing."
|
||||
"%s ambiguous; please cleanup using git directly"
|
||||
(let ((len (length ambiguous)))
|
||||
(cond
|
||||
((= len 1)
|
||||
(format "%s is" (seq-find #'magit-ref-ambiguous-p branches)))
|
||||
((= len (length refs))
|
||||
(format "These %s names are" len))
|
||||
((format "%s of these names are" len))))))
|
||||
((= len 1)
|
||||
(format "%s is" (seq-find #'magit-ref-ambiguous-p branches)))
|
||||
((= len (length refs))
|
||||
(format "These %s names are" len))
|
||||
((format "%s of these names are" len))))))
|
||||
(cond
|
||||
((string-match "^refs/remotes/\\([^/]+\\)" (car refs))
|
||||
(let* ((remote (match-str 1 (car refs)))
|
||||
(offset (1+ (length remote))))
|
||||
(cond
|
||||
((magit-confirm 'delete-branch-on-remote
|
||||
(list "Deleting local %s. Also delete on %s"
|
||||
(magit-ref-fullname (car branches))
|
||||
remote)
|
||||
(list "Deleting %d local refs. Also delete on %s"
|
||||
(length refs)
|
||||
remote)
|
||||
'noabort refs)
|
||||
;; The ref may actually point at another rev on the remote,
|
||||
;; but this is better than nothing.
|
||||
(dolist (ref refs)
|
||||
(message "Delete %s (was %s)" ref
|
||||
(magit-rev-parse "--short" ref)))
|
||||
;; Assume the branches actually still exist on the remote.
|
||||
(magit-run-git-async
|
||||
"push"
|
||||
(and (or force magit-branch-delete-never-verify) "--no-verify")
|
||||
remote
|
||||
(mapcar (##concat ":" (substring % offset)) branches))
|
||||
;; If that is not the case, then this deletes the tracking branches.
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
(apply-partially #'magit-delete-remote-branch-sentinel remote refs)))
|
||||
(t
|
||||
(dolist (ref refs)
|
||||
(message "Delete %s (was %s)" ref
|
||||
(magit-rev-parse "--short" ref))
|
||||
(magit-call-git "update-ref" "-d" ref))
|
||||
(magit-refresh)))))
|
||||
((length> branches 1)
|
||||
(setq branches (delete (magit-get-current-branch) branches))
|
||||
(mapc #'magit-branch-maybe-delete-pr-remote branches)
|
||||
(mapc #'magit-branch-unset-pushRemote branches)
|
||||
(magit-run-git "branch" (if force "-D" "-d") branches))
|
||||
(t ; And now for something completely different.
|
||||
(let* ((branch (car branches))
|
||||
(prompt (format "Branch %s is checked out. " branch))
|
||||
(target (magit-get-indirect-upstream-branch branch t)))
|
||||
(when (equal branch (magit-get-current-branch))
|
||||
(when (or (equal branch target)
|
||||
(not target))
|
||||
(setq target (magit-main-branch)))
|
||||
(pcase (if (or (equal branch target)
|
||||
(not target))
|
||||
(magit-read-char-case prompt nil
|
||||
(?d "[d]etach HEAD & delete" 'detach)
|
||||
(?a "[a]bort" 'abort))
|
||||
(magit-read-char-case prompt nil
|
||||
(?d "[d]etach HEAD & delete" 'detach)
|
||||
(?c (format "[c]heckout %s & delete" target) 'target)
|
||||
(?a "[a]bort" 'abort)))
|
||||
(`detach (unless (or (equal force '(4))
|
||||
(member branch force)
|
||||
(magit-branch-merged-p branch t))
|
||||
(magit-confirm 'delete-unmerged-branch
|
||||
"Delete unmerged branch %s" ""
|
||||
nil (list branch)))
|
||||
(magit-call-git "checkout" "--detach"))
|
||||
(`target (unless (or (equal force '(4))
|
||||
(member branch force)
|
||||
(magit-branch-merged-p branch target))
|
||||
(magit-confirm 'delete-unmerged-branch
|
||||
"Delete unmerged branch %s" ""
|
||||
nil (list branch)))
|
||||
(magit-call-git "checkout" target))
|
||||
(`abort (user-error "Abort")))
|
||||
(setq force t))
|
||||
(magit-branch-maybe-delete-pr-remote branch)
|
||||
(magit-branch-unset-pushRemote branch)
|
||||
(magit-run-git "branch" (if force "-D" "-d") branch))))))
|
||||
((string-match "^refs/remotes/\\([^/]+\\)" (car refs))
|
||||
(let* ((remote (match-str 1 (car refs)))
|
||||
(offset (1+ (length remote))))
|
||||
(cond
|
||||
((magit-confirm 'delete-branch-on-remote
|
||||
(list "Deleting local %s. Also delete on %s"
|
||||
(magit-ref-fullname (car branches))
|
||||
remote)
|
||||
(list "Deleting %d local refs. Also delete on %s"
|
||||
(length refs)
|
||||
remote)
|
||||
'noabort refs)
|
||||
;; The ref may actually point at another rev on the remote,
|
||||
;; but this is better than nothing.
|
||||
(dolist (ref refs)
|
||||
(message "Delete %s (was %s)" ref
|
||||
(magit-rev-parse "--short" ref)))
|
||||
;; Assume the branches actually still exist on the remote.
|
||||
(magit-run-git-async
|
||||
"push" "--delete"
|
||||
(and (or force magit-branch-delete-never-verify) "--no-verify")
|
||||
remote
|
||||
(mapcar (##concat "refs/heads/" (substring % offset)) branches))
|
||||
;; If that is not the case, then this deletes the tracking branches.
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
(apply-partially #'magit-delete-remote-branch-sentinel remote refs)))
|
||||
(t
|
||||
(dolist (ref refs)
|
||||
(message "Delete %s (was %s)" ref
|
||||
(magit-rev-parse "--short" ref))
|
||||
(magit-call-git "update-ref" "-d" ref))
|
||||
(magit-refresh)))))
|
||||
((length> branches 1)
|
||||
(setq branches (delete (magit-get-current-branch) branches))
|
||||
(mapc #'magit-branch-maybe-delete-pr-remote branches)
|
||||
(mapc #'magit-branch-unset-pushRemote branches)
|
||||
(magit-run-git "branch" (if force "-D" "-d") branches))
|
||||
(t ; And now for something completely different.
|
||||
(let* ((branch (car branches))
|
||||
(prompt (format "Branch %s is checked out. " branch))
|
||||
(target (magit-get-indirect-upstream-branch branch t)))
|
||||
(when (equal branch (magit-get-current-branch))
|
||||
(when (or (equal branch target)
|
||||
(not target))
|
||||
(setq target (magit-main-branch)))
|
||||
(pcase (if (or (equal branch target)
|
||||
(not target))
|
||||
(magit-read-char-case prompt nil
|
||||
(?d "[d]etach HEAD & delete" 'detach)
|
||||
(?a "[a]bort" 'abort))
|
||||
(magit-read-char-case prompt nil
|
||||
(?d "[d]etach HEAD & delete" 'detach)
|
||||
(?c (format "[c]heckout %s & delete" target) 'target)
|
||||
(?a "[a]bort" 'abort)))
|
||||
(`detach (unless (or (equal force '(4))
|
||||
(member branch force)
|
||||
(magit-branch-merged-p branch t))
|
||||
(magit-confirm 'delete-unmerged-branch
|
||||
"Delete unmerged branch %s" ""
|
||||
nil (list branch)))
|
||||
(magit-call-git "checkout" "--detach"))
|
||||
(`target (unless (or (equal force '(4))
|
||||
(member branch force)
|
||||
(magit-branch-merged-p branch target))
|
||||
(magit-confirm 'delete-unmerged-branch
|
||||
"Delete unmerged branch %s" ""
|
||||
nil (list branch)))
|
||||
(magit-call-git "checkout" target))
|
||||
(`abort (user-error "Abort")))
|
||||
(setq force t))
|
||||
(magit-branch-maybe-delete-pr-remote branch)
|
||||
(magit-branch-unset-pushRemote branch)
|
||||
(magit-run-git "branch" (if force "-D" "-d") branch))))))
|
||||
|
||||
(put 'magit-branch-delete 'interactive-only t)
|
||||
|
||||
@@ -758,11 +757,11 @@ the value of `magit-branch-rename-push-target' (which see) maybe
|
||||
set `branch.NEW.pushRemote' and maybe rename the push-target on
|
||||
the remote."
|
||||
(interactive
|
||||
(let ((branch (magit-read-local-branch "Rename branch")))
|
||||
(list branch
|
||||
(magit-read-string-ns (format "Rename branch '%s' to" branch)
|
||||
nil 'magit-revision-history)
|
||||
current-prefix-arg)))
|
||||
(let ((branch (magit-read-local-branch "Rename branch")))
|
||||
(list branch
|
||||
(magit-read-string-ns (format "Rename branch '%s' to" branch)
|
||||
nil 'magit-revision-history)
|
||||
current-prefix-arg)))
|
||||
(when (string-match "\\`heads/\\(.+\\)" old)
|
||||
(setq old (match-str 1 old)))
|
||||
(when (equal old new)
|
||||
@@ -868,11 +867,11 @@ Also rename the respective reflog file."
|
||||
("a m" magit-branch.autoSetupMerge)
|
||||
("a r" magit-branch.autoSetupRebase)]
|
||||
(interactive
|
||||
(list (or (and (not current-prefix-arg)
|
||||
(not (and magit-branch-direct-configure
|
||||
(eq transient-current-command 'magit-branch)))
|
||||
(magit-get-current-branch))
|
||||
(magit--read-branch-scope))))
|
||||
(list (or (and (not current-prefix-arg)
|
||||
(not (and magit-branch-direct-configure
|
||||
(eq transient-current-command 'magit-branch)))
|
||||
(magit-get-current-branch))
|
||||
(magit--read-branch-scope))))
|
||||
(transient-setup 'magit-branch-configure nil nil :scope branch))
|
||||
|
||||
(defun magit--read-branch-scope (&optional obj)
|
||||
@@ -891,7 +890,8 @@ Also rename the respective reflog file."
|
||||
(magit-run-git-with-editor "branch" "--edit-description" branch))
|
||||
|
||||
(defclass magit--git-branch:upstream (magit--git-variable)
|
||||
((format :initform " %k %m %M\n %r %R")))
|
||||
((format :initform " %k %m %M\n %r %R")
|
||||
(accessible-format :initform "%k %m is %M and %r is %R")))
|
||||
|
||||
(transient-define-infix magit-branch.<branch>.merge/remote ()
|
||||
:class 'magit--git-branch:upstream)
|
||||
@@ -919,7 +919,7 @@ Also rename the respective reflog file."
|
||||
(cl-defmethod transient-format ((obj magit--git-branch:upstream))
|
||||
(let ((branch (transient-scope)))
|
||||
(format-spec
|
||||
(oref obj format)
|
||||
(transient--get-format obj)
|
||||
`((?k . ,(transient-format-key obj))
|
||||
(?r . ,(format "branch.%s.remote" branch))
|
||||
(?m . ,(format "branch.%s.merge" branch))
|
||||
@@ -977,6 +977,7 @@ Also rename the respective reflog file."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-bundle.el --- Bundle support for Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -61,14 +61,14 @@
|
||||
("t" "create tracked bundle" magit-bundle-create-tracked)
|
||||
("u" "update tracked bundle" magit-bundle-update-tracked)]
|
||||
(interactive
|
||||
(and (eq transient-current-command 'magit-bundle-create)
|
||||
(list (read-file-name "Create bundle: " nil nil nil
|
||||
(concat (file-name-nondirectory
|
||||
(directory-file-name (magit-toplevel)))
|
||||
".bundle"))
|
||||
(magit-completing-read-multiple "Refnames (zero or more): "
|
||||
(magit-list-refnames))
|
||||
(transient-args 'magit-bundle-create))))
|
||||
(and (eq transient-current-command 'magit-bundle-create)
|
||||
(list (read-file-name "Create bundle: " nil nil nil
|
||||
(concat (file-name-nondirectory
|
||||
(directory-file-name (magit-toplevel)))
|
||||
".bundle"))
|
||||
(magit-completing-read-multiple "Refnames (zero or more): "
|
||||
(magit-list-refnames))
|
||||
(transient-args 'magit-bundle-create))))
|
||||
(if file
|
||||
(magit-git-bundle "create" file refs args)
|
||||
(transient-setup 'magit-bundle-create)))
|
||||
@@ -77,17 +77,17 @@
|
||||
(defun magit-bundle-create-tracked (file tag branch refs args)
|
||||
"Create and track a new bundle."
|
||||
(interactive
|
||||
(let ((tag (magit-read-tag "Track bundle using tag"))
|
||||
(branch (magit-read-branch "Bundle branch"))
|
||||
(refs (magit-completing-read-multiple
|
||||
"Additional refnames (zero or more): "
|
||||
(magit-list-refnames))))
|
||||
(list (read-file-name "File: " nil nil nil (concat tag ".bundle"))
|
||||
tag branch
|
||||
(if (equal branch (magit-get-current-branch))
|
||||
(cons "HEAD" refs)
|
||||
refs)
|
||||
(transient-args 'magit-bundle-create))))
|
||||
(let ((tag (magit-read-tag "Track bundle using tag"))
|
||||
(branch (magit-read-branch "Bundle branch"))
|
||||
(refs (magit-completing-read-multiple
|
||||
"Additional refnames (zero or more): "
|
||||
(magit-list-refnames))))
|
||||
(list (read-file-name "File: " nil nil nil (concat tag ".bundle"))
|
||||
tag branch
|
||||
(if (equal branch (magit-get-current-branch))
|
||||
(cons "HEAD" refs)
|
||||
refs)
|
||||
(transient-args 'magit-bundle-create))))
|
||||
(magit-git-bundle "create" file (cons branch refs) args)
|
||||
(magit-git "tag" "--force" tag branch
|
||||
"-m" (concat ";; git-bundle tracking\n"
|
||||
@@ -142,6 +142,7 @@
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-clone.el --- Clone a repository -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -337,12 +337,12 @@ Then show the status buffer for the new repository."
|
||||
url-format
|
||||
`((?h . ,host)
|
||||
(?n . ,(cond
|
||||
((string-search "/" repo) repo)
|
||||
((string-search "." user)
|
||||
(if-let ((user (magit-get user)))
|
||||
(concat user "/" repo)
|
||||
(user-error "Set %S or specify owner explicitly" user)))
|
||||
((concat user "/" repo))))))
|
||||
((string-search "/" repo) repo)
|
||||
((string-search "." user)
|
||||
(if-let ((user (magit-get user)))
|
||||
(concat user "/" repo)
|
||||
(user-error "Set %S or specify owner explicitly" user)))
|
||||
((concat user "/" repo))))))
|
||||
(user-error
|
||||
"Bogus `magit-clone-url-format' (bad type or missing default)")))
|
||||
|
||||
@@ -354,6 +354,7 @@ Then show the status buffer for the new repository."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-commit.el --- Create Git commits -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -144,6 +144,7 @@ This hook is still experimental.")
|
||||
"Create a new commit or replace an existing commit."
|
||||
:info-manual "(magit)Initiating a Commit"
|
||||
:man-page "git-commit"
|
||||
:value '("--verbose")
|
||||
["Arguments"
|
||||
("-a" "Stage all modified and deleted files" ("-a" "--all"))
|
||||
("-e" "Allow empty commit" "--allow-empty")
|
||||
@@ -457,49 +458,49 @@ Like `magit-commit-squash' but also run a `--autofixup' rebase."
|
||||
|
||||
(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"))))
|
||||
(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
|
||||
|
||||
@@ -520,18 +521,18 @@ is updated:
|
||||
- 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))))
|
||||
(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))
|
||||
@@ -548,20 +549,19 @@ is updated:
|
||||
(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))))
|
||||
(cond ((and commit (eq phase 'run))
|
||||
(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)
|
||||
@@ -669,18 +669,25 @@ an alternative implementation."
|
||||
'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.
|
||||
(when-let ((diff-buffer
|
||||
;; This signals an error if not inside a Git repository,
|
||||
;; but the user may be visiting COMMIT_EDITMSG using a
|
||||
;; tool other than git, which can be used outside a Git
|
||||
;; repository. See #5527.
|
||||
(ignore-error magit-outside-git-repo
|
||||
(magit-get-mode-buffer 'magit-diff-mode))))
|
||||
;; This window just started displaying the commit message buffer.
|
||||
;; Without unrecording 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)
|
||||
(with-demoted-errors "Error showing commit diff: %S"
|
||||
(magit-commit-diff--show))
|
||||
(quit)))))
|
||||
|
||||
(defun magit-commit-diff-1 ()
|
||||
(defun magit-commit-diff--args ()
|
||||
(let ((rev nil)
|
||||
(arg "--cached")
|
||||
(command (magit-repository-local-get 'this-commit-command))
|
||||
@@ -695,6 +702,9 @@ an alternative implementation."
|
||||
(and (file-exists-p f) (length (magit-file-lines f)))))
|
||||
(noalt nil))
|
||||
(pcase (list staged unstaged command)
|
||||
((guard (not (magit-commit-p "HEAD^")))
|
||||
(setq rev "HEAD")
|
||||
(setq arg nil))
|
||||
((and `(,_ ,_ magit-commit--rebase)
|
||||
(guard (integerp squash)))
|
||||
(setq rev (format "HEAD~%s" squash)))
|
||||
@@ -715,21 +725,24 @@ an alternative implementation."
|
||||
(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
|
||||
((not
|
||||
(and-let*
|
||||
((_(eq this-command 'magit-diff-while-committing))
|
||||
(buf (magit-get-mode-buffer 'magit-diff-mode nil 'selected))
|
||||
(_(equal rev (buffer-local-value 'magit-buffer-diff-range buf)))
|
||||
(_(equal arg (buffer-local-value 'magit-buffer-diff-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^"))
|
||||
((setq noalt t)))
|
||||
(list rev arg noalt)))
|
||||
|
||||
(defun magit-commit-diff--show ()
|
||||
(pcase-let ((`(,rev ,arg ,noalt) (magit-commit-diff--args)))
|
||||
(if noalt
|
||||
(message "No alternative diff while committing")
|
||||
(let ((magit-inhibit-save-previous-winconf 'unset)
|
||||
(magit-display-buffer-noselect t)
|
||||
(display-buffer-overriding-action
|
||||
@@ -858,6 +871,7 @@ Also see `git-commit-post-finish-hook'."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-core.el --- Core functionality -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -126,6 +126,7 @@ Each of these options falls into one or more of these categories:
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
;;; magit-dired.el --- Dired support for Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -116,6 +116,7 @@ Interactively, open the file at point."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-ediff.el --- Ediff extension for Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -42,19 +42,17 @@
|
||||
:group 'magit-extensions)
|
||||
|
||||
(defcustom magit-ediff-quit-hook
|
||||
(list #'magit-ediff-cleanup-auxiliary-buffers
|
||||
#'magit-ediff-restore-previous-winconf)
|
||||
(list #'magit-ediff-restore-previous-winconf)
|
||||
"Hooks to run after finishing Ediff, when that was invoked using Magit.
|
||||
The hooks are run in the Ediff control buffer. This is similar
|
||||
to `ediff-quit-hook' but takes the needs of Magit into account.
|
||||
The `ediff-quit-hook' is ignored by Ediff sessions which were
|
||||
invoked using Magit."
|
||||
:package-version '(magit . "2.2.0")
|
||||
:package-version '(magit . "4.6.0")
|
||||
:group 'magit-ediff
|
||||
:type 'hook
|
||||
:get #'magit-hook-custom-get
|
||||
:options (list #'magit-ediff-cleanup-auxiliary-buffers
|
||||
#'magit-ediff-restore-previous-winconf))
|
||||
:options (list #'magit-ediff-restore-previous-winconf))
|
||||
|
||||
(defcustom magit-ediff-dwim-resolve-function #'magit-ediff-resolve-rest
|
||||
"The function `magit-ediff-dwim' uses to resolve conflicts."
|
||||
@@ -115,7 +113,7 @@ recommend you do not further complicate that by enabling this.")
|
||||
|
||||
(defvar magit-ediff-previous-winconf nil)
|
||||
|
||||
;;;###autoload(autoload 'magit-ediff "magit-ediff" nil)
|
||||
;;;###autoload(autoload 'magit-ediff "magit-ediff" nil t)
|
||||
(transient-define-prefix magit-ediff ()
|
||||
"Show differences using the Ediff package."
|
||||
:info-manual "(ediff)"
|
||||
@@ -132,86 +130,8 @@ recommend you do not further complicate that by enabling this.")
|
||||
("r" "Show range" magit-ediff-compare)
|
||||
("z" "Show stash" magit-ediff-show-stash)]])
|
||||
|
||||
(defmacro magit-ediff-buffers (a b &optional c setup quit file)
|
||||
"Run Ediff on two or three buffers.
|
||||
This is a wrapper around `ediff-buffers-internal'.
|
||||
|
||||
A, B and C have the form (GET-BUFFER CREATE-BUFFER). If
|
||||
GET-BUFFER returns a non-nil value, then that buffer is used and
|
||||
it is not killed when exiting Ediff. Otherwise CREATE-BUFFER
|
||||
must return a buffer and that is killed when exiting Ediff.
|
||||
|
||||
If non-nil, SETUP must be a function. It is called without
|
||||
arguments after Ediff is done setting up buffers.
|
||||
|
||||
If non-nil, QUIT must be a function. It is added to
|
||||
`ediff-quit-hook' and is called without arguments.
|
||||
|
||||
If FILE is non-nil, then perform a merge. The merge result
|
||||
is put in FILE."
|
||||
(let (get make kill (char ?A))
|
||||
(dolist (spec (list a b c))
|
||||
(if (not spec)
|
||||
(push nil make)
|
||||
(pcase-let ((`(,g ,m) spec))
|
||||
(let ((b (intern (format "buf%c" char))))
|
||||
(push `(,b ,g) get)
|
||||
;; This is an unfortunate complication that I have added for
|
||||
;; the benefit of one user. Pretend we used this instead:
|
||||
;; (push `(or ,b ,m) make)
|
||||
(push `(if ,b
|
||||
(if magit-ediff-use-indirect-buffers
|
||||
(prog1 (make-indirect-buffer
|
||||
,b
|
||||
(generate-new-buffer-name (buffer-name ,b))
|
||||
t)
|
||||
(setq ,b nil))
|
||||
,b)
|
||||
,m)
|
||||
make)
|
||||
(push `(unless ,b
|
||||
;; For merge jobs Ediff switches buffer names around.
|
||||
;; See (if ediff-merge-job ...) in `ediff-setup'.
|
||||
(let ((var ,(if (and file (= char ?C))
|
||||
'ediff-ancestor-buffer
|
||||
(intern (format "ediff-buffer-%c" char)))))
|
||||
(ediff-kill-buffer-carefully var)))
|
||||
kill))
|
||||
(cl-incf char))))
|
||||
(setq get (nreverse get))
|
||||
(setq make (nreverse make))
|
||||
(setq kill (nreverse kill))
|
||||
(let ((mconf (gensym "conf"))
|
||||
(mfile (gensym "file")))
|
||||
`(magit-with-toplevel
|
||||
(let ((,mconf (current-window-configuration))
|
||||
(,mfile ,file)
|
||||
,@get)
|
||||
(ediff-buffers-internal
|
||||
,@make
|
||||
(list ,@(and setup (list setup))
|
||||
(lambda ()
|
||||
;; We do not want to kill buffers that existed before
|
||||
;; Ediff was invoked, so we cannot use Ediff's default
|
||||
;; quit functions. Ediff splits quitting across two
|
||||
;; hooks for merge jobs but we only ever use one.
|
||||
(setq-local ediff-quit-merge-hook nil)
|
||||
(setq-local ediff-quit-hook
|
||||
(list
|
||||
,@(and quit (list quit))
|
||||
(lambda ()
|
||||
,@kill
|
||||
(let ((magit-ediff-previous-winconf ,mconf))
|
||||
(run-hooks 'magit-ediff-quit-hook)))))))
|
||||
(pcase (list ,(and c t) (and ,mfile t))
|
||||
('(nil nil) 'ediff-buffers)
|
||||
('(nil t) 'ediff-merge-buffers)
|
||||
('(t nil) 'ediff-buffers3)
|
||||
('(t t) 'ediff-merge-buffers-with-ancestor))
|
||||
,mfile))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-resolve-all (file)
|
||||
;;;###autoload(autoload 'magit-ediff-resolve-all "magit-ediff" nil t)
|
||||
(transient-define-suffix magit-ediff-resolve-all (file)
|
||||
"Resolve all conflicts in the FILE at point using Ediff.
|
||||
|
||||
If there is no file at point or if it doesn't have any unmerged
|
||||
@@ -219,16 +139,17 @@ changes, then prompt for a file.
|
||||
|
||||
See info node `(magit) Ediffing' for more information about this
|
||||
and alternative commands."
|
||||
:inapt-if-not #'magit-anything-unmerged-p
|
||||
(interactive (list (magit-read-unmerged-file)))
|
||||
(magit-with-toplevel
|
||||
(let* ((dir (magit-gitdir))
|
||||
(revA (or (magit-name-branch "HEAD")
|
||||
(magit-commit-p "HEAD")))
|
||||
(magit-commit-oid "HEAD")))
|
||||
(revB (cl-find-if (##file-exists-p (expand-file-name % dir))
|
||||
'("MERGE_HEAD" "CHERRY_PICK_HEAD" "REVERT_HEAD")))
|
||||
(revB (or (magit-name-branch revB)
|
||||
(magit-commit-p revB)))
|
||||
(revC (magit-commit-p (magit-git-string "merge-base" revA revB)))
|
||||
(magit-commit-oid revB)))
|
||||
(revC (magit-commit-oid (magit-git-string "merge-base" revA revB)))
|
||||
(fileA (magit--rev-file-name file revA revB))
|
||||
(fileB (magit--rev-file-name file revB revA))
|
||||
(fileC (or (magit--rev-file-name file revC revA)
|
||||
@@ -256,7 +177,7 @@ and alternative commands."
|
||||
,(format ">>>>>>> %s" revB)))))
|
||||
(quit (lambda ()
|
||||
;; For merge jobs Ediff switches buffer names around.
|
||||
;; At this point `ediff-buffer-C' no longer refer to
|
||||
;; At this point `ediff-buffer-C' no longer refers to
|
||||
;; the ancestor buffer but to the merge result buffer.
|
||||
;; See (if ediff-merge-job ...) in `ediff-setup'.
|
||||
(when (buffer-live-p ediff-buffer-C)
|
||||
@@ -266,24 +187,14 @@ and alternative commands."
|
||||
(goto-char (point-min))
|
||||
(unless (re-search-forward "^<<<<<<< " nil t)
|
||||
(magit-stage-files (list file)))))))))
|
||||
(cond (fileC
|
||||
(magit-ediff-buffers
|
||||
((magit-get-revision-buffer revA fileA)
|
||||
(magit-find-file-noselect revA fileA))
|
||||
((magit-get-revision-buffer revB fileB)
|
||||
(magit-find-file-noselect revB fileB))
|
||||
((magit-get-revision-buffer revC fileC)
|
||||
(magit-find-file-noselect revC fileC))
|
||||
setup quit file))
|
||||
((magit-ediff-buffers
|
||||
((magit-get-revision-buffer revA fileA)
|
||||
(magit-find-file-noselect revA fileA))
|
||||
((magit-get-revision-buffer revB fileB)
|
||||
(magit-find-file-noselect revB fileB))
|
||||
nil setup quit file)))))))
|
||||
(magit-ediff-buffers
|
||||
(magit-ediff--find-file revA fileA)
|
||||
(magit-ediff--find-file revB fileB)
|
||||
(and fileC (magit-ediff--find-file revC fileC))
|
||||
setup quit file)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-resolve-rest (file)
|
||||
;;;###autoload(autoload 'magit-ediff-resolve-rest "magit-ediff" nil t)
|
||||
(transient-define-suffix magit-ediff-resolve-rest (file)
|
||||
"Resolve outstanding conflicts in the FILE at point using Ediff.
|
||||
|
||||
If there is no file at point or if it doesn't have any unmerged
|
||||
@@ -291,6 +202,7 @@ changes, then prompt for a file.
|
||||
|
||||
See info node `(magit) Ediffing' for more information about this
|
||||
and alternative commands."
|
||||
:inapt-if-not #'magit-anything-unmerged-p
|
||||
(interactive (list (magit-read-unmerged-file)))
|
||||
(magit-with-toplevel
|
||||
(with-current-buffer (find-file-noselect file)
|
||||
@@ -314,32 +226,27 @@ and alternative commands."
|
||||
(let ((magit-ediff-previous-winconf smerge-ediff-windows))
|
||||
(run-hooks 'magit-ediff-quit-hook)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-stage (file)
|
||||
;;;###autoload(autoload 'magit-ediff-stage "magit-ediff" nil t)
|
||||
(transient-define-suffix magit-ediff-stage (file)
|
||||
"Stage and unstage changes to FILE using Ediff.
|
||||
FILE has to be relative to the top directory of the repository."
|
||||
:inapt-if-not #'magit-anything-modified-p
|
||||
(interactive
|
||||
(let ((files (magit-tracked-files)))
|
||||
(list (magit-completing-read "Selectively stage file" files nil t nil nil
|
||||
(car (member (magit-current-file) files))))))
|
||||
(let ((files (magit-tracked-files)))
|
||||
(list (magit-completing-read "Selectively stage file" files nil t nil nil
|
||||
(car (member (magit-current-file) files))))))
|
||||
(magit-with-toplevel
|
||||
(let* ((bufA (magit-get-revision-buffer "HEAD" file))
|
||||
(bufB (magit-get-revision-buffer "{index}" file))
|
||||
(lockB (and bufB (buffer-local-value 'buffer-read-only bufB)))
|
||||
(bufC (get-file-buffer file))
|
||||
(let* ((bufC (magit-ediff--find-file "{worktree}" file))
|
||||
;; Use the same encoding for all three buffers or we
|
||||
;; may end up changing the file in an unintended way.
|
||||
(bufC* (or bufC (find-file-noselect file)))
|
||||
(coding-system-for-read
|
||||
(buffer-local-value 'buffer-file-coding-system bufC*))
|
||||
(bufA* (magit-find-file-noselect "HEAD" file t))
|
||||
(bufB* (magit-find-file-index-noselect file t)))
|
||||
(with-current-buffer bufB* (setq buffer-read-only nil))
|
||||
(buffer-local-value 'buffer-file-coding-system bufC))
|
||||
(bufA (magit-ediff--find-file "HEAD" file))
|
||||
(bufB (magit-ediff--find-file "{index}" file))
|
||||
(lockB (buffer-local-value 'buffer-read-only bufB)))
|
||||
(with-current-buffer bufB (setq buffer-read-only nil))
|
||||
(magit-ediff-buffers
|
||||
(bufA bufA*)
|
||||
(bufB bufB*)
|
||||
(bufC bufC*)
|
||||
nil
|
||||
bufA bufB bufC nil
|
||||
(lambda ()
|
||||
(when (buffer-live-p ediff-buffer-B)
|
||||
(when lockB
|
||||
@@ -353,13 +260,12 @@ FILE has to be relative to the top directory of the repository."
|
||||
(when (y-or-n-p (format "Save file %s? " buffer-file-name))
|
||||
(save-buffer)))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-compare (revA revB fileA fileB)
|
||||
;;;###autoload(autoload 'magit-ediff-compare "magit-ediff" nil t)
|
||||
(transient-define-suffix magit-ediff-compare (revA revB fileA fileB)
|
||||
"Compare REVA:FILEA with REVB:FILEB using Ediff.
|
||||
|
||||
FILEA and FILEB have to be relative to the top directory of the
|
||||
repository. If REVA or REVB is nil, then this stands for the
|
||||
working tree state.
|
||||
repository.
|
||||
|
||||
If the region is active, use the revisions on the first and last
|
||||
line of the region. With a prefix argument, instead of diffing
|
||||
@@ -367,15 +273,13 @@ the revisions, choose a revision to view changes along, starting
|
||||
at the common ancestor of both revisions (i.e., use a \"...\"
|
||||
range)."
|
||||
(interactive
|
||||
(pcase-let ((`(,revA ,revB) (magit-ediff-compare--read-revisions
|
||||
nil current-prefix-arg)))
|
||||
(nconc (list revA revB)
|
||||
(magit-ediff-read-files revA revB))))
|
||||
(pcase-let ((`(,revA ,revB) (magit-ediff-compare--read-revisions
|
||||
nil current-prefix-arg)))
|
||||
(nconc (list revA revB)
|
||||
(magit-ediff-read-files revA revB))))
|
||||
(magit-ediff-buffers
|
||||
((if revA (magit-get-revision-buffer revA fileA) (get-file-buffer fileA))
|
||||
(if revA (magit-find-file-noselect revA fileA) (find-file-noselect fileA)))
|
||||
((if revB (magit-get-revision-buffer revB fileB) (get-file-buffer fileB))
|
||||
(if revB (magit-find-file-noselect revB fileB) (find-file-noselect fileB)))))
|
||||
(magit-ediff--find-file revA fileA)
|
||||
(magit-ediff--find-file revB fileB)))
|
||||
|
||||
(defun magit-ediff-compare--read-revisions (&optional arg mbase)
|
||||
(let ((input (or arg (magit-diff-read-range-or-commit
|
||||
@@ -383,12 +287,14 @@ range)."
|
||||
nil mbase))))
|
||||
(if-let ((range (magit-split-range input)))
|
||||
(list (car range) (cdr range))
|
||||
(list input nil))))
|
||||
(list input "{worktree}"))))
|
||||
|
||||
(defun magit-ediff-read-files (revA revB &optional fileB)
|
||||
"Read file in REVB, return it and the corresponding file in REVA.
|
||||
When FILEB is non-nil, use this as REVB's file instead of
|
||||
prompting for it."
|
||||
(when (equal revA "{worktree}") (setq revA nil))
|
||||
(when (equal revB "{worktree}") (setq revB nil))
|
||||
(unless (and fileB (member fileB (magit-revision-files revB)))
|
||||
(setq fileB
|
||||
(or (and fileB
|
||||
@@ -416,8 +322,8 @@ prompting for it."
|
||||
revA revB)))
|
||||
fileB))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-dwim ()
|
||||
;;;###autoload(autoload 'magit-ediff-dwim "magit-ediff" nil t)
|
||||
(transient-define-suffix magit-ediff-dwim ()
|
||||
"Compare, stage, or resolve using Ediff.
|
||||
This command tries to guess what file, and what commit or range
|
||||
the user wants to compare, stage, or resolve using Ediff. It
|
||||
@@ -459,7 +365,7 @@ mind at all, then it asks the user for a command to run."
|
||||
(pcase (magit-diff-type)
|
||||
('committed (pcase-let ((`(,a ,b)
|
||||
(magit-ediff-compare--read-revisions
|
||||
magit-buffer-range)))
|
||||
magit-buffer-diff-range)))
|
||||
(setq revA a)
|
||||
(setq revB b)))
|
||||
((guard (not magit-ediff-dwim-show-on-hunks))
|
||||
@@ -490,55 +396,52 @@ mind at all, then it asks the user for a command to run."
|
||||
(funcall command file))
|
||||
((call-interactively command)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-show-staged (file)
|
||||
;;;###autoload(autoload 'magit-ediff-show-staged "magit-ediff" nil t)
|
||||
(transient-define-suffix magit-ediff-show-staged (file)
|
||||
"Show staged changes using Ediff.
|
||||
|
||||
This only allows looking at the changes; to stage, unstage,
|
||||
and discard changes using Ediff, use `magit-ediff-stage'.
|
||||
|
||||
FILE must be relative to the top directory of the repository."
|
||||
:inapt-if-not #'magit-anything-staged-p
|
||||
(interactive
|
||||
(list (magit-read-file-choice "Show staged changes for file"
|
||||
(magit-staged-files)
|
||||
"No staged files")))
|
||||
(magit-ediff-buffers ((magit-get-revision-buffer "HEAD" file)
|
||||
(magit-find-file-noselect "HEAD" file))
|
||||
((get-buffer (concat file ".~{index}~"))
|
||||
(magit-find-file-index-noselect file t))))
|
||||
(list (magit-read-file-choice "Show staged changes for file"
|
||||
(magit-staged-files)
|
||||
"No staged files")))
|
||||
(magit-ediff-buffers (magit-ediff--find-file "HEAD" file)
|
||||
(magit-ediff--find-file "{index}" file)))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-show-unstaged (file)
|
||||
;;;###autoload(autoload 'magit-ediff-show-unstaged "magit-ediff" nil t)
|
||||
(transient-define-suffix magit-ediff-show-unstaged (file)
|
||||
"Show unstaged changes using Ediff.
|
||||
|
||||
This only allows looking at the changes; to stage, unstage,
|
||||
and discard changes using Ediff, use `magit-ediff-stage'.
|
||||
|
||||
FILE must be relative to the top directory of the repository."
|
||||
:inapt-if-not #'magit-anything-unstaged-p
|
||||
(interactive
|
||||
(list (magit-read-file-choice "Show unstaged changes for file"
|
||||
(magit-unstaged-files)
|
||||
"No unstaged files")))
|
||||
(magit-ediff-buffers ((get-buffer (concat file ".~{index}~"))
|
||||
(magit-find-file-index-noselect file t))
|
||||
((get-file-buffer file)
|
||||
(find-file-noselect file))))
|
||||
(list (magit-read-file-choice "Show unstaged changes for file"
|
||||
(magit-unstaged-files)
|
||||
"No unstaged files")))
|
||||
(magit-ediff-buffers (magit-ediff--find-file "{index}" file)
|
||||
(magit-ediff--find-file "{worktree}" file)))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-show-working-tree (file)
|
||||
;;;###autoload(autoload 'magit-ediff-show-working-tree "magit-ediff" nil t)
|
||||
(transient-define-suffix magit-ediff-show-working-tree (file)
|
||||
"Show changes between `HEAD' and working tree using Ediff.
|
||||
FILE must be relative to the top directory of the repository."
|
||||
:inapt-if-not #'magit-anything-modified-p
|
||||
(interactive
|
||||
(list (magit-read-file-choice "Show changes in file"
|
||||
(magit-changed-files "HEAD")
|
||||
"No changed files")))
|
||||
(magit-ediff-buffers ((magit-get-revision-buffer "HEAD" file)
|
||||
(magit-find-file-noselect "HEAD" file))
|
||||
((get-file-buffer file)
|
||||
(find-file-noselect file))))
|
||||
(list (magit-read-file-choice "Show changes in file"
|
||||
(magit-modified-files)
|
||||
"No changed files")))
|
||||
(magit-ediff-buffers (magit-ediff--find-file "HEAD" file)
|
||||
(magit-ediff--find-file "{worktree}" file)))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-show-commit (commit)
|
||||
;;;###autoload(autoload 'magit-ediff-show-commit "magit-ediff" nil t)
|
||||
(transient-define-suffix magit-ediff-show-commit (commit)
|
||||
"Show changes introduced by COMMIT using Ediff."
|
||||
(interactive (list (magit-read-branch-or-commit "Revision")))
|
||||
(let ((revA (concat commit "^"))
|
||||
@@ -547,12 +450,13 @@ FILE must be relative to the top directory of the repository."
|
||||
revA revB
|
||||
(magit-ediff-read-files revA revB (magit-current-file)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-ediff-show-stash (stash)
|
||||
;;;###autoload(autoload 'magit-ediff-show-stash "magit-ediff" nil t)
|
||||
(transient-define-suffix magit-ediff-show-stash (stash)
|
||||
"Show changes introduced by STASH using Ediff.
|
||||
`magit-ediff-show-stash-with-index' controls whether a
|
||||
three-buffer Ediff is used in order to distinguish changes in the
|
||||
stash that were staged."
|
||||
`magit-ediff-show-stash-with-index' controls whether a three-buffer
|
||||
Ediff is used in order to distinguish changes in the stash that were
|
||||
staged."
|
||||
:inapt-if-not #'magit-list-stashes
|
||||
(interactive (list (magit-read-stash "Stash")))
|
||||
(pcase-let* ((revA (concat stash "^1"))
|
||||
(revB (concat stash "^2"))
|
||||
@@ -562,15 +466,64 @@ stash that were staged."
|
||||
(if (and magit-ediff-show-stash-with-index
|
||||
(member fileA (magit-changed-files revB revA)))
|
||||
(magit-ediff-buffers
|
||||
((magit-get-revision-buffer revA fileA)
|
||||
(magit-find-file-noselect revA fileA))
|
||||
((magit-get-revision-buffer revB fileB)
|
||||
(magit-find-file-noselect revB fileB))
|
||||
((magit-get-revision-buffer revC fileC)
|
||||
(magit-find-file-noselect revC fileC)))
|
||||
(magit-ediff--find-file revA fileA)
|
||||
(magit-ediff--find-file revB fileB)
|
||||
(magit-ediff--find-file revC fileC))
|
||||
(magit-ediff-compare revA revC fileA fileC))))
|
||||
|
||||
(defun magit-ediff-cleanup-auxiliary-buffers ()
|
||||
;;; Setup
|
||||
|
||||
(defun magit-ediff-buffers (a b &optional c setup quit file)
|
||||
"Run Ediff on two or three buffers A, B and C.
|
||||
|
||||
If optional FILE is non-nil, then perform a merge. The merge result
|
||||
is put in FILE.
|
||||
|
||||
Neutralize the hooks `ediff-quit-hook' and `ediff-quit-merge-hook'
|
||||
because they usually feature functions that do not work for Magit.
|
||||
Instead run optional QUIT (if non-nil), `magit-ediff--cleanup-buffers'
|
||||
and `magit-ediff-quit-hook', with no arguments.
|
||||
|
||||
Optional SETUP, if non-nil, is called with no arguments after Ediff
|
||||
is done setting up buffers."
|
||||
(magit-with-toplevel
|
||||
(ediff-buffers-internal
|
||||
a b c
|
||||
(let ((winconf (current-window-configuration)))
|
||||
(list (lambda ()
|
||||
(when setup
|
||||
(funcall setup))
|
||||
(setq-local ediff-quit-merge-hook nil)
|
||||
(setq-local ediff-quit-hook nil)
|
||||
(when quit
|
||||
(add-hook 'ediff-quit-hook quit nil t))
|
||||
(add-hook 'ediff-quit-hook #'magit-ediff--cleanup-buffers t t)
|
||||
(add-hook 'ediff-quit-hook
|
||||
(lambda ()
|
||||
(let ((magit-ediff-previous-winconf winconf))
|
||||
(run-hooks 'magit-ediff-quit-hook)))
|
||||
t t))))
|
||||
(pcase (list (and c t) (and file t))
|
||||
('(nil nil) 'ediff-buffers)
|
||||
('(nil t) 'ediff-merge-buffers)
|
||||
('(t nil) 'ediff-buffers3)
|
||||
('(t t) 'ediff-merge-buffers-with-ancestor))
|
||||
file)))
|
||||
|
||||
(defun magit-ediff--find-file (rev file)
|
||||
(let ((buffer (magit-find-file-noselect rev file t 'ediff)))
|
||||
(when magit-ediff-use-indirect-buffers
|
||||
(setq buffer (make-indirect-buffer
|
||||
buffer (generate-new-buffer-name (buffer-name buffer)) t)))
|
||||
buffer))
|
||||
|
||||
;;; Quit
|
||||
|
||||
(defun magit-ediff--cleanup-buffers ()
|
||||
(magit-ediff--bury-buffer ediff-buffer-A)
|
||||
(magit-ediff--bury-buffer ediff-buffer-B)
|
||||
(magit-ediff--bury-buffer ediff-buffer-C)
|
||||
(magit-ediff--bury-buffer ediff-ancestor-buffer)
|
||||
(let* ((ctl-buf ediff-control-buffer)
|
||||
(ctl-win (ediff-get-visible-buffer-window ctl-buf))
|
||||
(ctl-frm ediff-control-frame)
|
||||
@@ -596,6 +549,11 @@ stash that were staged."
|
||||
(when (frame-live-p main-frame)
|
||||
(select-frame main-frame))))
|
||||
|
||||
(defun magit-ediff--bury-buffer (buffer)
|
||||
(when (and (ediff-buffer-live-p buffer)
|
||||
(eq (buffer-local-value 'magit-buffer--volatile buffer) 'ediff))
|
||||
(kill-buffer (get-buffer buffer))))
|
||||
|
||||
(defun magit-ediff-restore-previous-winconf ()
|
||||
(set-window-configuration magit-ediff-previous-winconf))
|
||||
|
||||
@@ -607,6 +565,7 @@ stash that were staged."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-extras.el --- Additional functionality for Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -60,11 +60,11 @@ alternative commands."
|
||||
["Actions"
|
||||
(" m" "Invoke mergetool" magit-git-mergetool)]
|
||||
(interactive
|
||||
(if (and (not (eq transient-current-command 'magit-git-mergetool))
|
||||
current-prefix-arg)
|
||||
(list nil nil t)
|
||||
(list (magit-read-unmerged-file "Resolve")
|
||||
(transient-args 'magit-git-mergetool))))
|
||||
(if (and (not (eq transient-current-command 'magit-git-mergetool))
|
||||
current-prefix-arg)
|
||||
(list nil nil t)
|
||||
(list (magit-read-unmerged-file "Resolve")
|
||||
(transient-args 'magit-git-mergetool))))
|
||||
(if transient
|
||||
(transient-setup 'magit-git-mergetool)
|
||||
(magit-run-git-async "mergetool" "--gui" args "--" file)))
|
||||
@@ -131,18 +131,18 @@ prefix or when the current file cannot be determined let the user
|
||||
choose. When the current buffer is visiting FILENAME instruct
|
||||
blame to center around the line point is on."
|
||||
(interactive
|
||||
(let (revision filename)
|
||||
(when (or current-prefix-arg
|
||||
(progn
|
||||
(setq revision "HEAD")
|
||||
(not (setq filename (magit-file-relative-name nil 'tracked)))))
|
||||
(setq revision (magit-read-branch-or-commit "Blame from revision"))
|
||||
(setq filename (magit-read-file-from-rev revision "Blame file")))
|
||||
(list revision filename
|
||||
(and (equal filename
|
||||
(ignore-errors
|
||||
(magit-file-relative-name buffer-file-name)))
|
||||
(line-number-at-pos)))))
|
||||
(let (revision filename)
|
||||
(when (or current-prefix-arg
|
||||
(progn
|
||||
(setq revision "HEAD")
|
||||
(not (setq filename (magit-file-relative-name nil 'tracked)))))
|
||||
(setq revision (magit-read-branch-or-commit "Blame from revision"))
|
||||
(setq filename (magit-read-file-from-rev revision "Blame file")))
|
||||
(list revision filename
|
||||
(and (equal filename
|
||||
(ignore-errors
|
||||
(magit-file-relative-name buffer-file-name)))
|
||||
(line-number-at-pos)))))
|
||||
(magit-with-toplevel
|
||||
(magit-process-git 0 "gui" "blame"
|
||||
(and linenum (list (format "--line=%d" linenum)))
|
||||
@@ -447,69 +447,69 @@ list returned by `magit-rebase-arguments'."
|
||||
(user-error "Refusing to reshelve detached head")))
|
||||
(backup (concat "refs/original/refs/heads/" current)))
|
||||
(cond
|
||||
((not commit)
|
||||
(when (and (magit-ref-p backup)
|
||||
(not (magit-y-or-n-p
|
||||
(format "Backup ref %s already exists. Override? "
|
||||
backup))))
|
||||
(user-error "Abort"))
|
||||
(magit-log-select
|
||||
(lambda (rev)
|
||||
(magit-reshelve-since rev keyid))
|
||||
"Type %p on a commit to reshelve it and the commits above it,"))
|
||||
(t
|
||||
(cl-flet ((adjust (time offset)
|
||||
(format-time-string
|
||||
"%F %T %z"
|
||||
(+ (floor time)
|
||||
(* offset 60)
|
||||
(- (car (decode-time time)))))))
|
||||
(let* ((start (concat commit "^"))
|
||||
(range (concat start ".." current))
|
||||
(time-rev (adjust (float-time (string-to-number
|
||||
(magit-rev-format "%at" start)))
|
||||
1))
|
||||
(time-now (adjust (float-time)
|
||||
(- (string-to-number
|
||||
(magit-git-string "rev-list" "--count"
|
||||
range))))))
|
||||
(push time-rev magit--reshelve-history)
|
||||
(let ((date (floor
|
||||
(float-time
|
||||
(date-to-time
|
||||
(read-string "Date for first commit: "
|
||||
time-now 'magit--reshelve-history))))))
|
||||
(with-environment-variables (("FILTER_BRANCH_SQUELCH_WARNING" "1"))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git-async
|
||||
"filter-branch" "--force" "--env-filter"
|
||||
(format
|
||||
"case $GIT_COMMIT in %s\nesac"
|
||||
(mapconcat
|
||||
(lambda (rev)
|
||||
(prog1
|
||||
(concat
|
||||
(format "%s) " rev)
|
||||
(and (not magit-reshelve-since-committer-only)
|
||||
(format "export GIT_AUTHOR_DATE=\"%s\"; " date))
|
||||
(format "export GIT_COMMITTER_DATE=\"%s\";;" date))
|
||||
(cl-incf date 60)))
|
||||
(magit-git-lines "rev-list" "--reverse" range)
|
||||
" "))
|
||||
(and keyid
|
||||
(list "--commit-filter"
|
||||
(format "git commit-tree --gpg-sign=%s \"$@\";"
|
||||
keyid)))
|
||||
range "--"))
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
(lambda (process event)
|
||||
(when (memq (process-status process) '(exit signal))
|
||||
(if (> (process-exit-status process) 0)
|
||||
(magit-process-sentinel process event)
|
||||
(process-put process 'inhibit-refresh t)
|
||||
(magit-process-sentinel process event)
|
||||
(magit-run-git "update-ref" "-d" backup)))))))))))))
|
||||
((not commit)
|
||||
(when (and (magit-ref-p backup)
|
||||
(not (magit-y-or-n-p
|
||||
(format "Backup ref %s already exists. Override? "
|
||||
backup))))
|
||||
(user-error "Abort"))
|
||||
(magit-log-select
|
||||
(lambda (rev)
|
||||
(magit-reshelve-since rev keyid))
|
||||
"Type %p on a commit to reshelve it and the commits above it,"))
|
||||
(t
|
||||
(cl-flet ((adjust (time offset)
|
||||
(format-time-string
|
||||
"%F %T %z"
|
||||
(+ (floor time)
|
||||
(* offset 60)
|
||||
(- (car (decode-time time)))))))
|
||||
(let* ((start (concat commit "^"))
|
||||
(range (concat start ".." current))
|
||||
(time-rev (adjust (float-time (string-to-number
|
||||
(magit-rev-format "%at" start)))
|
||||
1))
|
||||
(time-now (adjust (float-time)
|
||||
(- (string-to-number
|
||||
(magit-git-string "rev-list" "--count"
|
||||
range))))))
|
||||
(push time-rev magit--reshelve-history)
|
||||
(let ((date (floor
|
||||
(float-time
|
||||
(date-to-time
|
||||
(read-string "Date for first commit: "
|
||||
time-now 'magit--reshelve-history))))))
|
||||
(with-environment-variables (("FILTER_BRANCH_SQUELCH_WARNING" "1"))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git-async
|
||||
"filter-branch" "--force" "--env-filter"
|
||||
(format
|
||||
"case $GIT_COMMIT in %s\nesac"
|
||||
(mapconcat
|
||||
(lambda (rev)
|
||||
(prog1
|
||||
(concat
|
||||
(format "%s) " rev)
|
||||
(and (not magit-reshelve-since-committer-only)
|
||||
(format "export GIT_AUTHOR_DATE=\"%s\"; " date))
|
||||
(format "export GIT_COMMITTER_DATE=\"%s\";;" date))
|
||||
(cl-incf date 60)))
|
||||
(magit-git-lines "rev-list" "--reverse" range)
|
||||
" "))
|
||||
(and keyid
|
||||
(list "--commit-filter"
|
||||
(format "git commit-tree --gpg-sign=%s \"$@\";"
|
||||
keyid)))
|
||||
range "--"))
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
(lambda (process event)
|
||||
(when (memq (process-status process) '(exit signal))
|
||||
(if (> (process-exit-status process) 0)
|
||||
(magit-process-sentinel process event)
|
||||
(process-put process 'inhibit-refresh t)
|
||||
(magit-process-sentinel process event)
|
||||
(magit-run-git "update-ref" "-d" backup)))))))))))))
|
||||
|
||||
;;; Revision Stack
|
||||
|
||||
@@ -593,16 +593,16 @@ revision). If not called inside a repository and with an empty
|
||||
stack, or with two prefix arguments, then read the repository in
|
||||
the minibuffer too."
|
||||
(interactive
|
||||
(if (or current-prefix-arg (not magit-revision-stack))
|
||||
(let ((default-directory
|
||||
(or (and (not (= (prefix-numeric-value current-prefix-arg) 16))
|
||||
(or (magit-toplevel)
|
||||
(cadr (car magit-revision-stack))))
|
||||
(magit-read-repository))))
|
||||
(list (magit-read-branch-or-commit "Insert revision")
|
||||
default-directory))
|
||||
(push (caar magit-revision-stack) magit-revision-history)
|
||||
(pop magit-revision-stack)))
|
||||
(if (or current-prefix-arg (not magit-revision-stack))
|
||||
(let ((default-directory
|
||||
(or (and (not (= (prefix-numeric-value current-prefix-arg) 16))
|
||||
(or (magit-toplevel)
|
||||
(cadr (car magit-revision-stack))))
|
||||
(magit-read-repository))))
|
||||
(list (magit-read-branch-or-commit "Insert revision")
|
||||
default-directory))
|
||||
(push (caar magit-revision-stack) magit-revision-history)
|
||||
(pop magit-revision-stack)))
|
||||
(unless rev
|
||||
(user-error "Revision stack is empty"))
|
||||
(pcase-let ((`(,pnt-format ,eob-format ,idx-format)
|
||||
@@ -737,9 +737,9 @@ abbreviated revision to the `kill-ring' and the
|
||||
(cl-case major-mode
|
||||
(magit-diff-mode
|
||||
(if (string-match "\\.\\.\\.?\\(.+\\)"
|
||||
magit-buffer-range)
|
||||
(match-str 1 magit-buffer-range)
|
||||
magit-buffer-range))
|
||||
magit-buffer-diff-range)
|
||||
(match-str 1 magit-buffer-diff-range)
|
||||
magit-buffer-diff-range))
|
||||
(magit-status-mode "HEAD")))]
|
||||
[_(magit-commit-p rev)]
|
||||
(setq rev (magit-rev-parse
|
||||
@@ -833,6 +833,7 @@ In Magit diffs, also skip over - and + at the beginning of the line."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-fetch.el --- Download objects and refs -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -81,10 +81,10 @@ push-remote."
|
||||
(remote (magit-get-push-remote branch))
|
||||
(v (magit--push-remote-variable branch t)))
|
||||
(cond
|
||||
((member remote (magit-list-remotes)) remote)
|
||||
(remote
|
||||
(format "%s, replacing invalid" v))
|
||||
((format "%s, setting that" v)))))
|
||||
((member remote (magit-list-remotes)) remote)
|
||||
(remote
|
||||
(format "%s, replacing invalid" v))
|
||||
((format "%s, setting that" v)))))
|
||||
|
||||
;;;###autoload(autoload 'magit-fetch-from-upstream "magit-fetch" nil t)
|
||||
(transient-define-suffix magit-fetch-from-upstream (remote args)
|
||||
@@ -117,20 +117,20 @@ results in an error."
|
||||
(defun magit-fetch-branch (remote branch args)
|
||||
"Fetch a BRANCH from a REMOTE."
|
||||
(interactive
|
||||
(let ((remote (magit-read-remote-or-url "Fetch from remote or url")))
|
||||
(list remote
|
||||
(magit-read-remote-branch "Fetch branch" remote)
|
||||
(magit-fetch-arguments))))
|
||||
(let ((remote (magit-read-remote-or-url "Fetch from remote or url")))
|
||||
(list remote
|
||||
(magit-read-remote-branch "Fetch branch" remote)
|
||||
(magit-fetch-arguments))))
|
||||
(magit-git-fetch remote (cons branch args)))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-fetch-refspec (remote refspec args)
|
||||
"Fetch a REFSPEC from a REMOTE."
|
||||
(interactive
|
||||
(let ((remote (magit-read-remote-or-url "Fetch from remote or url")))
|
||||
(list remote
|
||||
(magit-read-refspec "Fetch using refspec" remote)
|
||||
(magit-fetch-arguments))))
|
||||
(let ((remote (magit-read-remote-or-url "Fetch from remote or url")))
|
||||
(list remote
|
||||
(magit-read-refspec "Fetch using refspec" remote)
|
||||
(magit-fetch-arguments))))
|
||||
(magit-git-fetch remote (cons refspec args)))
|
||||
|
||||
;;;###autoload
|
||||
@@ -188,6 +188,7 @@ with a prefix argument."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-files.el --- Finding files -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -31,10 +31,21 @@
|
||||
|
||||
(require 'magit)
|
||||
|
||||
(declare-function ediff-quit "ediff-util" (reverse-default-keep-variants))
|
||||
|
||||
;;; Find Blob
|
||||
|
||||
(defvar magit-find-file-hook nil)
|
||||
(add-hook 'magit-find-file-hook #'magit-blob-mode)
|
||||
(define-obsolete-variable-alias 'magit-find-file-hook
|
||||
'magit-find-blob-hook "Magit 4.6.0")
|
||||
|
||||
(define-obsolete-variable-alias 'magit-find-index-hook
|
||||
'magit-find-blob-hook "Magit 4.6.0")
|
||||
|
||||
(defvar magit-find-blob-hook (list #'magit-blob-mode))
|
||||
|
||||
(defvar-local magit-buffer-blob-oid--init nil)
|
||||
(defvar-local magit-buffer--volatile nil)
|
||||
(put 'magit-buffer--volatile 'permanent-local t)
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-find-file (rev file)
|
||||
@@ -44,7 +55,7 @@ already exists. If prior to calling this command the current
|
||||
buffer and/or cursor position is about the same file, then go
|
||||
to the line and column corresponding to that location."
|
||||
(interactive (magit-find-file-read-args "Find file"))
|
||||
(magit-find-file--internal rev file #'pop-to-buffer-same-window))
|
||||
(pop-to-buffer-same-window (magit-find-file-noselect rev file)))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-find-file-other-window (rev file)
|
||||
@@ -54,7 +65,7 @@ already exists. If prior to calling this command the current
|
||||
buffer and/or cursor position is about the same file, then go to
|
||||
the line and column corresponding to that location."
|
||||
(interactive (magit-find-file-read-args "Find file in other window"))
|
||||
(magit-find-file--internal rev file #'switch-to-buffer-other-window))
|
||||
(switch-to-buffer-other-window (magit-find-file-noselect rev file)))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-find-file-other-frame (rev file)
|
||||
@@ -64,140 +75,256 @@ already exists. If prior to calling this command the current
|
||||
buffer and/or cursor position is about the same file, then go to
|
||||
the line and column corresponding to that location."
|
||||
(interactive (magit-find-file-read-args "Find file in other frame"))
|
||||
(magit-find-file--internal rev file #'switch-to-buffer-other-frame))
|
||||
(switch-to-buffer-other-frame (magit-find-file-noselect rev file)))
|
||||
|
||||
(defun magit-find-file-read-args (prompt)
|
||||
(let ((pseudo-revs '("{worktree}" "{index}")))
|
||||
(let ((rev (magit-completing-read "Find file from revision"
|
||||
(append pseudo-revs
|
||||
(magit-list-refnames nil t))
|
||||
nil 'any nil 'magit-revision-history
|
||||
(or (magit-branch-or-commit-at-point)
|
||||
(magit-get-current-branch)))))
|
||||
(list rev
|
||||
(magit-read-file-from-rev (if (member rev pseudo-revs) "HEAD" rev)
|
||||
prompt)))))
|
||||
(let* ((pseudo-revs '("{worktree}" "{index}"))
|
||||
(rev (magit-completing-read "Find file from revision"
|
||||
(append pseudo-revs
|
||||
(magit-list-refnames nil t))
|
||||
nil 'any nil 'magit-revision-history
|
||||
(or (magit-branch-or-commit-at-point)
|
||||
(magit-get-current-branch)))))
|
||||
(list rev
|
||||
(magit-read-file-from-rev (if (member rev pseudo-revs) "HEAD" rev)
|
||||
prompt))))
|
||||
|
||||
(defun magit-find-file--internal (rev file fn)
|
||||
(let ((buf (magit-find-file-noselect rev file))
|
||||
line col)
|
||||
(when-let ((visited-file (magit-file-relative-name)))
|
||||
(setq line (line-number-at-pos))
|
||||
(setq col (current-column))
|
||||
(cond
|
||||
((not (equal visited-file file)))
|
||||
((equal magit-buffer-revision rev))
|
||||
((equal rev "{worktree}")
|
||||
(setq line (magit-diff-visit--offset file magit-buffer-revision line)))
|
||||
((equal rev "{index}")
|
||||
(setq line (magit-diff-visit--offset file nil line)))
|
||||
(magit-buffer-revision
|
||||
(setq line (magit-diff-visit--offset
|
||||
file (concat magit-buffer-revision ".." rev) line)))
|
||||
((setq line (magit-diff-visit--offset file (list "-R" rev) line)))))
|
||||
(funcall fn buf)
|
||||
(when line
|
||||
(with-current-buffer buf
|
||||
(widen)
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- line))
|
||||
(move-to-column col)))
|
||||
buf))
|
||||
|
||||
(defun magit-find-file-noselect (rev file &optional revert)
|
||||
(defun magit-find-file-noselect (rev file &optional no-restore-position volatile)
|
||||
"Read FILE from REV into a buffer and return the buffer.
|
||||
REV is a revision or one of \"{worktree}\" or \"{index}\". FILE must
|
||||
be relative to the top directory of the repository. Non-nil REVERT
|
||||
means to revert the buffer. If `ask-revert', then only after asking.
|
||||
A non-nil value for REVERT is ignored if REV is \"{worktree}\"."
|
||||
(let* ((topdir (magit-toplevel))
|
||||
(absolute (file-name-absolute-p file))
|
||||
(file-abs (if absolute file (expand-file-name file topdir)))
|
||||
(file-rel (if absolute (file-relative-name file topdir) file))
|
||||
(defdir (file-name-directory file-abs))
|
||||
(rev (magit--abbrev-if-hash rev)))
|
||||
(if (equal rev "{worktree}")
|
||||
(let ((revert-without-query
|
||||
(if (and$ (find-buffer-visiting file-abs)
|
||||
(buffer-local-value 'auto-revert-mode $))
|
||||
(cons "." revert-without-query)
|
||||
revert-without-query)))
|
||||
(find-file-noselect file-abs))
|
||||
(with-current-buffer (magit-get-revision-buffer-create rev file-rel)
|
||||
(when (or (not magit-buffer-file-name)
|
||||
(if (eq revert 'ask-revert)
|
||||
(y-or-n-p (format "%s already exists; revert it? "
|
||||
(buffer-name))))
|
||||
revert)
|
||||
(setq magit-buffer-revision rev)
|
||||
(setq magit-buffer-refname rev)
|
||||
(setq magit-buffer-file-name file-abs)
|
||||
(setq default-directory (if (file-exists-p defdir) defdir topdir))
|
||||
(setq-local revert-buffer-function #'magit-revert-rev-file-buffer)
|
||||
(revert-buffer t t)
|
||||
(run-hooks (if (equal rev "{index}")
|
||||
'magit-find-index-hook
|
||||
'magit-find-file-hook)))
|
||||
(current-buffer)))))
|
||||
REV is a revision or one of \"{worktree}\" or \"{index}\".
|
||||
Non-interactively REV can also be a blob object."
|
||||
(let* ((rev (pcase rev
|
||||
('nil "{worktree}")
|
||||
((and "{index}"
|
||||
(guard (length> (magit--file-index-stages file) 1)))
|
||||
"{worktree}")
|
||||
(rev rev)))
|
||||
(topdir (magit-toplevel))
|
||||
(file (expand-file-name file topdir))
|
||||
(file-relative (file-relative-name file topdir))
|
||||
(buffer
|
||||
(cond-let
|
||||
((equal rev "{worktree}")
|
||||
(let ((revert-without-query
|
||||
(if (and$ (find-buffer-visiting file)
|
||||
(buffer-local-value 'auto-revert-mode $))
|
||||
(cons "." revert-without-query)
|
||||
revert-without-query)))
|
||||
(find-file-noselect file volatile)))
|
||||
((not topdir)
|
||||
(error "%s is not inside a Git repository" file))
|
||||
([defdir (file-name-directory file)]
|
||||
[rev (magit--abbrev-if-oid rev)]
|
||||
(unless (file-in-directory-p file topdir)
|
||||
(error "%s is not inside Git repository %s" file topdir))
|
||||
(with-current-buffer
|
||||
(magit--get-blob-buffer rev file-relative volatile)
|
||||
(if (magit-blob-p rev)
|
||||
(setq magit-buffer-blob-oid--init (magit-rev-parse rev))
|
||||
(setq magit-buffer-revision rev))
|
||||
(setq magit-buffer-file-name file)
|
||||
(setq default-directory
|
||||
(if (file-exists-p defdir) defdir topdir))
|
||||
(setq-local revert-buffer-function #'magit--revert-blob-buffer)
|
||||
(magit--refresh-blob-buffer)
|
||||
(current-buffer)))
|
||||
((error "Unexpected error")))))
|
||||
(when (and (not no-restore-position)
|
||||
(equal (magit-file-relative-name) file-relative))
|
||||
(let ((pos (magit-find-file--position)))
|
||||
(with-current-buffer buffer
|
||||
(apply #'magit-find-file--restore-position pos))))
|
||||
buffer))
|
||||
|
||||
(defun magit-get-revision-buffer-create (rev file)
|
||||
(magit-get-revision-buffer rev file t))
|
||||
(defun magit--get-blob-buffer (obj file &optional volatile)
|
||||
;; If OBJ is a commit, is assummed to be abbreviated.
|
||||
;; FILE is assumed to be relative to the top-level.
|
||||
(cond-let
|
||||
([buf (if (magit-blob-p obj)
|
||||
(magit--find-buffer 'magit-buffer-blob-oid (magit-rev-parse obj)
|
||||
'magit-buffer-file-name file)
|
||||
(magit--find-buffer 'magit-buffer-revision obj
|
||||
'magit-buffer-file-name file))]
|
||||
(with-current-buffer buf
|
||||
(when (and (not volatile) magit-buffer--volatile)
|
||||
(setq magit-buffer--volatile nil)
|
||||
(rename-buffer (magit--blob-buffer-name obj file))
|
||||
(magit--blob-cache-remove buf)))
|
||||
buf)
|
||||
([buf (get-buffer-create (magit--blob-buffer-name obj file volatile))]
|
||||
(with-current-buffer buf
|
||||
(setq magit-buffer--volatile volatile)
|
||||
(magit--blob-cache-put buf))
|
||||
(buffer-enable-undo buf)
|
||||
buf)))
|
||||
|
||||
(defun magit-get-revision-buffer (rev file &optional create)
|
||||
(funcall (if create #'get-buffer-create #'get-buffer)
|
||||
(format "%s.~%s~" file (subst-char-in-string ?/ ?_ rev))))
|
||||
(defun magit--blob-buffer-name (obj file &optional volatile)
|
||||
(format "%s%s.~%s~"
|
||||
(if volatile " " "")
|
||||
(or file (and (magit-blob-p obj) "{blob}"))
|
||||
(subst-char-in-string ?/ ?_ obj)))
|
||||
|
||||
(defun magit-revert-rev-file-buffer (_ignore-auto noconfirm)
|
||||
(when (or noconfirm
|
||||
(and (not (buffer-modified-p))
|
||||
(catch 'found
|
||||
(dolist (regexp revert-without-query)
|
||||
(when (string-match regexp magit-buffer-file-name)
|
||||
(throw 'found t)))))
|
||||
(yes-or-no-p (format "Revert buffer from Git %s? "
|
||||
(if (equal magit-buffer-refname "{index}")
|
||||
"index"
|
||||
(concat "revision " magit-buffer-refname)))))
|
||||
(let* ((inhibit-read-only t)
|
||||
(default-directory (magit-toplevel))
|
||||
(file (file-relative-name magit-buffer-file-name))
|
||||
(coding-system-for-read (or coding-system-for-read 'undecided)))
|
||||
(erase-buffer)
|
||||
(magit-git-insert "cat-file" "-p"
|
||||
(if (equal magit-buffer-refname "{index}")
|
||||
(concat ":" file)
|
||||
(concat magit-buffer-refname ":" file)))
|
||||
(setq buffer-file-coding-system last-coding-system-used))
|
||||
(let ((buffer-file-name magit-buffer-file-name)
|
||||
(after-change-major-mode-hook
|
||||
(seq-difference after-change-major-mode-hook
|
||||
'(global-diff-hl-mode-enable-in-buffer ; Emacs >= 30
|
||||
global-diff-hl-mode-enable-in-buffers ; Emacs < 30
|
||||
eglot--maybe-activate-editing-mode)
|
||||
#'eq)))
|
||||
(defun magit--revert-blob-buffer (_ignore-auto _noconfirm)
|
||||
(let ((pos (magit-find-file--position)))
|
||||
(magit--refresh-blob-buffer t)
|
||||
(apply #'magit-find-file--restore-position pos)))
|
||||
|
||||
(defun magit--refresh-blob-buffer (&optional force)
|
||||
(let ((old-blob-oid magit-buffer-blob-oid))
|
||||
(cond
|
||||
(magit-buffer-revision
|
||||
(setq magit-buffer-revision-oid
|
||||
(magit-commit-oid magit-buffer-revision t))
|
||||
(setq magit-buffer-blob-oid
|
||||
(magit-blob-oid magit-buffer-revision magit-buffer-file-name)))
|
||||
(magit-buffer-blob-oid--init
|
||||
(setq magit-buffer-blob-oid magit-buffer-blob-oid--init)
|
||||
(setq magit-buffer-blob-oid--init nil)))
|
||||
(when (or force (not (equal old-blob-oid magit-buffer-blob-oid)))
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer)
|
||||
(save-excursion
|
||||
(magit--insert-blob-contents magit-buffer-revision
|
||||
(magit-file-relative-name))))
|
||||
(magit--blob-normal-mode))))
|
||||
|
||||
(defun magit--blob-normal-mode ()
|
||||
(let ((buffer-file-name magit-buffer-file-name)
|
||||
(after-change-major-mode-hook
|
||||
;; Inhibit diff-hl and eglot; see bb8a65269d and 234a787b8c.
|
||||
(seq-difference after-change-major-mode-hook
|
||||
'(global-diff-hl-mode-enable-in-buffer ; Emacs >= 30
|
||||
global-diff-hl-mode-enable-in-buffers ; Emacs < 30
|
||||
eglot--maybe-activate-editing-mode)
|
||||
#'eq))
|
||||
(buffer-name (buffer-name)))
|
||||
;; `font-lock-mode' contains a hardcoded condition that prevents it
|
||||
;; from being enabled in hidden buffers. Use a fake `buffer-name'
|
||||
;; to trick it into believing the buffer is not hidden.
|
||||
(if (eq (aref buffer-name 0) ?\s)
|
||||
(letrec ((adv (lambda (fn &optional buffer)
|
||||
(let ((name (funcall fn buffer)))
|
||||
(if (equal name buffer-name)
|
||||
(substring name 1)
|
||||
name)))))
|
||||
(advice-add 'buffer-name :around adv)
|
||||
(unwind-protect
|
||||
(normal-mode (not enable-local-variables))
|
||||
(advice-remove 'buffer-name adv)))
|
||||
;; We want `normal-mode' to respect nil `enable-local-variables'.
|
||||
;; The FIND-FILE argument wasn't designed for our use case, so we
|
||||
;; have to use this strange invocation to achieve that.
|
||||
;; The FIND-FILE argument wasn't designed for our use case,
|
||||
;; so we have to use this strange invocation to achieve that.
|
||||
(normal-mode (not enable-local-variables)))
|
||||
(setq buffer-read-only t)
|
||||
(set-buffer-modified-p nil)
|
||||
(goto-char (point-min))))
|
||||
(run-hooks 'magit-find-blob-hook)))
|
||||
|
||||
(defun magit-find-file--position ()
|
||||
(list (or magit-buffer-revision-oid magit-buffer-revision "{worktree}")
|
||||
magit-buffer-blob-oid
|
||||
(line-number-at-pos)
|
||||
(current-column)))
|
||||
|
||||
(defun magit-find-file--restore-position (before blob line col)
|
||||
(let ((file (magit-file-relative-name))
|
||||
(rev (or magit-buffer-revision-oid magit-buffer-revision "{worktree}")))
|
||||
(goto-char (point-min))
|
||||
(forward-line
|
||||
(1-
|
||||
(pcase (list before rev)
|
||||
((guard (equal magit-buffer-blob-oid blob)) line)
|
||||
('("{worktree}" "{worktree}") line)
|
||||
('("{worktree}" "{index}")
|
||||
(magit-diff-visit--offset line file "-R"))
|
||||
(`("{worktree}" ,_)
|
||||
(magit-diff-visit--offset line file "-R" rev))
|
||||
('("{index}" "{worktree}")
|
||||
(magit-diff-visit--offset line file))
|
||||
('("{index}" "{index}")
|
||||
(magit-diff-visit--offset line (list blob magit-buffer-blob-oid file)))
|
||||
(`("{index}" ,_)
|
||||
(magit-diff-visit--offset line file "-R" "--cached"))
|
||||
(`(,_ "{worktree}")
|
||||
(magit-diff-visit--offset line file before))
|
||||
(`(,_ "{index}")
|
||||
(magit-diff-visit--offset line file "--cached"))
|
||||
(_
|
||||
(magit-diff-visit--offset line file before rev)))))
|
||||
(move-to-column col)))
|
||||
|
||||
(defvar magit--blob-cache-limit 100
|
||||
"Limit number of volatile blob buffers to be kept alive.
|
||||
If nil, only use `magit--blob-cache-timeout'.")
|
||||
(defvar magit--blob-cache-timeout 1800
|
||||
"Limit age, since last access, of volatile blob buffers to be kept alive.
|
||||
Age is tracked in seconds. If nil, only use `magit--blob-cache-limit'.")
|
||||
(defvar magit--blob-cache-interval 600
|
||||
"Seconds between volatile blob buffer pruning runs.")
|
||||
(defvar magit--blob-cache-timer nil)
|
||||
(defvar magit--blob-cache nil)
|
||||
|
||||
(defun magit--blob-cache-put (buffer)
|
||||
(if-let ((elt (assq buffer magit--blob-cache)))
|
||||
(setcdr elt (current-time))
|
||||
(push (cons buffer (current-time)) magit--blob-cache))
|
||||
(magit--blob-cache-start))
|
||||
|
||||
(defun magit--blob-cache-remove (buffer)
|
||||
(and$ (assq buffer magit--blob-cache)
|
||||
(delq $ magit--blob-cache)))
|
||||
|
||||
(defun magit--blob-cache-start ()
|
||||
(when (and magit--blob-cache-interval
|
||||
(not magit--blob-cache-timer))
|
||||
(setq magit--blob-cache-timer
|
||||
(run-with-timer magit--blob-cache-interval nil
|
||||
#'magit--blob-cache-prune))))
|
||||
|
||||
(defun magit--blob-cache-prune ()
|
||||
(when magit--blob-cache-timer
|
||||
(cancel-timer magit--blob-cache-timer))
|
||||
(pcase-let
|
||||
((`(,active ,rest)
|
||||
(magit--separate
|
||||
(pcase-lambda (`(,buffer))
|
||||
(or (get-buffer-window buffer t)
|
||||
(not (eq (buffer-local-value 'magit-buffer--volatile buffer) t))))
|
||||
magit--blob-cache)))
|
||||
(when magit--blob-cache-timeout
|
||||
(setq rest
|
||||
(seq-filter (pcase-lambda (`(,buffer . ,time))
|
||||
(cond
|
||||
((not (buffer-live-p buffer)) nil)
|
||||
((time-less-p (time-subtract (current-time) time)
|
||||
magit--blob-cache-timeout)
|
||||
buffer)
|
||||
(t (kill-buffer buffer) nil)))
|
||||
rest)))
|
||||
(when-let* ((_ magit--blob-cache-limit)
|
||||
(ceiling (- magit--blob-cache-limit (length active)))
|
||||
(_ (length> rest ceiling)))
|
||||
(let ((sorted (static-if (>= emacs-major-version 30)
|
||||
(sort rest :key (##float-time (cdr %))
|
||||
:lessp #'< :reverse t)
|
||||
(cl-sort rest #'> :key (##float-time (cdr %))))))
|
||||
(dolist (kill (nthcdr ceiling sorted))
|
||||
(kill-buffer (car kill)))
|
||||
(setq rest (ntake ceiling sorted))))
|
||||
(setq magit--blob-cache (nconc active rest)))
|
||||
(magit--blob-cache-start))
|
||||
|
||||
(defun magit--blob-cache-zap ()
|
||||
(pcase-dolist (`(,buffer) magit--blob-cache)
|
||||
(kill-buffer buffer))
|
||||
(setq magit--blob-cache nil))
|
||||
|
||||
(define-advice lsp (:around (fn &rest args) magit-find-file)
|
||||
"Do nothing when visiting blob using `magit-find-file' and similar.
|
||||
See also https://github.com/doomemacs/doomemacs/pull/6309."
|
||||
(unless magit-buffer-revision
|
||||
(unless magit-buffer-blob-oid
|
||||
(apply fn args)))
|
||||
|
||||
;;; Find Index
|
||||
|
||||
(defvar magit-find-index-hook nil)
|
||||
(add-hook 'magit-find-index-hook #'magit-blob-mode)
|
||||
|
||||
(defun magit-find-file-index-noselect (file &optional revert)
|
||||
"Read FILE from the index into a buffer and return the buffer.
|
||||
FILE must to be relative to the top directory of the repository."
|
||||
(magit-find-file-noselect "{index}" file (or revert 'ask-revert)))
|
||||
;;; Update Index
|
||||
|
||||
(defun magit-update-index ()
|
||||
"Update the index with the contents of the current buffer.
|
||||
@@ -205,7 +332,7 @@ The current buffer has to be visiting a file in the index, which
|
||||
is done using `magit-find-index-noselect'."
|
||||
(interactive)
|
||||
(let ((file (magit-file-relative-name)))
|
||||
(unless (equal magit-buffer-refname "{index}")
|
||||
(unless (equal magit-buffer-revision "{index}")
|
||||
(user-error "%s isn't visiting the index" file))
|
||||
(if (y-or-n-p (format "Update index with contents of %s?" (buffer-name)))
|
||||
(let ((index (make-temp-name
|
||||
@@ -230,8 +357,8 @@ is done using `magit-find-index-noselect'."
|
||||
(set-buffer-modified-p nil)
|
||||
(magit-run-after-apply-functions file "un-/stage"))
|
||||
(message "Abort")))
|
||||
(when-let ((buffer (magit-get-mode-buffer 'magit-status-mode)))
|
||||
(with-current-buffer buffer
|
||||
(when$ (magit-get-mode-buffer 'magit-status-mode)
|
||||
(with-current-buffer $
|
||||
(magit-refresh)))
|
||||
t)
|
||||
|
||||
@@ -248,9 +375,9 @@ This command is like `find-file', except that it temporarily
|
||||
binds `default-directory' to the actual git directory, while
|
||||
reading the FILENAME."
|
||||
(interactive
|
||||
(let ((default-directory (magit-gitdir)))
|
||||
(find-file-read-args "Find file: "
|
||||
(confirm-nonexistent-file-or-buffer))))
|
||||
(let ((default-directory (magit-gitdir)))
|
||||
(find-file-read-args "Find file: "
|
||||
(confirm-nonexistent-file-or-buffer))))
|
||||
(find-file filename wildcards))
|
||||
|
||||
(defun magit-find-git-config-file-other-window (filename &optional wildcards)
|
||||
@@ -264,9 +391,9 @@ This command is like `find-file-other-window', except that it
|
||||
temporarily binds `default-directory' to the actual git
|
||||
directory, while reading the FILENAME."
|
||||
(interactive
|
||||
(let ((default-directory (magit-gitdir)))
|
||||
(find-file-read-args "Find file in other window: "
|
||||
(confirm-nonexistent-file-or-buffer))))
|
||||
(let ((default-directory (magit-gitdir)))
|
||||
(find-file-read-args "Find file in other window: "
|
||||
(confirm-nonexistent-file-or-buffer))))
|
||||
(find-file-other-window filename wildcards))
|
||||
|
||||
(defun magit-find-git-config-file-other-frame (filename &optional wildcards)
|
||||
@@ -280,9 +407,9 @@ This command is like `find-file-other-frame', except that it
|
||||
temporarily binds `default-directory' to the actual git
|
||||
directory, while reading the FILENAME."
|
||||
(interactive
|
||||
(let ((default-directory (magit-gitdir)))
|
||||
(find-file-read-args "Find file in other frame: "
|
||||
(confirm-nonexistent-file-or-buffer))))
|
||||
(let ((default-directory (magit-gitdir)))
|
||||
(find-file-read-args "Find file in other frame: "
|
||||
(confirm-nonexistent-file-or-buffer))))
|
||||
(find-file-other-frame filename wildcards))
|
||||
|
||||
;;; File Dispatch
|
||||
@@ -345,6 +472,7 @@ to `magit-dispatch'."
|
||||
|
||||
(defvar-keymap magit-blob-mode-map
|
||||
:doc "Keymap for `magit-blob-mode'."
|
||||
"g" #'revert-buffer
|
||||
"p" #'magit-blob-previous
|
||||
"n" #'magit-blob-next
|
||||
"b" #'magit-blame-addition
|
||||
@@ -360,18 +488,32 @@ Currently this only adds the following key bindings.
|
||||
:package-version '(magit . "2.3.0"))
|
||||
|
||||
(defun magit-bury-buffer (&optional kill-buffer)
|
||||
"Bury the current buffer, or with a prefix argument kill it."
|
||||
"Bury the current buffer, or with a prefix argument kill it.
|
||||
|
||||
If the buffer is used by an Ediff session, refuse to kill or bury just
|
||||
that buffer. That former would break the session and the latter makes
|
||||
little sense in this context. Instead offer to quit the whole session."
|
||||
(interactive "P")
|
||||
(if kill-buffer (kill-buffer) (bury-buffer)))
|
||||
(cond ((bound-and-true-p ediff-this-buffer-ediff-sessions)
|
||||
(ediff-quit nil))
|
||||
(kill-buffer (kill-buffer))
|
||||
((bury-buffer))))
|
||||
|
||||
(defun magit-bury-or-kill-buffer (&optional bury-buffer)
|
||||
"Bury the current buffer if displayed in multiple windows, else kill it.
|
||||
With a prefix argument only bury the buffer even if it is only displayed
|
||||
in a single window."
|
||||
|
||||
With a prefix argument only bury the buffer even if it is only
|
||||
displayed in a single window.
|
||||
|
||||
If the buffer is used by an Ediff session, refuse to kill or bury just
|
||||
that buffer. That former would break the session and the latter makes
|
||||
little sense in this context. Instead offer to quit the whole session."
|
||||
(interactive "P")
|
||||
(if (or bury-buffer (cdr (get-buffer-window-list nil nil t)))
|
||||
(bury-buffer)
|
||||
(kill-buffer)))
|
||||
(cond ((bound-and-true-p ediff-this-buffer-ediff-sessions)
|
||||
(ediff-quit nil))
|
||||
((or bury-buffer (cdr (get-buffer-window-list nil nil t)))
|
||||
(bury-buffer))
|
||||
((kill-buffer))))
|
||||
|
||||
(defun magit-kill-this-buffer ()
|
||||
"Kill the current buffer."
|
||||
@@ -412,7 +554,7 @@ When visiting a blob or the version from the index, then go to
|
||||
the same location in the respective file in the working tree."
|
||||
(interactive)
|
||||
(if-let ((file (magit-file-relative-name)))
|
||||
(magit-find-file--internal "{worktree}" file #'pop-to-buffer-same-window)
|
||||
(pop-to-buffer-same-window (magit-find-file-noselect "{worktree}" file))
|
||||
(user-error "Not visiting a blob")))
|
||||
|
||||
(defun magit-blob-visit (rev file)
|
||||
@@ -495,11 +637,11 @@ staged as well as unstaged changes."
|
||||
NEWNAME may be a file or directory name. If FILE isn't tracked in
|
||||
Git, fallback to using `rename-file'."
|
||||
(interactive
|
||||
(let* ((file (magit-read-file "Rename file"))
|
||||
(path (expand-file-name file (magit-toplevel))))
|
||||
(list path (expand-file-name
|
||||
(read-file-name (format "Move %s to destination: " file)
|
||||
(file-name-directory path))))))
|
||||
(let* ((file (magit-read-file "Rename file"))
|
||||
(path (expand-file-name file (magit-toplevel))))
|
||||
(list path (expand-file-name
|
||||
(read-file-name (format "Move %s to destination: " file)
|
||||
(file-name-directory path))))))
|
||||
(let ((oldbuf (get-file-buffer file))
|
||||
(dstdir (file-name-directory newname))
|
||||
(dstfile (if (directory-name-p newname)
|
||||
@@ -548,9 +690,9 @@ Git, then fallback to using `delete-file'."
|
||||
(defun magit-file-checkout (rev file)
|
||||
"Checkout FILE from REV."
|
||||
(interactive
|
||||
(let ((rev (magit-read-branch-or-commit
|
||||
"Checkout from revision" magit-buffer-revision)))
|
||||
(list rev (magit-read-file-from-rev rev "Checkout file" nil t))))
|
||||
(let ((rev (magit-read-branch-or-commit
|
||||
"Checkout from revision" magit-buffer-revision)))
|
||||
(list rev (magit-read-file-from-rev rev "Checkout file" nil t))))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git "checkout" rev "--" file)))
|
||||
|
||||
@@ -624,6 +766,17 @@ If DEFAULT is non-nil, use this as the default value instead of
|
||||
(define-obsolete-function-alias 'magit-find-file-noselect-1
|
||||
'magit-find-file-noselect "Magit 4.4.0")
|
||||
|
||||
(defun magit-find-file--internal (rev file display)
|
||||
(declare (obsolete magit-find-file-noselect "Magit 4.6.0"))
|
||||
(let ((buf (magit-find-file-noselect rev file)))
|
||||
(funcall display buf)
|
||||
buf))
|
||||
|
||||
(defun magit-find-file-index-noselect (file)
|
||||
"Read FILE from the index into a buffer and return the buffer."
|
||||
(declare (obsolete magit-find-file-noselect "Magit 4.6.0"))
|
||||
(magit-find-file-noselect "{index}" file t))
|
||||
|
||||
(provide 'magit-files)
|
||||
;; Local Variables:
|
||||
;; read-symbol-shorthands: (
|
||||
@@ -631,6 +784,7 @@ If DEFAULT is non-nil, use this as the default value instead of
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-git.el --- Git functionality -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -48,7 +48,6 @@
|
||||
(defvar magit-buffer-file-name)
|
||||
(defvar magit-buffer-log-args)
|
||||
(defvar magit-buffer-log-files)
|
||||
(defvar magit-buffer-refname)
|
||||
(defvar magit-buffer-revision)
|
||||
|
||||
;; From `magit-process'.
|
||||
@@ -72,6 +71,8 @@
|
||||
(cl-pushnew 'orig-rev eieio--known-slot-names)
|
||||
(cl-pushnew 'number eieio--known-slot-names))
|
||||
|
||||
(defvar crm-prompt) ; Emacs 31.1
|
||||
|
||||
;;; Options
|
||||
|
||||
;; For now this is shared between `magit-process' and `magit-git'.
|
||||
@@ -393,7 +394,7 @@ to do the following.
|
||||
|
||||
* Prepend `magit-git-global-arguments' to ARGS.
|
||||
* If ASYNC is non-nil and `magit-overriding-githook-directory' is non-nil
|
||||
and valid, set `core.hooksPath' by adding additional aguments to ARGS.
|
||||
and valid, set `core.hooksPath' by adding additional arguments to ARGS.
|
||||
* Flatten ARGS, removing nil arguments.
|
||||
* If `system-type' is `windows-nt', encode ARGS to `w32-ansi-code-page'."
|
||||
(cond ((not async))
|
||||
@@ -434,22 +435,6 @@ to do the following.
|
||||
"Execute Git with ARGS, returning t if its exit code is 1."
|
||||
(= (magit-git-exit-code args) 1))
|
||||
|
||||
(defun magit-git-string-p (&rest args)
|
||||
"Execute Git with ARGS, returning the first line of its output.
|
||||
If the exit code isn't zero or if there is no output, then return
|
||||
nil. Neither of these results is considered an error; if that is
|
||||
what you want, then use `magit-git-string-ng' instead.
|
||||
|
||||
This is an experimental replacement for `magit-git-string', and
|
||||
still subject to major changes."
|
||||
(magit--with-refresh-cache (cons default-directory args)
|
||||
(magit--with-temp-process-buffer
|
||||
(and (zerop (magit-process-git t args))
|
||||
(not (bobp))
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
(buffer-substring-no-properties (point) (line-end-position)))))))
|
||||
|
||||
(defun magit-git-string-ng (&rest args)
|
||||
"Execute Git with ARGS, returning the first line of its output.
|
||||
If the exit code isn't zero or if there is no output, then that
|
||||
@@ -459,7 +444,7 @@ buffer (creating it if necessary) and the error message is shown
|
||||
in the status buffer (provided it exists).
|
||||
|
||||
This is an experimental replacement for `magit-git-string', and
|
||||
still subject to major changes. Also see `magit-git-string-p'."
|
||||
still subject to major changes."
|
||||
(magit--with-refresh-cache
|
||||
(list default-directory 'magit-git-string-ng args)
|
||||
(magit--with-temp-process-buffer
|
||||
@@ -526,9 +511,9 @@ signal `magit-invalid-git-boolean'."
|
||||
|
||||
(defun magit-git-config-p (variable &optional default)
|
||||
"Return the boolean value of the Git variable VARIABLE.
|
||||
VARIABLE has to be specified as a string. Return DEFAULT (which
|
||||
defaults to nil) if VARIABLE is unset. If VARIABLE's value isn't
|
||||
a boolean, then raise an error."
|
||||
VARIABLE has to be specified as a string. If VARIABLE is unset,
|
||||
return nil by default, unless DEFAULT is non-nil, in which case
|
||||
return t. Signal an error if VARIABLE is set but not a boolean."
|
||||
(let ((args (list "config" "--bool" "--default" (if default "true" "false")
|
||||
variable)))
|
||||
(magit--with-refresh-cache (cons default-directory args)
|
||||
@@ -561,12 +546,12 @@ insert the run command and stderr into the process buffer."
|
||||
(goto-char (point-max))
|
||||
(setq errmsg
|
||||
(cond
|
||||
((eq return-error 'full)
|
||||
(let ((str (buffer-string)))
|
||||
(and (not (equal str "")) str)))
|
||||
((functionp magit-git-debug)
|
||||
(funcall magit-git-debug (buffer-string)))
|
||||
((magit--locate-error-message)))))
|
||||
((eq return-error 'full)
|
||||
(let ((str (buffer-string)))
|
||||
(and (not (equal str "")) str)))
|
||||
((functionp magit-git-debug)
|
||||
(funcall magit-git-debug (buffer-string)))
|
||||
((magit--locate-error-message)))))
|
||||
(when magit-git-debug
|
||||
(let ((magit-git-debug nil))
|
||||
(with-current-buffer (magit-process-buffer t)
|
||||
@@ -577,10 +562,12 @@ insert the run command and stderr into the process buffer."
|
||||
exit log 'magit-section-secondary-heading)
|
||||
exit)))))
|
||||
(cond ((not magit-git-debug))
|
||||
(errmsg (message "%s" errmsg))
|
||||
(errmsg (message "magit--git-insert: %S" errmsg))
|
||||
((zerop exit))
|
||||
((message "Git returned with exit-code %s" exit))))
|
||||
(or errmsg exit))
|
||||
((message "magit--git-insert: %s %s"
|
||||
"Git returned with exit-code" exit))))
|
||||
(or (and return-error errmsg)
|
||||
exit))
|
||||
(ignore-errors (delete-file log))))
|
||||
(magit-process-git (list t nil) args)))
|
||||
|
||||
@@ -591,16 +578,18 @@ insert the run command and stderr into the process buffer."
|
||||
(match-str 1)))
|
||||
|
||||
(defun magit-git-string (&rest args)
|
||||
"Execute Git with ARGS, returning the first line of its output.
|
||||
If there is no output, return nil. If the output begins with a
|
||||
newline, return an empty string."
|
||||
"Execute Git with ARGS, returning the first line of its output (stdout).
|
||||
If the exit code isn't zero or if there is no output, then return nil.
|
||||
Neither of these results is considered an error; if that is what you
|
||||
want, then use `magit-git-string-ng' instead."
|
||||
(setq args (flatten-tree args))
|
||||
(magit--with-refresh-cache (cons default-directory args)
|
||||
(magit--with-temp-process-buffer
|
||||
(apply #'magit-git-insert args)
|
||||
(unless (bobp)
|
||||
(goto-char (point-min))
|
||||
(buffer-substring-no-properties (point) (line-end-position))))))
|
||||
(and (zerop (apply #'magit-git-insert args))
|
||||
(not (bobp))
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
(buffer-substring-no-properties (point) (line-end-position)))))))
|
||||
|
||||
(defun magit-git-lines (&rest args)
|
||||
"Execute Git with ARGS, returning its output as a list of lines.
|
||||
@@ -708,29 +697,29 @@ format."
|
||||
(status (magit-process-git t "version"))
|
||||
(output (buffer-string)))
|
||||
(cond
|
||||
((not (zerop status))
|
||||
(display-warning
|
||||
'magit
|
||||
(format "%S\n\nRunning \"%s --version\" failed with output:\n\n%s"
|
||||
(if host
|
||||
(format "Magit cannot find Git on host %S.\n
|
||||
((not (zerop status))
|
||||
(display-warning
|
||||
'magit
|
||||
(format "%S\n\nRunning \"%s --version\" failed with output:\n\n%s"
|
||||
(if host
|
||||
(format "Magit cannot find Git on host %S.\n
|
||||
Check the value of `magit-remote-git-executable' using
|
||||
`magit-debug-git-executable' and consult the info node
|
||||
`(tramp)Remote programs'." host)
|
||||
"Magit cannot find Git.\n
|
||||
"Magit cannot find Git.\n
|
||||
Check the values of `magit-git-executable' and `exec-path'
|
||||
using `magit-debug-git-executable'.")
|
||||
(magit-git-executable)
|
||||
output)))
|
||||
((save-match-data
|
||||
(and (string-match magit--git-version-regexp output)
|
||||
(let ((version (match-str 1 output)))
|
||||
(push (cons host version)
|
||||
magit--host-git-version-cache)
|
||||
version))))
|
||||
((error "Unexpected \"%s --version\" output: %S"
|
||||
(magit-git-executable)
|
||||
output)))))))))
|
||||
(magit-git-executable)
|
||||
output)))
|
||||
((save-match-data
|
||||
(and (string-match magit--git-version-regexp output)
|
||||
(let ((version (match-str 1 output)))
|
||||
(push (cons host version)
|
||||
magit--host-git-version-cache)
|
||||
version))))
|
||||
((error "Unexpected \"%s --version\" output: %S"
|
||||
(magit-git-executable)
|
||||
output)))))))))
|
||||
|
||||
(defun magit-git-version-assert (&optional minimal who)
|
||||
"Assert that the used Git version is greater than or equal to MINIMAL.
|
||||
@@ -1019,9 +1008,7 @@ returning the truename."
|
||||
|
||||
(define-error 'magit-outside-git-repo "Not inside Git repository")
|
||||
(define-error 'magit-corrupt-git-config "Corrupt Git configuration")
|
||||
(define-error 'magit-git-executable-not-found
|
||||
(concat "Git executable cannot be found "
|
||||
"(see https://magit.vc/goto/e6a78ed2)"))
|
||||
(define-error 'magit-git-executable-not-found "Git executable cannot be found")
|
||||
|
||||
(defun magit--assert-usable-git ()
|
||||
(if (not (executable-find (magit-git-executable) t))
|
||||
@@ -1121,8 +1108,8 @@ tracked file."
|
||||
(file-relative-name file dir))))
|
||||
|
||||
(defun magit-file-ignored-p (file)
|
||||
(magit-git-string-p "ls-files" "--others" "--ignored" "--exclude-standard"
|
||||
"--" (magit-convert-filename-for-git file)))
|
||||
(magit-git-string "ls-files" "--others" "--ignored" "--exclude-standard"
|
||||
"--" (magit-convert-filename-for-git file)))
|
||||
|
||||
(defun magit-file-tracked-p (file)
|
||||
(magit-git-success "ls-files" "--error-unmatch"
|
||||
@@ -1145,6 +1132,16 @@ issue."
|
||||
(and (not all) "--exclude-standard")
|
||||
"--" files))
|
||||
|
||||
(defun magit--untracked-files (&optional directory all)
|
||||
(magit-with-toplevel
|
||||
(seq-keep (##and (eq (aref % 0) ??)
|
||||
(substring % 3))
|
||||
(magit-git-items "status" "-z" "--porcelain"
|
||||
(if all
|
||||
"--untracked-files=all"
|
||||
"--untracked-files=normal")
|
||||
"--" directory))))
|
||||
|
||||
(defun magit-list-untracked-files (&optional files)
|
||||
"Return a list of untracked files.
|
||||
|
||||
@@ -1258,13 +1255,12 @@ or if no rename is detected."
|
||||
(y (char-after (1+ pos)))
|
||||
(file (buffer-substring (+ pos 3) (point))))
|
||||
(forward-char)
|
||||
(if (memq x '(?R ?C))
|
||||
(progn
|
||||
(setq pos (point))
|
||||
(skip-chars-forward "[:print:]")
|
||||
(push (list file (buffer-substring pos (point)) x y) status)
|
||||
(forward-char))
|
||||
(push (list file nil x y) status)))
|
||||
(cond ((memq x '(?R ?C))
|
||||
(setq pos (point))
|
||||
(skip-chars-forward "[:print:]")
|
||||
(push (list file (buffer-substring pos (point)) x y) status)
|
||||
(forward-char))
|
||||
((push (list file nil x y) status))))
|
||||
(setq pos (point)))
|
||||
status)))
|
||||
|
||||
@@ -1279,11 +1275,10 @@ or if no rename is detected."
|
||||
"Failed to parse Cygwin mount: %S" mount)))
|
||||
;; If --exec-path is not a native Windows path,
|
||||
;; then we probably have a cygwin git.
|
||||
(and (not (string-match-p
|
||||
"\\`[a-zA-Z]:"
|
||||
(car (magit--early-process-lines
|
||||
magit-git-executable "--exec-path"))))
|
||||
(magit--early-process-lines "mount")))
|
||||
(and-let ((dirs (magit--early-process-lines
|
||||
magit-git-executable "--exec-path")))
|
||||
(and (not (string-match-p "\\`[a-zA-Z]:" (car dirs)))
|
||||
(magit--early-process-lines "mount"))))
|
||||
#'> :key (pcase-lambda (`(,cyg . ,_win)) (length cyg))))
|
||||
"Alist of (CYGWIN . WIN32) directory names.
|
||||
Sorted from longest to shortest CYGWIN name."
|
||||
@@ -1344,6 +1339,36 @@ Sorted from longest to shortest CYGWIN name."
|
||||
(and (derived-mode-p 'magit-log-mode)
|
||||
(car magit-buffer-log-files))))
|
||||
|
||||
;;; Blobs
|
||||
|
||||
(defun magit-blob-p (obj)
|
||||
(equal (magit-object-type obj) "blob"))
|
||||
|
||||
(defun magit-blob-oid (rev file)
|
||||
(cond-let
|
||||
((equal rev "{index}")
|
||||
(cadr (car (magit--file-index-stages file))))
|
||||
;; --object-only and --format were only added in Git v2.36.0.
|
||||
([out (magit-git-string "ls-tree" "--full-tree" rev "--"
|
||||
(magit-convert-filename-for-git file))]
|
||||
(nth 2 (split-string out "[\s\t]")))))
|
||||
|
||||
(defun magit--file-index-stages (file)
|
||||
(mapcar (##split-string % " ")
|
||||
(magit-git-lines "ls-files" "--stage" "--"
|
||||
(magit-convert-filename-for-git file))))
|
||||
|
||||
(defun magit--insert-blob-contents (obj file)
|
||||
(let ((coding-system-for-read (or coding-system-for-read 'undecided)))
|
||||
(if (magit-blob-p obj)
|
||||
(magit-git-insert "cat-file" "blob" obj)
|
||||
(magit-git-insert "cat-file" "-p"
|
||||
(if (equal obj "{index}")
|
||||
(concat ":" file)
|
||||
(concat obj ":" file))))
|
||||
(setq buffer-file-coding-system last-coding-system-used)
|
||||
nil))
|
||||
|
||||
;;; Predicates
|
||||
|
||||
(defun magit-no-commit-p ()
|
||||
@@ -1441,21 +1466,29 @@ string \"true\", otherwise return nil."
|
||||
(equal (magit-git-str "rev-parse" args) "true"))
|
||||
|
||||
(defun magit-rev-verify (rev)
|
||||
(magit-git-string-p "rev-parse" "--verify" rev))
|
||||
(magit-git-string "rev-parse" "--verify" rev))
|
||||
|
||||
(defun magit-commit-p (rev)
|
||||
"Return full hash for REV if it names an existing commit."
|
||||
"Return non-nil if REV can be dereferences as a commit.
|
||||
Otherwise return nil. Use `magit-commit-oid' if you actually need
|
||||
the oid; eventually this function will return t instead of the oid,
|
||||
as it currently does for backward compatibility."
|
||||
;; TODO Return t instead of the oid.
|
||||
(magit-rev-verify (magit--rev-dereference rev)))
|
||||
|
||||
(defalias 'magit-rev-verify-commit #'magit-commit-p)
|
||||
|
||||
(defalias 'magit-rev-hash #'magit-commit-p)
|
||||
(defun magit-commit-oid (rev &optional noerror)
|
||||
"Return commit oid for REV if it can be dereferences as a commit.
|
||||
Otherwise signal an error, or return nil, if optional NOERROR is non-nil."
|
||||
(cond ((magit-rev-verify (magit--rev-dereference rev)))
|
||||
(noerror nil)
|
||||
((error "%s cannot be dereferenced as a commit" rev))))
|
||||
|
||||
(defun magit--rev-dereference (rev)
|
||||
"Return a rev that forces Git to interpret REV as a commit.
|
||||
If REV is nil or has the form \":/TEXT\", return REV itself."
|
||||
Do so by appending \"^{commit}\"; see \"--verify\" in git-rev-parse(1).
|
||||
However, if REV is nil or has the form \":/TEXT\", return REV itself."
|
||||
(cond ((not rev) nil)
|
||||
((string-match-p "^:/" rev) rev)
|
||||
((string-prefix-p ":/" rev) rev)
|
||||
((concat rev "^{commit}"))))
|
||||
|
||||
(defun magit-rev-equal (a b)
|
||||
@@ -1464,20 +1497,18 @@ If REV is nil or has the form \":/TEXT\", return REV itself."
|
||||
|
||||
(defun magit-rev-eq (a b)
|
||||
"Return t if A and B refer to the same commit."
|
||||
(let ((a (magit-commit-p a))
|
||||
(b (magit-commit-p b)))
|
||||
(and a b (equal a b))))
|
||||
(and-let ((a (magit-commit-oid a t))
|
||||
(b (magit-commit-oid b t)))
|
||||
(equal a b)))
|
||||
|
||||
(defun magit-rev-ancestor-p (a b)
|
||||
"Return non-nil if commit A is an ancestor of commit B."
|
||||
(magit-git-success "merge-base" "--is-ancestor" a b))
|
||||
|
||||
(defun magit-rev-head-p (rev)
|
||||
"Return t if REV can be dereferences as the `HEAD' commit."
|
||||
(or (equal rev "HEAD")
|
||||
(and rev
|
||||
(not (string-search ".." rev))
|
||||
(equal (magit-rev-parse rev)
|
||||
(magit-rev-parse "HEAD")))))
|
||||
(magit-rev-eq rev "HEAD")))
|
||||
|
||||
(defun magit-rev-author-p (rev)
|
||||
"Return t if the user is the author of REV.
|
||||
@@ -1584,9 +1615,9 @@ nil, then use \"heads/\"."
|
||||
A symbolic-ref pointing to some ref, is `equal' to that ref,
|
||||
as are two symbolic-refs pointing to the same ref. Refnames
|
||||
may be abbreviated."
|
||||
(let ((a (magit-ref-fullname a))
|
||||
(b (magit-ref-fullname b)))
|
||||
(and a b (equal a b))))
|
||||
(and-let ((a (magit-ref-fullname a))
|
||||
(b (magit-ref-fullname b)))
|
||||
(equal a b)))
|
||||
|
||||
(defun magit-ref-eq (a b)
|
||||
"Return t if the refnames A and B are `eq'.
|
||||
@@ -1682,7 +1713,7 @@ to, or to some other symbolic-ref that points to the same ref."
|
||||
(magit-current-blame-chunk))))
|
||||
(oref chunk orig-rev))
|
||||
(and magit-buffer-file-name
|
||||
magit-buffer-refname)
|
||||
magit-buffer-revision)
|
||||
(and (derived-mode-p 'magit-stash-mode
|
||||
'magit-merge-preview-mode
|
||||
'magit-revision-mode)
|
||||
@@ -1750,10 +1781,10 @@ The amount of time spent searching is limited by
|
||||
(concat remote "/" newname))))
|
||||
(pcase-dolist (`(,branch ,upstream) branches)
|
||||
(cond
|
||||
((equal upstream oldname)
|
||||
(magit-set-upstream-branch branch new))
|
||||
((equal upstream (concat remote "/" oldname))
|
||||
(magit-set-upstream-branch branch (concat remote "/" newname))))))))
|
||||
((equal upstream oldname)
|
||||
(magit-set-upstream-branch branch new))
|
||||
((equal upstream (concat remote "/" oldname))
|
||||
(magit-set-upstream-branch branch (concat remote "/" newname))))))))
|
||||
|
||||
(defun magit--get-default-branch (&optional update)
|
||||
(let ((remote (magit-primary-remote)))
|
||||
@@ -1874,19 +1905,18 @@ according to the branch type."
|
||||
(defun magit-get-push-branch (&optional branch verify)
|
||||
(magit--with-refresh-cache
|
||||
(list default-directory 'magit-get-push-branch branch verify)
|
||||
(and-let* ((branch (or branch (setq branch (magit-get-current-branch))))
|
||||
(remote (magit-get-push-remote branch))
|
||||
(target (concat remote "/" branch)))
|
||||
(and-let*
|
||||
((branch (magit-ref-abbrev (or branch (magit-get-current-branch))))
|
||||
(remote (magit-get-push-remote branch))
|
||||
(target (concat remote "/" branch)))
|
||||
(and (or (not verify)
|
||||
(magit-rev-verify target))
|
||||
(magit--propertize-face target 'magit-branch-remote)))))
|
||||
|
||||
(defun magit-get-@{push}-branch (&optional branch)
|
||||
(let ((ref (magit-rev-parse "--symbolic-full-name"
|
||||
(concat branch "@{push}"))))
|
||||
(and ref
|
||||
(string-prefix-p "refs/remotes/" ref)
|
||||
(substring ref 13))))
|
||||
(and-let* ((branch (magit-ref-abbrev (or branch (magit-get-current-branch))))
|
||||
(target (magit-ref-fullname (concat branch "@{push}"))))
|
||||
(magit-ref-abbrev target)))
|
||||
|
||||
(defun magit-get-remote (&optional branch)
|
||||
(and (or branch (setq branch (magit-get-current-branch)))
|
||||
@@ -2232,11 +2262,11 @@ specified using `core.worktree'."
|
||||
(let* ((default-directory (car worktree))
|
||||
(wt (and (not (magit-get-boolean "core.bare"))
|
||||
(magit-get "core.worktree"))))
|
||||
(if (and wt (file-exists-p (expand-file-name wt)))
|
||||
(progn (setf (nth 0 worktree) (expand-file-name wt))
|
||||
(setf (nth 2 worktree) (magit-rev-parse "HEAD"))
|
||||
(setf (nth 3 worktree) (magit-get-current-branch)))
|
||||
(setf (nth 3 worktree) t))))
|
||||
(cond ((and wt (file-exists-p (expand-file-name wt)))
|
||||
(setf (nth 0 worktree) (expand-file-name wt))
|
||||
(setf (nth 2 worktree) (magit-rev-parse "HEAD"))
|
||||
(setf (nth 3 worktree) (magit-get-current-branch)))
|
||||
((setf (nth 3 worktree) t)))))
|
||||
((string-equal line "detached")
|
||||
(setf (nth 4 worktree) t))
|
||||
((string-prefix-p line "locked")
|
||||
@@ -2271,8 +2301,8 @@ specified using `core.worktree'."
|
||||
'magit-branch-local
|
||||
'magit-branch-remote)))
|
||||
|
||||
(defun magit-tag-p (rev)
|
||||
(car (member rev (magit-list-tags))))
|
||||
(defun magit-tag-p (obj)
|
||||
(equal (magit-object-type obj) "tag"))
|
||||
|
||||
(defun magit-remote-p (string)
|
||||
(car (member string (magit-list-remotes))))
|
||||
@@ -2342,10 +2372,10 @@ If `first-parent' is set, traverse only first parents."
|
||||
(defun magit-rev-abbrev (rev)
|
||||
(magit-rev-parse (magit-abbrev-arg "short") rev))
|
||||
|
||||
(defun magit--abbrev-if-hash (rev)
|
||||
(cond ((or (magit-ref-p rev) (member rev '("{index}" "{worktree}"))) rev)
|
||||
((magit-rev-parse (magit-abbrev-arg "short") rev))
|
||||
(rev)))
|
||||
(defun magit--abbrev-if-oid (obj)
|
||||
(cond ((or (magit-ref-p obj) (member obj '("{index}" "{worktree}"))) obj)
|
||||
((magit-rev-parse (magit-abbrev-arg "short") obj))
|
||||
(obj)))
|
||||
|
||||
(defun magit-commit-children (rev &optional args)
|
||||
(seq-keep (lambda (line)
|
||||
@@ -2469,18 +2499,18 @@ and this option only controls what face is used.")
|
||||
(string-match "^[^/]*/" push)
|
||||
(setq push (substring push 0 (match-end 0))))
|
||||
(cond
|
||||
((equal name current)
|
||||
(setq head
|
||||
(concat push
|
||||
(magit--propertize-face
|
||||
name 'magit-branch-current))))
|
||||
((equal name target)
|
||||
(setq upstream
|
||||
(concat push
|
||||
(magit--propertize-face
|
||||
name '(magit-branch-upstream
|
||||
magit-branch-local)))))
|
||||
((push (concat push name) combined)))))
|
||||
((equal name current)
|
||||
(setq head
|
||||
(concat push
|
||||
(magit--propertize-face
|
||||
name 'magit-branch-current))))
|
||||
((equal name target)
|
||||
(setq upstream
|
||||
(concat push
|
||||
(magit--propertize-face
|
||||
name '(magit-branch-upstream
|
||||
magit-branch-local)))))
|
||||
((push (concat push name) combined)))))
|
||||
(cond-let
|
||||
((or upstream (not target)))
|
||||
((member target remotes)
|
||||
@@ -2568,8 +2598,8 @@ and this option only controls what face is used.")
|
||||
(beg (or beg "HEAD"))
|
||||
(end (or end "HEAD")))
|
||||
(when abbrev
|
||||
(setq beg (magit--abbrev-if-hash beg))
|
||||
(setq end (magit--abbrev-if-hash end)))
|
||||
(setq beg (magit--abbrev-if-oid beg))
|
||||
(setq end (magit--abbrev-if-oid end)))
|
||||
(pcase sep
|
||||
(".." (cons beg end))
|
||||
("..." (and$ (magit-git-string "merge-base" beg end)
|
||||
@@ -2583,15 +2613,18 @@ and this option only controls what face is used.")
|
||||
(list beg end sep)))))
|
||||
|
||||
(defun magit-hash-range (range)
|
||||
"Return a string with the revisions in RANGE replaced with commit oids.
|
||||
Either side of RANGE may be omitted, and RANGE may be just a revision.
|
||||
If either revision cannot be dereferenced as a commit, signal an error."
|
||||
(if (string-match magit-range-re range)
|
||||
(magit-bind-match-strings (beg sep end) range
|
||||
(and (or beg end)
|
||||
(let ((beg-hash (and beg (magit-rev-hash beg)))
|
||||
(end-hash (and end (magit-rev-hash end))))
|
||||
(and (or (not beg) beg-hash)
|
||||
(or (not end) end-hash)
|
||||
(concat beg-hash sep end-hash)))))
|
||||
(magit-rev-hash range)))
|
||||
(let ((beg-oid (and beg (magit-commit-oid beg)))
|
||||
(end-oid (and end (magit-commit-oid end))))
|
||||
(and (or (not beg) beg-oid)
|
||||
(or (not end) end-oid)
|
||||
(concat beg-oid sep end-oid)))))
|
||||
(magit-commit-oid range)))
|
||||
|
||||
(defvar magit-revision-faces
|
||||
'(magit-hash
|
||||
@@ -2645,7 +2678,7 @@ and this option only controls what face is used.")
|
||||
(and (not (equal string "@"))
|
||||
(or (and (>= (length string) 7)
|
||||
(string-match-p "[a-z]" string)
|
||||
(magit-commit-p string))
|
||||
(magit-commit-oid string t))
|
||||
(and (magit-ref-p string)
|
||||
(member (get-text-property (point) 'face)
|
||||
magit-revision-faces)))
|
||||
@@ -2729,10 +2762,11 @@ and this option only controls what face is used.")
|
||||
(lambda ()
|
||||
(magit--minibuf-default-add-commit)
|
||||
(setq-local crm-separator "\\.\\.\\.?"))
|
||||
(magit-completing-read-multiple
|
||||
(concat prompt ": ")
|
||||
(magit-list-refnames)
|
||||
nil 'any nil 'magit-revision-history default nil t)))
|
||||
(let ((crm-prompt "%p"))
|
||||
(magit-completing-read-multiple
|
||||
(concat prompt ": ")
|
||||
(magit-list-refnames)
|
||||
nil 'any nil 'magit-revision-history default nil t))))
|
||||
|
||||
(defun magit-read-remote-branch
|
||||
(prompt &optional remote default local-branch require-match)
|
||||
@@ -2812,6 +2846,23 @@ and this option only controls what face is used.")
|
||||
(magit-completing-read prompt (delete exclude (magit-list-refnames))
|
||||
nil 'any nil 'magit-revision-history default))))
|
||||
|
||||
(defun magit-read-other-branches-or-commits
|
||||
(prompt &optional exclude secondary-default)
|
||||
(let* ((current (magit-get-current-branch))
|
||||
(atpoint (magit-branch-or-commit-at-point))
|
||||
(exclude (or exclude current))
|
||||
(default (or (and (not (equal atpoint exclude))
|
||||
(not (and (not current)
|
||||
(magit-rev-equal atpoint "HEAD")))
|
||||
atpoint)
|
||||
(and (not (equal current exclude)) current)
|
||||
secondary-default
|
||||
(magit-get-previous-branch))))
|
||||
(minibuffer-with-setup-hook #'magit--minibuf-default-add-commit
|
||||
(magit-completing-read-multiple
|
||||
prompt (delete exclude (magit-list-refnames))
|
||||
nil 'any nil 'magit-revision-history default))))
|
||||
|
||||
(defun magit-read-other-local-branch
|
||||
(prompt &optional exclude secondary-default)
|
||||
(let* ((current (magit-get-current-branch))
|
||||
@@ -2993,6 +3044,21 @@ out. Only existing branches can be selected."
|
||||
(server-send-string client msg))))
|
||||
|
||||
;;; _
|
||||
|
||||
(define-obsolete-function-alias 'magit-git-string-p
|
||||
#'magit-git-string "Magit 4.6.0")
|
||||
|
||||
(define-obsolete-function-alias 'magit-rev-verify-commit
|
||||
#'magit-commit-p "Magit 4.6.0")
|
||||
|
||||
(define-obsolete-function-alias 'magit-rev-hash
|
||||
#'magit-commit-p "Magit 4.6.0"
|
||||
"Return oid for REV if it names an existing commit, nil otherwise.
|
||||
Instead use `magit-commit-p' or `magit-commit-oid'.")
|
||||
|
||||
(define-obsolete-function-alias 'magit--abbrev-if-hash
|
||||
#'magit--abbrev-if-oid "Magit 4.6.0")
|
||||
|
||||
(provide 'magit-git)
|
||||
;; Local Variables:
|
||||
;; read-symbol-shorthands: (
|
||||
@@ -3000,6 +3066,7 @@ out. Only existing branches can be selected."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-gitignore.el --- Intentionally untracked files -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -35,16 +35,10 @@
|
||||
"Instruct Git to ignore a file or pattern."
|
||||
:man-page "gitignore"
|
||||
["Gitignore"
|
||||
("t" "shared at toplevel (.gitignore)"
|
||||
magit-gitignore-in-topdir)
|
||||
("s" "shared in subdirectory (path/to/.gitignore)"
|
||||
magit-gitignore-in-subdir)
|
||||
("p" "privately (.git/info/exclude)"
|
||||
magit-gitignore-in-gitdir)
|
||||
("g" magit-gitignore-on-system
|
||||
:if (##magit-get "core.excludesfile")
|
||||
:description (##format "privately for all repositories (%s)"
|
||||
(magit-get "core.excludesfile")))]
|
||||
("t" magit-gitignore-in-topdir)
|
||||
("s" magit-gitignore-in-subdir)
|
||||
("p" magit-gitignore-in-gitdir)
|
||||
("g" magit-gitignore-on-system)]
|
||||
["Skip worktree"
|
||||
(7 "w" "do skip worktree" magit-skip-worktree)
|
||||
(7 "W" "do not skip worktree" magit-no-skip-worktree)]
|
||||
@@ -54,51 +48,56 @@
|
||||
|
||||
;;; Gitignore Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-gitignore-in-topdir (rule)
|
||||
;;;###autoload(autoload 'magit-gitignore-in-topdir "magit-gitignore" nil t)
|
||||
(transient-define-suffix magit-gitignore-in-topdir (rule)
|
||||
"Add the Git ignore RULE to the top-level \".gitignore\" file.
|
||||
Since this file is tracked, it is shared with other clones of the
|
||||
repository. Also stage the file."
|
||||
:description "shared at toplevel (.gitignore)"
|
||||
(interactive (list (magit-gitignore-read-pattern)))
|
||||
(magit-with-toplevel
|
||||
(magit--gitignore rule ".gitignore")
|
||||
(magit-run-git "add" ".gitignore")))
|
||||
(magit--gitignore rule (expand-file-name ".gitignore" (magit-toplevel)) t))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-gitignore-in-subdir (rule directory)
|
||||
;;;###autoload(autoload 'magit-gitignore-in-subdir "magit-gitignore" nil t)
|
||||
(transient-define-suffix magit-gitignore-in-subdir (rule directory)
|
||||
"Add the Git ignore RULE to a \".gitignore\" file in DIRECTORY.
|
||||
Prompt the user for a directory and add the rule to the
|
||||
\".gitignore\" file in that directory. Since such files are
|
||||
tracked, they are shared with other clones of the repository.
|
||||
Also stage the file."
|
||||
(interactive (list (magit-gitignore-read-pattern)
|
||||
(read-directory-name "Limit rule to files in: ")))
|
||||
(magit-with-toplevel
|
||||
(let ((file (expand-file-name ".gitignore" directory)))
|
||||
(magit--gitignore rule file)
|
||||
(magit-run-git "add" (magit-convert-filename-for-git file)))))
|
||||
:description "shared in subdirectory (path/to/.gitignore)"
|
||||
(interactive (let ((dir (expand-file-name
|
||||
(read-directory-name
|
||||
"Limit rule to files in: "
|
||||
(and$ (magit-current-file)
|
||||
(file-name-directory
|
||||
(expand-file-name $ (magit-toplevel))))))))
|
||||
(list (magit-gitignore-read-pattern dir) dir)))
|
||||
(magit--gitignore rule (expand-file-name ".gitignore" directory) t))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-gitignore-in-gitdir (rule)
|
||||
;;;###autoload(autoload 'magit-gitignore-in-gitdir "magit-gitignore" nil t)
|
||||
(transient-define-suffix magit-gitignore-in-gitdir (rule)
|
||||
"Add the Git ignore RULE to \"$GIT_DIR/info/exclude\".
|
||||
Rules in that file only affects this clone of the repository."
|
||||
:description "privately (.git/info/exclude)"
|
||||
(interactive (list (magit-gitignore-read-pattern)))
|
||||
(magit--gitignore rule (expand-file-name "info/exclude" (magit-gitdir)))
|
||||
(magit-refresh))
|
||||
(magit--gitignore rule (expand-file-name "info/exclude" (magit-gitdir))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-gitignore-on-system (rule)
|
||||
;;;###autoload(autoload 'magit-gitignore-on-system "magit-gitignore" nil t)
|
||||
(transient-define-suffix magit-gitignore-on-system (rule)
|
||||
"Add the Git ignore RULE to the file specified by `core.excludesFile'.
|
||||
Rules that are defined in that file affect all local repositories."
|
||||
:inapt-if-not (##magit-get "core.excludesfile")
|
||||
:description (##format "privately for all repositories (%s)"
|
||||
(or (magit-get "core.excludesfile")
|
||||
"core.excludesfile is not set"))
|
||||
(interactive (list (magit-gitignore-read-pattern)))
|
||||
(magit--gitignore rule
|
||||
(or (magit-get "core.excludesFile")
|
||||
(error "Variable `core.excludesFile' isn't set")))
|
||||
(magit-refresh))
|
||||
(if-let ((file (magit-get "core.excludesFile")))
|
||||
(magit--gitignore rule file)
|
||||
(error "Variable `core.excludesFile' isn't set")))
|
||||
|
||||
(defun magit--gitignore (rule file)
|
||||
(when-let ((directory (file-name-directory file)))
|
||||
(make-directory directory t))
|
||||
(defun magit--gitignore (rule file &optional stage)
|
||||
(when$ (file-name-directory file)
|
||||
(make-directory $ t))
|
||||
(with-temp-buffer
|
||||
(when (file-exists-p file)
|
||||
(insert-file-contents file))
|
||||
@@ -107,31 +106,21 @@ Rules that are defined in that file affect all local repositories."
|
||||
(insert "\n"))
|
||||
(insert (replace-regexp-in-string "\\(\\\\*\\)" "\\1\\1" rule))
|
||||
(insert "\n")
|
||||
(write-region nil nil file)))
|
||||
(write-region nil nil file))
|
||||
(if stage
|
||||
(magit-with-toplevel
|
||||
(magit-run-git "add" (magit-convert-filename-for-git file)))
|
||||
(magit-refresh)))
|
||||
|
||||
(defun magit-gitignore-read-pattern ()
|
||||
(let* ((default (magit-current-file))
|
||||
(base (car magit-buffer-diff-files))
|
||||
(base (and base (file-directory-p base) base))
|
||||
(choices
|
||||
(delete-dups
|
||||
(mapcan
|
||||
(lambda (file)
|
||||
(cons (concat "/" file)
|
||||
(and$ (file-name-extension file)
|
||||
(list (concat "/" (file-name-directory file) "*." $)
|
||||
(concat "*." $)))))
|
||||
(sort (nconc
|
||||
(magit-untracked-files nil base)
|
||||
;; The untracked section of the status buffer lists
|
||||
;; directories containing only untracked files.
|
||||
;; Add those as candidates.
|
||||
(seq-filter #'directory-name-p
|
||||
(magit-list-files
|
||||
"--other" "--exclude-standard" "--directory"
|
||||
"--no-empty-directory" "--" base)))
|
||||
#'string-lessp)))))
|
||||
(defun magit-gitignore-read-pattern (&optional directory)
|
||||
(let ((choices (magit--gitignore-patterns directory))
|
||||
(default (magit-current-file)))
|
||||
(when default
|
||||
(when directory
|
||||
(setq default
|
||||
(substring default
|
||||
(length
|
||||
(file-relative-name directory (magit-toplevel))))))
|
||||
(setq default (concat "/" default))
|
||||
(unless (member default choices)
|
||||
(setq default (concat "*." (file-name-extension default)))
|
||||
@@ -140,18 +129,40 @@ Rules that are defined in that file affect all local repositories."
|
||||
(magit-completing-read "File or pattern to ignore"
|
||||
choices nil 'any nil nil default)))
|
||||
|
||||
(defun magit--gitignore-patterns (&optional directory)
|
||||
(let* ((topdir (magit-toplevel))
|
||||
(default-directory (or directory topdir))
|
||||
(files (magit--untracked-files directory t))
|
||||
;; Include directories that contain only untracked files.
|
||||
(dirs (seq-filter (##equal (substring % -1) "/")
|
||||
(magit--untracked-files directory)))
|
||||
(globs nil)
|
||||
(dirglobs nil))
|
||||
(when directory
|
||||
(let ((beg (length (file-relative-name directory topdir))))
|
||||
(setq files (mapcar (##substring % beg) files))
|
||||
(setq dirs (mapcar (##substring % beg) dirs))))
|
||||
(dolist (file files)
|
||||
(when-let ((ext (file-name-extension file)))
|
||||
(cl-pushnew (concat "*." ext) globs :test #'equal)
|
||||
(when-let ((dir (file-name-directory file)))
|
||||
(cl-pushnew (concat dir "*." ext) dirglobs :test #'equal))))
|
||||
(sort (nconc globs
|
||||
(mapcar (##concat "/" %) (nconc files dirs dirglobs)))
|
||||
#'string<)))
|
||||
|
||||
;;; Skip Worktree Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-skip-worktree (file)
|
||||
"Call \"git update-index --skip-worktree -- FILE\"."
|
||||
(interactive
|
||||
(list (magit-read-file-choice "Skip worktree for"
|
||||
(magit-with-toplevel
|
||||
(cl-set-difference
|
||||
(magit-list-files)
|
||||
(magit-skip-worktree-files)
|
||||
:test #'equal)))))
|
||||
(list (magit-read-file-choice "Skip worktree for"
|
||||
(magit-with-toplevel
|
||||
(cl-set-difference
|
||||
(magit-list-files)
|
||||
(magit-skip-worktree-files)
|
||||
:test #'equal)))))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git "update-index" "--skip-worktree" "--" file)))
|
||||
|
||||
@@ -159,9 +170,9 @@ Rules that are defined in that file affect all local repositories."
|
||||
(defun magit-no-skip-worktree (file)
|
||||
"Call \"git update-index --no-skip-worktree -- FILE\"."
|
||||
(interactive
|
||||
(list (magit-read-file-choice "Do not skip worktree for"
|
||||
(magit-with-toplevel
|
||||
(magit-skip-worktree-files)))))
|
||||
(list (magit-read-file-choice "Do not skip worktree for"
|
||||
(magit-with-toplevel
|
||||
(magit-skip-worktree-files)))))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git "update-index" "--no-skip-worktree" "--" file)))
|
||||
|
||||
@@ -171,12 +182,12 @@ Rules that are defined in that file affect all local repositories."
|
||||
(defun magit-assume-unchanged (file)
|
||||
"Call \"git update-index --assume-unchanged -- FILE\"."
|
||||
(interactive
|
||||
(list (magit-read-file-choice "Assume file to be unchanged"
|
||||
(magit-with-toplevel
|
||||
(cl-set-difference
|
||||
(magit-list-files)
|
||||
(magit-assume-unchanged-files)
|
||||
:test #'equal)))))
|
||||
(list (magit-read-file-choice "Assume file to be unchanged"
|
||||
(magit-with-toplevel
|
||||
(cl-set-difference
|
||||
(magit-list-files)
|
||||
(magit-assume-unchanged-files)
|
||||
:test #'equal)))))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git "update-index" "--assume-unchanged" "--" file)))
|
||||
|
||||
@@ -184,9 +195,9 @@ Rules that are defined in that file affect all local repositories."
|
||||
(defun magit-no-assume-unchanged (file)
|
||||
"Call \"git update-index --no-assume-unchanged -- FILE\"."
|
||||
(interactive
|
||||
(list (magit-read-file-choice "Do not assume file to be unchanged"
|
||||
(magit-with-toplevel
|
||||
(magit-assume-unchanged-files)))))
|
||||
(list (magit-read-file-choice "Do not assume file to be unchanged"
|
||||
(magit-with-toplevel
|
||||
(magit-assume-unchanged-files)))))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git "update-index" "--no-assume-unchanged" "--" file)))
|
||||
|
||||
@@ -198,6 +209,7 @@ Rules that are defined in that file affect all local repositories."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-log.el --- Inspect Git history -*- lexical-binding:t; coding:utf-8 -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -374,6 +374,12 @@ commits before and half after."
|
||||
:group 'magit-log
|
||||
:type 'integer)
|
||||
|
||||
;;; Variables
|
||||
|
||||
(defvar-local magit-buffer-log-revisions nil)
|
||||
(defvar-local magit-buffer-log-args nil)
|
||||
(defvar-local magit-buffer-log-files nil)
|
||||
|
||||
;;; Arguments
|
||||
;;;; Prefix Classes
|
||||
|
||||
@@ -569,19 +575,19 @@ commits before and half after."
|
||||
("b" "buffer lock" magit-toggle-buffer-lock)]]
|
||||
(interactive)
|
||||
(cond
|
||||
((not (eq transient-current-command 'magit-log-refresh))
|
||||
(pcase major-mode
|
||||
('magit-reflog-mode
|
||||
(user-error "Cannot change log arguments in reflog buffers"))
|
||||
('magit-cherry-mode
|
||||
(user-error "Cannot change log arguments in cherry buffers")))
|
||||
(transient-setup 'magit-log-refresh))
|
||||
(t
|
||||
(pcase-let ((`(,args ,files) (magit-log-arguments)))
|
||||
(setq magit-buffer-log-args args)
|
||||
(unless (derived-mode-p 'magit-log-select-mode)
|
||||
(setq magit-buffer-log-files files)))
|
||||
(magit-refresh))))
|
||||
((not (eq transient-current-command 'magit-log-refresh))
|
||||
(pcase major-mode
|
||||
('magit-reflog-mode
|
||||
(user-error "Cannot change log arguments in reflog buffers"))
|
||||
('magit-cherry-mode
|
||||
(user-error "Cannot change log arguments in cherry buffers")))
|
||||
(transient-setup 'magit-log-refresh))
|
||||
(t
|
||||
(pcase-let ((`(,args ,files) (magit-log-arguments)))
|
||||
(setq magit-buffer-log-args args)
|
||||
(unless (derived-mode-p 'magit-log-select-mode)
|
||||
(setq magit-buffer-log-files files)))
|
||||
(magit-refresh))))
|
||||
|
||||
;;;; Infix Commands
|
||||
|
||||
@@ -696,26 +702,26 @@ When the upstream is a local branch, then also show its own
|
||||
upstream. When `HEAD' is detached, then show log for that, the
|
||||
previously checked out branch and its upstream and push-target."
|
||||
(interactive
|
||||
(cons (let ((current (magit-get-current-branch))
|
||||
head rebase target upstream upup)
|
||||
(unless current
|
||||
(setq rebase (magit-rebase--get-state-lines "head-name"))
|
||||
(cond (rebase
|
||||
(setq rebase (magit-ref-abbrev rebase))
|
||||
(setq current rebase)
|
||||
(setq head "HEAD"))
|
||||
((setq current (magit-get-previous-branch)))))
|
||||
(cond (current
|
||||
(setq current
|
||||
(magit--propertize-face current 'magit-branch-local))
|
||||
(setq target (magit-get-push-branch current t))
|
||||
(setq upstream (magit-get-upstream-branch current))
|
||||
(when upstream
|
||||
(setq upup (and (magit-local-branch-p upstream)
|
||||
(magit-get-upstream-branch upstream)))))
|
||||
((setq head "HEAD")))
|
||||
(delq nil (list current head target upstream upup)))
|
||||
(magit-log-arguments)))
|
||||
(cons (let ((current (magit-get-current-branch))
|
||||
head rebase target upstream upup)
|
||||
(unless current
|
||||
(setq rebase (magit-rebase--get-state-lines "head-name"))
|
||||
(cond (rebase
|
||||
(setq rebase (magit-ref-abbrev rebase))
|
||||
(setq current rebase)
|
||||
(setq head "HEAD"))
|
||||
((setq current (magit-get-previous-branch)))))
|
||||
(cond (current
|
||||
(setq current
|
||||
(magit--propertize-face current 'magit-branch-local))
|
||||
(setq target (magit-get-push-branch current t))
|
||||
(setq upstream (magit-get-upstream-branch current))
|
||||
(when upstream
|
||||
(setq upup (and (magit-local-branch-p upstream)
|
||||
(magit-get-upstream-branch upstream)))))
|
||||
((setq head "HEAD")))
|
||||
(delq nil (list current head target upstream upup)))
|
||||
(magit-log-arguments)))
|
||||
(magit-log-setup-buffer revs args files))
|
||||
|
||||
;;;###autoload
|
||||
@@ -779,7 +785,7 @@ restrict the log to the lines that the region touches."
|
||||
(require 'magit)
|
||||
(if-let ((file (magit-file-relative-name)))
|
||||
(magit-log-setup-buffer
|
||||
(list (or magit-buffer-refname
|
||||
(list (or magit-buffer-revision
|
||||
(magit-get-current-branch)
|
||||
"HEAD"))
|
||||
(let ((args (car (magit-log-arguments))))
|
||||
@@ -802,7 +808,7 @@ restrict the log to the lines that the region touches."
|
||||
(user-error "Buffer isn't visiting a file"))
|
||||
(or (funcall magit-log-trace-definition-function)
|
||||
(user-error "No function at point found"))
|
||||
(or magit-buffer-refname
|
||||
(or magit-buffer-revision
|
||||
(magit-get-current-branch)
|
||||
"HEAD")))
|
||||
(require 'magit)
|
||||
@@ -844,10 +850,10 @@ directly on BRANCH, then show approximately
|
||||
This command requires git-when-merged, which is available from
|
||||
https://github.com/mhagger/git-when-merged."
|
||||
(interactive
|
||||
(append (let ((commit (magit-read-branch-or-commit "Log merge of commit")))
|
||||
(list commit
|
||||
(magit-read-other-branch "Merged into" commit)))
|
||||
(magit-log-arguments)))
|
||||
(append (let ((commit (magit-read-branch-or-commit "Log merge of commit")))
|
||||
(list commit
|
||||
(magit-read-other-branch "Merged into" commit)))
|
||||
(magit-log-arguments)))
|
||||
(unless (magit-git-executable-find "git-when-merged")
|
||||
(user-error "This command requires git-when-merged (%s)"
|
||||
"https://github.com/mhagger/git-when-merged"))
|
||||
@@ -872,7 +878,7 @@ https://github.com/mhagger/git-when-merged."
|
||||
(to (if (<= to 0)
|
||||
branch
|
||||
(format "%s~%s" branch to))))
|
||||
(unless (magit-rev-verify-commit from)
|
||||
(unless (magit-commit-p from)
|
||||
(setq from (magit-git-string "rev-list" "--max-parents=0"
|
||||
commit)))
|
||||
(magit-log-setup-buffer (list (concat from ".." to))
|
||||
@@ -970,13 +976,13 @@ nothing else.
|
||||
If invoked outside any log buffer, then display the log buffer
|
||||
of the current repository first; creating it if necessary."
|
||||
(interactive
|
||||
(list (magit-completing-read
|
||||
"In log, jump to"
|
||||
(magit-list-refnames nil t)
|
||||
nil 'any nil 'magit-revision-history
|
||||
(or (and$ (magit-commit-at-point)
|
||||
(magit-rev-fixup-target $))
|
||||
(magit-get-current-branch)))))
|
||||
(list (magit-completing-read
|
||||
"In log, jump to"
|
||||
(magit-list-refnames nil t)
|
||||
nil 'any nil 'magit-revision-history
|
||||
(or (and$ (magit-commit-at-point)
|
||||
(magit-rev-fixup-target $))
|
||||
(magit-get-current-branch)))))
|
||||
(with-current-buffer
|
||||
(cond ((derived-mode-p 'magit-log-mode)
|
||||
(current-buffer))
|
||||
@@ -1020,16 +1026,16 @@ of the current repository first; creating it if necessary."
|
||||
(defun magit-shortlog-since (commit args)
|
||||
"Show a history summary for commits since REV."
|
||||
(interactive
|
||||
(list (magit-read-branch-or-commit "Shortlog since" (magit-get-current-tag))
|
||||
(transient-args 'magit-shortlog)))
|
||||
(list (magit-read-branch-or-commit "Shortlog since" (magit-get-current-tag))
|
||||
(transient-args 'magit-shortlog)))
|
||||
(magit-git-shortlog (concat commit "..") args))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-shortlog-range (rev-or-range args)
|
||||
"Show a history summary for commit or range REV-OR-RANGE."
|
||||
(interactive
|
||||
(list (magit-read-range-or-commit "Shortlog for revision or range")
|
||||
(transient-args 'magit-shortlog)))
|
||||
(list (magit-read-range-or-commit "Shortlog for revision or range")
|
||||
(transient-args 'magit-shortlog)))
|
||||
(magit-git-shortlog rev-or-range args))
|
||||
|
||||
;;;; Movement Commands
|
||||
@@ -1131,7 +1137,7 @@ Type \\[magit-reset] to reset `HEAD' to the commit at point.
|
||||
(require 'magit)
|
||||
(with-current-buffer
|
||||
(magit-setup-buffer #'magit-log-mode locked
|
||||
(magit-buffer-revisions revs)
|
||||
(magit-buffer-log-revisions revs)
|
||||
(magit-buffer-log-args args)
|
||||
(magit-buffer-log-files files))
|
||||
(when (if focus
|
||||
@@ -1141,7 +1147,7 @@ Type \\[magit-reset] to reset `HEAD' to the commit at point.
|
||||
(current-buffer)))
|
||||
|
||||
(defun magit-log-refresh-buffer ()
|
||||
(let ((revs magit-buffer-revisions)
|
||||
(let ((revs magit-buffer-log-revisions)
|
||||
(args magit-buffer-log-args)
|
||||
(files magit-buffer-log-files)
|
||||
(limit (magit-log-get-commit-limit)))
|
||||
@@ -1181,24 +1187,24 @@ Type \\[magit-reset] to reset `HEAD' to the commit at point.
|
||||
(defvar-local magit-log--color-graph nil)
|
||||
|
||||
(defun magit-log--maybe-drop-color-graph (args limit)
|
||||
(if (member "--color" args)
|
||||
(if (cond ((not (member "--graph" args)))
|
||||
((not magit-log-color-graph-limit) nil)
|
||||
((not limit)
|
||||
(message "Dropping --color because -n isn't set (see %s)"
|
||||
'magit-log-color-graph-limit))
|
||||
((> limit magit-log-color-graph-limit)
|
||||
(message "Dropping --color because -n is larger than %s"
|
||||
'magit-log-color-graph-limit)))
|
||||
(progn (setq args (remove "--color" args))
|
||||
(setq magit-log--color-graph nil))
|
||||
(setq magit-log--color-graph t))
|
||||
(setq magit-log--color-graph nil))
|
||||
(cond ((not (member "--color" args))
|
||||
(setq magit-log--color-graph nil))
|
||||
((cond ((not (member "--graph" args)) t)
|
||||
((not magit-log-color-graph-limit) nil)
|
||||
((not limit)
|
||||
(message "Dropping --color because -n isn't set (see %s)"
|
||||
'magit-log-color-graph-limit))
|
||||
((> limit magit-log-color-graph-limit)
|
||||
(message "Dropping --color because -n is larger than %s"
|
||||
'magit-log-color-graph-limit)))
|
||||
(setq args (remove "--color" args))
|
||||
(setq magit-log--color-graph nil))
|
||||
((setq magit-log--color-graph t)))
|
||||
args)
|
||||
|
||||
(cl-defmethod magit-buffer-value (&context (major-mode magit-log-mode))
|
||||
(append magit-buffer-revisions
|
||||
(if (and magit-buffer-revisions magit-buffer-log-files)
|
||||
(append magit-buffer-log-revisions
|
||||
(if (and magit-buffer-log-revisions magit-buffer-log-files)
|
||||
(cons "--" magit-buffer-log-files)
|
||||
magit-buffer-log-files)))
|
||||
|
||||
@@ -1257,17 +1263,17 @@ Do not add this to a hook variable."
|
||||
(setq args (remove "--show-signature" args))
|
||||
(let ((limit (magit-log-get-commit-limit args)))
|
||||
(cond
|
||||
((not limit)
|
||||
(message
|
||||
"Dropping --show-signature because -n isn't set (see %s)"
|
||||
'magit-log-show-signatures-limit)
|
||||
"")
|
||||
((> limit magit-log-show-signatures-limit)
|
||||
(message
|
||||
"Dropping --show-signature because -n is larger than %s"
|
||||
'magit-log-show-signatures-limit)
|
||||
"")
|
||||
("%G?"))))
|
||||
((not limit)
|
||||
(message
|
||||
"Dropping --show-signature because -n isn't set (see %s)"
|
||||
'magit-log-show-signatures-limit)
|
||||
"")
|
||||
((> limit magit-log-show-signatures-limit)
|
||||
(message
|
||||
"Dropping --show-signature because -n is larger than %s"
|
||||
'magit-log-show-signatures-limit)
|
||||
"")
|
||||
("%G?"))))
|
||||
(if magit-log-margin-show-committer-date "%ct" "%at")
|
||||
(if magit-log-trailer-labels
|
||||
(format "%%(trailers:%s%s)"
|
||||
@@ -1779,20 +1785,20 @@ Type \\[magit-log-select-quit] to abort without selecting a commit."
|
||||
|
||||
(defun magit-log-select-setup-buffer (revs args)
|
||||
(magit-setup-buffer #'magit-log-select-mode nil
|
||||
(magit-buffer-revisions revs)
|
||||
(magit-buffer-log-revisions revs)
|
||||
(magit-buffer-log-args args)))
|
||||
|
||||
(defun magit-log-select-refresh-buffer ()
|
||||
(setq magit-section-inhibit-markers t)
|
||||
(setq magit-section-insert-in-reverse t)
|
||||
(magit-insert-section (logbuf)
|
||||
(magit--insert-log t magit-buffer-revisions
|
||||
(magit--insert-log t magit-buffer-log-revisions
|
||||
(magit-log--maybe-drop-color-graph
|
||||
magit-buffer-log-args
|
||||
(magit-log-get-commit-limit)))))
|
||||
|
||||
(cl-defmethod magit-buffer-value (&context (major-mode magit-log-select-mode))
|
||||
magit-buffer-revisions)
|
||||
magit-buffer-log-revisions)
|
||||
|
||||
(defvar-local magit-log-select-pick-function nil)
|
||||
(defvar-local magit-log-select-quit-function nil)
|
||||
@@ -1881,11 +1887,14 @@ Type \\[magit-cherry-pick] to apply the commit at point.
|
||||
(magit-hack-dir-local-variables)
|
||||
(setq magit--imenu-group-types 'cherries))
|
||||
|
||||
(defvar-local magit-buffer-cherry-upstream nil)
|
||||
(defvar-local magit-buffer-cherry-range nil)
|
||||
|
||||
(defun magit-cherry-setup-buffer (head upstream)
|
||||
(magit-setup-buffer #'magit-cherry-mode nil
|
||||
(magit-buffer-refname head)
|
||||
(magit-buffer-upstream upstream)
|
||||
(magit-buffer-range (concat upstream ".." head))))
|
||||
(magit-buffer-cherry-upstream upstream)
|
||||
(magit-buffer-cherry-range (concat upstream ".." head))))
|
||||
|
||||
(defun magit-cherry-refresh-buffer ()
|
||||
(setq magit-section-insert-in-reverse t)
|
||||
@@ -1893,15 +1902,15 @@ Type \\[magit-cherry-pick] to apply the commit at point.
|
||||
(magit-run-section-hook 'magit-cherry-sections-hook)))
|
||||
|
||||
(cl-defmethod magit-buffer-value (&context (major-mode magit-cherry-mode))
|
||||
magit-buffer-range)
|
||||
magit-buffer-cherry-range)
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-cherry (head upstream)
|
||||
"Show commits in a branch that are not merged in the upstream branch."
|
||||
(interactive
|
||||
(let ((head (magit-read-branch "Cherry head")))
|
||||
(list head (magit-read-other-branch "Cherry upstream" head
|
||||
(magit-get-upstream-branch head)))))
|
||||
(let ((head (magit-read-branch "Cherry head")))
|
||||
(list head (magit-read-other-branch "Cherry upstream" head
|
||||
(magit-get-upstream-branch head)))))
|
||||
(require 'magit)
|
||||
(magit-cherry-setup-buffer head upstream))
|
||||
|
||||
@@ -1909,10 +1918,11 @@ Type \\[magit-cherry-pick] to apply the commit at point.
|
||||
"Insert headers appropriate for `magit-cherry-mode' buffers."
|
||||
(let ((branch (propertize magit-buffer-refname
|
||||
'font-lock-face 'magit-branch-local))
|
||||
(upstream (propertize magit-buffer-upstream 'font-lock-face
|
||||
(if (magit-local-branch-p magit-buffer-upstream)
|
||||
'magit-branch-local
|
||||
'magit-branch-remote))))
|
||||
(upstream (propertize
|
||||
magit-buffer-cherry-upstream 'font-lock-face
|
||||
(if (magit-local-branch-p magit-buffer-cherry-upstream)
|
||||
'magit-branch-local
|
||||
'magit-branch-remote))))
|
||||
(magit-insert-head-branch-header branch)
|
||||
(magit-insert-upstream-branch-header branch upstream "Upstream: ")
|
||||
(insert ?\n)))
|
||||
@@ -1923,7 +1933,7 @@ Type \\[magit-cherry-pick] to apply the commit at point.
|
||||
(magit-insert-heading t "Cherry commits")
|
||||
(magit-git-wash (apply-partially #'magit-log-wash-log 'cherry)
|
||||
"cherry" "-v" "--abbrev"
|
||||
magit-buffer-upstream
|
||||
magit-buffer-cherry-upstream
|
||||
magit-buffer-refname)))
|
||||
|
||||
;;; Log Sections
|
||||
@@ -2114,6 +2124,7 @@ all others with \"-\"."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-margin.el --- Margins in Magit buffers -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -263,6 +263,7 @@ English.")
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-merge.el --- Merge functionality -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -95,8 +95,10 @@ the user inspect the result. With a prefix argument pretend the
|
||||
merge failed to give the user the opportunity to inspect the
|
||||
merge.
|
||||
|
||||
To create an octopus-merge, separate branches with commas.
|
||||
|
||||
\(git merge --no-edit|--no-commit [ARGS] REV)"
|
||||
(interactive (list (magit-read-other-branch-or-commit "Merge")
|
||||
(interactive (list (magit-read-other-branches-or-commits "Merge")
|
||||
(magit-merge-arguments)
|
||||
current-prefix-arg))
|
||||
(magit-merge-assert)
|
||||
@@ -105,24 +107,30 @@ merge.
|
||||
;;;###autoload
|
||||
(defun magit-merge-editmsg (rev &optional args)
|
||||
"Merge commit REV into the current branch; and edit message.
|
||||
|
||||
Perform the merge and prepare a commit message but let the user
|
||||
edit it.
|
||||
\n(git merge --edit --no-ff [ARGS] REV)"
|
||||
(interactive (list (magit-read-other-branch-or-commit "Merge")
|
||||
|
||||
To create an octopus-merge, separate branches with commas.
|
||||
|
||||
\(git merge --edit --no-ff [ARGS] REV)"
|
||||
(interactive (list (magit-read-other-branches-or-commits "Merge")
|
||||
(magit-merge-arguments)))
|
||||
(magit-merge-assert)
|
||||
(cl-pushnew "--no-ff" args :test #'equal)
|
||||
(apply #'magit-run-git-with-editor "merge" "--edit"
|
||||
(append (delete "--ff-only" args)
|
||||
(list rev))))
|
||||
(magit-run-git-with-editor "merge" "--edit" (delete "--ff-only" args) rev))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-merge-nocommit (rev &optional args)
|
||||
"Merge commit REV into the current branch; pretending it failed.
|
||||
|
||||
Pretend the merge failed to give the user the opportunity to
|
||||
inspect the merge and change the commit message.
|
||||
\n(git merge --no-commit --no-ff [ARGS] REV)"
|
||||
(interactive (list (magit-read-other-branch-or-commit "Merge")
|
||||
|
||||
To create an octopus-merge, separate branches with commas.
|
||||
|
||||
\(git merge --no-commit --no-ff [ARGS] REV)"
|
||||
(interactive (list (magit-read-other-branches-or-commits "Merge")
|
||||
(magit-merge-arguments)))
|
||||
(magit-merge-assert)
|
||||
(cl-pushnew "--no-ff" args :test #'equal)
|
||||
@@ -139,12 +147,12 @@ obsolete version of the commits that are being merged. Finally
|
||||
if `forge-branch-pullreq' was used to create the merged branch,
|
||||
then also remove the respective remote branch."
|
||||
(interactive
|
||||
(list (let ((branch (magit-get-current-branch)))
|
||||
(magit-read-other-local-branch
|
||||
(format "Merge `%s' into" (or branch (magit-rev-parse "HEAD")))
|
||||
nil
|
||||
(and branch (magit-get-local-upstream-branch branch))))
|
||||
(magit-merge-arguments)))
|
||||
(list (let ((branch (magit-get-current-branch)))
|
||||
(magit-read-other-local-branch
|
||||
(format "Merge `%s' into" (or branch (magit-rev-parse "HEAD")))
|
||||
nil
|
||||
(and branch (magit-get-local-upstream-branch branch))))
|
||||
(magit-merge-arguments)))
|
||||
(let ((current (magit-get-current-branch))
|
||||
(head (magit-rev-parse "HEAD")))
|
||||
(when (zerop (magit-call-git "checkout" branch))
|
||||
@@ -240,15 +248,15 @@ then also remove the respective remote branch."
|
||||
(defun magit-checkout-stage (file arg)
|
||||
"During a conflict checkout and stage side, or restore conflict."
|
||||
(interactive
|
||||
(let ((file (magit-completing-read "Checkout file"
|
||||
(magit-tracked-files) nil 'any nil
|
||||
'magit-read-file-hist
|
||||
(magit-current-file))))
|
||||
(cond ((member file (magit-unmerged-files))
|
||||
(list file (magit-checkout-read-stage file)))
|
||||
((yes-or-no-p (format "Restore conflicts in %s? " file))
|
||||
(list file "--merge"))
|
||||
((user-error "Quit")))))
|
||||
(let ((file (magit-completing-read "Checkout file"
|
||||
(magit-tracked-files) nil 'any nil
|
||||
'magit-read-file-hist
|
||||
(magit-current-file))))
|
||||
(cond ((member file (magit-unmerged-files))
|
||||
(list file (magit-checkout-read-stage file)))
|
||||
((yes-or-no-p (format "Restore conflicts in %s? " file))
|
||||
(list file "--merge"))
|
||||
((user-error "Quit")))))
|
||||
(pcase (cons arg (cddr (car (magit-file-status file))))
|
||||
((or `("--ours" ?D ,_)
|
||||
'("--ours" ?U ?A)
|
||||
@@ -317,6 +325,7 @@ If no merge is in progress, do nothing."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-mode.el --- Create and refresh Magit buffers -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -51,6 +51,8 @@
|
||||
(declare-function magit-wip-get-ref "magit-wip" ())
|
||||
(declare-function magit-wip-commit-worktree "magit-wip" (ref files msg))
|
||||
|
||||
(declare-function magit--blob-cache-zap "magit-files" ())
|
||||
|
||||
;;; Options
|
||||
|
||||
(defcustom magit-mode-hook nil
|
||||
@@ -578,35 +580,41 @@ Magit is documented in info node `(magit)'."
|
||||
|
||||
;;; Local Variables
|
||||
|
||||
(defvar-local magit-buffer-arguments nil)
|
||||
(defvar-local magit-buffer-diff-type nil)
|
||||
(defvar-local magit-buffer-diff-args nil)
|
||||
(defvar-local magit-buffer-diff-files nil)
|
||||
(defvar-local magit-buffer-diff-files-suspended nil)
|
||||
(defvar-local magit-buffer-file-name nil)
|
||||
(defvar-local magit-buffer-files nil)
|
||||
(defvar-local magit-buffer-log-args nil)
|
||||
(defvar-local magit-buffer-log-files nil)
|
||||
(defvar-local magit-buffer-range nil)
|
||||
(defvar-local magit-buffer-range-hashed nil)
|
||||
(defvar-local magit-buffer-refname nil)
|
||||
(defvaralias 'magit-buffer-refname 'magit-buffer-revision)
|
||||
(defvar-local magit-buffer-revision nil)
|
||||
(defvar-local magit-buffer-revision-hash nil)
|
||||
(defvar-local magit-buffer-revisions nil)
|
||||
(defvar-local magit-buffer-typearg nil)
|
||||
(defvar-local magit-buffer-upstream nil)
|
||||
(defvar-local magit-buffer-revision-oid nil)
|
||||
(defvar-local magit-buffer-blob-oid nil)
|
||||
(defvar-local magit-buffer-file-name nil)
|
||||
|
||||
;; These variables are also used in file-visiting buffers.
|
||||
;; Because the user may change the major-mode, they have
|
||||
;; to be permanent buffer-local.
|
||||
(put 'magit-buffer-file-name 'permanent-local t)
|
||||
(put 'magit-buffer-refname 'permanent-local t)
|
||||
;; Preserve when major-mode is changed in file-visiting buffers.
|
||||
(put 'magit-buffer-revision 'permanent-local t)
|
||||
(put 'magit-buffer-revision-hash 'permanent-local t)
|
||||
(put 'magit-buffer-revision-oid 'permanent-local t)
|
||||
(put 'magit-buffer-blob-oid 'permanent-local t)
|
||||
(put 'magit-buffer-file-name 'permanent-local t)
|
||||
|
||||
;; `magit-status' re-enables mode function but its refresher
|
||||
;; function does not reinstate this.
|
||||
(put 'magit-buffer-diff-files-suspended 'permanent-local t)
|
||||
(eval-and-compile
|
||||
(defvar magit-define-aliases-for:magit-buffer-* t)
|
||||
(when magit-define-aliases-for:magit-buffer-*
|
||||
;; Unfortunately defvar-local can only be used at top-level,
|
||||
;; so instead we have to use make-variable-buffer-local below.
|
||||
(defvar magit-buffer-arguments nil)
|
||||
(make-obsolete-variable 'magit-buffer-arguments
|
||||
"use a mode- or package-specific `magit-buffer-{*}-args' instead"
|
||||
"magit 4.6.0")
|
||||
(defvar magit-buffer-upstream nil)
|
||||
(make-obsolete-variable 'magit-buffer-upstream
|
||||
"use a mode- or package-specific `magit-buffer-{*}-upstream' instead"
|
||||
"magit 4.6.0")
|
||||
(define-obsolete-variable-alias 'magit-buffer-range-hashed
|
||||
'magit-buffer-diff-range-oids "magit 4.6.0")
|
||||
(define-obsolete-variable-alias 'magit-buffer-revisions
|
||||
'magit-buffer-log-revisions "magit 4.6.0")
|
||||
(define-obsolete-variable-alias 'magit-buffer-revision-hash
|
||||
'magit-buffer-revision-oid "magit 4.6.0")
|
||||
(define-obsolete-variable-alias 'magit-buffer-typearg
|
||||
'magit-buffer-diff-typearg "magit 4.6.0")))
|
||||
(make-variable-buffer-local 'magit-buffer-arguments)
|
||||
(make-variable-buffer-local 'magit-buffer-upstream)
|
||||
|
||||
(defun magit-buffer-file-name ()
|
||||
"Return `magit-buffer-file-name' or if that is nil `buffer-file-name'.
|
||||
@@ -617,6 +625,7 @@ In an indirect buffer get the value for its base buffer."
|
||||
(defun magit-buffer-revision ()
|
||||
"Return `magit-buffer-revision' or if that is nil \"{worktree}\".
|
||||
If not visiting a blob or file, or the file isn't being tracked,
|
||||
return nil. If visiting a blob but `magit-buffer-revision' is nil,
|
||||
return nil."
|
||||
(or magit-buffer-revision
|
||||
(and buffer-file-name
|
||||
@@ -1098,21 +1107,21 @@ The arguments are for internal use."
|
||||
(when magit-refresh-verbose
|
||||
(message "%s buffer `%s'..." action (buffer-name)))
|
||||
(cond
|
||||
(created
|
||||
(funcall refresh)
|
||||
(cond (initial-section (funcall initial-section))
|
||||
(select-section (funcall select-section))))
|
||||
(t
|
||||
(deactivate-mark)
|
||||
(setq magit-section-pre-command-section nil)
|
||||
(setq magit-section-highlight-overlays nil)
|
||||
(setq magit-section-selection-overlays nil)
|
||||
(setq magit-section-highlighted-sections nil)
|
||||
(setq magit-section-focused-sections nil)
|
||||
(let ((positions (magit--refresh-buffer-get-positions)))
|
||||
(funcall refresh)
|
||||
(cond (select-section (funcall select-section))
|
||||
((magit--refresh-buffer-set-positions positions))))))
|
||||
(created
|
||||
(funcall refresh)
|
||||
(cond (initial-section (funcall initial-section))
|
||||
(select-section (funcall select-section))))
|
||||
(t
|
||||
(deactivate-mark)
|
||||
(setq magit-section-pre-command-section nil)
|
||||
(setq magit-section-highlight-overlays nil)
|
||||
(setq magit-section-selection-overlays nil)
|
||||
(setq magit-section-highlighted-sections nil)
|
||||
(setq magit-section-focused-sections nil)
|
||||
(let ((positions (magit--refresh-buffer-get-positions)))
|
||||
(funcall refresh)
|
||||
(cond (select-section (funcall select-section))
|
||||
((magit--refresh-buffer-set-positions positions))))))
|
||||
(let ((magit-section-cache-visibility nil))
|
||||
(magit-section-show magit-root-section))
|
||||
(run-hooks 'magit-refresh-buffer-hook)
|
||||
@@ -1179,7 +1188,21 @@ The arguments are for internal use."
|
||||
;; for the wrong buffer. Originally reported in #4196 and
|
||||
;; fixed with 482c25a3204468a4f6c2fe12ff061666b61f5f4d.
|
||||
(let ((magit-section-movement-hook nil))
|
||||
(magit-section-goto-successor section line char)))))
|
||||
(magit-section-goto-successor section line char)
|
||||
;; To store the point value for the selected window, it isn't
|
||||
;; enough for it to be current, the window has to "display" it.
|
||||
;; The effect of `goto-char', used by the above function, is not
|
||||
;; preserved, and using just `set-window-point' would affect the
|
||||
;; wrong buffer.
|
||||
(unless (eq (window-dedicated-p) t)
|
||||
(let ((restore (window-buffer))
|
||||
(window-scroll-functions nil)
|
||||
(window-configuration-change-hook nil))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(set-window-buffer nil (current-buffer) t)
|
||||
(set-window-point nil (point)))
|
||||
(set-window-buffer nil restore t))))))))
|
||||
|
||||
(defun magit-revert-buffer (_ignore-auto _noconfirm)
|
||||
"Wrapper around `magit-refresh-buffer' suitable as `revert-buffer-function'."
|
||||
@@ -1539,13 +1562,14 @@ repositories."
|
||||
"Zap caches for the current repository.
|
||||
|
||||
Remove the repository's entry from `magit-repository-local-cache',
|
||||
remove the host's entry from `magit--host-git-version-cache', and
|
||||
set `magit-section-visibility-cache' to nil for all Magit buffers
|
||||
of the repository.
|
||||
remove the host's entry from `magit--host-git-version-cache', set
|
||||
`magit-section-visibility-cache' to nil for all Magit buffers of
|
||||
the repository, and empty the `magit--blob-cache'.
|
||||
|
||||
With a prefix argument or if optional ALL is non-nil, discard the
|
||||
mentioned caches completely."
|
||||
(interactive)
|
||||
(magit--blob-cache-zap)
|
||||
(cond (all
|
||||
(setq magit-repository-local-cache nil)
|
||||
(setq magit--host-git-version-cache nil)
|
||||
@@ -1580,21 +1604,21 @@ The additional output can be found in the *Messages* buffer."
|
||||
|
||||
(defun magit-run-hook-with-benchmark (hook)
|
||||
(cond
|
||||
((not hook))
|
||||
(magit-refresh-verbose
|
||||
(message "Running %s..." hook)
|
||||
(message "Running %s...done (%.3fs)" hook
|
||||
(benchmark-elapse
|
||||
(run-hook-wrapped
|
||||
hook
|
||||
(lambda (fn)
|
||||
(message " %-50s %f" fn (benchmark-elapse (funcall fn))))))))
|
||||
((run-hooks hook))))
|
||||
((not hook))
|
||||
(magit-refresh-verbose
|
||||
(message "Running %s..." hook)
|
||||
(message "Running %s...done (%.3fs)" hook
|
||||
(benchmark-elapse
|
||||
(run-hook-wrapped
|
||||
hook
|
||||
(lambda (fn)
|
||||
(message " %-50s %f" fn (benchmark-elapse (funcall fn))))))))
|
||||
((run-hooks hook))))
|
||||
|
||||
(defun magit-file-region-line-numbers ()
|
||||
"Return the bounds of the region as line numbers.
|
||||
The returned value has the form (BEGINNING-LINE END-LINE). If
|
||||
the region end at the beginning of a line, do not include that
|
||||
the region ends at the beginning of a line, do not include that
|
||||
line. Avoid including the line after the end of the file."
|
||||
(and (magit-buffer-file-name)
|
||||
(region-active-p)
|
||||
@@ -1615,6 +1639,7 @@ line. Avoid including the line after the end of the file."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-notes.el --- Notes support -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -204,6 +204,7 @@ Also see `magit-notes-merge'."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-patch.el --- Creating and applying patches -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -99,18 +99,18 @@ which creates patches for all commits that are reachable from
|
||||
["Actions"
|
||||
("c" "Create patches" magit-patch-create)]
|
||||
(interactive
|
||||
(if (not (eq transient-current-command 'magit-patch-create))
|
||||
(list nil nil nil)
|
||||
(cons (if-let ((revs (magit-region-values 'commit t)))
|
||||
(concat (car (last revs)) "^.." (car revs))
|
||||
(let ((range (magit-read-range-or-commit
|
||||
"Create patches for range or commit")))
|
||||
(if (string-search ".." range)
|
||||
range
|
||||
(format "%s~..%s" range range))))
|
||||
(let ((args (transient-args 'magit-patch-create)))
|
||||
(list (seq-filter #'stringp args)
|
||||
(cdr (assoc "--" args)))))))
|
||||
(if (not (eq transient-current-command 'magit-patch-create))
|
||||
(list nil nil nil)
|
||||
(cons (if-let ((revs (magit-region-values 'commit t)))
|
||||
(concat (car (last revs)) "^.." (car revs))
|
||||
(let ((range (magit-read-range-or-commit
|
||||
"Create patches for range or commit")))
|
||||
(if (string-search ".." range)
|
||||
range
|
||||
(format "%s~..%s" range range))))
|
||||
(let ((args (transient-args 'magit-patch-create)))
|
||||
(list (seq-filter #'stringp args)
|
||||
(cdr (assoc "--" args)))))))
|
||||
(if (not range)
|
||||
(transient-setup 'magit-patch-create)
|
||||
(magit-run-git "format-patch" range args "--" files)
|
||||
@@ -247,14 +247,14 @@ which creates patches for all commits that are reachable from
|
||||
["Actions"
|
||||
("a" "Apply patch" magit-patch-apply)]
|
||||
(interactive
|
||||
(if (not (eq transient-current-command 'magit-patch-apply))
|
||||
(list nil)
|
||||
(list (expand-file-name
|
||||
(read-file-name "Apply patch: "
|
||||
default-directory nil nil
|
||||
(and$ (magit-file-at-point)
|
||||
(file-relative-name $))))
|
||||
(transient-args 'magit-patch-apply))))
|
||||
(if (not (eq transient-current-command 'magit-patch-apply))
|
||||
(list nil)
|
||||
(list (expand-file-name
|
||||
(read-file-name "Apply patch: "
|
||||
default-directory nil nil
|
||||
(and$ (magit-file-at-point)
|
||||
(file-relative-name $))))
|
||||
(transient-args 'magit-patch-apply))))
|
||||
(if (not file)
|
||||
(transient-setup 'magit-patch-apply)
|
||||
(magit-run-git "apply" args "--" (magit-convert-filename-for-git file))))
|
||||
@@ -286,8 +286,8 @@ same differences as those shown in the buffer are always used."
|
||||
current-prefix-arg))
|
||||
(unless (derived-mode-p 'magit-diff-mode)
|
||||
(user-error "Only diff buffers can be saved as patches"))
|
||||
(let ((rev magit-buffer-range)
|
||||
(typearg magit-buffer-typearg)
|
||||
(let ((rev magit-buffer-diff-range)
|
||||
(typearg magit-buffer-diff-typearg)
|
||||
(args magit-buffer-diff-args)
|
||||
(files magit-buffer-diff-files))
|
||||
(cond ((eq magit-patch-save-arguments 'buffer)
|
||||
@@ -313,9 +313,9 @@ START is a commit that already is in the upstream repository.
|
||||
END is the last commit, usually a branch name, which upstream
|
||||
is asked to pull. START has to be reachable from that commit."
|
||||
(interactive
|
||||
(list (magit-get "remote" (magit-read-remote "Remote") "url")
|
||||
(magit-read-branch-or-commit "Start" (magit-get-upstream-branch))
|
||||
(magit-read-branch-or-commit "End")))
|
||||
(list (magit-get "remote" (magit-read-remote "Remote") "url")
|
||||
(magit-read-branch-or-commit "Start" (magit-get-upstream-branch))
|
||||
(magit-read-branch-or-commit "End")))
|
||||
(require 'message)
|
||||
(let ((dir default-directory))
|
||||
;; mu4e changes default-directory
|
||||
@@ -333,6 +333,7 @@ is asked to pull. START has to be reachable from that commit."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
;; -*- no-byte-compile: t; lexical-binding: nil -*-
|
||||
(define-package "magit" "20251217.1836"
|
||||
(define-package "magit" "20260401.2251"
|
||||
"A Git porcelain inside Emacs."
|
||||
'((emacs "28.1")
|
||||
(compat "30.1")
|
||||
(cond-let "0.1")
|
||||
(cond-let "0.2")
|
||||
(llama "1.0")
|
||||
(magit-section "4.4")
|
||||
(magit-section "4.5")
|
||||
(seq "2.24")
|
||||
(transient "0.10")
|
||||
(transient "0.12")
|
||||
(with-editor "3.4"))
|
||||
:url "https://github.com/magit/magit"
|
||||
:commit "655bc502a3bdd7f07928524515a736e4b8101eaf"
|
||||
:revdesc "655bc502a3bd"
|
||||
:commit "6db34dc77d10fc9b8c925e79b4e0e21d9f78ac5c"
|
||||
:revdesc "6db34dc77d10"
|
||||
:keywords '("git" "tools" "vc")
|
||||
:authors '(("Marius Vollmer" . "marius.vollmer@gmail.com")
|
||||
("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev"))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-process.el --- Process functionality -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -572,25 +572,26 @@ flattened before use."
|
||||
;; On w32, git expects UTF-8 encoded input, ignore any user
|
||||
;; configuration telling us otherwise (see #3250).
|
||||
(encode-coding-region (point-min) (point-max) 'utf-8-unix))
|
||||
(if (file-remote-p default-directory)
|
||||
;; We lack `process-file-region', so fall back to asynch +
|
||||
;; waiting in remote case.
|
||||
(progn
|
||||
(magit-start-git (current-buffer) args)
|
||||
(while (and magit-this-process
|
||||
(eq (process-status magit-this-process) 'run))
|
||||
(sleep-for 0.005)))
|
||||
(run-hooks 'magit-pre-call-git-hook)
|
||||
(pcase-let* ((process-environment (magit-process-environment))
|
||||
(default-process-coding-system (magit--process-coding-system))
|
||||
(flat-args (magit-process-git-arguments args t))
|
||||
(`(,process-buf . ,section)
|
||||
(magit-process-setup (magit-git-executable) flat-args))
|
||||
(inhibit-read-only t))
|
||||
(magit-process-finish
|
||||
(apply #'call-process-region (point-min) (point-max)
|
||||
(magit-git-executable) nil process-buf nil flat-args)
|
||||
process-buf nil default-directory section))))
|
||||
(cond
|
||||
((file-remote-p default-directory)
|
||||
;; We lack `process-file-region', so fall back to asynch +
|
||||
;; waiting in remote case.
|
||||
(magit-start-git (current-buffer) args)
|
||||
(while (and magit-this-process
|
||||
(eq (process-status magit-this-process) 'run))
|
||||
(sleep-for 0.005)))
|
||||
(t
|
||||
(run-hooks 'magit-pre-call-git-hook)
|
||||
(pcase-let* ((process-environment (magit-process-environment))
|
||||
(default-process-coding-system (magit--process-coding-system))
|
||||
(flat-args (magit-process-git-arguments args t))
|
||||
(`(,process-buf . ,section)
|
||||
(magit-process-setup (magit-git-executable) flat-args))
|
||||
(inhibit-read-only t))
|
||||
(magit-process-finish
|
||||
(apply #'call-process-region (point-min) (point-max)
|
||||
(magit-git-executable) nil process-buf nil flat-args)
|
||||
process-buf nil default-directory section)))))
|
||||
|
||||
;;; Asynchronous Processes
|
||||
|
||||
@@ -805,26 +806,26 @@ Magit status buffer."
|
||||
|
||||
(defun magit-process--format-arguments (program args)
|
||||
(cond
|
||||
((and args (equal program (magit-git-executable)))
|
||||
(let ((global (magit-process-git-arguments--length)))
|
||||
(concat
|
||||
(propertize (file-name-nondirectory program)
|
||||
'font-lock-face 'magit-section-heading)
|
||||
" "
|
||||
(propertize (magit--ellipsis)
|
||||
'font-lock-face 'magit-section-heading
|
||||
'help-echo (string-join (seq-take args global) " "))
|
||||
" "
|
||||
(propertize (mapconcat #'shell-quote-argument (seq-drop args global) " ")
|
||||
'font-lock-face 'magit-section-heading))))
|
||||
((and args (equal program shell-file-name))
|
||||
(propertize (cadr args)
|
||||
'font-lock-face 'magit-section-heading))
|
||||
((concat (propertize (file-name-nondirectory program)
|
||||
'font-lock-face 'magit-section-heading)
|
||||
" "
|
||||
(propertize (mapconcat #'shell-quote-argument args " ")
|
||||
'font-lock-face 'magit-section-heading)))))
|
||||
((and args (equal program (magit-git-executable)))
|
||||
(let ((global (magit-process-git-arguments--length)))
|
||||
(concat
|
||||
(propertize (file-name-nondirectory program)
|
||||
'font-lock-face 'magit-section-heading)
|
||||
" "
|
||||
(propertize (magit--ellipsis)
|
||||
'font-lock-face 'magit-section-heading
|
||||
'help-echo (string-join (seq-take args global) " "))
|
||||
" "
|
||||
(propertize (mapconcat #'shell-quote-argument (seq-drop args global) " ")
|
||||
'font-lock-face 'magit-section-heading))))
|
||||
((and args (equal program shell-file-name))
|
||||
(propertize (cadr args)
|
||||
'font-lock-face 'magit-section-heading))
|
||||
((concat (propertize (file-name-nondirectory program)
|
||||
'font-lock-face 'magit-section-heading)
|
||||
" "
|
||||
(propertize (mapconcat #'shell-quote-argument args " ")
|
||||
'font-lock-face 'magit-section-heading)))))
|
||||
|
||||
(defun magit-process-truncate-log ()
|
||||
(let* ((head nil)
|
||||
@@ -1358,6 +1359,7 @@ Limited by `magit-process-error-tooltip-max-lines'."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-pull.el --- Update local objects and refs -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -138,15 +138,15 @@ the upstream."
|
||||
(merge (magit-get "branch" branch "merge"))
|
||||
(u (magit--propertize-face "@{upstream}" 'bold)))
|
||||
(cond
|
||||
((magit--unnamed-upstream-p remote merge)
|
||||
(format "%s of %s"
|
||||
(magit--propertize-face merge 'magit-branch-remote)
|
||||
(magit--propertize-face remote 'bold)))
|
||||
((magit--valid-upstream-p remote merge)
|
||||
(concat u ", replacing non-existent"))
|
||||
((or remote merge)
|
||||
(concat u ", replacing invalid"))
|
||||
((concat u ", setting that")))))))
|
||||
((magit--unnamed-upstream-p remote merge)
|
||||
(format "%s of %s"
|
||||
(magit--propertize-face merge 'magit-branch-remote)
|
||||
(magit--propertize-face remote 'bold)))
|
||||
((magit--valid-upstream-p remote merge)
|
||||
(concat u ", replacing non-existent"))
|
||||
((or remote merge)
|
||||
(concat u ", replacing invalid"))
|
||||
((concat u ", setting that")))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-pull-branch (source args)
|
||||
@@ -166,6 +166,7 @@ the upstream."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-push.el --- Update remote objects and refs -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -100,14 +100,14 @@ argument the push-remote can be changed before pushed to it."
|
||||
(remote (magit-get-push-remote branch))
|
||||
(v (magit--push-remote-variable branch t)))
|
||||
(cond
|
||||
(target)
|
||||
((member remote (magit-list-remotes))
|
||||
(format "%s, creating it"
|
||||
(magit--propertize-face (concat remote "/" branch)
|
||||
'magit-branch-remote)))
|
||||
(remote
|
||||
(format "%s, replacing invalid" v))
|
||||
((format "%s, setting that" v)))))
|
||||
(target)
|
||||
((member remote (magit-list-remotes))
|
||||
(format "%s, creating it"
|
||||
(magit--propertize-face (concat remote "/" branch)
|
||||
'magit-branch-remote)))
|
||||
(remote
|
||||
(format "%s, replacing invalid" v))
|
||||
((format "%s, setting that" v)))))
|
||||
|
||||
;;;###autoload(autoload 'magit-push-current-to-upstream "magit-push" nil t)
|
||||
(transient-define-suffix magit-push-current-to-upstream (args)
|
||||
@@ -160,27 +160,27 @@ the upstream."
|
||||
(merge (magit-get "branch" branch "merge"))
|
||||
(u (magit--propertize-face "@{upstream}" 'bold)))
|
||||
(cond
|
||||
((magit--unnamed-upstream-p remote merge)
|
||||
(format "%s as %s"
|
||||
(magit--propertize-face remote 'bold)
|
||||
(magit--propertize-face merge 'magit-branch-remote)))
|
||||
((magit--valid-upstream-p remote merge)
|
||||
(format "%s creating %s"
|
||||
(magit--propertize-face remote 'magit-branch-remote)
|
||||
(magit--propertize-face merge 'magit-branch-remote)))
|
||||
((or remote merge)
|
||||
(concat u ", creating it and replacing invalid"))
|
||||
((concat u ", creating it")))))))
|
||||
((magit--unnamed-upstream-p remote merge)
|
||||
(format "%s as %s"
|
||||
(magit--propertize-face remote 'bold)
|
||||
(magit--propertize-face merge 'magit-branch-remote)))
|
||||
((magit--valid-upstream-p remote merge)
|
||||
(format "%s creating %s"
|
||||
(magit--propertize-face remote 'magit-branch-remote)
|
||||
(magit--propertize-face merge 'magit-branch-remote)))
|
||||
((or remote merge)
|
||||
(concat u ", creating it and replacing invalid"))
|
||||
((concat u ", creating it")))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-push-current (target args)
|
||||
"Push the current branch to a branch read in the minibuffer."
|
||||
(interactive
|
||||
(if-let ((current (magit-get-current-branch)))
|
||||
(list (magit-read-remote-branch (format "Push %s to" current)
|
||||
nil nil current 'confirm)
|
||||
(magit-push-arguments))
|
||||
(user-error "No branch is checked out")))
|
||||
(if-let ((current (magit-get-current-branch)))
|
||||
(list (magit-read-remote-branch (format "Push %s to" current)
|
||||
nil nil current 'confirm)
|
||||
(magit-push-arguments))
|
||||
(user-error "No branch is checked out")))
|
||||
(magit-git-push (magit-get-current-branch) target args))
|
||||
|
||||
;;;###autoload
|
||||
@@ -188,18 +188,18 @@ the upstream."
|
||||
"Push an arbitrary branch or commit somewhere.
|
||||
Both the source and the target are read in the minibuffer."
|
||||
(interactive
|
||||
(let ((source (magit-read-local-branch-or-commit "Push")))
|
||||
(list source
|
||||
(magit-read-remote-branch
|
||||
(format "Push %s to" source) nil
|
||||
(cond ((magit-local-branch-p source)
|
||||
(or (magit-get-push-branch source)
|
||||
(magit-get-upstream-branch source)))
|
||||
((magit-rev-ancestor-p source "HEAD")
|
||||
(or (magit-get-push-branch)
|
||||
(magit-get-upstream-branch))))
|
||||
source 'confirm)
|
||||
(magit-push-arguments))))
|
||||
(let ((source (magit-read-local-branch-or-commit "Push")))
|
||||
(list source
|
||||
(magit-read-remote-branch
|
||||
(format "Push %s to" source) nil
|
||||
(cond ((magit-local-branch-p source)
|
||||
(or (magit-get-push-branch source)
|
||||
(magit-get-upstream-branch source)))
|
||||
((magit-rev-ancestor-p source "HEAD")
|
||||
(or (magit-get-push-branch)
|
||||
(magit-get-upstream-branch))))
|
||||
source 'confirm)
|
||||
(magit-push-arguments))))
|
||||
(magit-git-push source target args))
|
||||
|
||||
(defvar magit-push-refspecs-history nil)
|
||||
@@ -212,12 +212,12 @@ use multiple REFSPECS, separate them with commas. Completion is
|
||||
only available for the part before the colon, or when no colon
|
||||
is used."
|
||||
(interactive
|
||||
(list (magit-read-remote "Push to remote")
|
||||
(magit-completing-read-multiple
|
||||
"Push refspec,s: "
|
||||
(cons "HEAD" (magit-list-local-branch-names))
|
||||
nil 'any nil 'magit-push-refspecs-history)
|
||||
(magit-push-arguments)))
|
||||
(list (magit-read-remote "Push to remote")
|
||||
(magit-completing-read-multiple
|
||||
"Push refspec,s: "
|
||||
(cons "HEAD" (magit-list-local-branch-names))
|
||||
nil 'any nil 'magit-push-refspecs-history)
|
||||
(magit-push-arguments)))
|
||||
(run-hooks 'magit-credential-hook)
|
||||
(magit-run-git-async "push" "-v" args remote refspecs))
|
||||
|
||||
@@ -246,9 +246,9 @@ branch as default."
|
||||
(defun magit-push-tag (tag remote &optional args)
|
||||
"Push a tag to another repository."
|
||||
(interactive
|
||||
(let ((tag (magit-read-tag "Push tag")))
|
||||
(list tag (magit-read-remote (format "Push %s to remote" tag) nil t)
|
||||
(magit-push-arguments))))
|
||||
(let ((tag (magit-read-tag "Push tag")))
|
||||
(list tag (magit-read-remote (format "Push %s to remote" tag) nil t)
|
||||
(magit-push-arguments))))
|
||||
(run-hooks 'magit-credential-hook)
|
||||
(magit-run-git-async "push" remote tag args))
|
||||
|
||||
@@ -256,10 +256,10 @@ branch as default."
|
||||
(defun magit-push-notes-ref (ref remote &optional args)
|
||||
"Push a notes ref to another repository."
|
||||
(interactive
|
||||
(let ((note (magit-notes-read-ref "Push notes")))
|
||||
(list note
|
||||
(magit-read-remote (format "Push %s to remote" note) nil t)
|
||||
(magit-push-arguments))))
|
||||
(let ((note (magit-notes-read-ref "Push notes")))
|
||||
(list note
|
||||
(magit-read-remote (format "Push %s to remote" note) nil t)
|
||||
(magit-push-arguments))))
|
||||
(run-hooks 'magit-credential-hook)
|
||||
(magit-run-git-async "push" remote ref args))
|
||||
|
||||
@@ -300,12 +300,12 @@ what this command will do. To add it use something like:
|
||||
;; Note: Avoid `magit-get-remote' because it
|
||||
;; filters out the local repo case (".").
|
||||
(magit-get "branch" branch "remote")
|
||||
(cond-let
|
||||
[[remotes (magit-list-remotes)]]
|
||||
((and (magit-git-version>= "2.27")
|
||||
(length= remotes 1))
|
||||
(car remotes))
|
||||
((car (member "origin" remotes)))))))
|
||||
(cond-let
|
||||
[[remotes (magit-list-remotes)]]
|
||||
((and (magit-git-version>= "2.27")
|
||||
(length= remotes 1))
|
||||
(car remotes))
|
||||
((car (member "origin" remotes)))))))
|
||||
(if (null remote)
|
||||
"nothing (no remote)"
|
||||
(let ((refspec (magit-get "remote" remote "push")))
|
||||
@@ -326,17 +326,17 @@ what this command will do. To add it use something like:
|
||||
(format "%s to %s"
|
||||
(magit--propertize-face branch 'magit-branch-current)
|
||||
(cond
|
||||
((string-prefix-p "refs/heads/" ref)
|
||||
(magit--propertize-face
|
||||
(format "%s/%s" remote
|
||||
(substring ref (length "refs/heads/")))
|
||||
'magit-branch-remote))
|
||||
((not (string-match "/" ref))
|
||||
(magit--propertize-face (format "%s/%s" remote ref)
|
||||
'magit-branch-remote))
|
||||
((format "%s as %s"
|
||||
(magit--propertize-face remote 'bold)
|
||||
(magit--propertize-face ref 'bold)))))
|
||||
((string-prefix-p "refs/heads/" ref)
|
||||
(magit--propertize-face
|
||||
(format "%s/%s" remote
|
||||
(substring ref (length "refs/heads/")))
|
||||
'magit-branch-remote))
|
||||
((not (string-match "/" ref))
|
||||
(magit--propertize-face (format "%s/%s" remote ref)
|
||||
'magit-branch-remote))
|
||||
((format "%s as %s"
|
||||
(magit--propertize-face remote 'bold)
|
||||
(magit--propertize-face ref 'bold)))))
|
||||
"nothing (no upstream)")))
|
||||
("matching" (format "all matching to %s"
|
||||
(magit--propertize-face remote 'bold)))))))))
|
||||
@@ -374,6 +374,7 @@ You can add this command as a suffix using something like:
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-reflog.el --- Inspect ref history -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -211,6 +211,7 @@ Type \\[magit-reset] to reset `HEAD' to the commit at point.
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-refs.el --- Listing references -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -308,24 +308,27 @@ Type \\[magit-reset] to reset `HEAD' to the commit at point.
|
||||
(magit-hack-dir-local-variables)
|
||||
(setq magit--imenu-group-types '(local remote tags)))
|
||||
|
||||
(defvar-local magit-buffer-refs-args nil)
|
||||
(defvar-local magit-buffer-refs-upstream nil)
|
||||
|
||||
(defun magit-refs-setup-buffer (ref args)
|
||||
(magit-setup-buffer #'magit-refs-mode nil
|
||||
(magit-buffer-upstream ref)
|
||||
(magit-buffer-arguments args)))
|
||||
(magit-buffer-refs-upstream ref)
|
||||
(magit-buffer-refs-args args)))
|
||||
|
||||
(defun magit-refs-refresh-buffer ()
|
||||
(setq magit--right-margin-delayed (not (magit--right-margin-active)))
|
||||
(unless (magit-rev-verify magit-buffer-upstream)
|
||||
(unless (magit-rev-verify magit-buffer-refs-upstream)
|
||||
(setq magit-refs-show-commit-count nil))
|
||||
(magit-set-header-line-format
|
||||
(format "%s %s" magit-buffer-upstream
|
||||
(string-join magit-buffer-arguments " ")))
|
||||
(format "%s %s" magit-buffer-refs-upstream
|
||||
(string-join magit-buffer-refs-args " ")))
|
||||
(magit-insert-section (branchbuf)
|
||||
(magit-run-section-hook 'magit-refs-sections-hook))
|
||||
(add-hook 'kill-buffer-hook #'magit-preserve-section-visibility-cache))
|
||||
|
||||
(cl-defmethod magit-buffer-value (&context (major-mode magit-refs-mode))
|
||||
(cons magit-buffer-upstream magit-buffer-arguments))
|
||||
(cons magit-buffer-refs-upstream magit-buffer-refs-args))
|
||||
|
||||
;;; Commands
|
||||
|
||||
@@ -360,11 +363,11 @@ Type \\[magit-reset] to reset `HEAD' to the commit at point.
|
||||
((eq transient-current-command 'magit-show-refs)
|
||||
(transient-args 'magit-show-refs))
|
||||
((eq major-mode 'magit-refs-mode)
|
||||
magit-buffer-arguments)
|
||||
magit-buffer-refs-args)
|
||||
([_(memq use-buffer-args '(always selected))]
|
||||
[buffer (magit-get-mode-buffer 'magit-refs-mode nil
|
||||
(eq use-buffer-args 'selected))]
|
||||
(buffer-local-value 'magit-buffer-arguments buffer))
|
||||
(buffer-local-value 'magit-buffer-refs-args buffer))
|
||||
((alist-get 'magit-show-refs transient-values))))
|
||||
|
||||
(transient-define-argument magit-for-each-ref:--contains ()
|
||||
@@ -468,13 +471,13 @@ Branch %s already exists.
|
||||
(?r (magit-call-git "checkout" "-B" branch ref))
|
||||
(?a (user-error "Abort"))))
|
||||
(magit-call-git "checkout" "-b" branch ref))
|
||||
(setq magit-buffer-upstream branch)
|
||||
(setq magit-buffer-refs-upstream branch)
|
||||
(magit-refresh)))
|
||||
((or (memq 'checkout-any magit-visit-ref-behavior)
|
||||
(and (memq 'checkout-branch magit-visit-ref-behavior)
|
||||
(magit-section-match [branch local])))
|
||||
(magit-call-git "checkout" ref)
|
||||
(setq magit-buffer-upstream ref)
|
||||
(setq magit-buffer-refs-upstream ref)
|
||||
(magit-refresh))
|
||||
((call-interactively #'magit-show-commit))))
|
||||
|
||||
@@ -540,7 +543,7 @@ line is inserted at all."
|
||||
|
||||
(defun magit-insert-tags ()
|
||||
"Insert sections showing all tags."
|
||||
(when-let ((tags (magit-git-lines "tag" "--list" "-n" magit-buffer-arguments)))
|
||||
(when-let ((tags (magit-git-lines "tag" "--list" "-n" magit-buffer-refs-args)))
|
||||
(let ((_head (magit-rev-parse "HEAD")))
|
||||
(magit-insert-section (tags)
|
||||
(magit-insert-heading (length tags) "Tags")
|
||||
@@ -581,42 +584,42 @@ line is inserted at all."
|
||||
(dolist (line (magit-git-lines "for-each-ref" "--format=\
|
||||
%(symref:short)%00%(refname:short)%00%(refname)%00%(subject)"
|
||||
(concat "refs/remotes/" remote)
|
||||
magit-buffer-arguments))
|
||||
magit-buffer-refs-args))
|
||||
(pcase-let ((`(,head-branch ,branch ,ref ,msg)
|
||||
(cl-substitute nil ""
|
||||
(split-string line "\0")
|
||||
:test #'equal)))
|
||||
(cond
|
||||
(head-branch
|
||||
;; Note: Use `ref' instead of `branch' for the check
|
||||
;; below because 'refname:short' shortens the remote
|
||||
;; HEAD to '<remote>' instead of '<remote>/HEAD' as of
|
||||
;; Git v2.40.0.
|
||||
(cl-assert
|
||||
(equal ref (concat "refs/remotes/" remote "/HEAD")))
|
||||
(setq head head-branch))
|
||||
((not (equal ref (concat "refs/remotes/" remote "/HEAD")))
|
||||
;; ^ Skip mis-configured remotes where HEAD is not a
|
||||
;; symref. See #5092.
|
||||
(when (magit-refs--insert-refname-p branch)
|
||||
(magit-insert-section (branch branch t)
|
||||
(let ((headp (equal branch head))
|
||||
(abbrev (if magit-refs-show-remote-prefix
|
||||
branch
|
||||
(substring branch (1+ (length remote))))))
|
||||
(magit-insert-heading
|
||||
(magit-refs--format-focus-column branch)
|
||||
(magit-refs--propertize-branch
|
||||
abbrev ref (and headp 'magit-branch-remote-head))
|
||||
(make-string
|
||||
(max 1 (- (if (consp magit-refs-primary-column-width)
|
||||
(car magit-refs-primary-column-width)
|
||||
magit-refs-primary-column-width)
|
||||
(length abbrev)))
|
||||
?\s)
|
||||
(and msg (magit-log--wash-summary msg))))
|
||||
(magit-refs--maybe-format-margin branch)
|
||||
(magit-refs--insert-cherry-commits branch))))))))
|
||||
(head-branch
|
||||
;; Note: Use `ref' instead of `branch' for the check
|
||||
;; below because 'refname:short' shortens the remote
|
||||
;; HEAD to '<remote>' instead of '<remote>/HEAD' as of
|
||||
;; Git v2.40.0.
|
||||
(cl-assert
|
||||
(equal ref (concat "refs/remotes/" remote "/HEAD")))
|
||||
(setq head head-branch))
|
||||
((not (equal ref (concat "refs/remotes/" remote "/HEAD")))
|
||||
;; ^ Skip mis-configured remotes where HEAD is not a
|
||||
;; symref. See #5092.
|
||||
(when (magit-refs--insert-refname-p branch)
|
||||
(magit-insert-section (branch branch t)
|
||||
(let ((headp (equal branch head))
|
||||
(abbrev (if magit-refs-show-remote-prefix
|
||||
branch
|
||||
(substring branch (1+ (length remote))))))
|
||||
(magit-insert-heading
|
||||
(magit-refs--format-focus-column branch)
|
||||
(magit-refs--propertize-branch
|
||||
abbrev ref (and headp 'magit-branch-remote-head))
|
||||
(make-string
|
||||
(max 1 (- (if (consp magit-refs-primary-column-width)
|
||||
(car magit-refs-primary-column-width)
|
||||
magit-refs-primary-column-width)
|
||||
(length abbrev)))
|
||||
?\s)
|
||||
(and msg (magit-log--wash-summary msg))))
|
||||
(magit-refs--maybe-format-margin branch)
|
||||
(magit-refs--insert-cherry-commits branch))))))))
|
||||
(insert ?\n)
|
||||
(magit-make-margin-overlay))))
|
||||
|
||||
@@ -661,7 +664,7 @@ line is inserted at all."
|
||||
%(push:remotename)%00%(push)%00%(push:track)%00%(subject)"
|
||||
"%00%00%00%(subject)"))
|
||||
"refs/heads"
|
||||
magit-buffer-arguments))))
|
||||
magit-buffer-refs-args))))
|
||||
(unless (magit-get-current-branch)
|
||||
(push (magit-refs--format-local-branch
|
||||
(concat "*\0\0\0\0\0\0\0\0" (magit-rev-format "%s")))
|
||||
@@ -750,7 +753,7 @@ line is inserted at all."
|
||||
(and msg (magit-log--wash-summary msg))))))))
|
||||
|
||||
(defun magit-refs--format-focus-column (ref &optional type)
|
||||
(let ((focus magit-buffer-upstream)
|
||||
(let ((focus magit-buffer-refs-upstream)
|
||||
(width (if magit-refs-show-commit-count
|
||||
magit-refs-focus-column-width
|
||||
1)))
|
||||
@@ -766,7 +769,7 @@ line is inserted at all."
|
||||
(eq magit-refs-show-commit-count 'all)
|
||||
magit-refs-show-commit-count)
|
||||
(pcase-let ((`(,behind ,ahead)
|
||||
(magit-rev-diff-count magit-buffer-upstream ref)))
|
||||
(magit-rev-diff-count magit-buffer-refs-upstream ref)))
|
||||
(magit--propertize-face
|
||||
(cond ((> ahead 0) (concat "<" (number-to-string ahead)))
|
||||
((> behind 0) (concat (number-to-string behind) ">"))
|
||||
@@ -795,7 +798,7 @@ line is inserted at all."
|
||||
(let ((start (point))
|
||||
(magit-insert-section--current nil))
|
||||
(magit-git-wash (apply-partially #'magit-log-wash-log 'cherry)
|
||||
"cherry" "-v" (magit-abbrev-arg) magit-buffer-upstream ref)
|
||||
"cherry" "-v" (magit-abbrev-arg) magit-buffer-refs-upstream ref)
|
||||
(if (= (point) start)
|
||||
(message "No cherries for %s" ref)
|
||||
(magit-make-margin-overlay)))))
|
||||
@@ -814,6 +817,7 @@ line is inserted at all."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-remote.el --- Transfer Git commits -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -73,7 +73,8 @@ has to be used to view and change remote related variables."
|
||||
("U" magit-remote.<remote>.fetch)
|
||||
("s" magit-remote.<remote>.pushurl)
|
||||
("S" magit-remote.<remote>.push)
|
||||
("O" magit-remote.<remote>.tagopt)]
|
||||
("O" magit-remote.<remote>.tagopt)
|
||||
("h" magit-remote.<remote>.followremotehead)]
|
||||
["Arguments for add"
|
||||
("-f" "Fetch after add" "-f")]
|
||||
["Actions"
|
||||
@@ -98,31 +99,31 @@ has to be used to view and change remote related variables."
|
||||
(defun magit-remote-add (remote url &optional args)
|
||||
"Add a remote named REMOTE and fetch it."
|
||||
(interactive
|
||||
(let ((origin (magit-get "remote.origin.url"))
|
||||
(remote (magit-read-string-ns "Remote name")))
|
||||
(list remote
|
||||
(magit-read-url
|
||||
"Remote url"
|
||||
(and origin
|
||||
(string-match "\\([^:/]+\\)/[^/]+\\(\\.git\\)?\\'" origin)
|
||||
(replace-match remote t t origin 1)))
|
||||
(transient-args 'magit-remote))))
|
||||
(if (pcase (list magit-remote-add-set-remote.pushDefault
|
||||
(magit-get "remote.pushDefault"))
|
||||
(`(,(pred stringp) ,_) t)
|
||||
((or `(ask ,_) '(ask-if-unset nil))
|
||||
(y-or-n-p (format "Set `remote.pushDefault' to \"%s\"? " remote))))
|
||||
(progn (magit-call-git "remote" "add" args remote url)
|
||||
(setf (magit-get "remote.pushDefault") remote)
|
||||
(magit-refresh))
|
||||
(magit-run-git-async "remote" "add" args remote url)))
|
||||
(let ((origin (magit-get "remote.origin.url"))
|
||||
(remote (magit-read-string-ns "Remote name")))
|
||||
(list remote
|
||||
(magit-read-url
|
||||
"Remote url"
|
||||
(and origin
|
||||
(string-match "\\([^:/]+\\)/[^/]+\\(\\.git\\)?\\'" origin)
|
||||
(replace-match remote t t origin 1)))
|
||||
(transient-args 'magit-remote))))
|
||||
(cond ((pcase (list magit-remote-add-set-remote.pushDefault
|
||||
(magit-get "remote.pushDefault"))
|
||||
(`(,(pred stringp) ,_) t)
|
||||
((or `(ask ,_) '(ask-if-unset nil))
|
||||
(y-or-n-p (format "Set `remote.pushDefault' to \"%s\"? " remote))))
|
||||
(magit-call-git "remote" "add" args remote url)
|
||||
(setf (magit-get "remote.pushDefault") remote)
|
||||
(magit-refresh))
|
||||
((magit-run-git-async "remote" "add" args remote url))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-remote-rename (old new)
|
||||
"Rename the remote named OLD to NEW."
|
||||
(interactive
|
||||
(let ((remote (magit-read-remote "Rename remote")))
|
||||
(list remote (magit-read-string-ns (format "Rename %s to" remote)))))
|
||||
(let ((remote (magit-read-remote "Rename remote")))
|
||||
(list remote (magit-read-string-ns (format "Rename %s to" remote)))))
|
||||
(unless (string= old new)
|
||||
(magit-call-git "remote" "rename" old new)
|
||||
(magit-remote--cleanup-push-variables old new)
|
||||
@@ -239,11 +240,11 @@ accordingly. With a prefix argument query for the branch to be
|
||||
used, which allows you to select an incorrect value if you fancy
|
||||
doing that."
|
||||
(interactive
|
||||
(let ((remote (magit-read-remote "Set HEAD for remote")))
|
||||
(list remote
|
||||
(and current-prefix-arg
|
||||
(magit-read-remote-branch (format "Set %s/HEAD to" remote)
|
||||
remote nil nil t)))))
|
||||
(let ((remote (magit-read-remote "Set HEAD for remote")))
|
||||
(list remote
|
||||
(and current-prefix-arg
|
||||
(magit-read-remote-branch (format "Set %s/HEAD to" remote)
|
||||
remote nil nil t)))))
|
||||
(magit-run-git "remote" "set-head" remote (or branch "--auto")))
|
||||
|
||||
;;;###autoload
|
||||
@@ -262,28 +263,28 @@ Delete the symbolic-ref \"refs/remotes/<remote>/HEAD\"."
|
||||
(pcase-let ((`(,_remote ,oldname) (magit--get-default-branch))
|
||||
(`( ,remote ,newname) (magit--get-default-branch t)))
|
||||
(cond
|
||||
((equal oldname newname)
|
||||
(setq oldname
|
||||
(read-string
|
||||
(format
|
||||
"Name of default branch is still `%s', %s\n%s `%s': " oldname
|
||||
"but the upstreams of some local branches might need updating."
|
||||
"Name of upstream branches to replace with" newname)))
|
||||
(magit--set-default-branch newname oldname)
|
||||
(magit-refresh))
|
||||
(t
|
||||
(unless oldname
|
||||
(setq oldname
|
||||
(magit-read-other-local-branch
|
||||
(format "Name of old default branch to be renamed to `%s'"
|
||||
newname)
|
||||
newname "master")))
|
||||
(cond
|
||||
((y-or-n-p (format "Default branch changed from `%s' to `%s' on %s.%s?"
|
||||
oldname newname remote " Do the same locally"))
|
||||
(magit--set-default-branch newname oldname)
|
||||
(magit-refresh))
|
||||
((user-error "Abort")))))))
|
||||
((equal oldname newname)
|
||||
(setq oldname
|
||||
(read-string
|
||||
(format
|
||||
"Name of default branch is still `%s', %s\n%s `%s': " oldname
|
||||
"but the upstreams of some local branches might need updating."
|
||||
"Name of upstream branches to replace with" newname)))
|
||||
(magit--set-default-branch newname oldname)
|
||||
(magit-refresh))
|
||||
(t
|
||||
(unless oldname
|
||||
(setq oldname
|
||||
(magit-read-other-local-branch
|
||||
(format "Name of old default branch to be renamed to `%s'"
|
||||
newname)
|
||||
newname "master")))
|
||||
(cond
|
||||
((y-or-n-p (format "Default branch changed from `%s' to `%s' on %s.%s?"
|
||||
oldname newname remote " Do the same locally"))
|
||||
(magit--set-default-branch newname oldname)
|
||||
(magit-refresh))
|
||||
((user-error "Abort")))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-remote-unshallow (remote)
|
||||
@@ -316,13 +317,14 @@ refspec."
|
||||
("U" magit-remote.<remote>.fetch)
|
||||
("s" magit-remote.<remote>.pushurl)
|
||||
("S" magit-remote.<remote>.push)
|
||||
("O" magit-remote.<remote>.tagopt)]
|
||||
("O" magit-remote.<remote>.tagopt)
|
||||
("h" magit-remote.<remote>.followremotehead)]
|
||||
(interactive
|
||||
(list (or (and (not current-prefix-arg)
|
||||
(not (and magit-remote-direct-configure
|
||||
(eq transient-current-command 'magit-remote)))
|
||||
(magit-get-current-remote))
|
||||
(magit--read-remote-scope))))
|
||||
(list (or (and (not current-prefix-arg)
|
||||
(not (and magit-remote-direct-configure
|
||||
(eq transient-current-command 'magit-remote)))
|
||||
(magit-get-current-remote))
|
||||
(magit--read-remote-scope))))
|
||||
(transient-setup 'magit-remote-configure nil nil :scope remote))
|
||||
|
||||
(defun magit--read-remote-scope (&optional obj)
|
||||
@@ -364,6 +366,27 @@ refspec."
|
||||
:variable "remote.%s.tagOpt"
|
||||
:choices '("--no-tags" "--tags"))
|
||||
|
||||
(transient-define-infix magit-remote.<remote>.followremotehead ()
|
||||
"How \"git fetch\" handles updates to \"remotes/<remote>/HEAD\".
|
||||
|
||||
This command sets the local value of the Git variable
|
||||
`remote.<remote>.followRemoteHEAD', where <remote> is a stand-in for
|
||||
the actual remote, as displayed in the menu, from which this command
|
||||
is invoked. This variable is documented in (man \"git-config(1)\").
|
||||
|
||||
Unfortunately Git does not provide a variable to set a default for
|
||||
all remotes of all repositories, but you can set the global value for
|
||||
a remote name used in multiple repository, which will then be used as
|
||||
the default for that remote in all repositories. You should consider
|
||||
using \"always\" for remotes named \"origin\".
|
||||
|
||||
git config set --global remote.origin.followRemoteHEAD always"
|
||||
:class 'magit--git-variable:choices
|
||||
:scope #'magit--read-remote-scope
|
||||
:variable "remote.%s.followRemoteHEAD"
|
||||
:choices '("create" "always" "warn")
|
||||
:default "create")
|
||||
|
||||
;;; Transfer Utilities
|
||||
|
||||
(defun magit--push-remote-variable (&optional branch short)
|
||||
@@ -399,6 +422,7 @@ refspec."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-repos.el --- Listing repositories -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -535,7 +535,9 @@ instead."
|
||||
(magit-list-repos-uniquify
|
||||
(mapcar (lambda (v)
|
||||
(cons (concat
|
||||
key "\\"
|
||||
key
|
||||
(or (bound-and-true-p uniquify-separator)
|
||||
"\\")
|
||||
(file-name-nondirectory
|
||||
(directory-file-name
|
||||
(substring v 0 (- (1+ (length key)))))))
|
||||
@@ -557,6 +559,7 @@ instead."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-reset.el --- Reset functionality -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -140,6 +140,7 @@ or \"detached head\" will be substituted for %s."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-sequence.el --- History manipulation in Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -95,12 +95,12 @@
|
||||
"Resume the current cherry-pick or revert sequence."
|
||||
(interactive)
|
||||
(cond
|
||||
((not (magit-sequencer-in-progress-p))
|
||||
(user-error "No cherry-pick or revert in progress"))
|
||||
((magit-anything-unmerged-p)
|
||||
(user-error "Cannot continue due to unresolved conflicts"))
|
||||
((magit-run-git-sequencer
|
||||
(if (magit-revert-in-progress-p) "revert" "cherry-pick") "--continue"))))
|
||||
((not (magit-sequencer-in-progress-p))
|
||||
(user-error "No cherry-pick or revert in progress"))
|
||||
((magit-anything-unmerged-p)
|
||||
(user-error "Cannot continue due to unresolved conflicts"))
|
||||
((magit-run-git-sequencer
|
||||
(if (magit-revert-in-progress-p) "revert" "cherry-pick") "--continue"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-sequencer-skip ()
|
||||
@@ -117,13 +117,13 @@
|
||||
This discards all changes made since the sequence started."
|
||||
(interactive)
|
||||
(cond
|
||||
((not (magit-sequencer-in-progress-p))
|
||||
(user-error "No cherry-pick or revert in progress"))
|
||||
((magit-revert-in-progress-p)
|
||||
(magit-confirm 'abort-revert "Really abort revert")
|
||||
(magit-run-git-sequencer "revert" "--abort"))
|
||||
((magit-confirm 'abort-cherry-pick "Really abort cherry-pick")
|
||||
(magit-run-git-sequencer "cherry-pick" "--abort"))))
|
||||
((not (magit-sequencer-in-progress-p))
|
||||
(user-error "No cherry-pick or revert in progress"))
|
||||
((magit-revert-in-progress-p)
|
||||
(magit-confirm 'abort-revert "Really abort revert")
|
||||
(magit-run-git-sequencer "revert" "--abort"))
|
||||
((magit-confirm 'abort-cherry-pick "Really abort cherry-pick")
|
||||
(magit-run-git-sequencer "cherry-pick" "--abort"))))
|
||||
|
||||
(defun magit-sequencer-in-progress-p ()
|
||||
(or (magit-cherry-pick-in-progress-p)
|
||||
@@ -229,18 +229,18 @@ Remove the COMMITS from BRANCH and stay on the current branch.
|
||||
If a conflict occurs, then you have to fix that and finish the
|
||||
process manually."
|
||||
(interactive
|
||||
(magit--cherry-move-read-args "harvest" nil
|
||||
(lambda (commits)
|
||||
(list (let ((branches (magit-list-containing-branches (car commits))))
|
||||
(pcase (length branches)
|
||||
(0 nil)
|
||||
(1 (car branches))
|
||||
(_ (magit-completing-read
|
||||
(let ((len (length commits)))
|
||||
(if (= len 1)
|
||||
"Remove 1 cherry from branch"
|
||||
(format "Remove %s cherries from branch" len)))
|
||||
branches nil t))))))))
|
||||
(magit--cherry-move-read-args "harvest" nil
|
||||
(lambda (commits)
|
||||
(list (let ((branches (magit-list-containing-branches (car commits))))
|
||||
(pcase (length branches)
|
||||
(0 nil)
|
||||
(1 (car branches))
|
||||
(_ (magit-completing-read
|
||||
(let ((len (length commits)))
|
||||
(if (= len 1)
|
||||
"Remove 1 cherry from branch"
|
||||
(format "Remove %s cherries from branch" len)))
|
||||
branches nil t))))))))
|
||||
(magit--cherry-move commits branch (magit-get-current-branch) args nil t))
|
||||
|
||||
;;;###autoload
|
||||
@@ -250,14 +250,14 @@ Remove COMMITS from the current branch and stay on that branch.
|
||||
If a conflict occurs, then you have to fix that and finish the
|
||||
process manually. `HEAD' is allowed to be detached initially."
|
||||
(interactive
|
||||
(magit--cherry-move-read-args "donate" t
|
||||
(lambda (commits)
|
||||
(list (magit-read-other-branch
|
||||
(let ((len (length commits)))
|
||||
(if (= len 1)
|
||||
"Move 1 cherry to branch"
|
||||
(format "Move %s cherries to branch" len))))))
|
||||
'allow-detached))
|
||||
(magit--cherry-move-read-args "donate" t
|
||||
(lambda (commits)
|
||||
(list (magit-read-other-branch
|
||||
(let ((len (length commits)))
|
||||
(if (= len 1)
|
||||
"Move 1 cherry to branch"
|
||||
(format "Move %s cherries to branch" len))))))
|
||||
'allow-detached))
|
||||
(magit--cherry-move commits
|
||||
(or (magit-get-current-branch)
|
||||
(magit-rev-parse "HEAD"))
|
||||
@@ -289,8 +289,8 @@ the process manually."
|
||||
(unless (magit-branch-p dst)
|
||||
(let ((magit-process-raise-error t))
|
||||
(magit-call-git "branch" dst start-point))
|
||||
(when-let ((upstream (magit-get-indirect-upstream-branch start-point)))
|
||||
(magit-call-git "branch" "--set-upstream-to" upstream dst)))
|
||||
(when$ (magit-get-indirect-upstream-branch start-point)
|
||||
(magit-call-git "branch" "--set-upstream-to" $ dst)))
|
||||
(unless (equal dst current)
|
||||
(let ((magit-process-raise-error t))
|
||||
(magit-call-git "checkout" dst)))
|
||||
@@ -308,32 +308,32 @@ the process manually."
|
||||
(process-put process 'inhibit-refresh t)
|
||||
(magit-process-sentinel process event)
|
||||
(cond
|
||||
((magit-rev-equal tip src)
|
||||
(magit-call-git "update-ref"
|
||||
"-m" (format "reset: moving to %s" keep)
|
||||
(magit-ref-fullname src)
|
||||
keep tip)
|
||||
(if (not checkout-dst)
|
||||
(magit-run-git "checkout" src)
|
||||
(magit-refresh)))
|
||||
(t
|
||||
(magit-git "checkout" src)
|
||||
(with-environment-variables
|
||||
(("GIT_SEQUENCE_EDITOR"
|
||||
(format "%s -i -ne '/^pick (%s)/ or print'"
|
||||
magit-perl-executable
|
||||
(mapconcat #'magit-rev-abbrev commits "|"))))
|
||||
(magit-run-git-sequencer "rebase" "-i" keep))
|
||||
(when checkout-dst
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
(lambda (process event)
|
||||
(when (memq (process-status process) '(exit signal))
|
||||
(if (> (process-exit-status process) 0)
|
||||
(magit-process-sentinel process event)
|
||||
(process-put process 'inhibit-refresh t)
|
||||
(magit-process-sentinel process event)
|
||||
(magit-run-git "checkout" dst))))))))))))))))
|
||||
((magit-rev-equal tip src)
|
||||
(magit-call-git "update-ref"
|
||||
"-m" (format "reset: moving to %s" keep)
|
||||
(magit-ref-fullname src)
|
||||
keep tip)
|
||||
(if (not checkout-dst)
|
||||
(magit-run-git "checkout" src)
|
||||
(magit-refresh)))
|
||||
(t
|
||||
(magit-git "checkout" src)
|
||||
(with-environment-variables
|
||||
(("GIT_SEQUENCE_EDITOR"
|
||||
(format "%s -i -ne '/^pick (%s)/ or print'"
|
||||
magit-perl-executable
|
||||
(mapconcat #'magit-rev-abbrev commits "|"))))
|
||||
(magit-run-git-sequencer "rebase" "-i" keep))
|
||||
(when checkout-dst
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
(lambda (process event)
|
||||
(when (memq (process-status process) '(exit signal))
|
||||
(if (> (process-exit-status process) 0)
|
||||
(magit-process-sentinel process event)
|
||||
(process-put process 'inhibit-refresh t)
|
||||
(magit-process-sentinel process event)
|
||||
(magit-run-git "checkout" dst))))))))))))))))
|
||||
|
||||
(defun magit--cherry-pick (commits args &optional revert)
|
||||
(let ((command (if revert "revert" "cherry-pick")))
|
||||
@@ -345,16 +345,16 @@ the process manually."
|
||||
(if revert "revert" "cherry-pick")
|
||||
(let ((merges (seq-filter #'magit-merge-commit-p commits)))
|
||||
(cond
|
||||
((not merges)
|
||||
(seq-remove (##string-prefix-p "--mainline=" %) args))
|
||||
((cl-set-difference commits merges :test #'equal)
|
||||
(user-error "Cannot %s merge and non-merge commits at once"
|
||||
command))
|
||||
((seq-find (##string-prefix-p "--mainline=" %) args)
|
||||
args)
|
||||
((cons (format "--mainline=%s"
|
||||
(read-number "Replay merges relative to parent: "))
|
||||
args))))
|
||||
((not merges)
|
||||
(seq-remove (##string-prefix-p "--mainline=" %) args))
|
||||
((cl-set-difference commits merges :test #'equal)
|
||||
(user-error "Cannot %s merge and non-merge commits at once"
|
||||
command))
|
||||
((seq-find (##string-prefix-p "--mainline=" %) args)
|
||||
args)
|
||||
((cons (format "--mainline=%s"
|
||||
(read-number "Replay merges relative to parent: "))
|
||||
args))))
|
||||
commits)))
|
||||
|
||||
(defun magit-cherry-pick-in-progress-p ()
|
||||
@@ -491,11 +491,11 @@ without prompting."
|
||||
"Resume the current patch applying sequence."
|
||||
(interactive)
|
||||
(cond
|
||||
((not (magit-am-in-progress-p))
|
||||
(user-error "Not applying any patches"))
|
||||
((magit-anything-unstaged-p t)
|
||||
(user-error "Cannot continue due to unstaged changes"))
|
||||
((magit-run-git-sequencer "am" "--continue"))))
|
||||
((not (magit-am-in-progress-p))
|
||||
(user-error "Not applying any patches"))
|
||||
((magit-anything-unstaged-p t)
|
||||
(user-error "Cannot continue due to unstaged changes"))
|
||||
((magit-run-git-sequencer "am" "--continue"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-am-skip ()
|
||||
@@ -628,13 +628,13 @@ the upstream."
|
||||
(merge (magit-get "branch" branch "merge"))
|
||||
(u (magit--propertize-face "@{upstream}" 'bold)))
|
||||
(cond
|
||||
((magit--unnamed-upstream-p remote merge)
|
||||
(concat u ", replacing unnamed"))
|
||||
((magit--valid-upstream-p remote merge)
|
||||
(concat u ", replacing non-existent"))
|
||||
((or remote merge)
|
||||
(concat u ", replacing invalid"))
|
||||
((concat u ", setting that")))))))
|
||||
((magit--unnamed-upstream-p remote merge)
|
||||
(concat u ", replacing unnamed"))
|
||||
((magit--valid-upstream-p remote merge)
|
||||
(concat u ", replacing non-existent"))
|
||||
((or remote merge)
|
||||
(concat u ", replacing invalid"))
|
||||
((concat u ", setting that")))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-rebase-branch (target args)
|
||||
@@ -823,25 +823,25 @@ In some cases this pops up a commit message buffer for you do
|
||||
edit. With a prefix argument the old message is reused as-is."
|
||||
(interactive "P")
|
||||
(cond
|
||||
((not (magit-rebase-in-progress-p))
|
||||
(user-error "No rebase in progress"))
|
||||
((magit-anything-unstaged-p t)
|
||||
(user-error "Cannot continue rebase with unstaged changes"))
|
||||
(t
|
||||
(let ((dir (magit-gitdir)))
|
||||
(when (and (magit-anything-staged-p)
|
||||
(file-exists-p (expand-file-name "rebase-merge" dir))
|
||||
(not (member (magit-toplevel)
|
||||
magit--rebase-public-edit-confirmed)))
|
||||
(magit-commit-amend-assert
|
||||
(magit-file-line (expand-file-name "rebase-merge/orig-head" dir)))))
|
||||
(if noedit
|
||||
(with-environment-variables (("GIT_EDITOR" "true"))
|
||||
(magit-run-git-async (magit--rebase-resume-command) "--continue")
|
||||
(set-process-sentinel magit-this-process
|
||||
#'magit-sequencer-process-sentinel)
|
||||
magit-this-process)
|
||||
(magit-run-git-sequencer (magit--rebase-resume-command) "--continue")))))
|
||||
((not (magit-rebase-in-progress-p))
|
||||
(user-error "No rebase in progress"))
|
||||
((magit-anything-unstaged-p t)
|
||||
(user-error "Cannot continue rebase with unstaged changes"))
|
||||
(t
|
||||
(let ((dir (magit-gitdir)))
|
||||
(when (and (magit-anything-staged-p)
|
||||
(file-exists-p (expand-file-name "rebase-merge" dir))
|
||||
(not (member (magit-toplevel)
|
||||
magit--rebase-public-edit-confirmed)))
|
||||
(magit-commit-amend-assert
|
||||
(magit-file-line (expand-file-name "rebase-merge/orig-head" dir)))))
|
||||
(if noedit
|
||||
(with-environment-variables (("GIT_EDITOR" "true"))
|
||||
(magit-run-git-async (magit--rebase-resume-command) "--continue")
|
||||
(set-process-sentinel magit-this-process
|
||||
#'magit-sequencer-process-sentinel)
|
||||
magit-this-process)
|
||||
(magit-run-git-sequencer (magit--rebase-resume-command) "--continue")))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-rebase-skip ()
|
||||
@@ -932,8 +932,9 @@ If no such sequence is in progress, do nothing."
|
||||
patch commit)
|
||||
(while (and patches (>= i cur))
|
||||
(setq patch (pop patches))
|
||||
(setq commit (magit-commit-p
|
||||
(cadr (split-string (magit-file-line patch)))))
|
||||
(setq commit
|
||||
(magit-commit-oid (cadr (split-string (magit-file-line patch)))
|
||||
t))
|
||||
(cond ((and commit (= i cur))
|
||||
(magit-sequence-insert-commit
|
||||
"stop" commit 'magit-sequence-stop))
|
||||
@@ -988,8 +989,8 @@ If no such sequence is in progress, do nothing."
|
||||
|
||||
(defun magit-rebase--todo ()
|
||||
"Return `git-rebase-action' instances for remaining rebase actions.
|
||||
These are ordered in that the same way they'll be sorted in the
|
||||
status buffer (i.e., the reverse of how they will be applied)."
|
||||
These are ordered the same way they'll be sorted in the status
|
||||
buffer (i.e., the reverse of how they will be applied)."
|
||||
(let ((comment-start (or (magit-get "core.commentChar") "#"))
|
||||
(commits ())
|
||||
(actions ()))
|
||||
@@ -1066,37 +1067,37 @@ status buffer (i.e., the reverse of how they will be applied)."
|
||||
(if-let ((matched (car (assoc (##equal (magit-patch-id %) id) done))))
|
||||
(setq stop matched)
|
||||
(cond
|
||||
((assoc (##magit-rev-equal % stop) done)
|
||||
;; The commit's testament has been executed.
|
||||
(magit-sequence-insert-commit "void" stop 'magit-sequence-drop))
|
||||
;; The faith of the commit is still undecided...
|
||||
((magit-anything-unmerged-p)
|
||||
;; ...and time travel isn't for the faint of heart.
|
||||
(magit-sequence-insert-commit "join" stop 'magit-sequence-part))
|
||||
((magit-anything-modified-p t)
|
||||
;; ...and the dust hasn't settled yet...
|
||||
(magit-sequence-insert-commit
|
||||
(let* ((magit--refresh-cache nil)
|
||||
(staged (magit-commit-tree "oO" nil "HEAD"))
|
||||
(unstaged (magit-commit-worktree "oO" "--reset")))
|
||||
(cond
|
||||
;; ...but we could end up at the same tree just by committing.
|
||||
((or (magit-rev-equal staged stop)
|
||||
(magit-rev-equal unstaged stop))
|
||||
"goal")
|
||||
;; ...but the changes are still there, untainted.
|
||||
((or (equal (magit-patch-id staged) id)
|
||||
(equal (magit-patch-id unstaged) id))
|
||||
"same")
|
||||
;; ...and some changes are gone and/or others were added.
|
||||
("work")))
|
||||
stop 'magit-sequence-part))
|
||||
;; The commit is definitely gone...
|
||||
((assoc (##magit-rev-equal % stop) done)
|
||||
;; ...but all of its changes are still in effect.
|
||||
(magit-sequence-insert-commit "poof" stop 'magit-sequence-drop))
|
||||
;; ...and some changes are gone and/or other changes were added.
|
||||
((magit-sequence-insert-commit "gone" stop 'magit-sequence-drop)))
|
||||
((assoc (##magit-rev-equal % stop) done)
|
||||
;; The commit's testament has been executed.
|
||||
(magit-sequence-insert-commit "void" stop 'magit-sequence-drop))
|
||||
;; The faith of the commit is still undecided...
|
||||
((magit-anything-unmerged-p)
|
||||
;; ...and time travel isn't for the faint of heart.
|
||||
(magit-sequence-insert-commit "join" stop 'magit-sequence-part))
|
||||
((magit-anything-modified-p t)
|
||||
;; ...and the dust hasn't settled yet...
|
||||
(magit-sequence-insert-commit
|
||||
(let* ((magit--refresh-cache nil)
|
||||
(staged (magit-commit-tree "oO" nil "HEAD"))
|
||||
(unstaged (magit-commit-worktree "oO" "--reset")))
|
||||
(cond
|
||||
;; ...but we could end up at the same tree just by committing.
|
||||
((or (magit-rev-equal staged stop)
|
||||
(magit-rev-equal unstaged stop))
|
||||
"goal")
|
||||
;; ...but the changes are still there, untainted.
|
||||
((or (equal (magit-patch-id staged) id)
|
||||
(equal (magit-patch-id unstaged) id))
|
||||
"same")
|
||||
;; ...and some changes are gone and/or others were added.
|
||||
("work")))
|
||||
stop 'magit-sequence-part))
|
||||
;; The commit is definitely gone...
|
||||
((assoc (##magit-rev-equal % stop) done)
|
||||
;; ...but all of its changes are still in effect.
|
||||
(magit-sequence-insert-commit "poof" stop 'magit-sequence-drop))
|
||||
;; ...and some changes are gone and/or other changes were added.
|
||||
((magit-sequence-insert-commit "gone" stop 'magit-sequence-drop)))
|
||||
(setq stop nil))))
|
||||
(pcase-dolist (`(,rev ,abbrev ,msg) done)
|
||||
(apply #'magit-sequence-insert-commit
|
||||
@@ -1144,6 +1145,7 @@ status buffer (i.e., the reverse of how they will be applied)."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-sparse-checkout.el --- Sparse checkout support for Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Kyle Meyer <kyle@kyleam.com>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -82,12 +82,12 @@ See the `git sparse-checkout' manpage for details about
|
||||
To extend rather than override the currently configured
|
||||
directories, call `magit-sparse-checkout-add' instead."
|
||||
(interactive
|
||||
(list (magit-completing-read-multiple
|
||||
"Include these directories: "
|
||||
;; Note: Given that the appeal of sparse checkouts is
|
||||
;; dealing with very large trees, listing all subdirectories
|
||||
;; may need to be reconsidered.
|
||||
(magit-revision-directories "HEAD"))))
|
||||
(list (magit-completing-read-multiple
|
||||
"Include these directories: "
|
||||
;; Note: Given that the appeal of sparse checkouts is
|
||||
;; dealing with very large trees, listing all subdirectories
|
||||
;; may need to be reconsidered.
|
||||
(magit-revision-directories "HEAD"))))
|
||||
(magit-sparse-checkout--auto-enable)
|
||||
(magit-run-git-async "sparse-checkout" "set" directories))
|
||||
|
||||
@@ -97,16 +97,16 @@ directories, call `magit-sparse-checkout-add' instead."
|
||||
To override rather than extend the currently configured
|
||||
directories, call `magit-sparse-checkout-set' instead."
|
||||
(interactive
|
||||
(list (magit-completing-read-multiple
|
||||
"Add these directories: "
|
||||
;; Same performance note as in `magit-sparse-checkout-set',
|
||||
;; but even more so given the additional processing.
|
||||
(seq-remove
|
||||
(let ((re (concat
|
||||
"\\`"
|
||||
(regexp-opt (magit-sparse-checkout-directories)))))
|
||||
(##string-match-p re %))
|
||||
(magit-revision-directories "HEAD")))))
|
||||
(list (magit-completing-read-multiple
|
||||
"Add these directories: "
|
||||
;; Same performance note as in `magit-sparse-checkout-set',
|
||||
;; but even more so given the additional processing.
|
||||
(seq-remove
|
||||
(let ((re (concat
|
||||
"\\`"
|
||||
(regexp-opt (magit-sparse-checkout-directories)))))
|
||||
(##string-match-p re %))
|
||||
(magit-revision-directories "HEAD")))))
|
||||
(magit-sparse-checkout--auto-enable)
|
||||
(magit-run-git-async "sparse-checkout" "add" directories))
|
||||
|
||||
@@ -153,6 +153,7 @@ This header is not inserted by default. To enable it, add it to
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-stash.el --- Stash support for Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -127,13 +127,13 @@ Untracked files are included according to infix arguments.
|
||||
One prefix argument is equivalent to `--include-untracked'
|
||||
while two prefix arguments are equivalent to `--all'."
|
||||
(interactive
|
||||
(progn (when (and (magit-merge-in-progress-p)
|
||||
(not (magit-y-or-n-p "\
|
||||
(progn (when (and (magit-merge-in-progress-p)
|
||||
(not (magit-y-or-n-p "\
|
||||
Stashing and resetting during a merge conflict. \
|
||||
Applying the resulting stash won't restore the merge state. \
|
||||
Proceed anyway? ")))
|
||||
(user-error "Abort"))
|
||||
(magit-stash-read-args)))
|
||||
(user-error "Abort"))
|
||||
(magit-stash-read-args)))
|
||||
(magit-stash-save message t t include-untracked t))
|
||||
|
||||
;;;###autoload
|
||||
@@ -370,9 +370,9 @@ want to fall back to using \"--3way\", without being prompted."
|
||||
"Remove a stash from the stash list.
|
||||
When the region is active offer to drop all contained stashes."
|
||||
(interactive
|
||||
(list (if-let ((values (magit-region-values 'stash)))
|
||||
(magit-confirm 'drop-stashes nil "Drop %d stashes" nil values)
|
||||
(magit-read-stash "Drop stash"))))
|
||||
(list (if-let ((values (magit-region-values 'stash)))
|
||||
(magit-confirm 'drop-stashes nil "Drop %d stashes" nil values)
|
||||
(magit-read-stash "Drop stash"))))
|
||||
(dolist (stash (if (listp stash)
|
||||
(nreverse (prog1 stash (setq stash (car stash))))
|
||||
(list stash)))
|
||||
@@ -386,7 +386,8 @@ When the region is active offer to drop all contained stashes."
|
||||
(defun magit-stash-clear (ref)
|
||||
"Remove all stashes saved in REF's reflog by deleting REF."
|
||||
(interactive (let ((ref (or (magit-section-value-if 'stashes) "refs/stash")))
|
||||
(magit-confirm t (list "Drop all stashes in %s" ref))
|
||||
(magit-confirm 'drop-stashes
|
||||
(list "Drop all stashes in %s" ref))
|
||||
(list ref)))
|
||||
(magit-run-git "update-ref" "-d" ref))
|
||||
|
||||
@@ -619,7 +620,7 @@ See also info node `(magit)Section Movement'."
|
||||
(defun magit-stash-setup-buffer (stash args files)
|
||||
(magit-setup-buffer #'magit-stash-mode nil
|
||||
(magit-buffer-revision stash)
|
||||
(magit-buffer-range (format "%s^..%s" stash stash))
|
||||
(magit-buffer-diff-range (format "%s^..%s" stash stash))
|
||||
(magit-buffer-diff-args args)
|
||||
(magit-buffer-diff-files files)))
|
||||
|
||||
@@ -630,7 +631,7 @@ See also info node `(magit)Section Movement'."
|
||||
'font-lock-face
|
||||
(list :weight 'normal :foreground
|
||||
(face-attribute 'default :foreground)))))
|
||||
(setq magit-buffer-revision-hash (magit-rev-parse magit-buffer-revision))
|
||||
(setq magit-buffer-revision-oid (magit-commit-oid magit-buffer-revision))
|
||||
(magit-insert-section (stash)
|
||||
(magit-run-section-hook 'magit-stash-sections-hook)))
|
||||
|
||||
@@ -687,6 +688,7 @@ that make up the stash."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-status.el --- The grand overview -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -259,18 +259,18 @@ has to confirm that it should be reinitialized.
|
||||
|
||||
Non-interactively DIRECTORY is (re-)initialized unconditionally."
|
||||
(interactive
|
||||
(let ((directory (file-name-as-directory
|
||||
(expand-file-name
|
||||
(read-directory-name "Create repository in: ")))))
|
||||
(when-let ((toplevel (magit-toplevel directory)))
|
||||
(setq toplevel (expand-file-name toplevel))
|
||||
(unless (y-or-n-p (if (file-equal-p toplevel directory)
|
||||
(format "Reinitialize existing repository %s? "
|
||||
directory)
|
||||
(format "%s is a repository. Create another in %s? "
|
||||
toplevel directory)))
|
||||
(user-error "Abort")))
|
||||
(list directory)))
|
||||
(let ((directory (file-name-as-directory
|
||||
(expand-file-name
|
||||
(read-directory-name "Create repository in: ")))))
|
||||
(when-let ((toplevel (magit-toplevel directory)))
|
||||
(setq toplevel (expand-file-name toplevel))
|
||||
(unless (y-or-n-p (if (file-equal-p toplevel directory)
|
||||
(format "Reinitialize existing repository %s? "
|
||||
directory)
|
||||
(format "%s is a repository. Create another in %s? "
|
||||
toplevel directory)))
|
||||
(user-error "Abort")))
|
||||
(list directory)))
|
||||
;; `git init' does not understand the meaning of "~"!
|
||||
(magit-call-git "init" (magit-convert-filename-for-git
|
||||
(expand-file-name directory)))
|
||||
@@ -310,12 +310,12 @@ prefix arguments:
|
||||
then fall back to the same behavior as with two prefix
|
||||
arguments."
|
||||
(interactive
|
||||
(let ((magit--refresh-cache (list (cons 0 0))))
|
||||
(list (and (or current-prefix-arg (not (magit-toplevel)))
|
||||
(progn (magit--assert-usable-git)
|
||||
(magit-read-repository
|
||||
(>= (prefix-numeric-value current-prefix-arg) 16))))
|
||||
magit--refresh-cache)))
|
||||
(let ((magit--refresh-cache (list (cons 0 0))))
|
||||
(list (and (or current-prefix-arg (not (magit-toplevel)))
|
||||
(progn (magit--assert-usable-git)
|
||||
(magit-read-repository
|
||||
(>= (prefix-numeric-value current-prefix-arg) 16))))
|
||||
magit--refresh-cache)))
|
||||
(let ((magit--refresh-cache (or cache (list (cons 0 0)))))
|
||||
(if directory
|
||||
(let ((toplevel (magit-toplevel directory)))
|
||||
@@ -622,33 +622,33 @@ arguments are for internal use only."
|
||||
(insert (format "%-10s" (or keyword (if rebase "Rebase: " "Merge: "))))
|
||||
(insert
|
||||
(cond
|
||||
(upstream
|
||||
(concat (and magit-status-show-hashes-in-headers
|
||||
(concat (propertize (magit-rev-format "%h" upstream)
|
||||
'font-lock-face 'magit-hash)
|
||||
" "))
|
||||
upstream " "
|
||||
(magit-log--wash-summary
|
||||
(or (magit-rev-format "%s" upstream)
|
||||
"(no commit message)"))))
|
||||
((magit--unnamed-upstream-p remote merge)
|
||||
(concat (propertize merge 'font-lock-face 'magit-branch-remote)
|
||||
" from "
|
||||
(propertize remote 'font-lock-face 'bold)))
|
||||
((magit--valid-upstream-p remote merge)
|
||||
(if (equal remote ".")
|
||||
(concat
|
||||
(propertize merge 'font-lock-face 'magit-branch-local) " "
|
||||
(propertize "does not exist"
|
||||
'font-lock-face 'magit-branch-warning))
|
||||
(format
|
||||
"%s %s %s"
|
||||
(propertize merge 'font-lock-face 'magit-branch-remote)
|
||||
(propertize "does not exist on"
|
||||
'font-lock-face 'magit-branch-warning)
|
||||
(propertize remote 'font-lock-face 'magit-branch-remote))))
|
||||
((propertize "invalid upstream configuration"
|
||||
'font-lock-face 'magit-branch-warning))))
|
||||
(upstream
|
||||
(concat (and magit-status-show-hashes-in-headers
|
||||
(concat (propertize (magit-rev-format "%h" upstream)
|
||||
'font-lock-face 'magit-hash)
|
||||
" "))
|
||||
upstream " "
|
||||
(magit-log--wash-summary
|
||||
(or (magit-rev-format "%s" upstream)
|
||||
"(no commit message)"))))
|
||||
((magit--unnamed-upstream-p remote merge)
|
||||
(concat (propertize merge 'font-lock-face 'magit-branch-remote)
|
||||
" from "
|
||||
(propertize remote 'font-lock-face 'bold)))
|
||||
((magit--valid-upstream-p remote merge)
|
||||
(if (equal remote ".")
|
||||
(concat
|
||||
(propertize merge 'font-lock-face 'magit-branch-local) " "
|
||||
(propertize "does not exist"
|
||||
'font-lock-face 'magit-branch-warning))
|
||||
(format
|
||||
"%s %s %s"
|
||||
(propertize merge 'font-lock-face 'magit-branch-remote)
|
||||
(propertize "does not exist on"
|
||||
'font-lock-face 'magit-branch-warning)
|
||||
(propertize remote 'font-lock-face 'magit-branch-remote))))
|
||||
((propertize "invalid upstream configuration"
|
||||
'font-lock-face 'magit-branch-warning))))
|
||||
(insert ?\n))))))
|
||||
|
||||
(defun magit-insert-push-branch-header ()
|
||||
@@ -826,6 +826,7 @@ Honor the buffer's file filter, which can be set using \"D - -\"."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-submodule.el --- Submodule support for Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -216,13 +216,13 @@ it is nil, then PATH also becomes the name."
|
||||
:class 'magit--git-submodule-suffix
|
||||
:description "Add git submodule add [--force]"
|
||||
(interactive
|
||||
(magit-with-toplevel
|
||||
(let* ((url (magit-read-string-ns "Add submodule (remote url)"))
|
||||
(path (magit-submodule-read-path "Add submodules at path: " url)))
|
||||
(list url
|
||||
(directory-file-name path)
|
||||
(magit-submodule-read-name-for-path path)
|
||||
(magit-submodule-arguments "--force")))))
|
||||
(magit-with-toplevel
|
||||
(let* ((url (magit-read-string-ns "Add submodule (remote url)"))
|
||||
(path (magit-submodule-read-path "Add submodules at path: " url)))
|
||||
(list url
|
||||
(directory-file-name path)
|
||||
(magit-submodule-read-name-for-path path)
|
||||
(magit-submodule-arguments "--force")))))
|
||||
(magit-submodule-add-1 url path name args))
|
||||
|
||||
(defun magit-submodule-read-path (prompt url)
|
||||
@@ -277,7 +277,7 @@ single module from the user."
|
||||
;; the modules.
|
||||
:description "Register git submodule init"
|
||||
(interactive
|
||||
(list (magit-module-confirm "Register" 'magit-module-no-worktree-p)))
|
||||
(list (magit-module-confirm "Register" 'magit-module-no-worktree-p)))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git-async "submodule" "init" "--" modules)))
|
||||
|
||||
@@ -295,8 +295,8 @@ single module from the user."
|
||||
:class 'magit--git-submodule-suffix
|
||||
:description "Populate git submodule update --init [--recursive]"
|
||||
(interactive
|
||||
(list (magit-module-confirm "Populate" 'magit-module-no-worktree-p)
|
||||
(magit-submodule-arguments "--recursive")))
|
||||
(list (magit-module-confirm "Populate" 'magit-module-no-worktree-p)
|
||||
(magit-submodule-arguments "--recursive")))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git-async "submodule" "update" "--init" args "--" modules)))
|
||||
|
||||
@@ -316,10 +316,10 @@ single module from the user."
|
||||
:description "Update git submodule update [--force] [--no-fetch]
|
||||
[--remote] [--recursive] [--checkout|--rebase|--merge]"
|
||||
(interactive
|
||||
(list (magit-module-confirm "Update" 'magit-module-worktree-p)
|
||||
(magit-submodule-arguments
|
||||
"--force" "--remote" "--recursive" "--checkout" "--rebase" "--merge"
|
||||
"--no-fetch")))
|
||||
(list (magit-module-confirm "Update" 'magit-module-worktree-p)
|
||||
(magit-submodule-arguments
|
||||
"--force" "--remote" "--recursive" "--checkout" "--rebase" "--merge"
|
||||
"--no-fetch")))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git-async "submodule" "update" args "--" modules)))
|
||||
|
||||
@@ -334,8 +334,8 @@ single module from the user."
|
||||
:class 'magit--git-submodule-suffix
|
||||
:description "Synchronize git submodule sync [--recursive]"
|
||||
(interactive
|
||||
(list (magit-module-confirm "Synchronize" 'magit-module-worktree-p)
|
||||
(magit-submodule-arguments "--recursive")))
|
||||
(list (magit-module-confirm "Synchronize" 'magit-module-worktree-p)
|
||||
(magit-submodule-arguments "--recursive")))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git-async "submodule" "sync" args "--" modules)))
|
||||
|
||||
@@ -357,8 +357,8 @@ single module from the user."
|
||||
:class 'magit--git-submodule-suffix
|
||||
:description "Unpopulate git submodule deinit [--force]"
|
||||
(interactive
|
||||
(list (magit-module-confirm "Unpopulate")
|
||||
(magit-submodule-arguments "--force")))
|
||||
(list (magit-module-confirm "Unpopulate")
|
||||
(magit-submodule-arguments "--force")))
|
||||
(magit-with-toplevel
|
||||
(magit-run-git-async "submodule" "deinit" args "--" modules)))
|
||||
|
||||
@@ -377,11 +377,11 @@ Both actions are very dangerous and have to be confirmed. There
|
||||
are additional safety precautions in place, so you might be able
|
||||
to recover from making a mistake here, but don't count on it."
|
||||
(interactive
|
||||
(list (if-let ((modules (magit-region-values 'magit-module-section t)))
|
||||
(magit-confirm 'remove-modules nil "Remove %d modules" nil modules)
|
||||
(list (magit-read-module-path "Remove module")))
|
||||
(magit-submodule-arguments "--force")
|
||||
current-prefix-arg))
|
||||
(list (if-let ((modules (magit-region-values 'magit-module-section t)))
|
||||
(magit-confirm 'remove-modules nil "Remove %d modules" nil modules)
|
||||
(list (magit-read-module-path "Remove module")))
|
||||
(magit-submodule-arguments "--force")
|
||||
current-prefix-arg))
|
||||
(when magit-submodule-remove-trash-gitdirs
|
||||
(setq trash-gitdirs t))
|
||||
(magit-with-toplevel
|
||||
@@ -539,20 +539,20 @@ With a prefix argument, visit in another window."
|
||||
(magit-with-toplevel
|
||||
(let ((path (expand-file-name module)))
|
||||
(cond
|
||||
((file-exists-p (expand-file-name ".git" module))
|
||||
(magit-diff-visit-directory path other-window))
|
||||
((y-or-n-p (format "Initialize submodule '%s' first?" module))
|
||||
(magit-run-git-async "submodule" "update" "--init" "--" module)
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
(lambda (process event)
|
||||
(let ((magit-process-raise-error t))
|
||||
(magit-process-sentinel process event))
|
||||
(when (and (eq (process-status process) 'exit)
|
||||
(= (process-exit-status process) 0))
|
||||
(magit-diff-visit-directory path other-window)))))
|
||||
((file-exists-p path)
|
||||
(dired-jump other-window (concat path "/.")))))))
|
||||
((file-exists-p (expand-file-name ".git" module))
|
||||
(magit-diff-visit-directory path other-window))
|
||||
((y-or-n-p (format "Initialize submodule '%s' first?" module))
|
||||
(magit-run-git-async "submodule" "update" "--init" "--" module)
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
(lambda (process event)
|
||||
(let ((magit-process-raise-error t))
|
||||
(magit-process-sentinel process event))
|
||||
(when (and (eq (process-status process) 'exit)
|
||||
(= (process-exit-status process) 0))
|
||||
(magit-diff-visit-directory path other-window)))))
|
||||
((file-exists-p path)
|
||||
(dired-jump other-window (concat path "/.")))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-insert-modules-unpulled-from-upstream ()
|
||||
@@ -720,6 +720,7 @@ These sections can be expanded to show the respective commits."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-subtree.el --- Subtree support for Magit -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -129,40 +129,40 @@
|
||||
(defun magit-subtree-add (prefix repository ref args)
|
||||
"Add REF from REPOSITORY as a new subtree at PREFIX."
|
||||
(interactive
|
||||
(cons (magit-subtree-prefix 'magit-subtree-import "Add subtree")
|
||||
(let ((remote (magit-read-remote-or-url "From repository")))
|
||||
(list remote
|
||||
(magit-read-refspec "Ref" remote)
|
||||
(magit-subtree-arguments 'magit-subtree-import)))))
|
||||
(cons (magit-subtree-prefix 'magit-subtree-import "Add subtree")
|
||||
(let ((remote (magit-read-remote-or-url "From repository")))
|
||||
(list remote
|
||||
(magit-read-refspec "Ref" remote)
|
||||
(magit-subtree-arguments 'magit-subtree-import)))))
|
||||
(magit-git-subtree "add" prefix args repository ref))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-subtree-add-commit (prefix commit args)
|
||||
"Add COMMIT as a new subtree at PREFIX."
|
||||
(interactive
|
||||
(list (magit-subtree-prefix 'magit-subtree-import "Add subtree")
|
||||
(magit-read-string-ns "Commit")
|
||||
(magit-subtree-arguments 'magit-subtree-import)))
|
||||
(list (magit-subtree-prefix 'magit-subtree-import "Add subtree")
|
||||
(magit-read-string-ns "Commit")
|
||||
(magit-subtree-arguments 'magit-subtree-import)))
|
||||
(magit-git-subtree "add" prefix args commit))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-subtree-merge (prefix commit args)
|
||||
"Merge COMMIT into the PREFIX subtree."
|
||||
(interactive
|
||||
(list (magit-subtree-prefix 'magit-subtree-import "Merge into subtree")
|
||||
(magit-read-string-ns "Commit")
|
||||
(magit-subtree-arguments 'magit-subtree-import)))
|
||||
(list (magit-subtree-prefix 'magit-subtree-import "Merge into subtree")
|
||||
(magit-read-string-ns "Commit")
|
||||
(magit-subtree-arguments 'magit-subtree-import)))
|
||||
(magit-git-subtree "merge" prefix args commit))
|
||||
|
||||
;;;###autoload
|
||||
(defun magit-subtree-pull (prefix repository ref args)
|
||||
"Pull REF from REPOSITORY into the PREFIX subtree."
|
||||
(interactive
|
||||
(cons (magit-subtree-prefix 'magit-subtree-import "Pull into subtree")
|
||||
(let ((remote (magit-read-remote-or-url "From repository")))
|
||||
(list remote
|
||||
(magit-read-refspec "Ref" remote)
|
||||
(magit-subtree-arguments 'magit-subtree-import)))))
|
||||
(cons (magit-subtree-prefix 'magit-subtree-import "Pull into subtree")
|
||||
(let ((remote (magit-read-remote-or-url "From repository")))
|
||||
(list remote
|
||||
(magit-read-refspec "Ref" remote)
|
||||
(magit-subtree-arguments 'magit-subtree-import)))))
|
||||
(magit-git-subtree "pull" prefix args repository ref))
|
||||
|
||||
;;;###autoload
|
||||
@@ -190,6 +190,7 @@
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-tag.el --- Tag functionality -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -91,26 +91,26 @@ defaulting to the tag at point.
|
||||
(defun magit-tag-prune (tags remote-tags remote)
|
||||
"Offer to delete tags missing locally from REMOTE, and vice versa."
|
||||
(interactive
|
||||
(let* ((remote (magit-read-remote "Prune tags using remote"))
|
||||
(tags (magit-list-tags))
|
||||
(rtags (prog2 (message "Determining remote tags...")
|
||||
(magit-remote-list-tags remote)
|
||||
(message "Determining remote tags...done")))
|
||||
(ltags (cl-set-difference tags rtags :test #'equal))
|
||||
(rtags (cl-set-difference rtags tags :test #'equal)))
|
||||
(unless (or ltags rtags)
|
||||
(message "Same tags exist locally and remotely"))
|
||||
(unless (magit-confirm t
|
||||
"Delete %s locally"
|
||||
"Delete %d tags locally"
|
||||
'noabort ltags)
|
||||
(setq ltags nil))
|
||||
(unless (magit-confirm t
|
||||
"Delete %s from remote"
|
||||
"Delete %d tags from remote"
|
||||
'noabort rtags)
|
||||
(setq rtags nil))
|
||||
(list ltags rtags remote)))
|
||||
(let* ((remote (magit-read-remote "Prune tags using remote"))
|
||||
(tags (magit-list-tags))
|
||||
(rtags (prog2 (message "Determining remote tags...")
|
||||
(magit-remote-list-tags remote)
|
||||
(message "Determining remote tags...done")))
|
||||
(ltags (cl-set-difference tags rtags :test #'equal))
|
||||
(rtags (cl-set-difference rtags tags :test #'equal)))
|
||||
(unless (or ltags rtags)
|
||||
(message "Same tags exist locally and remotely"))
|
||||
(unless (magit-confirm t
|
||||
"Delete %s locally"
|
||||
"Delete %d tags locally"
|
||||
'noabort ltags)
|
||||
(setq ltags nil))
|
||||
(unless (magit-confirm t
|
||||
"Delete %s from remote"
|
||||
"Delete %d tags from remote"
|
||||
'noabort rtags)
|
||||
(setq rtags nil))
|
||||
(list ltags rtags remote)))
|
||||
(when tags
|
||||
(magit-call-git "tag" "-d" tags))
|
||||
(when remote-tags
|
||||
@@ -161,52 +161,52 @@ of the highest existing tag, provided that contains the corresponding
|
||||
version string, and substituting the new version string for that. If
|
||||
that is not the case, propose a message using a reasonable format."
|
||||
(interactive
|
||||
(save-match-data
|
||||
(pcase-let*
|
||||
((args (magit-tag-arguments))
|
||||
(`(,pver ,ptag ,pmsg) (car (magit--list-releases)))
|
||||
(msg (magit-rev-format "%s"))
|
||||
(ver (and (string-match magit-release-commit-regexp msg)
|
||||
(match-str 1 msg)))
|
||||
(_ (and (not ver)
|
||||
(require (quote sisyphus) nil t)
|
||||
(string-match magit-release-commit-regexp
|
||||
(magit-rev-format "%s" ptag))
|
||||
(user-error "Use `sisyphus-create-release' first")))
|
||||
(tag (cond
|
||||
((not ptag)
|
||||
;; Force the user to review the message used for the
|
||||
;; initial release tag, in case they do not like the
|
||||
;; default format.
|
||||
(cl-pushnew "--edit" args :test #'equal)
|
||||
(read-string "Create first release tag: "
|
||||
(if (and ver (string-match-p "\\`[0-9]" ver))
|
||||
(concat "v" ver)
|
||||
ver)))
|
||||
(ver
|
||||
(concat (and (string-match magit-release-tag-regexp ptag)
|
||||
(match-str 1 ptag))
|
||||
ver))
|
||||
((read-string (format "Create release tag (previous was %s): "
|
||||
ptag)
|
||||
ptag))))
|
||||
(ver (and (string-match magit-release-tag-regexp tag)
|
||||
(match-str 2 tag))))
|
||||
(list tag
|
||||
(and (seq-some (apply-partially
|
||||
#'string-match-p
|
||||
"\\`--\\(annotate\\|local-user\\|sign\\)")
|
||||
args)
|
||||
(cond ((and pver (string-match (regexp-quote pver) pmsg))
|
||||
(replace-match ver t t pmsg))
|
||||
((and ptag (string-match (regexp-quote ptag) pmsg))
|
||||
(replace-match tag t t pmsg))
|
||||
((format "%s %s"
|
||||
(capitalize
|
||||
(file-name-nondirectory
|
||||
(directory-file-name (magit-toplevel))))
|
||||
ver))))
|
||||
args))))
|
||||
(save-match-data
|
||||
(pcase-let*
|
||||
((args (magit-tag-arguments))
|
||||
(`(,pver ,ptag ,pmsg) (car (magit--list-releases)))
|
||||
(msg (magit-rev-format "%s"))
|
||||
(ver (and (string-match magit-release-commit-regexp msg)
|
||||
(match-str 1 msg)))
|
||||
(_ (and (not ver)
|
||||
(require (quote sisyphus) nil t)
|
||||
(string-match magit-release-commit-regexp
|
||||
(magit-rev-format "%s" ptag))
|
||||
(user-error "Use `sisyphus-create-release' first")))
|
||||
(tag (cond
|
||||
((not ptag)
|
||||
;; Force the user to review the message used for the
|
||||
;; initial release tag, in case they do not like the
|
||||
;; default format.
|
||||
(cl-pushnew "--edit" args :test #'equal)
|
||||
(read-string "Create first release tag: "
|
||||
(if (and ver (string-match-p "\\`[0-9]" ver))
|
||||
(concat "v" ver)
|
||||
ver)))
|
||||
(ver
|
||||
(concat (and (string-match magit-release-tag-regexp ptag)
|
||||
(match-str 1 ptag))
|
||||
ver))
|
||||
((read-string (format "Create release tag (previous was %s): "
|
||||
ptag)
|
||||
ptag))))
|
||||
(ver (and (string-match magit-release-tag-regexp tag)
|
||||
(match-str 2 tag))))
|
||||
(list tag
|
||||
(and (seq-some (apply-partially
|
||||
#'string-match-p
|
||||
"\\`--\\(annotate\\|local-user\\|sign\\)")
|
||||
args)
|
||||
(cond ((and pver (string-match (regexp-quote pver) pmsg))
|
||||
(replace-match ver t t pmsg))
|
||||
((and ptag (string-match (regexp-quote ptag) pmsg))
|
||||
(replace-match tag t t pmsg))
|
||||
((format "%s %s"
|
||||
(capitalize
|
||||
(file-name-nondirectory
|
||||
(directory-file-name (magit-toplevel))))
|
||||
ver))))
|
||||
args))))
|
||||
(magit-run-git-with-editor "tag" args (and msg (list "-m" msg)) tag)
|
||||
(set-process-sentinel
|
||||
magit-this-process
|
||||
@@ -249,6 +249,7 @@ a tag qualifies as a release tag."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-transient.el --- Support for transients -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -38,7 +38,8 @@
|
||||
(defclass magit--git-variable (transient-variable)
|
||||
((scope :initarg :scope)
|
||||
(global :initarg :global :initform nil)
|
||||
(default :initarg :default :initform nil)))
|
||||
(default :initarg :default :initform nil)
|
||||
(accessible-format :initform "%i%k %d is %v")))
|
||||
|
||||
(defclass magit--git-variable:choices (magit--git-variable)
|
||||
((choices :initarg :choices)
|
||||
@@ -75,7 +76,9 @@
|
||||
(oref obj scope)))
|
||||
(arg (if (oref obj global) "--global" "--local")))
|
||||
(oset obj variable variable)
|
||||
(oset obj value (if (magit-get-boolean arg variable) "true" "false"))))
|
||||
(oset obj value
|
||||
(and (zerop (magit-process-git t "config" "--bool" arg variable))
|
||||
(buffer-substring (point-min) (1- (point-max)))))))
|
||||
|
||||
;;;; Read
|
||||
|
||||
@@ -93,15 +96,18 @@
|
||||
(when (functionp choices)
|
||||
(setq choices (funcall choices)))
|
||||
(cond-let
|
||||
(current-prefix-arg
|
||||
((or transient-prefer-reading-value current-prefix-arg)
|
||||
(pcase-let*
|
||||
((`(,fallback . ,choices)
|
||||
((`(,unset . ,choices)
|
||||
(magit--git-variable-list-choices obj))
|
||||
(unset (or unset "(unset)"))
|
||||
(choice (magit-completing-read
|
||||
(format "Set `%s' to" (oref obj variable))
|
||||
(if fallback (nconc choices (list fallback)) choices)
|
||||
(nconc (mapcar #'magit--delete-text-properties choices)
|
||||
(list (propertize unset 'face
|
||||
'transient-inactive-value)))
|
||||
nil t)))
|
||||
(if (equal choice fallback) nil choice)))
|
||||
(if (equal choice unset) nil choice)))
|
||||
([value (oref obj value)]
|
||||
(cadr (member value choices)))
|
||||
((car choices)))))
|
||||
@@ -167,36 +173,37 @@
|
||||
'face 'transient-value)))
|
||||
([default (oref obj default)]
|
||||
[default (if (functionp default) (funcall default) default)]
|
||||
(concat (propertize "default:" 'face 'transient-inactive-value)
|
||||
(propertize default 'face 'transient-value)))
|
||||
(if transient-prefer-reading-value
|
||||
(format "unset, using default, which is %s"
|
||||
(propertize default 'face 'transient-value))
|
||||
(concat (propertize "default:" 'face 'transient-inactive-value)
|
||||
(propertize default 'face 'transient-value))))
|
||||
((propertize "unset" 'face 'transient-inactive-value))))
|
||||
|
||||
(cl-defmethod transient-format-value ((obj magit--git-variable:choices))
|
||||
(pcase-let ((`(,fallback . ,choices) (magit--git-variable-list-choices obj)))
|
||||
(concat
|
||||
(propertize "[" 'face 'transient-inactive-value)
|
||||
(mapconcat #'identity choices
|
||||
(propertize "|" 'face 'transient-inactive-value))
|
||||
(and fallback (propertize "|" 'face 'transient-inactive-value))
|
||||
fallback
|
||||
(propertize "]" 'face 'transient-inactive-value))))
|
||||
(if transient-prefer-reading-value
|
||||
(cl-call-next-method)
|
||||
(pcase-let ((`(,fallback . ,choices) (magit--git-variable-list-choices obj)))
|
||||
(concat
|
||||
(propertize "[" 'face 'transient-inactive-value)
|
||||
(mapconcat #'identity choices
|
||||
(propertize "|" 'face 'transient-inactive-value))
|
||||
(and fallback (propertize "|" 'face 'transient-inactive-value))
|
||||
fallback
|
||||
(propertize "]" 'face 'transient-inactive-value)))))
|
||||
|
||||
(defun magit--git-variable-list-choices (obj)
|
||||
(let* ((variable (oref obj variable))
|
||||
(choices (oref obj choices))
|
||||
(globalp (oref obj global))
|
||||
(value nil)
|
||||
(global (magit-git-string "config" "--global" variable))
|
||||
(value (oref obj value))
|
||||
(global (and (not (oref obj global))
|
||||
(magit-git-string "config" "--global" variable)))
|
||||
(defaultp (oref obj default))
|
||||
(default (if (functionp defaultp) (funcall defaultp obj) defaultp))
|
||||
(fallback (oref obj fallback))
|
||||
(fallback (and fallback
|
||||
(and$ (magit-get fallback)
|
||||
(concat fallback ":" $)))))
|
||||
(if (not globalp)
|
||||
(setq value (magit-git-string "config" "--local" variable))
|
||||
(setq value global)
|
||||
(setq global nil))
|
||||
(when (functionp choices)
|
||||
(setq choices (funcall choices)))
|
||||
(cons (cond (global
|
||||
@@ -236,6 +243,7 @@
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-version.el --- The Magit version you are using -*- lexical-binding:t -*-
|
||||
|
||||
(setq magit-version "4.4.2")
|
||||
(setq magit-version "4.5.0")
|
||||
|
||||
(provide 'magit-version)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-wip.el --- Commit snapshots to work-in-progress refs -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -70,6 +70,12 @@ Customize `magit-overriding-githook-directory' to enable use of
|
||||
Git hooks."
|
||||
:package-version '(magit . "2.90.0")
|
||||
:group 'magit-wip
|
||||
:set (lambda (symbol value)
|
||||
(set-default-toplevel-value symbol value)
|
||||
(when (bound-and-true-p magit-wip-mode)
|
||||
(if (eq value 'immediately)
|
||||
(add-hook 'git-commit-post-finish-hook #'magit-wip-commit)
|
||||
(remove-hook 'git-commit-post-finish-hook #'magit-wip-commit))))
|
||||
:type '(choice
|
||||
(const :tag "Yes (safely, just in time)" t)
|
||||
(const :tag "Yes (immediately, with race condition)" immediately)
|
||||
@@ -104,29 +110,28 @@ buffer."
|
||||
:package-version '(magit . "2.90.0")
|
||||
:lighter magit-wip-mode-lighter
|
||||
:global t
|
||||
:set-after '(magit-wip-merge-branch)
|
||||
(cond
|
||||
(magit-wip-mode
|
||||
(add-hook 'after-save-hook #'magit-wip-commit-buffer-file)
|
||||
(add-hook 'magit-after-apply-functions #'magit-wip-commit)
|
||||
(add-hook 'magit-before-change-functions #'magit-wip-commit)
|
||||
(add-hook 'before-save-hook #'magit-wip-commit-initial-backup)
|
||||
(add-hook 'magit-common-git-post-commit-functions #'magit-wip-post-commit)
|
||||
(add-hook 'git-commit-post-finish-hook #'magit-wip-commit-post-editmsg))
|
||||
(t
|
||||
(remove-hook 'after-save-hook #'magit-wip-commit-buffer-file)
|
||||
(remove-hook 'magit-after-apply-functions #'magit-wip-commit)
|
||||
(remove-hook 'magit-before-change-functions #'magit-wip-commit)
|
||||
(remove-hook 'before-save-hook #'magit-wip-commit-initial-backup)
|
||||
(remove-hook 'magit-common-git-post-commit-functions #'magit-wip-post-commit)
|
||||
(remove-hook 'git-commit-post-finish-hook #'magit-wip-commit-post-editmsg))))
|
||||
(magit-wip-mode
|
||||
(add-hook 'after-save-hook #'magit-wip-commit-buffer-file)
|
||||
(add-hook 'magit-after-apply-functions #'magit-wip-commit)
|
||||
(add-hook 'magit-before-change-functions #'magit-wip-commit)
|
||||
(add-hook 'before-save-hook #'magit-wip-commit-initial-backup)
|
||||
(add-hook 'magit-common-git-post-commit-functions #'magit-wip-post-commit)
|
||||
(when (eq magit-wip-merge-branch 'immediately)
|
||||
(add-hook 'git-commit-post-finish-hook #'magit-wip-commit)))
|
||||
(t
|
||||
(remove-hook 'after-save-hook #'magit-wip-commit-buffer-file)
|
||||
(remove-hook 'magit-after-apply-functions #'magit-wip-commit)
|
||||
(remove-hook 'magit-before-change-functions #'magit-wip-commit)
|
||||
(remove-hook 'before-save-hook #'magit-wip-commit-initial-backup)
|
||||
(remove-hook 'magit-common-git-post-commit-functions #'magit-wip-post-commit)
|
||||
(remove-hook 'git-commit-post-finish-hook #'magit-wip-commit))))
|
||||
|
||||
(defun magit-wip-commit-buffer-file (&optional msg)
|
||||
"Commit visited file to a worktree work-in-progress ref."
|
||||
(interactive (list "save %s snapshot"))
|
||||
(when (and (not magit--wip-inhibit-autosave)
|
||||
buffer-file-name
|
||||
(magit-inside-worktree-p t)
|
||||
(magit-file-tracked-p buffer-file-name))
|
||||
(when (magit-wip--commitable-p)
|
||||
(magit-wip-commit-worktree
|
||||
(magit-wip-get-ref)
|
||||
(list buffer-file-name)
|
||||
@@ -147,10 +152,7 @@ buffer."
|
||||
(put 'magit-wip-buffer-backed-up 'permanent-local t)
|
||||
|
||||
(defun magit-wip-commit-initial-backup ()
|
||||
(when (and (not magit-wip-buffer-backed-up)
|
||||
buffer-file-name
|
||||
(magit-inside-worktree-p t)
|
||||
(magit-file-tracked-p buffer-file-name))
|
||||
(when (magit-wip--commitable-p)
|
||||
(let ((magit-save-repository-buffers nil))
|
||||
(magit-wip-commit-buffer-file "autosave %s before save"))
|
||||
(setq magit-wip-buffer-backed-up t)))
|
||||
@@ -159,10 +161,6 @@ buffer."
|
||||
(when (eq magit-wip-merge-branch 'githook)
|
||||
(magit-wip-commit)))
|
||||
|
||||
(defun magit-wip-commit-post-editmsg ()
|
||||
(when (eq magit-wip-merge-branch 'immediately)
|
||||
(magit-wip-commit)))
|
||||
|
||||
;;; Core
|
||||
|
||||
(defun magit-wip-commit (&optional files msg)
|
||||
@@ -189,24 +187,29 @@ commit message."
|
||||
|
||||
(defun magit-wip-commit-worktree (ref files msg)
|
||||
(when (or (not files)
|
||||
;; `update-index' will either ignore (before Git v2.32.0)
|
||||
;; or fail when passed directories (relevant for the
|
||||
;; untracked files code paths).
|
||||
;; "git update-index" either ignores (before Git v2.32.0) or
|
||||
;; fails, when passed directories. This is relevant for the
|
||||
;; untracked files code paths.
|
||||
(setq files (seq-remove #'file-directory-p files)))
|
||||
(let* ((wipref (magit--wip-wtree-ref ref))
|
||||
(parent (magit-wip-get-parent ref wipref))
|
||||
(tree (magit-with-temp-index parent (list "--reset" "-i")
|
||||
(if files
|
||||
;; Note: `update-index' is used instead of `add'
|
||||
;; because `add' will fail if a file is already
|
||||
;; deleted in the temporary index.
|
||||
(magit-wip--git "update-index" "--add" "--remove"
|
||||
"--ignore-skip-worktree-entries"
|
||||
"--" files)
|
||||
(magit-with-toplevel
|
||||
(magit-wip--git "add" "-u" ".")))
|
||||
(magit-git-string "write-tree"))))
|
||||
(magit-wip-update-wipref ref wipref tree parent files msg "worktree"))))
|
||||
(tree (condition-case nil
|
||||
(magit-with-temp-index parent (list "--reset" "-i")
|
||||
(if files
|
||||
;; Use "git update-index" instead of "git add"
|
||||
;; because the latter fails if a file is already
|
||||
;; deleted in the temporary index.
|
||||
(magit-wip--git "update-index" "--add" "--remove"
|
||||
"--ignore-skip-worktree-entries"
|
||||
"--" files)
|
||||
(magit-with-toplevel
|
||||
(magit-wip--git "add" "-u" ".")))
|
||||
(magit-git-string "write-tree"))
|
||||
(error
|
||||
(message "Index locked; no worktree wip commit created")))))
|
||||
(when tree
|
||||
(magit-wip-update-wipref ref wipref tree parent
|
||||
files msg "worktree")))))
|
||||
|
||||
(defun magit-wip--git (&rest args)
|
||||
(if magit-wip-debug
|
||||
@@ -220,29 +223,29 @@ commit message."
|
||||
|
||||
(defun magit-wip-update-wipref (ref wipref tree parent files msg start-msg)
|
||||
(cond
|
||||
((and (not (equal parent wipref))
|
||||
(or (not magit-wip-merge-branch)
|
||||
(not (magit-rev-verify wipref))))
|
||||
(setq start-msg (concat "start autosaving " start-msg))
|
||||
(magit-wip--update-ref wipref start-msg
|
||||
(magit-git-string "commit-tree" "--no-gpg-sign"
|
||||
"-p" parent "-m" start-msg
|
||||
(concat parent "^{tree}")))
|
||||
(setq parent wipref))
|
||||
((and magit-wip-merge-branch
|
||||
(or (not (magit-rev-ancestor-p ref wipref))
|
||||
(not (magit-rev-ancestor-p
|
||||
(concat (magit-git-string "log" "--format=%H"
|
||||
"-1" "--merges" wipref)
|
||||
"^2")
|
||||
ref))))
|
||||
(setq start-msg (format "merge %s into %s" ref start-msg))
|
||||
(magit-wip--update-ref wipref start-msg
|
||||
(magit-git-string "commit-tree" "--no-gpg-sign"
|
||||
"-p" wipref "-p" ref
|
||||
"-m" start-msg
|
||||
(concat ref "^{tree}")))
|
||||
(setq parent wipref)))
|
||||
((and (not (equal parent wipref))
|
||||
(or (not magit-wip-merge-branch)
|
||||
(not (magit-rev-verify wipref))))
|
||||
(setq start-msg (concat "start autosaving " start-msg))
|
||||
(magit-wip--update-ref wipref start-msg
|
||||
(magit-git-string "commit-tree" "--no-gpg-sign"
|
||||
"-p" parent "-m" start-msg
|
||||
(concat parent "^{tree}")))
|
||||
(setq parent wipref))
|
||||
((and magit-wip-merge-branch
|
||||
(or (not (magit-rev-ancestor-p ref wipref))
|
||||
(not (magit-rev-ancestor-p
|
||||
(concat (magit-git-string "log" "--format=%H"
|
||||
"-1" "--merges" wipref)
|
||||
"^2")
|
||||
ref))))
|
||||
(setq start-msg (format "merge %s into %s" ref start-msg))
|
||||
(magit-wip--update-ref wipref start-msg
|
||||
(magit-git-string "commit-tree" "--no-gpg-sign"
|
||||
"-p" wipref "-p" ref
|
||||
"-m" start-msg
|
||||
(concat ref "^{tree}")))
|
||||
(setq parent wipref)))
|
||||
(when (magit-git-failure "diff-tree" "--quiet" parent tree "--" files)
|
||||
(unless (and msg (not (= (aref msg 0) ?\s)))
|
||||
(let ((len (length files)))
|
||||
@@ -289,6 +292,13 @@ commit message."
|
||||
(concat "refs/heads/" branch))
|
||||
"HEAD")))
|
||||
|
||||
(defun magit-wip--commitable-p ()
|
||||
(and (not magit--wip-inhibit-autosave)
|
||||
buffer-file-name
|
||||
(magit-inside-worktree-p t)
|
||||
(magit-file-tracked-p buffer-file-name)
|
||||
(magit-wip-get-ref)))
|
||||
|
||||
;;; Log
|
||||
|
||||
(defun magit-wip-log-index (args files)
|
||||
@@ -307,9 +317,9 @@ With a negative prefix argument only show the worktree wip ref.
|
||||
The absolute numeric value of the prefix argument controls how
|
||||
many \"branches\" of each wip ref are shown."
|
||||
(interactive
|
||||
(nconc (list (or (magit-get-current-branch) "HEAD"))
|
||||
(magit-log-arguments)
|
||||
(list (prefix-numeric-value current-prefix-arg))))
|
||||
(nconc (list (or (magit-get-current-branch) "HEAD"))
|
||||
(magit-log-arguments)
|
||||
(list (prefix-numeric-value current-prefix-arg))))
|
||||
(magit-wip-log branch args files count))
|
||||
|
||||
(defun magit-wip-log (branch args files count)
|
||||
@@ -318,16 +328,16 @@ With a negative prefix argument only show the worktree wip ref.
|
||||
The absolute numeric value of the prefix argument controls how
|
||||
many \"branches\" of each wip ref are shown."
|
||||
(interactive
|
||||
(nconc (list (magit-completing-read
|
||||
"Log branch and its wip refs"
|
||||
(nconc (magit-list-local-branch-names)
|
||||
(list "HEAD"))
|
||||
nil t nil 'magit-revision-history
|
||||
(or (magit-branch-at-point)
|
||||
(magit-get-current-branch)
|
||||
"HEAD")))
|
||||
(magit-log-arguments)
|
||||
(list (prefix-numeric-value current-prefix-arg))))
|
||||
(nconc (list (magit-completing-read
|
||||
"Log branch and its wip refs"
|
||||
(nconc (magit-list-local-branch-names)
|
||||
(list "HEAD"))
|
||||
nil t nil 'magit-revision-history
|
||||
(or (magit-branch-at-point)
|
||||
(magit-get-current-branch)
|
||||
"HEAD")))
|
||||
(magit-log-arguments)
|
||||
(list (prefix-numeric-value current-prefix-arg))))
|
||||
(magit-log-setup-buffer (nconc (list branch)
|
||||
(magit-wip-log-get-tips
|
||||
(magit--wip-wtree-ref branch)
|
||||
@@ -383,6 +393,7 @@ many \"branches\" of each wip ref are shown."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit-worktree.el --- Worktree support -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -143,11 +143,11 @@ just \"PREFIX_\". Always forward PROMPT as-is."
|
||||
COMMIT may, but does not have to be, a local branch.
|
||||
Interactively, use `magit-read-worktree-directory-function'."
|
||||
(interactive
|
||||
(let ((commit (magit-read-branch-or-commit
|
||||
"In new worktree; checkout" nil
|
||||
(mapcar #'caddr (magit-list-worktrees)))))
|
||||
(list (magit--read-worktree-directory commit (magit-local-branch-p commit))
|
||||
commit)))
|
||||
(let ((commit (magit-read-branch-or-commit
|
||||
"In new worktree; checkout" nil
|
||||
(mapcar #'caddr (magit-list-worktrees)))))
|
||||
(list (magit--read-worktree-directory commit (magit-local-branch-p commit))
|
||||
commit)))
|
||||
(when (zerop (magit-run-git "worktree" "add"
|
||||
(magit--expand-worktree directory) commit))
|
||||
(magit-diff-visit-directory directory)))
|
||||
@@ -157,11 +157,11 @@ Interactively, use `magit-read-worktree-directory-function'."
|
||||
"Create a new BRANCH and check it out in a new worktree at DIRECTORY.
|
||||
Interactively, use `magit-read-worktree-directory-function'."
|
||||
(interactive
|
||||
(pcase-let
|
||||
((`(,branch ,start-point)
|
||||
(magit-branch-read-args "In new worktree; checkout new branch")))
|
||||
(list (magit--read-worktree-directory branch t)
|
||||
branch start-point)))
|
||||
(pcase-let
|
||||
((`(,branch ,start-point)
|
||||
(magit-branch-read-args "In new worktree; checkout new branch")))
|
||||
(list (magit--read-worktree-directory branch t)
|
||||
branch start-point)))
|
||||
(when (zerop (magit-run-git "worktree" "add" "-b" branch
|
||||
(magit--expand-worktree directory) start-point))
|
||||
(magit-diff-visit-directory directory)))
|
||||
@@ -170,11 +170,11 @@ Interactively, use `magit-read-worktree-directory-function'."
|
||||
(defun magit-worktree-move (worktree directory)
|
||||
"Move existing WORKTREE directory to DIRECTORY."
|
||||
(interactive
|
||||
(list (magit-completing-read "Move worktree"
|
||||
(cdr (magit-list-worktrees))
|
||||
nil t nil nil
|
||||
(magit-section-value-if 'worktree))
|
||||
(read-directory-name "Move worktree to: ")))
|
||||
(list (magit-completing-read "Move worktree"
|
||||
(cdr (magit-list-worktrees))
|
||||
nil t nil nil
|
||||
(magit-section-value-if 'worktree))
|
||||
(read-directory-name "Move worktree to: ")))
|
||||
(if (file-directory-p (expand-file-name ".git" worktree))
|
||||
(user-error "You may not move the main working tree")
|
||||
(let ((preexisting-directory (file-directory-p directory)))
|
||||
@@ -194,18 +194,31 @@ Interactively, use `magit-read-worktree-directory-function'."
|
||||
"Delete a worktree, defaulting to the worktree at point.
|
||||
The primary worktree cannot be deleted."
|
||||
(interactive
|
||||
(list (magit-completing-read "Delete worktree"
|
||||
(mapcar #'car (cdr (magit-list-worktrees)))
|
||||
nil t nil nil
|
||||
(magit-section-value-if 'worktree))))
|
||||
(list (magit-completing-read "Delete worktree"
|
||||
(mapcar #'car (cdr (magit-list-worktrees)))
|
||||
nil t nil nil
|
||||
(magit-section-value-if 'worktree))))
|
||||
(if (file-directory-p (expand-file-name ".git" worktree))
|
||||
(user-error "Deleting %s would delete the shared .git directory" worktree)
|
||||
(let ((primary (file-name-as-directory (caar (magit-list-worktrees)))))
|
||||
(magit-confirm-files (if magit-delete-by-moving-to-trash 'trash 'delete)
|
||||
(list worktree))
|
||||
(when (file-exists-p worktree)
|
||||
(let ((delete-by-moving-to-trash magit-delete-by-moving-to-trash))
|
||||
(delete-directory worktree t magit-delete-by-moving-to-trash)))
|
||||
(let (uncommitted)
|
||||
(magit-confirm
|
||||
(cond ((let ((default-directory worktree))
|
||||
(or (magit-anything-modified-p)
|
||||
(magit-untracked-files)))
|
||||
(setq uncommitted 'danger))
|
||||
(magit-delete-by-moving-to-trash 'trash)
|
||||
('delete))
|
||||
(format "%s worktree \"%s\"%s"
|
||||
(if magit-delete-by-moving-to-trash "Trash" "Delete")
|
||||
(file-name-nondirectory (directory-file-name worktree))
|
||||
(if uncommitted " despite uncommitted changes" ""))
|
||||
nil nil (list worktree)))
|
||||
(if magit-delete-by-moving-to-trash
|
||||
(let ((delete-by-moving-to-trash t))
|
||||
(delete-directory worktree t t))
|
||||
(magit-call-git "worktree" "remove" "--force" worktree)))
|
||||
(if (file-exists-p default-directory)
|
||||
(magit-run-git "worktree" "prune")
|
||||
(let ((default-directory primary))
|
||||
@@ -221,13 +234,13 @@ minibuffer. If the worktree at point is the one whose
|
||||
status is already being displayed in the current buffer,
|
||||
then show it in Dired instead."
|
||||
(interactive
|
||||
(list (or (magit-section-value-if 'worktree)
|
||||
(magit-completing-read
|
||||
"Show status for worktree"
|
||||
(cl-delete (directory-file-name (magit-toplevel))
|
||||
(magit-list-worktrees)
|
||||
:test #'equal :key #'car)
|
||||
nil t))))
|
||||
(list (or (magit-section-value-if 'worktree)
|
||||
(magit-completing-read
|
||||
"Show status for worktree"
|
||||
(cl-delete (directory-file-name (magit-toplevel))
|
||||
(magit-list-worktrees)
|
||||
:test #'equal :key #'car)
|
||||
nil t))))
|
||||
(magit-diff-visit-directory worktree))
|
||||
|
||||
(defun magit--expand-worktree (directory)
|
||||
@@ -254,18 +267,21 @@ If there is only one worktree, then insert nothing."
|
||||
(let* ((cols
|
||||
(mapcar
|
||||
(lambda (config)
|
||||
(pcase-let ((`(,_ ,commit ,branch ,bare) config))
|
||||
(pcase-let ((`(,directory ,commit ,branch ,bare) config))
|
||||
(cons (cond
|
||||
(branch
|
||||
(propertize
|
||||
branch 'font-lock-face
|
||||
(if (equal branch (magit-get-current-branch))
|
||||
'magit-branch-current
|
||||
'magit-branch-local)))
|
||||
(commit
|
||||
(propertize (magit-rev-abbrev commit)
|
||||
'font-lock-face 'magit-hash))
|
||||
(bare "(bare)"))
|
||||
(branch
|
||||
(propertize
|
||||
branch 'font-lock-face
|
||||
(if (equal branch (magit-get-current-branch))
|
||||
'magit-branch-current
|
||||
'magit-branch-local)))
|
||||
(commit
|
||||
(propertize
|
||||
(magit-rev-abbrev commit) 'font-lock-face
|
||||
(if (file-equal-p default-directory directory)
|
||||
'(magit-hash magit-branch-current)
|
||||
'magit-hash)))
|
||||
(bare "(bare)"))
|
||||
config)))
|
||||
worktrees))
|
||||
(align (1+ (apply #'max (mapcar (##string-width (car %)) cols)))))
|
||||
@@ -300,6 +316,7 @@ with padding for alignment."
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;;; magit.el --- A Git porcelain inside Emacs -*- lexical-binding:t; coding:utf-8 -*-
|
||||
|
||||
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
||||
;; Copyright (C) 2008-2026 The Magit Project Contributors
|
||||
|
||||
;; Author: Marius Vollmer <marius.vollmer@gmail.com>
|
||||
;; Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
||||
@@ -17,16 +17,16 @@
|
||||
;; Homepage: https://github.com/magit/magit
|
||||
;; Keywords: git tools vc
|
||||
|
||||
;; Package-Version: 20251217.1836
|
||||
;; Package-Revision: 655bc502a3bd
|
||||
;; Package-Version: 20260401.2251
|
||||
;; Package-Revision: 6db34dc77d10
|
||||
;; Package-Requires: (
|
||||
;; (emacs "28.1")
|
||||
;; (compat "30.1")
|
||||
;; (cond-let "0.1")
|
||||
;; (cond-let "0.2")
|
||||
;; (llama "1.0")
|
||||
;; (magit-section "4.4")
|
||||
;; (magit-section "4.5")
|
||||
;; (seq "2.24")
|
||||
;; (transient "0.10")
|
||||
;; (transient "0.12")
|
||||
;; (with-editor "3.4"))
|
||||
|
||||
;; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
@@ -805,6 +805,7 @@ For X11 something like ~/.xinitrc should work.\n"
|
||||
;; ("and>" . "cond-let--and>")
|
||||
;; ("and-let" . "cond-let--and-let")
|
||||
;; ("if-let" . "cond-let--if-let")
|
||||
;; ("when$" . "cond-let--when$")
|
||||
;; ("when-let" . "cond-let--when-let")
|
||||
;; ("while-let" . "cond-let--while-let")
|
||||
;; ("match-string" . "match-string")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user