Checking out an old commit and maintaining the head on the master branch?

GitVersion Control

Git Problem Overview


Currently for switching to another git commit (on the same branch...actually, on the master branch!), I'm executing the command

git checkout ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Now, every time I do this git tells me that I'm now with a detached head. How do I go to an older commit and still maintain the head on the same branch?

Git Solutions


Solution 1 - Git

Most of the time when I do this I checkout to a temp branch:

git checkout -b temp-branch-name ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Then after I'm done i just delete the branch

Solution 2 - Git

It depends on what you want to do when you checkout that commit. If all you're doing is checking it out so you can build or test that revision, then there's nothing wrong with working with a detached head. Just remember to check out an actual branch before you make any commits (git checkout master, for example), so that you don't create commits that are not included in any branch.

If, however, you want to make more commits starting from that point, you should create a branch. If you make commits that are not referenced by a branch, they can easily get lost, and will eventually be cleaned up by git's garbage collector, as nothing refers to them. You can create a new branch by running:

git checkout -b newbranch ea3d5ed

To help visualize, here's are some diagrams demonstrating how working on a detached head differs from working on a branch.

Let's start out with 3 commits on master, A, B, and C. master is the current branch, so HEAD points to master, which points to commit C.

A  B  C
----* <-- master <-- HEAD

Now if we commit, git will create a commit that has C as a parent (because that's the current commit, pointed to from HEAD via master), and will update master to point to that new commit. All of our commits are now in master, and HEAD points to the new commit through master.

A  B  C  D
------ <-- master <-- HEAD

Now let's check out B, giving us a detached HEAD.

A  B  C  D
------ <-- master
^
-- HEAD

Everything works fine here; we can look at all of the files, build our program, test it, etc. We can even create new commits; but if we do so, there's no branch that we're on, so we can't point any branch at that new commit. The only thing pointing at it is HEAD:

A  B  C  D
------ <-- master

* <-- HEAD E

If we later decide to check out master again, there will be nothing referring to E.

A  B  C  D
------ <-- master <-- HEAD

* E

Since there's nothing referring to it, it can be hard to find, and git considers commits with no references to be abandoned (they happen quite commonly if you rebase, or squash patches in, or do other fun history manipulation; they usually represent abandoned patches that you no longer care about). After a certain amount of time, git will consider it garbage, to be discarded the next time garbage collection runs.

So, instead of checking out a bare revision and getting a detached head, if you feel like you are going to make more commits, you should use git checkout -b branch B to create a branch and check it out. Now your commits won't be lost, as they will be included in a branch, that you can easily refer to, and merge later on.

A  B  C  D
------ <-- master
^
-- branch <-- HEAD

If you forget to do this, and create commits off a branch, there's no need to worry. You can create a branch referring to the head revision with git checkout -b branch. If you have already switched back to the master branch, and realize that you forgot a stray commit, you can find it using git reflog, which will show you a history of what commits HEAD has pointed to over the last few days. Anything that's still in the reflog will not be garbage collected, and generally references are kept in the reflog for at least 30 days.

Solution 3 - Git

If you simply want to return to an earlier commit to play with it without making any changes, you can do

git co <previous-commit-id>

you'll will be on a branch called "(no branch)" after this command.

Confirm this by

git br

After you've played with this previously committed code, you can switch to the branch you were on by

git co <the-branch-you-were-on>

The "(no branch)" will be deleted automatically. This way you don't need to create a temporary branch.

Solution 4 - Git

Git’s HEAD is simply a pointer that says what’s in the working directory. If you want to check out a commit which is not the head of a branch, you simply have to redirect your HEAD to point at that commit. There’s no way around it. You can create a temporary branch at that commit, but HEAD will be directed away from master nonetheless.

That’s the short explanation. The verbosity below will hopefully aid in understanding how HEAD and master are different:

Normally, things look like this:

C ← refs/heads/master ← HEAD 
↓
BA

Which is to say: “The parent of C is B, and the parent of B is A. The branch master is pointing to C, and I currently have checked out the contents of master. In addition, when I commit, master shall be updated.”

Some assumptions are implicit in this which are necessary for a thorough understanding of the commit graph. Namely, commits refer only to their parents, and the contents of a branch are those commits (and only those commits) which can be reached by following the parent links. The (unmodified) contents of the working tree and the index must correspond to the commit named by HEAD, either indirectly (“symbolic”) or directly (“detached”).

Thus, if you want to check out an old commit, the HEAD must be updated to point to the desired commit. git-checkout does just that:

C ← refs/heads/master 
↓
B ← HEAD
↓
A

Now, you’ve left your branch behind you, since you’re looking at something old. That’s perfectly OK, as the “detached head” advice tell you calmly (emphasis mine):

> 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.

On the other hand, while resetting your branch also gets HEAD where it needs to be, it would have a very different effect!

C
↓
B ← refs/heads/master ← HEAD
↓
A

The commit C will become garbage, since you have declared that you do not wish for it to be part of the master branch anymore.

In short, all you have to do is understand what git means by “HEAD” — it’s where you are, not where any given branch is. And if where you are is not the same as where a branch is, there is no choice but to use a detached HEAD.

(Perhaps also look into GitHub, gitk, or gitweb to browse the commit history, if derailing your HEAD continues to irk you.)

Solution 5 - Git

The question is a bit vague, but if you want to just change files in your working tree, you can simply do this:

git checkout [commit|branch] -- .

You can then stage the changes and create a new commit if you wish. This is pretty useful sometimes.

Solution 6 - Git

I think I understand your questions. Here is what I found to solve it. and there is no GUI solution of it, you can only use command to solve it, and it's really simple.

step 1: creat a tag of the old commit which you want to go back.

like tag v2.0

step 2: git checkout v2.0

here it is, now your HEAD is pointing at 'v2.0' commit, but master is still pointing at last commit.

C:\Program Files\Git\doc\git\html\git-checkout.html this document may help you

or type git help < checkout >

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiondevoured elysiumView Question on Stackoverflow
Solution 1 - GitNick CanzoneriView Answer on Stackoverflow
Solution 2 - GitBrian CampbellView Answer on Stackoverflow
Solution 3 - GitZack XuView Answer on Stackoverflow
Solution 4 - GitJosh LeeView Answer on Stackoverflow
Solution 5 - GitLari HotariView Answer on Stackoverflow
Solution 6 - GitLion LaiView Answer on Stackoverflow