What is the HEAD in git?
Asked Answered
L

6

265

There seems to be a difference between the last commit, the HEAD and the state of the file I can see in my directory.

What is HEAD, what can I do with it and what mistake should I avoid?

Lehr answered 27/3, 2010 at 16:17 Comment(4)
See also https://mcmap.net/q/11804/-head-and-orig_head-in-git/…Pewit
Starting with Git v1.8.4, all the answers below that use HEAD or head can now use @ in place of HEAD instead. See this answer (last section) to learn why you can do that.Ahlers
From git-scm : The HEAD in Git is the pointer to the current branch reference, which is in turn a pointer to the last commit you made or the last commit that was checked out into your working directory. That also means it will be the parent of the next commit you do. It's generally simplest to think of it as HEAD is the snapshot of your last commit.Mali
Possible duplicate of What is HEAD in Git?Antifriction
A
207

HEAD is a reference to the last commit in the currently checked-out branch.


There is a small exception to this, which is the detached HEAD. A detached HEAD is the situation you end up in whenever you check out a commit (or tag) instead of a branch. In this case, you have to imagine this as a temporary branch without a name; so instead of having a named branch reference, we only have HEAD. It will still allow you to make commits (which will update HEAD), so the above short definition is still true if you think of a detached HEAD as a temporary branch without a name.

Antipersonnel answered 27/3, 2010 at 16:20 Comment(15)
So why can you have two heads?Lehr
@e-satis: because HEAD is a commit that you have checked in the working directory, and since the working directory can only display one commit at a time (from which you do some modifications), you can only have one HEAD.Pewit
@e-satis: sometimes you'll see branches referred to as heads - they're stored in refs/heads. Lower-case head is different from HEAD, though. My answer clarifies this a bit.Ludovick
Ok, and what does HEAD^ means? I use that in "reset", but don't know why. See any regexp knowledge helping me here.Lehr
@e-satis: That's not regex. The ^ is just git's notation for "the commit before" - that's the commit before the current one. (If the current is a merge, it uses the first parent.)Ludovick
@e-satis: See the specifying revisions section of the man page for git-rev-list for more information about all the ways to specify commits - this is just one tiny piece. kernel.org/pub/software/scm/git/docs/…Ludovick
You said "(If the current is a merge, it uses the first parent.)". Why does it do that ?Lehr
Because a merge commit has multiple parents, and there is just one notation. So they just decided to give you the first parent of the available ones. You can however use rev^2 to get the second parent of the revision, or for example HEAD^3 to get the third parent of the HEAD commit.Antipersonnel
Ok. The first parent of a merge, it's the one we merged from or the one we merged into? And is there difference between rev^2 and HEAD^2 if rev is the top revision? Sorry to harass you with that :-) I don't like to work with things I don't understand.Lehr
No, when rev and HEAD are pointing to the same commit, there is no difference. And you could even write the commit id (the SHA-1 value) instead of rev or HEAD. And don't worry, you don't harass us with the questions :) (me at least :P)Antipersonnel
Cheers. Love SO. You can have 14K rep and still be a noob :-pLehr
Even though this is the accepted answer, I don't think it's always correct. If you've checkedout a commit that's not the last (chronologically most recent) commit in the current branch, then HEAD is not a reference to the last commit in the current checked out branch.Rage
@Rage If you check out a commit, then you are no longer in the context of a branch (since commits do not belong to branches); you will get a detached HEAD which essentially means that HEAD is the only reference to the “temporary branch” you’re currently on. I’ve added that explanation to my answer, so hopefully that clears this up.Antipersonnel
This is the first time I've heard that commits do not belong to branches. I thought they did. I'll look that up.Rage
@Rage Branches are just pointers to commits, which makes that commit and all its parents contained on that branch. But there is nothing in a commit that links them to a branch. That link is only there in reverse. So when you check out a commit, you cannot tell which branch you possibly meant—it could be one branch, it could be all of them, or even none.Antipersonnel
L
91

HEAD is a ref (reference) to the currently checked out commit.

In normal states, it's actually a symbolic ref to the branch you have checked out - if you look at the contents of .git/HEAD you'll see something like "ref: refs/heads/master". The branch itself is a reference to the commit at the tip of the branch. Therefore, in the normal state, HEAD effectively refers to the commit at the tip of the current branch.

It's also possible to have a "detached HEAD". This happens when you check out something besides a (local) branch, like a remote branch, a specific commit, or a tag. The most common place to see this is during an interactive rebase, when you choose to edit a commit. In detached HEAD state, your HEAD is a direct reference to a commit - the contents of .git/HEAD will be a SHA1 hash.

