Git: drop file in a stash without applying it

2019-09-19 00:30发布

问题:

I am surprised not having found a response to this question but basically say I have a stash with several files in it (I can explore it through git extension for instance). Looking at its content, I'd like to drop some files before applying it, is it possible ?

The only workaround I can think of is:

  1. checkout the stash to a new branch
  2. checkout the targeted files
  3. stash again
  4. delete the branch

Is there something better ?

thanks

回答1:

You can directly use stash@{0} as a commit reference.

If your working tree is clean (no modified files), you can checkout the files you want :

git checkout stash@{0} -- file1 file2 file3

and then inspect the diff manually to keep what you want.


If you want something closer to the behaviour of git stash apply, you can create a patch and apply it :

git show -p stash@{0} -- file1 file2 file3 | git apply -

[EDIT] To list the files modified in stash@{0} :

git diff --name-only stash@{0}^ stash@{0}

If you want to get all files except one :

git show -p stash@{0} -- $(git diff --name-only stash@{0}^ stash@{0} |\
                           grep -v "thefile")


回答2:

Yes there is a better way to do it.

First of all, You can check the stash list as

git stash list

Then you can do below for checking contents of the stashed commits.

git stash show -p stash@{1}

Then you can apply using

git stash apply stash@{1}


回答3:

It seems like the tricky part of this question revolves around what you want the stash to look like when you're done. Since git stash {pop|apply} only affects the working tree (and sometimes index), it's easy enough to selectively take changes from a stash (enough so that I initially thought you were over-thinking the problem):

git stash pop
# maybe a 'git reset head' if the index was changed
git checkout -- file.with.unwanted.changes.in.stash

But those commands remove the stash entirely; and by contrast if I'd said git stash apply instead of pop, that leaves the stash unchanged. It sounds like you want the stash to remain with the changes you've selected (in case you should need to apply them again).

(I suppose alternately there could be times you'd want exactly the opposite - to leave in the stash those changes that you didn't apply, so they can be applied later.)

So taking a step back: what is involved in modifying a stash?

A stash, really, comprises two or three temporary commits. The special ref stash points to these commits, and a "stack" of multiple stashes is maintained using the reflog (which may be a bit of a hack, but generally is effective).

So to modify the stash is to create new commits (because commits are immutable) and rearrange the stash ref and reflog accordingly.

This is why the notations LeGEC mentions will work and may give you extra flexibility in reading a stash. But because the reflog usage is not the same as for non-stash refs, leveraging this fact to contrive a way to write to a stash could have unexpected results. I'm not saying the results would be horrible, or impossible to predict/control; but I think in the end you'd create more complexity than you'd avoid.

So if we confine ourselves to actual documented git-stash subcommands, then a stash is more or less atomic. When you create a stash there's a patch mode that lets you select working changes to put in the stack, but I'm aware of no corresponding functionality for pop/apply. Still, that's something:

If you have a clean work tree and a stash you want to split, you could try things like:

Apply some changes, and keep those same changes in the stash

git stash pop
git stash -p
# select the "good" changes
git checkout -- .
git stash apply

Apply some changes, and keep the remaining changes in the stash

git stash pop
git stash -p
# select the "unwanted" changes

Split the stash into two parts, decide what to do with them later

git stash pop
git stash -p
# select the "good" changes
git stash
# now the "good" changes are stash@{1} and the "unwanted" are stash@{0}


标签: git git-stash