I return from 'detached HEAD' state and git deleted my .env file from the local directory

1.2k views Asked by At

I was checking my commit history to fixing a bug. I use git checkout 4a3cf4ebfc8c3d4a7f8b055b3f38cc90acf2a0cd to Switching between commits. I was in - 'detached HEAD' state.

in that state, I use some command like: git add . git commit -m "massage" Then I Switch different commits by git checkout e255fb94e967c4c1463c25e44090bfc5a40b8463.

I return from 'detached HEAD' state by using the command: git checkout master.

Now, my .env file is gone from the local directory.

I am creating a react app. I am using .gitignore file to ignore my API key in .env file.

1

There are 1 answers

1
torek On

Given some particular conditions (which must be the case here), this is normal.

We start with these facts:

  • Git stores commits. Git does not store files, nor branches, but rather entire commits. (Each commit does store files, but you work with an entire commit at a time. Branch names help you—and Git—find commits, because the commit numbers are so random-looking.)

  • Each commit has a unique number, which looks random, but isn't: it's the commit's hash ID. The hash ID is actually a cryptographic checksum of the contents of the commit. No part of any commit can ever be changed.

  • Each commit actually stores two things: a snapshot—a complete set of all of your files—and some metadata. The entire commit is completely read-only, and all the files in the commit are stored in a read-only, Git-only, compressed and de-duplicated form.

  • This in turn means that you cannot actually work with the files inside a commit. The files that you see and work with are copied out of some commit, into an area Git calls your working tree or work-tree.

  • Hence the git checkout command, when given a name like master or a raw commit hash like e255fb94e967c4c1463c25e44090bfc5a40b8463, has to copy the files that are stored (forever) in that commit (but are not usable by programs like ReactJS) into your work-tree (where they are are usable by programs like ReactJS).

This means that the files you see and work with, in your work-tree, are not actually in Git in the first place. They're just copies of files from some commit in Git.

Now, suppose that the last commit of your master branch is a123456.... I made up hash ID, but we can be pretty sure that whatever the actual hash ID is, it's not e255fb94e967c4c1463c25e44090bfc5a40b8463. So your Git has, inside it, as two different commits:

  • e255fb94e967c4c1463c25e44090bfc5a40b8463: this commit has a .env file in it.

  • a123456...: this commit does not have a .env file in it.

When you check out commit e255fb94e967c4c1463c25e44090bfc5a40b8463, Git must extract the saved .env file to your work-tree.

When you switch back to a123456..., Git must remove that saved .env file to get it out of the way.

I am using .gitignore file to ignore my API key in .env file.

Unfortunately, the .gitignore file does not actually tell Git to ignore files. It can't: commit e255fb94e967c4c1463c25e44090bfc5a40b8463 has the .env file in it, and no part of any existing commit can ever be changed.

So, when you extract commit e255fb94e967c4c1463c25e44090bfc5a40b8463, Git copies the .env file out of that commit (into both Git's index or staging area, and your work-tree). If your .env file has valuable data in it, this copying-out process could destroy the valuable data.

Usually, when git checkout might destroy the contents of a file, Git will warn you about this, and refuse to do the git checkout until you have saved those file contents elsewhere. Unfortunately, one of the side effects of listing a file in .gitignore is that it sometimes (not always, but sometimes) gives Git permission to destroy the file's contents.

Perhaps, though, the .env file contents you had that were in your work-tree, but not in commit a123456..., matched the contents in commit e255fb94e967c4c1463c25e44090bfc5a40b8463. If that was the case, git checkout e255fb94e967c4c1463c25e44090bfc5a40b8463 left the contents in place, without actually destroying anything. Unfortunately Git will still remove the file when switching back, but you can, at this point, instruct Git to retrieve the contents of that one particular file from that one particular commit:

git show e255fb94e967c4c1463c25e44090bfc5a40b8463:.env > .env

for instance.