Advertisement
Advertisement


How can I reconcile detached HEAD with master/origin?


Question

I'm new at the branching complexities of Git. I always work on a single branch and commit changes and then periodically push to my remote origin.

Somewhere recently, I did a reset of some files to get them out of commit staging, and later did a rebase -i to get rid of a couple recent local commits. Now I'm in a state I don't quite understand.

In my working area, git log shows exactly what I'd expect-- I'm on the right train with the commits I didn't want gone, and new ones there, etc.

But I just pushed to the remote repository, and what's there is different-- a couple of the commits I'd killed in the rebase got pushed, and the new ones committed locally aren't there.

I think "master/origin" is detached from HEAD, but I'm not 100% clear on what that means, how to visualize it with the command line tools, and how to fix it.

2017/05/29
1
1580
5/29/2017 6:21:42 PM

Accepted Answer

First, let’s clarify what HEAD is and what it means when it is detached.

HEAD is the symbolic name for the currently checked out commit. When HEAD is not detached (the “normal”1 situation: you have a branch checked out), HEAD actually points to a branch’s “ref” and the branch points to the commit. HEAD is thus “attached” to a branch. When you make a new commit, the branch that HEAD points to is updated to point to the new commit. HEAD follows automatically since it just points to the branch.

  • git symbolic-ref HEAD yields refs/heads/master
    The branch named “master” is checked out.
  • git rev-parse refs/heads/master yield 17a02998078923f2d62811326d130de991d1a95a
    That commit is the current tip or “head” of the master branch.
  • git rev-parse HEAD also yields 17a02998078923f2d62811326d130de991d1a95a
    This is what it means to be a “symbolic ref”. It points to an object through some other reference.
    (Symbolic refs were originally implemented as symbolic links, but later changed to plain files with extra interpretation so that they could be used on platforms that do not have symlinks.)

We have HEADrefs/heads/master17a02998078923f2d62811326d130de991d1a95a

When HEAD is detached, it points directly to a commit—instead of indirectly pointing to one through a branch. You can think of a detached HEAD as being on an unnamed branch.

  • git symbolic-ref HEAD fails with fatal: ref HEAD is not a symbolic ref
  • git rev-parse HEAD yields 17a02998078923f2d62811326d130de991d1a95a
    Since it is not a symbolic ref, it must point directly to the commit itself.

We have HEAD17a02998078923f2d62811326d130de991d1a95a

The important thing to remember with a detached HEAD is that if the commit it points to is otherwise unreferenced (no other ref can reach it), then it will become “dangling” when you checkout some other commit. Eventually, such dangling commits will be pruned through the garbage collection process (by default, they are kept for at least 2 weeks and may be kept longer by being referenced by HEAD’s reflog).

1 It is perfectly fine to do “normal” work with a detached HEAD, you just have to keep track of what you are doing to avoid having to fish dropped history out of the reflog.


The intermediate steps of an interactive rebase are done with a detached HEAD (partially to avoid polluting the active branch’s reflog). If you finish the full rebase operation, it will update your original branch with the cumulative result of the rebase operation and reattach HEAD to the original branch. My guess is that you never fully completed the rebase process; this will leave you with a detached HEAD pointing to the commit that was most recently processed by the rebase operation.

To recover from your situation, you should create a branch that points to the commit currently pointed to by your detached HEAD:

git branch temp
git checkout temp

(these two commands can be abbreviated as git checkout -b temp)

This will reattach your HEAD to the new temp branch.

Next, you should compare the current commit (and its history) with the normal branch on which you expected to be working:

git log --graph --decorate --pretty=oneline --abbrev-commit master origin/master temp
git diff master temp
git diff origin/master temp

(You will probably want to experiment with the log options: add -p, leave off --pretty=… to see the whole log message, etc.)

If your new temp branch looks good, you may want to update (e.g.) master to point to it:

git branch -f master temp
git checkout master

(these two commands can be abbreviated as git checkout -B master temp)

You can then delete the temporary branch:

git branch -d temp

Finally, you will probably want to push the reestablished history:

git push origin master

You may need to add --force to the end of this command to push if the remote branch can not be “fast-forwarded” to the new commit (i.e. you dropped, or rewrote some existing commit, or otherwise rewrote some bit of history).

If you were in the middle of a rebase operation you should probably clean it up. You can check whether a rebase was in process by looking for the directory .git/rebase-merge/. You can manually clean up the in-progress rebase by just deleting that directory (e.g. if you no longer remember the purpose and context of the active rebase operation). Usually you would use git rebase --abort, but that does some extra resetting that you probably want to avoid (it moves HEAD back to the original branch and resets it back to the original commit, which will undo some of the work we did above).

2019/01/15
2561
1/15/2019 6:16:21 PM

Just do this:

git checkout master

Or, if you have changes that you want to keep, do this:

git checkout -b temp
git checkout -B master temp
2017/01/25

I ran into this issue and when I read in the top voted answer:

HEAD is the symbolic name for the currently checked out commit.

I thought: Ah-ha! If HEAD is the symbolic name for the currenlty checkout commit, I can reconcile it against master by rebasing it against master:

git rebase HEAD master

This command:

  1. checks out master
  2. identifies the parent commits of HEAD back to the point HEAD diverged from master
  3. plays those commits on top of master

The end result is that all commits that were in HEAD but not master are then also in master. master remains checked out.


Regarding the remote:

a couple of the commits I'd killed in the rebase got pushed, and the new ones committed locally aren't there.

The remote history can no longer be fast-forwarded using your local history. You'll need to force-push (git push -f) to overwrite the remote history. If you have any collaborators, it usually makes sense to coordinate this with them so everyone is on the same page.

After you push master to remote origin, your remote tracking branch origin/master will be updated to point to the same commit as master.

2017/09/27

Look here for basic explanation of detached head:

http://git-scm.com/docs/git-checkout

Command line to visualize it:

git branch

or

git branch -a

you will get output like below:

* (no branch)
master
branch1

The * (no branch) shows you are in detached head.

You could have come to this state by doing a git checkout somecommit etc. and it would have warned you with the following:

You are in 'detached HEAD' state. 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.

If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example:

git checkout -b new_branch_name

Now, to get them onto master:

Do a git reflog or even just git log and note your commits. Now git checkout master and git merge the commits.

git merge [email protected]{1}

Edit:

To add, use git rebase -i not only for deleting / killing commits that you don't need, but also for editing them. Just mention "edit" in the commit list and you will be able to amend your commit and then issue a git rebase --continue to go ahead. This would have ensured that you never came in to a detached HEAD.

2011/04/24

Get your detached commit onto its own branch

Simply run git checkout -b mynewbranch.

Then run git log, and you'll see that commit is now HEAD on this new branch.

2013/05/20

If you want to push your current detached HEAD (check git log before), try:

git push origin HEAD:master

to send your detached HEAD into master branch at origin. If your push gets rejected, try git pull origin master first to get the changes from origin. If you don't care about the changes from origin and it's rejected, because you did some intentional rebase and you want to replace origin/master with your currently detached branch - then you may force it (-f). In case you lost some access to previous commits, you can always run git reflog to see the history from all branches.


To get back on a master branch, while keeping the changes, try the following commands:

git rebase HEAD master
git checkout master

See: Git: "Not currently on any branch." Is there an easy way to get back on a branch, while keeping the changes?

2017/05/23

Source: https://stackoverflow.com/questions/5772192
Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Email: [email protected]