Generally speaking, HEAD is just a convenient name to mean "what you have checked out" and you don't really have to worry much about it. Just be aware of what you have checked out, and remember that you probably don't want to commit if you're not on a branch (detached HEAD state) unless you know what you're doing (e.g. are in an interactive rebase).

Ludovick answered 27/3, 2010 at 16:26 Comment(8)
This is something I don't understand. If you checkout a remote branch, why do you end up with a "detached HEAD". Why don't you automatically jump in the branch in your local repo that correspond to your remote?Lehr
@e-satis: If you want the local branch, check out the local branch. Remember that the two aren't necessarily the same - you have to tell the local one to merge the remote one (or pull). The tracking is just so it knows which one to automatically pull when you ask. The reason it's detached is that the remote branch is intended to be a pointer to the last-seen location of the branch in the remote repo. If you try to commit to it, the remote repo doesn't change, so the remote branch shouldn't either.Ludovick
OK, that's what I didn't get : having a local branch named in a way doesn't imply it's the same as the remote one. Really hard to get at the beginning cause I come from a SVN background :-) Thanks man. BTW, how do you move a headless HEAD to a local branch to commit it here ?Lehr
@e-satis: The general answer is git rebase <branch> HEAD. This will find the last common ancestor of <branch> and HEAD, and then take all the commits from there to HEAD and apply them (rebase them) onto <branch>. It essentially does this by applying them as patches, so if the two branches are really different, there could be conflicts. But if <branch> is an ancestor of HEAD (i.e. you were in the right place, just forgot you'd detached HEAD) the rebase is just a fast-forward merge.Ludovick
@akostadinov It does mention what a "head" is, but as for what HEAD is, my answer is definitely more complete.Ludovick
This is one of the most clear and accurate descriptions of git HEAD that I've seen, after searching for a while.Rage
@e-satis see here. Let's say you're on main branch and your current head points to commit d81da2jd. If you did git checkout d81da2jd then you'd again be in a detached head. It's because if you make a new commit from the d81da2jd SHA, git doesn't know where it should add onto i.e. it doesn't know it needs to update your main branch..., but if you made that change on a branch then git knows that it should add that commit and update the commit of that branch. (1/2)Grosberg
@e-satis When you checkout a remote branch, git doesn't know where to add a new commit...hence the name of detached head. tbh you could checkout out a commit and then add a commit onto it, but then how would you get back to it? You don't have a branch/tag to easily checkout to. And remembering commit SHAs is inefficient/wrong (2/2)Grosberg
G
74

I always thought HEAD~5 means go to 5 commits before. But it doesn't carry the GO part of the command. It only carries the reference part of the command.

What you can do with that reference varies by the command you select

In layman terms it's used to answer the question of: WHERE should I go? To which commit?

  • HEAD means (the reference to the) current commit
  • HEAD~1 means (the reference to) 1 commit before
  • HEAD~ ALSO means (the reference to) 1 commit before
  • HEAD~87 means (the reference to) 87 commits before
  • HEAD~3..HEAD means from 3 commits to current commit (in total 3 commits)

Usage:

  • git checkout HEAD~1 will actually GO/checkout to 1 commit/reference before
  • git reset HEAD~3 will uncommit your last 3 commits — without removing the changes, ie you can see all the changes made in the last 3 commits together, remove anything you don't like or add onto it and then commit them all again.
  • git reset --hard HEAD~3 will uncommit your last commit and remove their changes. It will completely remove those changes. For more see here.
  • git diff HEAD~3 to look into the changes of the last 3 commits
  • git diff someFile HEAD~3 to look into the last 3 changes of a specific file
  • git revert --no-commit HEAD~3..HEAD. Reverts 3 commits, without automatically commiting i.e. you have to do git commit -m yourself. For more see here
  • git rev-parse HEAD~2 outputs the SHA of two commit before.
  • See all changes between a pull request and main. It's a multi-step process.
git fetch origin/feature22
git checkout feature22
git merge-base feature22 main # will return the SHA of their 050dc022f3a65bdc78d97e2b1ac9b595a924c3f2
git reset 050dc022f3a65bdc78d97e2b1ac9b595a924c3f2

You could just do git reset main, but that only works if your colleague who created the pull request has the latest changes with main. If they're working on a big feature (and haven't merged with main for a couple days) and you just want to see what additions they have after their last pull from main, then you have to follow the steps above.


Also make sure you see this answer for What is a detached HEAD.

It has some good info on cat .git/HEAD


Out of scope, but super interesting:

Other than HEAD, there are other kinds of heads. See git revisions:

ORIG_HEAD

