-1

I did a git checkout 5c24d6d and lost my commits beyond this point. Then I tried to get back to my old state. To do so I executed below steps. I understand checkout is normally used to switch between branching. Can someone please explain what happened with the steps I followed. With my 1st checkout command, did git create a temporary branch (called HEAD detached at 5c24d6d)? What happened when I checked out origin? Did it create a new temporary branch again and removed the old temporary branch? Lastly, when I switched to master did git removed the temporary branch?

To experiment further: I did a git checkout 5c24d6d and then switched to master using git checkout master, it had the same result.

I was on master branch and did a git checkout 5c24d6d. This changed my working copy to the point of 5c24d6d. It also said that, I am in a detached HEAD state. Then when I did git log I can see logs till 5c24d6d but not the ones done after. At this point I looked at the branch and could see:

* (HEAD detached at 5c24d6d)
  master

As an attempt to recover, I executed git checkout origin. This brought me back to the last commit having SHA1, 8c584c3. At this point the branch looked like this:

* (HEAD detached at origin/master)
  master

Then I switched to branch master using, git checkout master and checked my branch using git branch. Now I can only see:

* master

The branch with head details is gone.

Below are my steps in details:

$ git log --oneline  -10
8c584c3 (HEAD -> master, origin/master, origin/HEAD) deleting test
c4fcf55 adding line 3
fdd4670 adding line 2
8504aa4 adding test
be91837 Deleting .gitignore files from projects
5c24d6d Merge branch 'master' of https://github.com/siddswork/gradle
c80ace7 Creating lib an bin directories inside run with empty .gitignore
e91516d Updating README.md
a54a193 Create README.md
89b60ec Adding makefiles

$ git branch
* master

$ git checkout 5c24d6d
Note: checking out '5c24d6d'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at 5c24d6d Merge branch 'master' of https://github.com/siddswork/gradle

$ git log --oneline  -3 
5c24d6d (HEAD) Merge branch 'master' of https://github.com/siddswork/gradle
c80ace7 Creating lib an bin directories inside run with empty .gitignore
e91516d Updating README.md

$ git branch
* (HEAD detached at 5c24d6d)
  master

$ git checkout origin
Previous HEAD position was 5c24d6d Merge branch 'master' of https://github.com/siddswork/gradle
HEAD is now at 8c584c3 deleting test

$ git branch
* (HEAD detached at origin/master)
  master

$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

$ git branch
* master

$ git log --oneline -10
8c584c3 (HEAD -> master, origin/master, origin/HEAD) deleting test
c4fcf55 adding line 3
fdd4670 adding line 2
8504aa4 adding test
be91837 Deleting .gitignore files from projects
5c24d6d Merge branch 'master' of https://github.com/siddswork/gradle
c80ace7 Creating lib an bin directories inside run with empty .gitignore
e91516d Updating README.md
a54a193 Create README.md
89b60ec Adding makefiles
Sidd
  • 19
  • 4

1 Answers1

0

I understand checkout is normally used to switch between branching ...

Hmm, well, normally is a bit of a loaded word. The git checkout command has, depending on how you count, anywhere from about 3 or 4 normal things, to maybe 7 or 8 normal things, that it does. That's why Git 2.23 has introduced two new commands that mostly do what git checkout does, but without as many opportunities for shooting yourself in the foot.

With my 1st checkout command, did git create a temporary branch (called HEAD detached at 5c24d6d)? What happened when I checked out origin? Did it create a new temporary branch again and removed the old temporary branch? Lastly, when I switched to master did git removed the temporary branch?

No, yes, maybe, and/or mu. This depends heavily on what you mean by "branch". See also What exactly do we mean by "branch"? One of the weirder things about Git is that branches are not really very important.

... Then when I did git log I can see logs till 5c24d6d but not the ones done after.

This, too, is normal.

People new to Git usually start, I think, by thinking that Git is about files. It's not. Upon realizing this, they think: Aha, Git must be all about branches! Alas, that too is false. Branch names do matter, and branches—however we end up defining them; there are multiple conflicting valid definitions—are important, but they're not the real key to using Git effectively. The key is, instead, commits.

Commits hold files, and commits are found in or by branches—which again leads to the question of defining the word branch—but in the end, the commit is the most important thing in Git. It's the way commits work that leads to everything else about Git.

Each commit has its own unique hash ID, which seems random. The hash ID would be totally predictable if only we knew in advance every byte that was going to go into that commit, including the date-and-time-stamp; but we just don't know these things in advance, so the hash IDs look random. They act as keys, in a big key-value database, that allows Git to fish out the complete commit given just the unique hash ID.

What's in a commit is:

  • a hash ID locates a snapshot (a tree object);
  • some hash IDs that locate some number of parent commits (usually one parent);
  • the author of the commit (name, email address, and date-and-time-stamp);
  • the committer (like the author, and often the same); and
  • the log message.

The snapshot hash ID means that the commit effectively contains all the files in their frozen-as-of-that-commit state, though multiple different commits can share those versions. We'd have to go deeper into this structure to see how that works, but the general idea is: you freeze snapshots of the files into commits, and every commit is independent of every other commit except for deliberately-shared files, and these parent linkages, all of which work by hash IDs. So everything in Git is in that big key-value database, indexed by hash ID.

But here's the problem: where do you get a hash ID? Do we have to trawl through the entire database, finding every hash ID, before we can get anything done? That would be terribly slow—so, no, we don't have to do that. This is where HEAD and branch names come in.

Finding commit hashes

There are two things that Git can store in the special file .git/HEAD:

  • a branch name, such as ref: refs/heads/master: you are then on that branch, in git status's description, or
  • the hash ID for some existing commit: you then have a detached HEAD.

Meanwhile, a branch name such as refs/heads/master always contains the hash ID of some existing commit. So if HEAD isn't detached, Git can read the branch name from HEAD, and then read the hash ID from the branch name. If HEAD is detached, Git can read the hash ID from HEAD. Either way, we have a hash ID.

The git log command takes, as an argument, anything that resolves to one or more hash IDs:

git log master develop a123456 ...

Branch names resolve to hash IDs by looking at their stored hash IDs. Hash IDs are already hash IDs. Either way, git log finds those objects and makes sure they are commit objects (as opposed to one of the other three internal object types). These are the first commits that git log might display.

If you don't give any hash IDs and/or branch names to git log, Git uses HEAD. This HEAD always1 resolves to a valid commit hash ID, and that's the first commit that git log will display.

Having displayed some commit, what git log does next is to look at the parent(s) in that commit. Those give Git some more hash IDs. So if there's one current commit found at HEAD, and it has one parent, that parent will be the next commit that git log will show. This repeats for the parent's parent, and so on.

If you git checkout some commit, then run git log, you see that commit, then its parent, then its parent's parent. That's what you saw. Is that a branch, though? I'll leave that to the linked question, What exactly do we mean by "branch"?


1There's an exception in a new, totally-empty repository. Here HEAD will contain the branch name master, but there are no commits, so master cannot exist. Various Git commands, over time, have adapted to handle this situation better: git status now says "no commits yet", for instance. You can also trigger this situation using git checkout --orphan: this writes the name of a branch that does not exist (yet) into HEAD. In both cases, creating a new commit creates the branch, resolving the uncomfortable situation.

torek
  • 448,244
  • 59
  • 642
  • 775