branch: elpa/git-commit commit ad68015aa1f7cab19eae0d86983193fa2b7038ca Author: Jonas Bernoulli <jo...@bernoul.li> Commit: Jonas Bernoulli <jo...@bernoul.li>
magit-confirm: Directly support two rounds of prompt formatting The PROMPT and PROMPT-N arguments are used as f-strings passed to `format'. Many callers of this function themselves use `format' themselves to prepare these arguments. The "%s" in the original f-string, which isn't to be replaced until the second round, has to be escaped as "%%s". Many of the first round `format' calls are used to embed branch names and other refs into the prompt, and here a problem arises: "%" is allowed in Git refs, so we may end up with unescaped "%d" in the final f-string. To avoid having to escape "%s" in many callers, add support for handling the first round of prompt formatting inside `magit-confirm'. PROMPT and PROMPT-N can now be lists of the form (F-STRING . ARGS), in which case they applied to `format', after escaping every "%s" in every argument that is a string. Closes #5178. --- lisp/magit-apply.el | 13 +++++++------ lisp/magit-base.el | 10 ++++++++++ lisp/magit-branch.el | 16 ++++++++-------- lisp/magit-push.el | 12 ++++-------- lisp/magit-remote.el | 4 ++-- lisp/magit-stash.el | 2 +- 6 files changed, 32 insertions(+), 25 deletions(-) diff --git a/lisp/magit-apply.el b/lisp/magit-apply.el index 2c7a79601f..7ad55e1401 100644 --- a/lisp/magit-apply.el +++ b/lisp/magit-apply.el @@ -548,9 +548,10 @@ of a side, then keep that side without prompting." (funcall apply section "--reverse" "--index")))) (defun magit-discard-hunks (sections) - (magit-confirm 'discard (format "Discard %d hunks from %s" - (length sections) - (magit-section-parent-value (car sections)))) + (magit-confirm 'discard + (list "Discard %d hunks from %s" + (length sections) + (magit-section-parent-value (car sections)))) (magit-discard-apply-n sections #'magit-apply-hunks)) (defun magit-discard-apply-n (sections apply) @@ -738,9 +739,9 @@ so causes the change to be applied to the index as well." (defun magit-reverse-hunks (sections args) (magit-confirm 'reverse - (format "Reverse %d hunks from %s" - (length sections) - (magit-section-parent-value (car sections)))) + (list "Reverse %d hunks from %s" + (length sections) + (magit-section-parent-value (car sections)))) (magit-reverse-apply sections #'magit-apply-hunks args)) (defun magit-reverse-file (section args) diff --git a/lisp/magit-base.el b/lisp/magit-base.el index a1c00f27d5..af809b286d 100644 --- a/lisp/magit-base.el +++ b/lisp/magit-base.el @@ -794,6 +794,16 @@ ACTION is a member of option `magit-slow-confirm'." (cl-defun magit-confirm ( action &optional prompt prompt-n noabort (items nil sitems) prompt-suffix) (declare (indent defun)) + (when (and prompt (listp prompt)) + (setq prompt + (apply #'format (car prompt) + (mapcar (lambda (a) (if (stringp a) (string-replace "%" "%%" a) a)) + (cdr prompt))))) + (when (and prompt-n (listp prompt-n)) + (setq prompt-n + (apply #'format (car prompt-n) + (mapcar (lambda (a) (if (stringp a) (string-replace "%" "%%" a) a)) + (cdr prompt-n))))) (setq prompt-n (format (concat (or prompt-n prompt) "? ") (length items))) (setq prompt (format (concat (or prompt (magit-confirm-make-prompt action)) "? ") diff --git a/lisp/magit-branch.el b/lisp/magit-branch.el index e313da4f78..d696265935 100644 --- a/lisp/magit-branch.el +++ b/lisp/magit-branch.el @@ -633,12 +633,12 @@ prompt is confusing." (offset (1+ (length remote)))) (cond ((magit-confirm 'delete-branch-on-remote - (format "Deleting local %s. Also delete on %s" - (magit-ref-fullname (car branches)) - remote) - (format "Deleting %d local refs. Also delete on %s" - (length refs) - 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. @@ -724,8 +724,8 @@ prompt is confusing." (when (member refspec refspecs) (if (and (length= refspecs 1) (magit-confirm 'delete-pr-remote - (format "Also delete remote %s (%s)" remote - "no pull-request branch remains") + (list "Also delete remote %s (%s)" remote + "no pull-request branch remains") nil t)) (magit-call-git "remote" "rm" remote) (magit-call-git "config" "--unset-all" variable diff --git a/lisp/magit-push.el b/lisp/magit-push.el index ddeeec9490..8e89d7362c 100644 --- a/lisp/magit-push.el +++ b/lisp/magit-push.el @@ -87,10 +87,8 @@ argument the push-remote can be changed before pushed to it." (magit--select-push-remote "push there"))) (when changed (magit-confirm 'set-and-push - (string-replace - "%" "%%" - (format "Really use \"%s\" as push-remote and push \"%s\" there" - remote branch)))) + (list "Really use \"%s\" as push-remote and push \"%s\" there" + remote branch))) (run-hooks 'magit-credential-hook) (magit-run-git-async "push" "-v" args remote (format "refs/heads/%s:refs/heads/%s" @@ -150,10 +148,8 @@ the upstream." ;; is what the user wants to happen. (setq merge (concat "refs/heads/" merge))) (magit-confirm 'set-and-push - (string-replace - "%" "%%" - (format "Really use \"%s\" as upstream and push \"%s\" there" - upstream branch)))) + (list "Really use \"%s\" as upstream and push \"%s\" there" + upstream branch))) (cl-pushnew "--set-upstream" args :test #'equal)) (run-hooks 'magit-credential-hook) (magit-run-git-async "push" "-v" args remote (concat branch ":" merge)))) diff --git a/lisp/magit-remote.el b/lisp/magit-remote.el index 3cc22b88e3..2cf913cffd 100644 --- a/lisp/magit-remote.el +++ b/lisp/magit-remote.el @@ -206,8 +206,8 @@ the now stale refspecs. Other stale branches are not removed." (if (if (length= stale 1) (pcase-let ((`(,refspec . ,refs) (car stale))) (magit-confirm 'prune-stale-refspecs - (format "Prune stale refspec %s and branch %%s" refspec) - (format "Prune stale refspec %s and %%d branches" refspec) + (list "Prune stale refspec %s and branch %%s" refspec) + (list "Prune stale refspec %s and %%d branches" refspec) nil refs)) (magit-confirm 'prune-stale-refspecs nil (format "Prune %%d stale refspecs and %d branches" diff --git a/lisp/magit-stash.el b/lisp/magit-stash.el index 37b60cf2f6..4ab2e20872 100644 --- a/lisp/magit-stash.el +++ b/lisp/magit-stash.el @@ -348,7 +348,7 @@ 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 (format "Drop all stashes in %s" ref)) + (magit-confirm t (list "Drop all stashes in %s" ref)) (list ref))) (magit-run-git "update-ref" "-d" ref))