9

Assume I have stashed some changes using one of:

git stash -u
git stash --include-untracked

I can checkout an individual file from the stash via

git checkout stash@{0} -- filename

That works if git knows about "filename", but doesn't work if "filename" is untracked. Is there any way to checkout untracked files from a stash?

eakst7
  • 641
  • 6
  • 12
  • Assuming you want to delete the untracked file, this is a possible duplicate of http://stackoverflow.com/questions/61212/removing-untracked-files-from-your-git-working-copy. `Checkout` means you want to restore another version of a file. If the file is untracked, it is a new one - there is no previous version - so you have to remove it using `git clean`. – Raul Rene May 12 '14 at 12:07
  • @RaulRene, I don't think that's a duplicate. It looks like the OP is trying to *partially apply* a stash, including an untracked file. – ChrisGPT was on strike May 12 '14 at 12:12
  • @Chris, yes, that's essentially what I'm trying to do. I've got a scratch branch that I experiment with but don't generally commit to. I want to pull over a few select changes into my real topic branch. (Yes, I'm sure there's a better workflow... :-) ) – eakst7 May 12 '14 at 12:14
  • I've found the most expedient solution was to simply add the files on my scratch branch and create a new stash. The files are now tracked and I can pull them into my real branch. Still, for curiosity sake, I'd like to know if it's possible to get at an untracked file in a stash. – eakst7 May 12 '14 at 12:16

3 Answers3

23

git stash internally creates special black magic merge commits to store different parts of your changes. The merge commit has the original base commit (what you had at the top of the branch when you stashed) as its first parent, a throwaway commit representing the index contents at time of stashing as its second parent, and (only if you used --include-untracked) a throwaway commit containing the untracked files as its third parent.

So, the merge commit references the untracked files (as one of its parents)... but it doesn't actually include those files in its own tree (if that doesn't make any sense, either you've got a few things to learn yet about Git's internals... or you know too much about merge commits and this whole construct just seems too horrible to think about ;)).

In short... to access the untracked parts of your stash, access its third parent: git checkout stash@{0}^3 -- filename

Jan Krüger
  • 17,870
  • 3
  • 59
  • 51
  • Amazing magic, thank you! To restore both parts from the stash (tracked and untracked), I used `git checkout stash@{0} -- . && git checkout stash@{0}^3 -- .`. Works like a charm, finally! – Erik Koopmans Apr 22 '19 at 17:13
  • What does it mean when you get `fatal: invalid reference: stash@{0}^3` in bash ? I googled but haven't found a workable answer yet. – steveb Nov 15 '20 at 18:27
  • @steveb the third parent only exists if you used `--include-untracked` when creating the stash, so if you didn't, the `^3` doesn't point to anything. (Also, if you don't have any stashes at all, even the `stash@{0}` part on its own is invalid...) – Jan Krüger Nov 21 '20 at 04:19
  • @JanKrüger I did a `git stash -u`, which should include them. I was able to figure out how to show / list the untracked files that are stashed. – steveb Nov 22 '20 at 05:38
2

I've got a scratch branch that I experiment with but don't generally commit to. I want to pull over a few select changes into my real topic branch.

In that case, don't bother with stashing the changes to move them from one branch to another. Do directly a git commit, and move the changes from the branch you committed on to the other one using git cherry-pick: git cherry-pick #hash_of_the_commit.

Raul Rene
  • 10,014
  • 9
  • 53
  • 75
1

For me, Jan Krüger's answer somehow did not work out (git 2.19.1). My workaround to checkout individual files from a stash:

Find commit id of stash commit part with untracked files (commit message is prefixed with "untracked files"):

git log --name-status stash@{0}

Then, do a normal git checkout of a file in this commit:

git checkout <commit id> -- <your file pathspec>
ford04
  • 66,267
  • 20
  • 199
  • 171