Git stash removes added changes

During development, I regularly add working versions of files (but do not oblige) to my git repository. I continue to work on these files until they reach a suitable scene, when I can complete them. So the repo looks lower

$ git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: testfile1 # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: testfile1 # modified: testfile2 

When I do git stash and then do git stash pop , I get

 # On branch master # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: testfile1 # modified: testfile2 

Questions

  • Why doesn't git hide them the way they were before?
  • How can I cross out my changes at a time, so that when I run the tooltip, I get an older state, not a newer one?

I am currently doing manually

 git stash --keep-index git stash git stash pop git add <stashed_files> git stash pop 

The problem with this is

  • It takes 5 steps to hide and pop, and I'm looking for 2
  • Sometimes I may not remember that there are two backs to backs, which complicates the task a bit.

Edit - I would prefer a command line solution, since I worked in a similar way on test servers.

+7
git git-stash git-index
source share
2 answers

use the --index option.

 git stash git stash pop --index 
+3
source share

I see that this has already been answered, but let me add a little more, and a warning: there is a buglet in git stash .

When running git stash or git stash save (by default it is save , so it’s the same thing) without using -p , the stash script -it lives in the git-core directory, the location of which varies depending on the git installation, maybe, for example , in /usr/libexec/git-core or /usr/local/libexec/git-core - creates a commit with two (or sometimes three) parent commits. To do this, these commits:

  • current index
  • with -u or -a , raw or even ignored files (and also uses git clean to cut them from the working directory)
  • a working directory based on deltas between the current working directory and the HEAD commit (this is the source of the booglet, see below).

Then it sets refs/stash to point to the last of these commits, the working directory commits. This commit has its parents:

  • HEAD command, like stash^ (first parent)
  • commit index, like stash^2 (second parent)
  • an untraceable / ignored commit, like stash^3 (third parent) if one exists.

This wallet actually contains everything that was in place at the time of the entry, but the heading appears best when you use git stash pop --index or git stash apply --index to restore the "pre-locking state". (I will use git stash apply exclusively below, but pop is just apply , followed by drop .)

Now, if you just run git stash apply , as you noted, it will give you many changes not staged for commit files, although you have carefully set some things before starting git stash save . This is because it is much easier to merge these changes, like this, regardless of the state of the working directory, including if you checked another branch or something else, and turned it on if you execute some files before running git stash apply . (In fact, git stash apply uses git code merging to make changes to the working folder.)

If you run git stash apply --index , however, the stash script first tries to add exactly what you had at the time the original save created to the index. (If nothing is delivered, this restores your original state.) Assuming that this can do it, he then tries to set up the working directory in the same way (again, using merge mechanisms). If it cannot configure the index correctly, it does nothing for the index and prompts you to try again without --index .

Here is where the buglet comes in. Suppose you start with a file, say basefile , unchanged. You will make the changes and follow these steps:

 $ cat basefile base $ git status --short $ echo add to basefile >> basefile; git add basefile 

but then you decide that you want the copy of the working directory to not change from the HEAD version:

 $ ed basefile 21 2d w 5 q $ git status --short MM basefile 

The hard bit here is that the basefile changes in the index and then changes again in the working directory, but the second change brings it back to what is in the HEAD commit. When you run git stash save , the stash script accidentally writes the index version, as if it were an incomplete version.

If you now run git stash apply --index and run git status --short :

 $ git stash save Saved working directory and index state WIP on master: 94824e1 initial HEAD is now at 94824e1 initial stash created $ git stash apply --index # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: basefile # $ git status --short M basefile 

Here git restored the index version to index, and then installed the working directory version of the same one as in the index version:

 $ cat basefile base add to basefile 

The fix for stash script is a one-word change, but still doesn't like it. Perhaps the problem is that if you use stash without --index , it effectively combines the index change (extra line, add to basefile ) with nothing, so the version of the working directory has an extra line. However, this is not consistent with how it behaves when different versions of the index and working directory are different:

 $ git stash drop Dropped refs/ stash@ {0} (61c83c866bc522c58df62320b77e647ffd28aa95) $ echo base > basefile $ git status --short $ echo add to basefile >> basefile $ git add basefile $ ed basefile 21 2c different change w 22 q $ git status --short MM basefile $ git stash save Saved working directory and index state WIP on master: 94824e1 initial HEAD is now at 94824e1 initial $ git stash apply # On branch master # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: basefile # no changes added to commit (use "git add" and/or "git commit -a") $ cat basefile base different change 

Here, “putting together”, the index and the processing tree change, but without --index we only restore changes to the working tree.

(Fortunately, since we use apply instead of pop , we can change our mind now:

 $ git reset --hard HEAD HEAD is now at 94824e1 initial $ git stash apply -q --index $ git status --short MM basefile 

and if we look at the index and work-dir versions, we can now see both versions of the basefile .)

(One-word fix in stash script is changing HEAD to $i_tree in line:

 git diff --name-only -z HEAD -- >"$TMP-stagenames" && 

around line 118. I posted this on the git mailing list and got ... crickets. :-))

+5
source share

All Articles