The other answer here is not quite right, although this:
but on our gitlab server it shows the branch just fine like nothing happened.
is promising and hence you may have an easy way to fix things.
I'll show an example where I destroy my own master
branch (in a clone of course). Since this particular system is case-sensitive I just hand-removed the master
branch (behind Git's back, as it were).
The Git bug: branch names are case sensitive, except when they're not
This is a bug in Git: it managed to delete the branch you were standing on.
The problem occurs because Git is case-sensitive: the branch exif
is, as far as Git is concerned, entirely separate from the branch Exif
. When you asked Git to delete exif
it, in essence, checked:
- Am I on branch
exif
?
- If so, reject the attempt because it will screw everything up.
- Otherwise, go ahead and delete
exif
.
So Git checked, and it saw that your current branch was Exif
, not exif
. Well, to Git, those are clearly different, it must be safe to delete! Only ... they're not, and it's not. The truth is complicated, but on Windows and MacOS systems, Git either needs some implementation changes—so that branch names really can be case-sensitive, all the time—or Git needs to stop believing that they are case-sensitive because they sometimes (usually, on these systems) aren't.
Fixing it
In the meantime, the tricky part is recovering your branch. This requires a bit of delving into Git internals.
The two parts
Within Git, there are two key components to the current branch. One is the file .git/HEAD
. This file contains a simple string:
$ cat .git/HEAD
ref: refs/heads/master
$
That is, this file contains (as plain text: not "rich text", not Unicode, not Windows UCS-2 format, just simple ASCII text) the literal string ref: refs/heads/
, with one space before refs/heads/
, followed by the name of the branch, followed by a newline. This file is unharmed!
The other half is more complicated. The good news is most of the complication goes away, because if it hadn't, the branch name would be case sensitive "the rest of the way" and you might (or might not) still be OK. But this:
$ git log
fatal: your current branch 'Exif' does not have any commits yet
proves the branch value was stored only in the file .git/refs/heads/branchname
.
That is, you used to have a file named:
.git/refs/heads/Exif
which had some hash value in it, something like this one (but with a different hash):
$ cat .git/refs/heads/master
3ab228137f980ff72dbdf5064a877d07bec76df9
What we need to do is to put that file back, with that same value.
Fixing it: if you have the value (or part of it)
If you can see the value—or even just part of it—somewhere, e.g., in an existing window, that is sufficient to get the whole value back. For instance, if I have 3ab228
showing somewhere I can do this:
$ git rev-parse 3ab228
3ab228137f980ff72dbdf5064a877d07bec76df9
which lets me do this to fix things:
$ git rev-parse 3ab228 > .git/refs/heads/master
and we're done. (See the last item below about reflogs.)
Fixing it: if you have it as a remote-tracking branch
If you have successfully pushed the branch earlier, you may have the value as a remote-tracking branch:
$ git rev-parse origin/master
3ab228137f980ff72dbdf5064a877d07bec76df9
In this case you can put that back in place:
$ git rev-parse origin/master > .git/refs/heads/master
and you're done. (See the last item below about reflogs.)
Fixing it: if your upstream has it
In your case it sounds like the remote named origin
has the right hash ID under the name exif
(all lower-case), i.e., for some reason it ignored the git push --delete
command. In this case, you can ask the remote for the value. Note that anyone else might have updated it since then:
$ git ls-remote origin master
454cb6bd52a4de614a3633e4f547af03d5c3b640 refs/heads/master
(clearly my remote has moved on, so I would not be able to use this, but if you're lucky yours has not). Make sure the value is good:
$ git rev-parse 454cb6bd
454cb6bd
fatal: ambiguous argument '454cb6bd': unknown revision or path
not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
(so this doesn't work for me, because they have indeed moved on).
As a last resort, you can run:
$ git fsck --unreachable
which is likely to spit out a whole lot of unreachable
messages:
unreachable commit fcfbe9a1165e38467b4d24d41b3166a20c1dfb80
unreachable commit c8056a11ef8eee1bfaaaa2e32a9cc92a02eae2e0
unreachable blob 010a1a22a90b2bd78e6b87a3922c3324c44a8a9b
unreachable commit c1163ad1eeff0c86f030483907ea8cedcdd431e3
unreachable commit f35fc21adeb21e1a4800ccebd5719317efb968dc
Ignore everything but the "commit"s: one of those is the right one. To find out which one, run git show
on each one, perhaps with something to help shorten them a bit, e.g.:
$ git show --pretty=oneline --no-patch fcfbe9a1165e38467b4d24d41b3166a20c1dfb80
fcfbe9a1165e38467b4d24d41b3166a20c1dfb80 WIP on precious: e59f6c2 The last
minute bits of fixes
(this shows that this particular item was actually a git stash
commit).
When you find the right one, you can put it into .git/refs/heads/Exif
to restore the value, and your repository will be mostly back in shape. The git rev-parse
method works here too, although since you have the full raw hash, you don't really have to rev-parse
it.
The reflog is gone; and where else you might look
Restoring the branch file fixes up the branch and makes everything ready again, e.g.:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
However, the reflog for the branch is now gone:
$ git reflog master
$
The reflog for HEAD
still works, and once you do a few more things on your branch, the reflog for the branch will be re-created.
If your file system gets backed up—e.g., via MacOS Time Machine, or by doing snapshots on a Windows network drive, or some such—you can look at your system backups. If you are lucky, you may find both the branch file and the reflog in these backups.
In this case, you can simply restore them in place. This has the same effect as finding the correct hash, but by restoring the reflog, you get your reflog back.