Home > Enterprise >  restore untracked and unstaged changes without affecting staged changes
restore untracked and unstaged changes without affecting staged changes

Time:12-24

I have a pre-commit hook that runs a script on the whole code base as if the commit was completed. To accomplish this I first git stash -k -u to stash my unstaged changes and untracked files. Then I run the check (which at this point only contains committed and staged code). Afterwards, I want to put the repo back in the same state it was before. This means the staged changes remain and the working tree is restored to as it was, including untracked files and unstaged changes.

I'm not sure exactly how to do the last part. First I tried git stash pop, but this fails when files with staged changes also have unstaged changes. Next I tried git restore -s stash . which fixes the previous problem, but doesn't restore the untracked changes. It's as if the . in the command refers to the layout of the working tree rather than the layout of source tree. Is this a bug? Is there some other way to accomplish what I'm describing? I'm out of ideas.

CodePudding user response:

emphasized textI have a pre-commit hook that runs a script on the whole code base as if the commit [had been] completed. To accomplish this I first git stash -k -u to stash my unstaged changes and untracked files. Then I run the check (which at this point only contains committed and staged code). Afterwards, I want to put the repo back in the same state it was before.

The recommended method for this is:

git reset --hard
git stash pop --index

The pop step here should always succeed, provided the git stash push step did anything in the first place. That last "provided" means there's an important proviso: you should check to see whether git stash -k -u actually made a stash, because under some conditions, it does nothing at all. In this case the git reset --hard is unnecessary (but safe as long as your tests did not modify anything), and the git stash pop must not be run since that would pop some other stash.

(How do you know whether git stash push actually created a stash? I recommend a two-step git rev-parse sequence, which works back into older version of git stash where the push verb doesn't even exist—where you have to use git stash save instead—where you do:

old=$(git rev-parse -q --verify refs/stash || true)
git stash push -k -u
new=$(git rev-parse -q --verify refs/stash || true)

You can now test whether $current and $new match as strings:

test "$old" != "$new" && do_unstash=true || do_unstash=false

for instance. Now you can condition your final git stash pop sequence with:

if $do_unstash; then git reset --hard && git stash pop --index; fi

If the push step made a stash, the outputs of the two git rev-parse commands differ: either $old is empty and $new is non-empty and is the hash ID of the stash, or $old is non-empty and $new is non-empty and they differ and $new is again the hash ID of the stash. But if git stash push decided to do nothing, then either $old is empty and $new is empty, or $old contains the old top-of-stash-stack hash ID and $new contains the same hash ID.

If you have the space for it, though, I'd suggest running the tests on a "clean" git checkout-index run in an empty temporary directory, after which you can just remove the empty temporary directory. This does not require any fancy git stash work, and hence functions correctly even when git stash has been broken (as it has been in several recent Git versions where, apparently based on questions here on StackOverflow, sometimes git stash pop silently fails to restore untracked files). This also neatly sidesteps all weird corner cases, such as intent-to-add index entries that never actually got added.

CodePudding user response:

Not sure this is a good answer, but there is git stash show --only-untracked-v (no idea why the --only-untracked switch even exists), which outputs the diff of only the untracked files in the stash. This diff can be piped into git apply - to recreate the file.

In total...

git stash show --only-untracked -v | git apply -
  •  Tags:  
  • git
  • Related