branch: elpa/git-commit commit 7d4092f00bab34d46c4e07d6d6c55658e413b3b6 Author: Jonas Bernoulli <jo...@bernoul.li> Commit: Jonas Bernoulli <jo...@bernoul.li>
Apply stash even if "git stash apply" cannot do it --- docs/magit.org | 56 +++++++++++++++++++++++++++++++++++------ docs/magit.texi | 57 ++++++++++++++++++++++++++++++++++++------ lisp/magit-base.el | 8 ++++++ lisp/magit-stash.el | 72 ++++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 167 insertions(+), 26 deletions(-) diff --git a/docs/magit.org b/docs/magit.org index c2aa7328cc..e16223ca2f 100644 --- a/docs/magit.org +++ b/docs/magit.org @@ -1525,6 +1525,13 @@ telling Magit to ask fewer questions. - Various: + - ~stash-apply-3way~ When a stash cannot be applied using + ~git stash apply~, then Magit uses ~git apply~ instead. + If doing so is safe, then it uses ~--3way~, when it is not + because doing so requires that some files are first staged, + then by default it prompts the user whether to use ~--3way~ + or ~--reject~. Add this symbol to always use ~--3way~. + - ~kill-process~ There seldom is a reason to kill a process. - Global settings: @@ -6396,16 +6403,51 @@ Also see [[man:git-stash]] - Key: z a (magit-stash-apply) :: - Apply a stash to the working tree. Try to preserve the stash index. - If that fails because there are staged changes, apply without - preserving the stash index. + Apply a stash to the working tree. + + 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 + be stored in the index, making it impossible to also store the + stash's index there as well. + + If the above failed, then try ~git stash apply~. This fails + (with or without ~--index~) if there are any uncommitted + changes to files that are also modified in the stash. + + If both of the above failed, then apply using ~git apply~. + If there are no conflicting files, use ~--3way~. If there are + conflicting files, then using ~--3way~ requires that those + files are staged first, which may be undesirable, so prompt + the user whether to use ~--3way~ or ~--reject~. + + Customize ~magit-no-confirm~ if you want to always use ~--3way~, + without being prompted. - Key: z p (magit-stash-pop) :: - Apply a stash to the working tree and remove it from stash list. - Try to preserve the stash index. If that fails because there are - staged changes, apply without preserving the stash index and forgo - removing the stash. + 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. + + 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 + be stored in the index, making it impossible to also store the + stash's index there as well. + + If the above failed, then try ~git stash apply~. This fails + (with or without ~--index~) if there are any uncommitted + changes to files that are also modified in the stash. + + If both of the above failed, then apply using ~git apply~. + If there are no conflicting files, use ~--3way~. If there are + conflicting files, then using ~--3way~ requires that those + files are staged first, which may be undesirable, so prompt + the user whether to use ~--3way~ or ~--reject~. + + Customize ~magit-no-confirm~ if you want to always use ~--3way~, + without being prompted. - Key: z k (magit-stash-drop) :: diff --git a/docs/magit.texi b/docs/magit.texi index 543e615f9a..4fcf527fc5 100644 --- a/docs/magit.texi +++ b/docs/magit.texi @@ -2021,6 +2021,14 @@ set @code{magit-published-branches} to @code{nil}. Various: @itemize +@item +@code{stash-apply-3way} When a stash cannot be applied using +@code{git stash apply}, then Magit uses @code{git apply} instead. +If doing so is safe, then it uses @code{--3way}, when it is not +because doing so requires that some files are first staged, +then by default it prompts the user whether to use @code{--3way} +or @code{--reject}. Add this symbol to always use @code{--3way}. + @item @code{kill-process} There seldom is a reason to kill a process. @end itemize @@ -7889,17 +7897,52 @@ prefix arguments are equivalent to @code{--all}-. @item @kbd{z a} (@code{magit-stash-apply}) @kindex z a @findex magit-stash-apply -Apply a stash to the working tree. Try to preserve the stash index. -If that fails because there are staged changes, apply without -preserving the stash index. +Apply a stash to the working tree. + +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 +be stored in the index, making it impossible to also store the +stash's index there as well. + +If the above failed, then try @code{git stash apply}. This fails +(with or without @code{--index}) if there are any uncommitted +changes to files that are also modified in the stash. + +If both of the above failed, then apply using @code{git apply}. +If there are no conflicting files, use @code{--3way}. If there are +conflicting files, then using @code{--3way} requires that those +files are staged first, which may be undesirable, so prompt +the user whether to use @code{--3way} or @code{--reject}. + +Customize @code{magit-no-confirm} if you want to always use @code{--3way}, +without being prompted. @item @kbd{z p} (@code{magit-stash-pop}) @kindex z p @findex magit-stash-pop -Apply a stash to the working tree and remove it from stash list. -Try to preserve the stash index. If that fails because there are -staged changes, apply without preserving the stash index and forgo -removing the stash. +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. + +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 +be stored in the index, making it impossible to also store the +stash's index there as well. + +If the above failed, then try @code{git stash apply}. This fails +(with or without @code{--index}) if there are any uncommitted +changes to files that are also modified in the stash. + +If both of the above failed, then apply using @code{git apply}. +If there are no conflicting files, use @code{--3way}. If there are +conflicting files, then using @code{--3way} requires that those +files are staged first, which may be undesirable, so prompt +the user whether to use @code{--3way} or @code{--reject}. + +Customize @code{magit-no-confirm} if you want to always use @code{--3way}, +without being prompted. @item @kbd{z k} (@code{magit-stash-drop}) @kindex z k diff --git a/lisp/magit-base.el b/lisp/magit-base.el index 8aad2fb3af..64c0a99805 100644 --- a/lisp/magit-base.el +++ b/lisp/magit-base.el @@ -168,6 +168,7 @@ The value has the form ((COMMAND nil|PROMPT DEFAULT)...). (const remove-modules) (const remove-dirty-modules) (const trash-module-gitdirs) + (const stash-apply-3way) (const kill-process) (const safe-with-wip))) @@ -317,6 +318,13 @@ Removing modules: Various: + `stash-apply-3way' When a stash cannot be applied using + \"git stash apply\", then Magit uses \"git apply\" instead. + If doing so is safe, then it uses \"--3way\", when it is not + because doing so requires that some files are first staged, + then by default it prompts the user whether to use \"--3way\" + or \"--reject\". Add this symbol to always use \"--3way\". + `kill-process' There seldom is a reason to kill a process. Global settings: diff --git a/lisp/magit-stash.el b/lisp/magit-stash.el index b784658b67..beed295563 100644 --- a/lisp/magit-stash.el +++ b/lisp/magit-stash.el @@ -237,23 +237,71 @@ specifying a list of files to be stashed." ;;;###autoload (defun magit-stash-apply (stash) "Apply a stash to the working tree. -Try to preserve the stash index. If that fails because there -are staged changes, apply without preserving the stash index." + +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 +be stored in the index, making it impossible to also store the +stash's index there as well. + +If the above failed, then try \"git stash apply\". This fails +\(with or without \"--index\") if there are any uncommitted +changes to files that are also modified in the stash. + +If both of the above failed, then apply using \"git apply\". +If there are no conflicting files, use \"--3way\". If there are +conflicting files, then using \"--3way\" requires that those +files are staged first, which may be undesirable, so prompt +the user whether to use \"--3way\" or \"--reject\"." (interactive (list (magit-read-stash "Apply stash"))) - (if (= (magit-call-git "stash" "apply" "--index" stash) 0) - (magit-refresh) - (magit-run-git "stash" "apply" stash))) + (magit-stash--apply "apply" stash)) ;;;###autoload (defun magit-stash-pop (stash) - "Apply a stash to the working tree and remove it from stash list. -Try to preserve the stash index. If that fails because there -are staged changes, apply without preserving the stash index -and forgo removing the stash." + "Apply a stash to the working tree, on success remove it from stash list. + +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 +be stored in the index, making it impossible to also store the +stash's index there as well. + +If the above failed, then try \"git stash apply\". This fails +\(with or without \"--index\") if there are any uncommitted +changes to files that are also modified in the stash. + +If both of the above failed, then apply using \"git apply\". +If there are no conflicting files, use \"--3way\". If there are +conflicting files, then using \"--3way\" requires that those +files are staged first, which may be undesirable, so prompt +the user whether to use \"--3way\" or \"--reject\"." (interactive (list (magit-read-stash "Pop stash"))) - (if (= (magit-call-git "stash" "apply" "--index" stash) 0) - (magit-stash-drop stash) - (magit-run-git "stash" "apply" stash))) + (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)) ;;;###autoload (defun magit-stash-drop (stash)