Ive got two commits in my git log
commit a38056f9da4dcf48c188e79fe632b3624e6ffef4 (HEAD, main)
Author: user
commit 2
commit 801608941f024901799208e328bb0d3908c2ba7a
Author: user
commit 1
, I wanna go back to commit 1, which I can do with git checkout 801608941f024901799208e328bb0d3908c2ba7a
.
Problem is, when I do this, my git log
turns to
commit 801608941f024901799208e328bb0d3908c2ba7a
Author: user
commit 1
And I can't find the SHA for commit 2 anymore. If I find commit 2's SHA by scrolling up to my first git log
then git checkout
commit 2's SHA, I go back to commit 2's snapshot as expected, but it's annoying that the git log
doesn't show commit 2's SHA Is this normal? If so, how am I supposed to find the commit history for commit 2
now?
CodePudding user response:
TL;DR
Run:
git switch main
or:
git checkout main
Long-ish
Git hasn't removed anything. You just can't see it. Here's why: The git log
command works by going backwards.
History, in a Git repository, is nothing more or less than the commits that are in that repository. Git finds commits by their big ugly hash IDs:
a38056f9da4dcf48c188e79fe632b3624e6ffef4
801608941f024901799208e328bb0d3908c2ba7a
Git desperately needs these hash IDs in order to find the commits. But these hash IDs are very bad for humans (quick, is 801608941f024901799208e328bb0d3908c2ba7a
the same as 801608941f024901797208e328bb0d3908c2ba7a
?). So we don't normally use the hash IDs. Git does use the hash IDs, but Git provides us branch names and tag names and many other kinds of names. Those are what we normally use.
Each commit stores two things:
- Directly, each commit stores some metadata, giving information such as the name and email address of the person who made the commit.
- Indirectly, each commit stores a full snapshot of every file.
All of this stuff, once inside a commit, is completely read-only: it can never be changed, not even by Git itself, as long as you retrieve that commit by its big ugly hash ID. That hash ID is the "true name" of that commit.
Inside each commit's metadata, Git stores a list of previous commits, and this is how git log
actually works: it starts at some particular commit, which has some particular big ugly hash ID, and git log
shows you that commit. Then git log
uses that commit's metadata to find the previous commit. The git log
command now shows that commit, and then uses its metadata to step backwards once again.
The end result of this is that you see all the commits, one at a time, backwards, from wherever you start (or is it "end"?), following the internal chains that Git forges as you work:
... <-F <-G <-H
Here the uppercase letters stand in for those big ugly hash IDs, so H
is short for some Hash. If you give Git the hash ID of H
, it shows you H
, then it uses H
to find the hash ID for G
. We say that commit H
points to earlier commit G
. Git then shows G
, and then uses G
's metadata to find the hash ID for F
, and so on.
But: if you run git log
without giving it a hash ID, how does it know where to start? The answer is that Git has the notion of a current commit, which Git finds using the special, magic name HEAD
.
HEAD usually contains a branch name
Because hash IDs are bad for humans, we tend not to use them. Git offers us the option of creating any number of branch names. Each branch name stores exactly one hash ID, and whatever hash ID is "inside" the branch name, that's the last commit that's "on" that branch:
... <-F <-G <-H <--main
Here the name main
points to H
, just as H
points to G
and G
points to F
and so on. So git log main
will start at H
and work backwards.
We can have as many branch names as we like, all pointing directly to commit H
:
...--G--H <-- main, develop, feature1, feature2
To remember which name is the current name, Git will attach the special name HEAD
to exactly one of these branch names:
...--G--H <-- main, develop, feature1 (HEAD), feature2
Here we are "on" branch feature1
—running git status
will say on branch feature1
—and git log
, without any starting point, will use the name feature1
to find commit H
, and show that commit, then G
and F
and whatever.
Detached HEAD mode
If, however, we run:
git checkout <hash-id>
for some big ugly hash ID, Git stores that hash ID directly in the special name HEAD
, and now we have:
...--F <-- HEAD
\
G--H <-- main, develop, ...
Running git log
now starts at commit F
and works backwards. What happened to commits G
and H
? Nothing at all: they're still in there. You just need to start git log
at commit H
, to see them. To do that, you can run:
git log main
since main
points to H
; or you can git switch main
or git checkout main
to re-attach HEAD
to the branch name, so that git log
starts there.
Note that commits are often on many branches
Given:
...--G--H <-- main, develop, feature1 (HEAD), feature2
which branch(es) are the commits on?
The answer is: all of them!
If we make a new commit now, though, here's what happens:
...--G--H <-- main, develop, feature2
\
I <-- feature1 (HEAD)
Git will not only write out the new commit, giving it a new unique hash ID (the big ugly hash IDs are unique), it will set up the new commit so that it points back to commit H
—the commit we were using as the current commit just a moment ago—and then write the new commit's hash ID into the current branch name.
This is how branches normally grow, one commit at a time.
There is a whole lot more, but these are essential things to know, to use Git.