branch: elpa/magit commit e52dedf07f7726528cd006db88235c23a481351d Author: Jonas Bernoulli <jo...@bernoul.li> Commit: Jonas Bernoulli <jo...@bernoul.li>
magit-stash-{pop,apply}: Stop after successfully installing conflict If we cannot successfully install the conflict with "git stash apply --index", we also try "git stash apply", and if that fails as well, we continue with "git apply --3way". That assumed that we could tell whether a stash was either applied with no conflict or the conflict was successfully installed by, or else the conflict could not be installed, by checking status. Unfortunately it turns out that the exit status is also 1 if the conflict was successfully installed. Instead we have to look at the output. Unfortunately we can only do that for Git v2.38.0 and later, and have to fall back to the less convenient behavior provided by Git itself. Closes #5253. --- CHANGELOG | 6 +++++ docs/magit.org | 10 +++++++ docs/magit.texi | 10 +++++++ lisp/magit-stash.el | 77 +++++++++++++++++++++++++++++++++++++---------------- 4 files changed, 80 insertions(+), 23 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ed2544303bb..b11e9147f19 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,12 @@ - Improved ~magit-process-password-prompt-regexps~. #5257 +Bug fixes: + +- ~magit-stash-pop~ and ~magit-stash-apply~ sometimes installed conflicts + for the user to resolve that are more complicated than they need to + be. #5253 + * v4.1.2 2024-11-02 - Add various minor process logging improvements: diff --git a/docs/magit.org b/docs/magit.org index 14fdc7f9b55..981fbb76a80 100644 --- a/docs/magit.org +++ b/docs/magit.org @@ -6423,6 +6423,11 @@ Also see [[man:git-stash]] Apply a stash to the working tree. + When using a Git release before v2.38.0, simply run ~git stash apply~ + or with a prefix argument ~git stash apply --index~. + + When using Git v2.38.0 or later, behave more intelligently: + First try ~git stash apply --index~, which tries to preserve the index stored in the stash, if any. This may fail because applying the stash could result in conflicts and those have to @@ -6448,6 +6453,11 @@ Also see [[man:git-stash]] stash can be applied without any conflicts, and while preserving the stash's index) then remove the stash from stash list. + When using a Git release before v2.38.0, simply run ~git stash pop~ + or with a prefix argument ~git stash pop --index~. + + When using Git v2.38.0 or later, behave more intelligently: + First try ~git stash pop --index~, which tries to preserve the index stored in the stash, if any. This may fail because applying the stash could result in conflicts and those have to diff --git a/docs/magit.texi b/docs/magit.texi index f00d9e7fff9..b4ee44b3129 100644 --- a/docs/magit.texi +++ b/docs/magit.texi @@ -7913,6 +7913,11 @@ prefix arguments are equivalent to @code{--all}-. @findex magit-stash-apply Apply a stash to the working tree. +When using a Git release before v2.38.0, simply run @code{git stash apply} +or with a prefix argument @code{git stash apply --index}. + +When using Git v2.38.0 or later, behave more intelligently: + First try @code{git stash apply --index}, which tries to preserve the index stored in the stash, if any. This may fail because applying the stash could result in conflicts and those have to @@ -7939,6 +7944,11 @@ Apply a stash to the working tree. On complete success (if the stash can be applied without any conflicts, and while preserving the stash's index) then remove the stash from stash list. +When using a Git release before v2.38.0, simply run @code{git stash pop} +or with a prefix argument @code{git stash pop --index}. + +When using Git v2.38.0 or later, behave more intelligently: + First try @code{git stash pop --index}, which tries to preserve the index stored in the stash, if any. This may fail because applying the stash could result in conflicts and those have to diff --git a/lisp/magit-stash.el b/lisp/magit-stash.el index 19d07865519..b929332224a 100644 --- a/lisp/magit-stash.el +++ b/lisp/magit-stash.el @@ -262,6 +262,11 @@ specifying a list of files to be stashed." (defun magit-stash-apply (stash) "Apply a stash to the working tree. +When using a Git release before v2.38.0, simply run \"git stash +apply\" or with a prefix argument \"git stash apply --index\". + +When using Git v2.38.0 or later, behave more intelligently: + First try \"git stash apply --index\", which tries to preserve the index stored in the stash, if any. This may fail because applying the stash could result in conflicts and those have to @@ -284,6 +289,11 @@ the user whether to use \"--3way\" or \"--reject\"." (defun magit-stash-pop (stash) "Apply a stash to the working tree, on success remove it from stash list. +When using a Git release before v2.38.0, simply run \"git stash +pop\" or with a prefix argument \"git stash pop --index\". + +When using Git v2.38.0 or later, behave more intelligently: + First try \"git stash pop --index\", which tries to preserve the index stored in the stash, if any. This may fail because applying the stash could result in conflicts and those have to @@ -303,29 +313,50 @@ the user whether to use \"--3way\" or \"--reject\"." (magit-stash--apply "pop" stash)) (defun magit-stash--apply (action stash) - (or (= (magit-call-git "stash" action "--index" stash) 0) - ;; The stash's index could not be applied, so always keep the stash. - (= (magit-call-git "stash" "apply" stash) 0) - (let* ((range (format "%s^..%s" stash stash)) - (stashed (magit-git-items "diff" "-z" "--name-only" range "--")) - (conflicts (cl-sort (cl-union (magit-unstaged-files t stashed) - (magit-untracked-files t stashed) - :test #'equal) - #'string<)) - (arg (cond - ((not conflicts) "--3way") - ((magit-confirm-files - 'stash-apply-3way conflicts - "Apply stash using `--3way', which requires first staging" - "(else use `--reject')" - t) - (magit-stage-1 nil conflicts) - "--3way") - ("--reject")))) - (with-temp-buffer - (magit-git-insert "diff" range) - (magit-run-git-with-input "apply" arg "-")))) - (magit-refresh)) + (if (magit-git-version< "2.38.0") + (magit-run-git "stash" action stash (and current-prefix-arg "--index")) + (or (magit--run-git-stash action "--index" stash) + ;; The stash's index could not be applied, so always keep the stash. + (magit--run-git-stash "apply" stash) + (let* ((range (format "%s^..%s" stash stash)) + (stashed (magit-git-items "diff" "-z" "--name-only" range "--")) + (conflicts (cl-sort (cl-union (magit-unstaged-files t stashed) + (magit-untracked-files t stashed) + :test #'equal) + #'string<)) + (arg (cond + ((not conflicts) "--3way") + ((magit-confirm-files + 'stash-apply-3way conflicts + "Apply stash using `--3way', which requires first staging" + "(else use `--reject')" + t) + (magit-stage-1 nil conflicts) + "--3way") + ("--reject")))) + (with-temp-buffer + (magit-git-insert "diff" range) + (magit-run-git-with-input "apply" arg "-")))) + (magit-refresh))) + +(defun magit--run-git-stash (&rest args) + (magit--with-temp-process-buffer + (let ((exit (save-excursion + (with-environment-variables (("LC_ALL" "en_US.utf8")) + (magit-process-git t "stash" args)))) + (buffer (current-buffer)) + (failed (looking-at "\\`error: \ +Your local changes to the following files would be overwritten by merge"))) + (with-current-buffer (magit-process-buffer t) + (magit-process-finish-section + (magit-process-insert-section default-directory magit-git-executable + (magit-process-git-arguments args) + exit buffer) + exit)) + (pcase (list exit failed) + (`(0 ,_) t) ; no conflict + (`(1 nil) t) ; successfully installed conflict + (_ nil))))) ; could not install conflict, or genuine error ;;;###autoload (defun magit-stash-drop (stash)