Andy Ray
Andy Ray

Reputation: 32076

git stash and pop shows file no longer marked as moved?

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

Answers (2)

manojlds
manojlds

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

Cascabel
Cascabel

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

Related Questions