jelte
jelte

Reputation: 57

Remote branch exists, local branch disappeared

Ok, so the context is that im working on my own branch, creating a feature for a certain project.

My branch is called Exif (capital E) and today i accidentally did

git push origin exif  (lowercase e)

so it created a new branch for me called exif

After I realized this , I did

$ git push origin --delete exif
$ git branch -d exif

effectively deleting the branch again.
I have to add that I executed these 2 lines while I was checked out inside Exif (capital E), But that shouldn't matter right?

Now, when I execute

git status

I get a huge list saying that every file inside the whole project is a new file

When I try

git log

It says , you're current branch doesn't have any commits yet

when I do

git branch -a

all branches are there, except Exif, its also not in the remote branch list

but on our gitlab server it shows the branch just fine like nothing happened.

Does anyone have any idea what happened here?

EDIT:

In eclipse it shows an asterix next to every file, meaning that every file is staged.

In eclipse it also shows next to the root project folder that the project has [NO-HEAD]

Upvotes: 0

Views: 2571

Answers (2)

torek
torek

Reputation: 487755

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.

Upvotes: 0

ggradnig
ggradnig

Reputation: 14149

When deleting a branch, git is not case sensitive, meaning your branch "Exif" would be deleted if you executed

git branch -d exif

You didn't even create a branch "Exif" in the first place, because by pushing to a different branch, you don't create that branch in your local repository automatically.

To go back to your original branch, I would create a new local branch by checking out the remote branch again like so:

git checkout origin/exif

Upvotes: 1

Related Questions