git stash restoring index state of deleted and renamed files
Remember: git does not track file renames. It tracks file contents.
When the index contains a deletion and a creation for different files with the same contents, git's rename detection will (probably) conclude that's a rename, and helpfully display it as such. This doesn't actually change what's in the index, though. What "really" happened was indeed a deletion-then-creation, git is just trying to display it more helpfully.
Try it yourself:
$ cp file1 boink$ rm file1$ git add .$ git status
Has the same effect as:
$ git mv file1 boink$ git status
This rename detection only works on things that have been added to the index. Do git reset
now and you'll see what I mean:
$ git reset$ git statusChanges not staged for commit: deleted: file1Untracked files: boink
We can use the git command git ls-files
to list the files present in the index. In effect, these are the files git considers existent at the moment.When we have a rename staged as above, the output from this is:
# git ls-filesboinkfile2file3file4
As far as the index is concerned, file1 no longer exists. We deleted it, and then we created boink
. git status
is friendly and shows us the results of rename detection, but remember that the index doesn't care about that.
Now we run git stash -k -u
. -k
tells stash not to touch the index, so it doesn't. Consider for a moment the first paragraph of the manpage for stash
:
Use git stash when you want to record the current state of the working directory and the index, but want to go back to a clean working directory. The command saves your local modifications away and reverts the working directory to match the HEAD commit.
So: we have asked stash to save our local modifications (without touching the index), and revert the working directory to match the HEAD commit.file1
exists in the HEAD commit, so it comes back. But file1
is no longer in the index, because we deleted it. Hence, file1
is now an untracked file. Since we're not touching the index, the new file boink
and the record that it was the result of a rename from file1
remains in the index. Hence you get the potentially surprising output from git status
:
Changes to be committed: renamed: file1 -> boinkUntracked files: file1
There is output of your script. As you can see there is no file3
and file4
after unstash. This files appear after stash -k
. Which is normal behaviour becouse stash
restores deleted files. And -k make it keep index changes which is deletion file3
from index. So you have both D file3
and ?? file3
at same time.
Initializing a fresh git dir...rm: refusing to remove '.' or '..' directory: skipping '.'rm: refusing to remove '.' or '..' directory: skipping '..'Initialized empty Git repository in /home/azzel/projects/test/bash/gitstash/gitStashIssue/.git/Preparing git state for test...[master (root-commit) 7e891f6] initial commit 4 files changed, 4 insertions(+) create mode 100644 file1 create mode 100644 file2 create mode 100644 file3 create mode 100644 file4rm 'file3'Current git status is:MM file1M file2D file3R file4 -> fileRenamed?? untrackedFileStasing changes...Saved working directory and index state WIP on master: 7e891f6 initial commitgit status after stashing files is:M file1M file2D file3R file4 -> fileRenamed?? file3?? file4cleaning up deleted and renamed files...Removing file3Removing file4git status after cleanup:M file1M file2D file3R file4 -> fileRenamedCommiting unstashed changes...[master 1156712] commit unstashed changes 4 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 file3 rename file4 => fileRenamed (100%)Auto-merging file1CONFLICT (content): Merge conflict in file1Recorded preimage for 'file1'Index was not unstashed.git status after unstashing:UU file1?? untrackedFile