ORIG_HEAD is created by commands that move your HEAD in a drastic way, to record the position of the HEAD before their operation, so that you can easily change the tip of the branch back to the state before you ran them

To undo a git merge

git reset --hard ORIG_HEAD

MERGE_HEAD

I got this error message after I tried to merge again before concluding an existing merge

fatal: You have not concluded your merge (MERGE_HEAD exists). Please, commit your changes before you merge.

To fix this, I had to conclude my merge. Then do another merge.

FETCH_HEAD

records the branch which you fetched from a remote repository with your last git fetch invocation

CHERRY_PICK_HEAD

records the commit which you are cherry-picking when you run git cherry-pick.

For more on that see this other answer and docs


More official explanation

Highly recommend to see this page from the docs.

Git as a system manages and manipulates three trees in its normal operation. (By “tree” here, we really mean “collection of files”, not specifically the data structure.)

The role of HEAD is:

Tree Role My Explanation
Head Last commit snapshot, next [future] parent Something stored in the stone. Fully recorded. Will be the parent record of the next record/commit.
Index Proposed next commit snapshot Something stored in paper. Is likely to be recorded permanently in the stone. You can remove/alter your paper. Unlike records/commits stored in the stone, if you remove or alter the paper, then you don't have a record of its previous state.
Working Directory Sandbox A playground environment. From here you decide if you want to store something in paper, so you can later store in stone.

HEAD is the pointer to the current branch reference, which is in turn a pointer to the last commit made on that branch.

That means HEAD will be the parent of the next commit that is created. It’s generally simplest to think of HEAD as the snapshot of your last commit on that branch.

tldr HEAD is always moving you in between commits, it doesn't help you move between staging steps or sandbox.

Grosberg answered 21/9, 2017 at 18:13 Comment(2)
coming back to my own answer :)Grosberg
Fabulous explanation of HEADGordon
B
18

HEAD pointer in Git

Git maintains a reference variable called HEAD. And we call this variable a pointer, because its purpose is to reference, or point to, a specific commit in the repository. As we make new commits the pointer is going to change or move to point to a new commit. HEAD always points to the tip of the current branch in our repository. Now, this has to do with our repository, not our staging index, or our working directory.

Another way to think of it is the last state of our repository or what was last checked out, and because it's where the repository left off or the last state, you can also say that the HEAD points to the parent of the next commit or it's where commit writing is going to take place.

I think a good metaphor to think about this is the playback and record head on a cassette tape recorder. As we start recording audio, the tape moves past the head, and it records onto it. when we press Stop the place where that record head is stopped is the place it'll start recording again when we press Record a second time.Now we can move around, we can move the head to different places, but wherever the head is positioned when we hit Record again that's where it's going to start recording.

The HEAD pointer in Git is very similar, it points at the place where we're going to start recording next. It's the place where we left off in our repository for the things that we've committed.

Blankenship answered 12/2, 2017 at 8:56 Comment(0)
S
1

In simple terms, HEAD is a reference to the last commit in the currently check-out branch.

Think of the HEAD as the "current branch". When you switch branches with git checkout, the HEAD revision changes to point to the tip of the new branch.

You can see what HEAD points to by doing:

cat .git/HEAD

It is possible for HEAD to refer to a specific revision that is not associated with a branch name. This situation is called a detached HEAD.

Smalto answered 10/3, 2019 at 12:33 Comment(1)
You're suggesting cat .git/HEAD without realizing that it contradicts your very explanation of what HEAD is. It never points to the last commit in the current branch because if a branch is checked out, it points to the branch.Mineraloid
R
-1

Basically HEAD is a pointer/reference which points to the last commit in the current branch.

You can use these two commands to verify this.

$ git log -1

commit 9883e13257f2e7555eb6e3b14b2c814978c75692 (HEAD -> MyLocalBranch)
Author: vikram <[email protected]>
Date:   Sun Oct 11 23:32:45 2020 -0400
this is my last commit message

Now use below command to see where HEAD is pointing:

$ git rev-parse HEAD
9883e13257f2e7555eb6e3b14b2c814978c75692

As you can see that these two commit hashes are the same. So HEAD always points to the latest/last commit in the current branch.

Roca answered 12/10, 2020 at 11:43 Comment(1)
The last commit in the current branch? No. Try cat .git/HEAD ;-) HEAD points either to a branch itself, or to a commit directly (detached HEAD). By definition, if a branch is "the current branch", HEAD does not point to a commit. The rev-parse command outputs a commit because you asked git to dereference a commitish to produce a commit hash.Mineraloid

© 2022 - 2024 — McMap. All rights reserved.