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.
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 likemaster
or a raw commit hash likee255fb94e967c4c1463c25e44090bfc5a40b8463
, 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 isa123456...
. I made up hash ID, but we can be pretty sure that whatever the actual hash ID is, it's note255fb94e967c4c1463c25e44090bfc5a40b8463
. 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.Unfortunately, the
.gitignore
file does not actually tell Git to ignore files. It can't: commite255fb94e967c4c1463c25e44090bfc5a40b8463
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 thegit 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 commita123456...
, matched the contents in commite255fb94e967c4c1463c25e44090bfc5a40b8463
. 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:for instance.