2

Say I am working on my master branch. I have the latest commit

commit a9993d589a45f108b68fda665d4714039482ede1 (HEAD -> master, origin/master, origin/HEAD)

If I do git checkout HEAD or git checkout master I get

➜  myproject:(master)$ git checkout HEAD
Your branch is up to date with 'origin/master'.
➜  myproject:(master)$ git checkout master
Already on 'master'
Your branch is up to date with 'origin/master'.

But if I checkout the commit with the SHA instead, where all points to the same thing, I end up in a detached state.

➜  myproject:(master)$ git checkout a9993d60c300f108b68fda665d4714039482ede1
Note: switching to 'a9993d60c300f108b68fda665d4714039482ede1'.

You are in 'detached HEAD' state.
(...)

Why is the last checkout any different from the first two?

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
chwi
  • 2,752
  • 2
  • 41
  • 66
  • Because you are _detaching_ from a branch. When you do that (or you run `git checkout --detach`), you basically want to start working without moving branches around (I do that frequently to try experiments without moving any branch.... or without the need to have a branch with me at all), The best follow-up question I can come up with to see if you already have the basics down is "do you know what a branch is in git?". Once you have that question down, probably the answer to your main question also comes down with it. – eftshift0 Aug 04 '22 at 10:57
  • 3
    so, short answer: if you provide a revision ID, git **can´t guess** that you mean to work on a branch.... there might be _no branch_.... or multiple branches on that revision.... and even if there is one, you asked for the revision ID so it _won't_ second guess you and checkout the branch (if it did, I would consider that a bug rather than a feature). – eftshift0 Aug 04 '22 at 11:08
  • This together with @faho's answer makes sense. It's clear now, thank you! – chwi Aug 10 '22 at 12:52

2 Answers2

1

In git, a branch is basically a named reference to a commit that is automatically updated, for example when you do git commit.

(and a tag is a named reference to a commit that stays the same)

And "HEAD" is a reference to what is currently checked out.

So when you tell git checkout mybranch, it makes the "mybranch" reference current, so if you did a commit you'd update that. The "HEAD" reference is also updated to that branch.

When you do git checkout a9993d60c300f, that's not a branch, that's a simple commit. Notably, there isn't a 1-to-1 mapping from commits to branches - you even have multiple branches pointing to that commit here.

Now, git could ask you if you meant these branches pointing to that commit, but instead it does the easy thing and gives you just the commit, and points the HEAD to that commit. Because you are now no longer attached to a branch, this is called the "detached HEAD" state.

When you do git checkout HEAD, git follows the HEAD reference, sees a branch and checks out the branch. If you first checked out a commit and then did git checkout HEAD, it would still give you the commit, and you would still have a detached HEAD.

And when you make a commit in detached HEAD state, git creates the commit and moves the HEAD reference, but no branch is pointing at it. So this would be a "branch" if you looked at your commit graph, it just wouldn't be one to git because it doesn't have a name. There's no formal branch referencing it.

You could then create a branch, for example with git branch foo; git checkout foo (or the shorthand git checkout -b foo).

faho
  • 14,470
  • 2
  • 37
  • 47
0

You could then create a branch, for example with git branch foo; git checkout foo (or the shorthand git checkout -b foo).

Actually, Git 2.40 (Q1 2023) updates that with the more recent git switch command.

See commit 9e37969 (09 Jan 2023) by Yutaro Ohno (ohno418).
(Merged by Junio C Hamano -- gitster -- in commit b106341, 21 Jan 2023)

doc: add "git switch -c" as another option on detached HEAD

Signed-off-by: Yutaro Ohno
Acked-by: Eric Sunshine

In the "DETACHED HEAD" section in the git-checkout(man) doc, it suggests using "git checkout -b <branch-name>"(man) to create a new branch on the detached head.

On the other hand, when you checkout a commit that is not at the tip of any named branch (e.g., when you checkout a tag), Git suggests using git switch -c <branch-name>(man).

Add "git switch -c" as another option and mitigate this inconsistency.

git checkout now includes in its man page:

$ git checkout -b foo  # or "git switch -c foo"  <1>
$ git branch foo                                 <2>
$ git tag foo                                    <3>

As I mentioned in "Why did my Git repo enter a detached HEAD state?", you should no longer use the git checkout command.

git switch aCommit would result in:

fatal: a branch is expected, got 

With git switch, you have to use explicitly the --detach option to get a detached HEAD.

git switch --detach a9993d60c300f108b68fda665d4714039482ede1

No ambiguity there.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250