Reputation: 32076
git mv file1 file2
git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# renamed: file1 -> file2
git stash
git stash pop
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: file2
#
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: file1
As you can see, git loses the renamed relationship after a stash / pop. Is there any way to regain this relationship, or have stash know that files were moved? I often stash to see what my system state is like pre-changes, but having it lose the rename relationship is a problem for me. I don't know how to fix it other than deleting the new file, doing a git mv again, and replacing the new file's contents.
Upvotes: 41
Views: 7700
Reputation: 301437
If you haven't already popped your stash, do:
git stash pop --index
This correctly preserves moved (but not committed) file relationships in a stash. According to git help stash
:
If the --index option is used, then tries to reinstate not only the working tree's changes, but also the index's ones. However, this can fail, when you have conflicts (which are stored in the index, where you therefore can no longer apply the changes as they were originally).
If you've already popped your stash and want to re-create the moved relationship, do:
git rm --cached file1
This removes the "old" unmoved file from the index:
Use this option to unstage and remove paths only from the index
Upvotes: 53
Reputation: 497472
Short answer: git rm --cached file1
All git mv
really does is rename the file (on the filesystem) and then add a file deletion and creation to the index (staging area). It's not specially marked. Other machinery subsequently realizes that it was a rename, by seeing that the content that was called file1 before is now called file2.
Since git stash
commands modify the index, they can perturb things. Note that now you have the creation staged, but not the deletion! (Normally git-stash leaves everything unstaged after a pop, but in this case I think it doesn't have much choice but to put the new file in the index, to avoid you having no idea which one to keep. You can avoid this ever happening with git stash pop --index
, but that bombs out if the stashed changes can't be cleanly applied, so it's not default.) git status
can't possibly show it as a rename anymore, because the rename is effectively split between the index and the work tree, and neither section can fully claim it.
Simply run git rm --cached file1
to stage the deletion (i.e. remove file1 from the index) and it will show up as a rename again. You could also run git add -u
to automatically add changes, as long as you have no other changes that you don't want to stage.
Note that this means that in practice you don't need to worry: when you properly stage everything in preparation to commit (e.g. with git add -u
), the "problem" takes care of itself.
Upvotes: 21