git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [FEATURE] Proposal: git stash --only-unstaged

From
PWPhillip Wood <phillip.wood123@gmail.com>
Date
Aug 16, 2025, 16:12 UTC
Message-ID
<60f38225-2bcb-4383-be40-36c7b02cf38c@gmail.com>
In-Reply-To
<12fe17735317215aa0de58a20055193a@dettweb.de>
On 13/08/2025 09:51, J. Dettweiler wrote:
Show 6 quoted lines
> 
> **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##*/} [<stash>]" 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
Previous: D. Ben KnobleNext: Junio C Hamano
Message 4 of 11 in “[FEATURE] Proposal: git stash --only-unstaged”
  1. J. DettweilerAug 13, 2025
  2. Junio C HamanoAug 13, 2025
  3. D. Ben KnobleAug 13, 2025
  4. Phillip WoodAug 16, 2025
  5. Junio C HamanoAug 17, 2025
  6. Phillip WoodAug 18, 2025
  7. Ben KnobleAug 18, 2025
  8. Phillip WoodAug 29, 2025
  9. D. Ben KnobleAug 29, 2025
  10. Phillip WoodSep 16, 2025
  11. D. Ben KnobleSep 16, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.