From: Phillip Wood Date: Sat, 16 Aug 2025 16:12:47 GMT Subject: Re: [FEATURE] Proposal: git stash --only-unstaged Message-ID: <60f38225-2bcb-4383-be40-36c7b02cf38c@gmail.com> In-Reply-To: <12fe17735317215aa0de58a20055193a@dettweb.de> On 13/08/2025 09:51, J. Dettweiler wrote: > > **Current limitations:** > - `git stash --keep-index` still saves the index in the stash object. > - When I later `git stash pop`, Git tries to restore those staged > changes, often causing merge conflicts if I’ve modified them during the > test. As the index and the worktree are saved in the stash we can restore the unstaged changes with a three-way merge between the stashed index, HEAD and the stashed worktree. The (lightly tested) script below does that and updates the worktree without changing the index unless there are conflicts. There will only be conflicts when a staged line that is adjacent to an unstaged line is changed. Changes to staged lines that are not next to unstaged lines should not create conflicts as they are only modified on one side of the merge. To use it create your stash with "git stash push --keep-index" and then use the script to pop the unstaged changes rather than using "git stash pop". If the script proves to be useful then perhaps we could add an "--unstaged" option to "git stash pop" Thanks Phillip ---- 8< ---- #!/bin/sh USAGE="${0##*/} []" SUBDIRECTORY_OK=1 . "$(git --exec-path)/git-sh-setup" cd_to_toplevel require_clean_work_tree stash-pop-unstaged LF=' ' stash="${1:-stash@{0}}" if ! stash_oid="$(git rev-parse --verify --quiet "$stash")" || ! oid1="$(git rev-parse --verify --quiet $stash_oid^1)" || ! oid2="$(git rev-parse $stash_oid^2^@ 2>/dev/null)" || test "$oid1" != "$oid2" then die "error: '$stash' does not look like a stash commit" fi merge_output="$(git merge-tree --merge-base=$stash_oid^2: HEAD: $stash_oid:)" status=$? if test $status = 0 then tree=$merge_output conflict_info= messages= elif test $status = 1 then merge_output="$merge_output$LF" tree="${merge_output%%$LF*}" conflict_info="${merge_output%%$LF$LF*}" conflict_info="${conflict_info#*$LF}" messages="${merge_output#*$LF$LF}" else exit 128 fi git read-tree --index-output="$GIT_DIR/stash-pop-index" -m -u HEAD $tree && rm "$GIT_DIR/stash-pop-index" && if test -n "$conflict_info" then printf '%s' "$messages" >&2 printf '%s\n' "$conflict_info" | git update-index --index-info else git stash drop "$stash" fi