Git: Deleted current branch and lost reflog

I ran into an unusual git issue earlier that I’ve since resolved, but I’m still curious as to why it happened.

The problem occurred when I accidentally deleted the branch I was currently working on. Normally git wouldn’t allow this, but due to case-insensitivity on OSX I got myself into a situation where I thought I had two branches, one named feature/ONE and another named feature/one. Thinking these were two separate branches (coming from a mostly linux/case-sensitive background) and that I was working on feature/ONE I attempted to delete feature/one using git branch -D.

  • Can git be configured to prevent rebase of already published commits?
  • Word wrap when using git grep
  • What to do after typing in commit message for git?
  • How to revert uncommitted changes including files and folders?
  • Remove 4 commits from my git history
  • Make git ignore the date in PDF files
  • I quickly noticed what I had done, tried to retrieve my lost work from git reflog, which gave me the error fatal: bad default revision 'HEAD'. I attempted to get back into a normal state using git checkout -f develop, which worked. However, when I looked at git reflog after this unfortunately it only had one entry stating checkout: moving from feature/ONE to develop. No previous actions appeared in the log.

    I’ve compiled some steps to replicate this kind of scenario (presumably this is only possible on case-insensitive filesystems):

    mkdir test
    cd test
    git init
    echo 'hi' > file1
    git add file1
    git commit -m 'test commit 1'
    git checkout -b new-branch
    echo 'test2' > file2
    git add file2
    git commit -m 'test commit 2'
    git branch -D NEW-branch
    git checkout -f master
    git reflog
    

    I’ve since been able to find my lost commits by checking git-fsck, but my question is this:

    Why did this sequence of actions break reflog? Shouldn’t reflog still know the history of the HEAD ref, even though the branch was deleted?

  • EC2/AWS, Jenkins, Git, SSH
  • What should I do with Grunt's node_modules directory when I am publisihing my project to GitHub?
  • Git Remote: Error: fatal: protocol error: bad line length character: Unab
  • Set up git to pull and push all branches
  • Should you check in your compiled assets to Git?
  • Make Github push to a remote server when it receives updates
  • 2 Solutions collect form web for “Git: Deleted current branch and lost reflog”

    In normal circumstances, HEAD either points to a SHA1 (in which case it’s called detached) or it points to an existing branch reference (in which case the named branch is considered to be checked out).

    When you check out new-branch (HEAD points to refs/heads/new-branch) and then somehow manage to delete the new-branch branch, Git simply deletes the branch’s ref file (.git/refs/heads/new-branch) and the branch’s reflog file (.git/logs/refs/heads/new-branch). Git does not delete HEAD, nor does it update it to point somewhere else (such as the SHA1 that new-branch used to point to), because there shouldn’t be a need—you’re not supposed to be able to delete the current branch. So HEAD still references the now-deleted branch, which means that HEAD no longer points to a valid commit.

    If you then do git checkout -f master, Git updates HEAD to point to refs/heads/master, a new entry is added to HEAD‘s reflog file (.git/logs/HEAD), the files are checked out, and the index is updated. All of this is normal—this is what Git always does when you check out another branch.

    The issue you encountered arises from how the reflog file is updated and how git reflog processes the updated reflog file. Each reflog entry contains a “from” and “to” SHA1. When you switch from the non-existent new-branch branch to master, Git doesn’t know what the “from” SHA1 is. Rather than error out, it uses the all-zeros SHA1 (0000000000000000000000000000000000000000). The all-zeros SHA1 is also used when a ref is created, so this most recent reflog entry makes it look like HEAD was just created, when in fact it was never deleted. Apparently the git reflog porcelain command stops walking the reflog when it encounters the all-zeros SHA1 even if there are more entries, which is why git reflog only prints one entry.

    The following illustrates this:

    $ git init test
    Initialized empty Git repository in /home/example/test/.git/
    $ cd test
    $ echo hi >file1
    $ git add file1
    $ git commit -m "test commit 1"
    [master (root-commit) 3c79ff8] test commit 1
     1 file changed, 1 insertion(+)
     create mode 100644 file1
    $ git checkout -b new-branch
    Switched to a new branch 'new-branch'
    $ echo test2 >file2
    $ git add file2
    $ git commit -m "test commit 2"
    [new-branch f828d50] test commit 2
     1 file changed, 1 insertion(+)
     create mode 100644 file2
    $ cat .git/HEAD
    ref: refs/heads/new-branch
    $ cat .git/refs/heads/new-branch
    f828d50ce633918f2fcaaaad5a52ac1ffa1c81b1
    $ git update-ref -d refs/heads/new-branch
    $ cat .git/HEAD
    ref: refs/heads/new-branch
    $ cat .git/refs/heads/new-branch
    cat: .git/refs/heads/new-branch: No such file or directory
    $ cat .git/logs/HEAD
    0000000000000000000000000000000000000000 3c79ff8fc5a55d7c143765b7f749db4dd8526266 Your Name <email@example.com> 1411018898 -0400        commit (initial): test commit 1
    3c79ff8fc5a55d7c143765b7f749db4dd8526266 3c79ff8fc5a55d7c143765b7f749db4dd8526266 Your Name <email@example.com> 1411018898 -0400        checkout: moving from master to new-branch
    3c79ff8fc5a55d7c143765b7f749db4dd8526266 f828d50ce633918f2fcaaaad5a52ac1ffa1c81b1 Your Name <email@example.com> 1411018898 -0400        commit: test commit 2
    $ git checkout -f master
    Switched to branch 'master'
    $ cat .git/logs/HEAD
    0000000000000000000000000000000000000000 3c79ff8fc5a55d7c143765b7f749db4dd8526266 Your Name <email@example.com> 1411018898 -0400        commit (initial): test commit 1
    3c79ff8fc5a55d7c143765b7f749db4dd8526266 3c79ff8fc5a55d7c143765b7f749db4dd8526266 Your Name <email@example.com> 1411018898 -0400        checkout: moving from master to new-branch
    3c79ff8fc5a55d7c143765b7f749db4dd8526266 f828d50ce633918f2fcaaaad5a52ac1ffa1c81b1 Your Name <email@example.com> 1411018898 -0400        commit: test commit 2
    0000000000000000000000000000000000000000 3c79ff8fc5a55d7c143765b7f749db4dd8526266 Your Name <email@example.com> 1411018898 -0400        checkout: moving from new-branch to master
    $ git reflog
    3c79ff8 HEAD@{0}: checkout: moving from new-branch to master
    

    As you can see, HEAD‘s reflog still has all of the old entries—they’re just not shown by git reflog. I consider this to be a bug in Git.

    Side note: When you delete a ref, the corresponding log is also deleted. I consider this to be a bug, as there’s no way to completely undo an accidental ref deletion unless you have a backup of the log.

    Deleted current branch and lost reflog

    Two years later, this issue should be alleviated in Git 2.13 (Q2 2017).

    See commit 39ee4c6, commit 893dbf5, commit de92266, commit 755b49a (21 Feb 2017) by Kyle Meyer (kyleam).
    (Merged by Junio C Hamano — gitster — in commit c13c783, 27 Feb 2017)

    branch: record creation of renamed branch in HEAD’s log

    Renaming the current branch adds an event to the current branch’s log
    and to HEAD’s log.
    However, the logged entries differ.
    The entry in the branch’s log represents the entire renaming operation (the old and new hash are identical), whereas the entry in HEAD’s log represents
    the deletion only (the new sha1 is null).

    Extend replace_each_worktree_head_symref(), whose only caller is
    branch_rename(), to take a reflog message argument.
    This allows the creation of the new ref to be recorded in HEAD’s log.
    As a result, the renaming event is represented by two entries (a deletion and a creation entry) in HEAD’s log.

    It’s a bit unfortunate that the branch’s log and HEAD’s log now represent the renaming event in different ways.
    Given that the renaming operation is not atomic, the two-entry form is a more
    accurate representation of the operation and is more useful for debugging purposes if a failure occurs between the deletion and creation events
    .

    It would make sense to move the branch’s log to the two-entry form, but this would involve changes to how the rename is carried out and to how the update flags and reflogs are processed for deletions, so it may not be worth the effort.

    Git Baby is a git and github fan, let's start git clone.