As CommuSoft says, you're not on master. You're in "detached HEAD" mode. You get that any time you explicitly check out something that is not a (local) branch name:
$ git checkout origin/master # detach to "remote branch"
or if there's a tag v1.7
:
$ git checkout v1.7 # detach to tag
and you can even explicitly detach when using a local branch name:
$ git checkout --detach master # forcibly detach
A "detached HEAD" means you're not on a branch. Being "on a branch" means you're not using the detached HEAD mode. Yes, that's pretty circular; see this question and its answers for more details.
As for the:
error: The following untracked working tree files would be overwritten ...
When you get git checkout
to move from one commit to another, it does two main things:
- choose whether to be in "detached HEAD" mode, and
- rearrange the work tree to match the moved-to commit.
Step 2 is where the problem is occurring. You're on the commit identified by origin/master
, and in that commit, there is no record of the files git is currently complaining about. You've asked to switch to the commit identified by master
, which is obviously a different commit.1 Git sees that in the commit identified by master
, there are some (maybe just one) files with the same names, that are different from the files or directories that are in your work-tree right now.
In order to switch from the current commit to the new one, git checkout
must remove the existing files-or-directories and replace them with the ones in the new commit—the one you're asking to switch to. If those files or directories were tracked in the current commit, git would be happy to remove and replace them as needed, as git can always get them back for you: just switch back to that old commit, and there they are. But they're not in the current, to-be-switched-away-from, commit. So git tells you: "Hey, if I make this switch you asked for, I can't guarantee that I'll be able to restore these files and/or directories."
It's now up to you, not git, to figure out what to do with these files and/or directories. Based on the error message, it's a directory,2 and switching to master
will cause that directory to be removed and replaced with something else (possibly a different directory with some file(s) in it, possibly just a file). Do you:
- want to save it/them?
- if so, do you want to save it/them in a commit, or just move them out of the way?
- or do you just want to blow them away?
To save them, either commit them, or move them out of the way (e.g., rename them to a path that's not part of the work-tree, or to a different untracked name that's "safer", whatever that would be).
To simply blow them away, remove them manually or use git checkout -f
(force) to make git do it.
Since you're not on a branch now (are in "detached HEAD" mode), if you want to commit them permanently to the repository, you can use something like the method CommuSoft added while I was writing this up. (You can create the new branch at any time, before or after doing a "git commit".)
You can also use git stash
. Git's stash
is deceptively complex little script: it makes commits that are not on any branch at all, that can later be transplanted to a branch. Using it is quite simple and easy: you just run git stash save
and all pending tracked changes are saved and cleaned up, or run git stash save -u
and all pending tracked changes and untracked files are saved and cleaned up. They are now all safely squirreled away in the repository, under a commit, even though it's not a commit-on-a-branch.
There is no single right answer for what to do here.
1Obviously different, because if you were already on the commit you're asking git to move to, then either the file would be in the commit and hence tracked, or it would be not-in-the-commit and hence you would not be asking git to clobber it.
2This is a little odd. If I make a directory that will be clobbered by my git checkout
, and I make it as an empty directory, git just goes ahead and clobbers it. Here the difference between master^
and master
is that moving forward from master^
to master
creates file mxgroup.py
(so stepping back removes it):
$ git checkout -q master^ # file goes away, now let's mkdir...
$ mkdir mxgroup.py; git checkout -q master
$ file mxgroup.py
mxgroup.py: Python script, ASCII text executable
However, if I have a non-empty directory, I get a different error message:
$ git checkout -q master^ # file goes away; mkdir and make file
$ mkdir mxgroup.py; touch mxgroup.py/file; git checkout -q master
error: Updating the following directories would lose untracked files in it:
mxgroup.py
Aborting
But this is with git version 2.0.2; perhaps older gits are not as clever.
Best 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
yieldsrefs/heads/master
The branch named “master” is checked out.
git rev-parse refs/heads/master
yield17a02998078923f2d62811326d130de991d1a95a
That commit is the current tip or “head” of the master branch.
git rev-parse HEAD
also yields17a02998078923f2d62811326d130de991d1a95a
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
HEAD
→refs/heads/master
→17a02998078923f2d62811326d130de991d1a95a
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 withfatal: ref HEAD is not a symbolic ref
git rev-parse HEAD
yields17a02998078923f2d62811326d130de991d1a95a
Since it is not a symbolic ref, it must point directly to the commit itself.
We have
HEAD
→17a02998078923f2d62811326d130de991d1a95a
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:
(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:
(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:(these two commands can be abbreviated as
git checkout -B master temp
)You can then delete the temporary branch:
Finally, you will probably want to push the reestablished history:
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 usegit 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).