I am fairly new to Git- having only been using it since I started working for my current employer.
I recently pushed some changes to the server, having merged a bug fix implemented by a colleague on my local development machine. I did this by pulling their branch
on which they had implemented the bug fix from the server, and merging it with my local master
branch, which was up-to-date with the live version of the code. I tested this on my local development machine, and it all seemed to be working correctly, so I pushed it to the server.
However the changes I made appear to have introduced another bug into the live version (the bug it introduced was not apparent when testing the fix locally, and only presented itself once the code had gone live).
This bug that I introduced actually caused a heavily used part of the internal company website to be inaccessible, so I immediately checked out the last working commit
& restarted the server, so that the site was then accessible again.
I am now working on resolving the issues that the fix has caused on my local development machine, before pushing the fix up to the server again, but having checked out an old commit
on the server has left the live version of the project in a detached HEAD
state. The detached HEAD
state that it is in is currently mostly functional (i.e. it is working the same as it was before my colleague started working on this bug fix- so the bug is still present).
I now want to resolve the detached HEAD
on the server, so that I can pull the working code from the server again, to start afresh from this point, but I'm not sure how I 'match' the rest of the code with the detached HEAD
that I am currently working from.
If I run git branch
on the server, the output shows that I only have the master
branch (which is broken- as it has the bug that I introduced when pushing the original bug fix), and (detached from 0e57d3d)
, which is the HEAD
that is currently being pointed to.
Other posts I've looked at seem to indicate that I should checkout master
in order to resolve the detached HEAD
, but I can't do this, as I know that master
is currently broken.
So how can I 'attach' the HEAD
again, so that the code works as it did in the state it was in during the commit
that the HEAD
is pointing to? Would I just run a git branch
from there, then checkout that branch
, and make it the master
? Or is there some other way to do this?
At first blush, this looks like a duplicate of Fix a Git detached head? It is in some ways, and not in others. Also, as Thorbjørn Ravn Andersen said in various comments, you probably want to coordinate with other colleagues as well. However, let's make a few notes that aren't in (and don't really go with) that other question.
First, a "detached HEAD" simply means that you have checked out one specific commit, often by its hash ID. There is only one functional difference between this and git checkout branch-name
, as that also checks out one specific commit, and this sounds like a circular definition, because it is: the difference is that when you do that, you are on a branch, and when you do this, you are not on a branch, i.e., you have a detached HEAD.
Hence, all "detached HEAD" means is "not on a branch". That is, if you check out one specific commit and get on a branch, you are "on a branch", as git status
will say; but if you check out the same specific commit and don't get on a branch, you have a "detached HEAD", as git status
will say.
git checkout
is how you get on, or off, a branchThere's only one user-oriented Git command to get onto any particular branch, or to detach your HEAD to get off a branch, and that's git checkout
. If you git checkout
a branch name, it checks out that commit and puts you on the branch. If you git checkout
anything else—including a tag, or a remote-tracking branch, or a raw hash ID—it gives you a detached HEAD. Hence, as in that other question's accepted answer, if you just want to get back on branch master
, you just git checkout master
.
You ran git checkout 1234567
or similar, to check out an older commit, and now have a "detached HEAD".
git checkout
also updates your index and/or work-tree(Very short reminder here: the work-tree is where Git writes, and reads back, copies of what you ultimately save forever as commits in the repository. It's in the form that the rest of the computer's systems understand, instead of a Git-specific form. The index is where you and Git build the next commit you will make; it's in a very Git-specific form, and has some extra goop to coordinate between Git-specific internal form, and "normal computer use" form. The commits are, in effect, saved indexes, minus the extra goop.)
Your server is, apparently, running off the work-tree—and the reason you ran git checkout 1234567
in the first place was because the commit that's the tip of branch master
, which is some commit other than 1234567, doesn't work. If you git checkout master
you'll re-break the server, by restoring the tip commit.
This is where you need to coordinate with others, because to re-attach your HEAD, you must do at least one of two things now:
git checkout -b newbranch
If you want to, and are allowed to, have the server be "on a branch" without changing commits, just create a new branch whose tip is the current commit. To do so, use git checkout -b
:
git checkout -b mostly-working
Now your HEAD is attached as you are on branch mostly-working
, which you just created. Nothing happens to the index and work-tree because the new branch name mostly-working
names commit 1234567 (or whatever one it was you checked out earlier).
This commit is probably somewhere behind master
. That is, if we were to draw part of the commit graph, it would look like this, before we do this git checkout -b
thing:
...--o--o--*--o--o <-- master
^
commit 1234567
HEAD
All that git checkout -b
does is add a new name pointing to this same commit. To draw it in plain text, we need to shove a few commits up or down (I'll go with up):
o--o <-- master
/
...--o--o--* <-- mostly-working (HEAD)
^
still commit 1234567
Since we haven't moved commits, the index and work-tree remain unchanged.
Just to complete the above, let's look at what happens if you modify something in the work-tree, git add
, and git commit
. Git will make a new commit on the new branch mostly-working
:
o--o <-- master
/
...--o--o--o--* <-- mostly-working (HEAD)
(incidentally, I'm using *
here to mark the current commit, a la git branch
marking the current branch).
Let's say that you didn't do the git checkout -b
, but did modify a file and commit. This would make a new commit as usual—but instead of being on a branch, the new commit would be the new detached HEAD. That is, we would draw the new commit like this:
o--o <-- master
/
...--o--o--o--* <-- HEAD
This, then, is really the difference between "on a branch" and "detached HEAD". There's nothing special about being "on a branch" except that new commits, when made, advance that branch. The branch name points to the tip of the branch. When you git checkout
the branch, you check out the tip-most commit of that branch. When you make new commits, you add them to the branch, by making the name point to the new commit. (The new commit, itself, points back to the previous HEAD
commit.) Since HEAD
just says which branch is current, updating the branch name to point to the new commit also updates HEAD
to point to the new commit.
When you have a detached HEAD, the process is exactly the same: the new commit points back to the previous commit, and the new commit becomes the new HEAD
commit. There's just no name for this commit (well, except HEAD
of course, but HEAD
changes when you git checkout
!).