How HEAD works in git

I recently conducted a survey at Mastodon asking how confident my readers were in their understanding of HEAD in Git. The results (based on about 1700 votes) surprised me a little:

  • 10% – 100%

  • 36% – quite confident

  • 39% – somewhat confident

  • 15% – I have no idea

I was surprised that people were not sure of their understanding: I thought that HEAD – This is a fairly simple topic.

Usually, when others find a topic confusing as I do, it is because there is some hidden complexity that I am not considering. And in further discussions it turned out that HEAD really A little more difficult than I thought!

HEAD actually has several meanings

After talking to a bunch of people about HEADI realized that HEAD actually has several closely related meanings:

  1. File .git/HEAD

  2. HEAD V git show HEAD (git calls this a “revision parameter”)

  3. All the ways git uses HEAD in the output of various commands (<<<<<<<<<<HEAD, (HEAD -> main), detached HEAD state, On branch main and so on)

All of these topics are very closely related to each other, but I don't think this connection is obvious to those who are just starting to work with git.

File .git/HEAD

There is a very important file in Git .git/HEAD. It may contain any of the following:

  1. Name branches (For example, ref: refs/heads/main)

  2. Commit ID (For example, 96fa6899ea34697257e84865fefc56beb42d6390)

This file defines what your “current branch” is in Git. For example, when you do git status and you see the following:

$ git status
On branch main

this means that the file .git/HEAD contains ref: refs/heads/main.

If .git/HEAD contains the commit ID instead of the branch, git calls this the “detached HEAD state”. We'll come back to this later.

(Sometimes people say that HEAD contains the name links Is there a commit ID, but I'm pretty sure this link should be branch. Strictly speaking, Can make sure that .git/HEAD contained the name of a non-branch link, manually edited .git/HEAD, but I don't think this can be done using the git command. But I'm curious if it's possible to use the standard git command to turn .git/HEAD into a non-branch link, and if so, why it might be needed!)

HEAD in git show HEAD

In git commands HEAD very often used as a reference to the commit ID, for example:

Everything shown above (HEAD, HEAD^^^, HEAD@{2}) are called “revision parameters”. They are documented in man gitrevisionsand Git tries to resolve them into a commit ID.

(To be honest, I've never heard the term “revision parameter” before, but that's what will lead you to the documentation of this concept)

HEAD in git show HEAD has a fairly simple meaning: it resolves current commit, for which checkout! Git resolve HEAD in one of two ways:

  1. If .git/HEAD contains the name of the branch, it will be the last commit on that branch (for example, when reading it from .git/refs/heads/main)

  2. If .git/HEAD contains the commit ID, this will be the commit ID

All output formats

We have reviewed the file .git/HEAD and revision parameter HEADused in git show HEAD. It remains to consider all the ways that git uses HEAD in the output.

git status: “on branch main” or “HEAD detached”

By doing git status the first line will look like one of two options:

  1. on branch main. It means that .git/HEAD contains a branch.

  2. HEAD detached at 90c81c72. It means that .git/HEAD contains the commit ID.

Above, I promised that I would explain the meaning of “HEAD detached,” so let’s figure it out.

State detached HEAD

“HEAD is detached” or “detached HEAD state” means that there is no current branch.

Not having a current branch can be a little dangerous because when new commits are made, those commits won't stick to any of the branches – they'll be “orphans”! Orphan commits can cause the following problems:

  1. commits will be harder to find (can't be done git log somebranchto find them)

  2. orphan commits will be removed by the git garbage collector over time

Personally, I'm very careful to avoid commits in the detached HEAD state, although some people prefer to work this way. Exiting the detached HEAD state is quite simple; you can do one of the following:

  1. Return to thread (git checkout main)

  2. Create a new branch in this commit (git checkout -b newbranch)

  3. If you are in the detached HEAD state because a rebase is in progress, complete or abort the rebase (git rebase --abort)

Now let's return to other git commands that have in the output HEAD.

git log: (HEAD -> main)

Having completed git log and looking at the first line, you can see one of the following three options:

  1. commit 96fa6899ea (HEAD -> main)

  2. commit 96fa6899ea (HEAD, main)

  3. commit 96fa6899ea (HEAD)

Their interpretation is not entirely obvious, so let's look at them:

  • inside (...) git lists all references that reference this commit, e.g. (HEAD -> main, origin/main, origin/HEAD) means that HEAD, main, origin/main And origin/HEAD point to this commit (directly or indirectly)

  • HEAD -> main means the current branch is main

  • If this line says HEAD, but not HEAD ->this means that we are in the detached HEAD state (there is no current branch)

If we use these rules to explain the three examples above, the result will be:

  1. commit 96fa6899ea (HEAD -> main) means:

  2. commit 96fa6899ea (HEAD, main) means:

  3. commit 96fa6899ea (HEAD) means:

    • .git/HEAD contains 96fa6899ea (detached HEAD)

    • .git/refs/heads/main or contains the ID of another commit, or does not exist

Merge conflicts: <<<<<<< HEAD is confusing

When resolving a merge conflict, you might encounter something like this:

<<<<<<< HEAD
def parse(input):
    return input.split("\n")
=======
def parse(text):
    return text.split("\n\n")
>>>>>>> somebranch

I believe that HEAD in this context the situation is very confusing and I usually just ignore it. Reasons for this:

  • By doing merge HEAD in a merge conflict is the same as HEAD by doing git merge. Everything is very simple.

  • By doing rebase HEAD in a merger conflict is something completely different: it another commit, on top of which we perform a rebase. That is, it is completely different from that HEADwhich was during execution git rebase. This appears to be because rebase first checkouts another commit and then repeatedly picks up commits on top of it.

In addition, in merge and rebase the meanings of “own” and “foreign” were swapped.

What's the meaning HEAD changes depending on whether I do a rebase or a merge is too confusing for me so it's much easier for me to ignore completely HEAD and use another method to figure out which part is where when.

Thoughts on Terminology Consistency

I think HEAD would be more intuitive if git's terminology regarding HEAD was more internally consistent.

For example, git talks about a “detached HEAD” state, but not an “attached HEAD” state – the git documentation never uses the word “attached” to refer to HEAD. And git talks about being “in” a branch, but it never talks about being “out” of a branch.

It's very difficult to understand what it really is on branch main opposite HEAD detached. How is the user supposed to guess that HEAD detached has something to do with branches in general, or that “on branch main” has something to do with HEAD?

That's all!

If I remember other uses HEAD in Git (specifically using HEAD in Git output), I can add them to the post later.

If HEAD is confusing you, then I hope my article helped you a little!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *