Git: remote current branch and lost reflog

I ran into an unusual git problem that I have since solved, but I'm still wondering why this happened.

The problem arose when I accidentally deleted a branch in which I worked. Normally, git would not allow this, but due to case insensitivity on OSX, I got into a situation where I thought I had two branches, one called feature/ONE and the other called feature/ONE . I think that these were two separate branches (coming from the main background, depending on linux / case), and that I was working on the function / ONE I tried to delete the function / one using git branch -D .

I quickly noticed what I did, tried to recover the lost work from git reflog , which gave me the error fatal: bad default revision 'HEAD' . I tried to return to normal using git checkout -f develop , which worked. However, when I looked at git reflog after that, unfortunately, he only had one entry with the message checkout: moving from feature/ONE to develop . There were no previous actions in the journal.

I compiled some steps to replicate such a scenario (presumably this is only possible for case-insensitive file systems):

 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 

Since then, I have been able to find my lost commits by checking git-fsck , but my question is this:

Why did this sequence break reflog? Should reflog still know the READ HEAD history, even if the branch is removed?

+5
source share
2 answers

Under normal conditions, HEAD points to SHA1 (in this case, it is called disconnected) or points to an existing branch reference (in this case, the named branch is checked).

When you check new-branch ( HEAD points to refs/heads/new-branch ) and then somehow remove the new-branch , Git just removes the branch referencing file ( .git/refs/heads/new-branch ) and branch reflog file ( .git/logs/refs/heads/new-branch ). Git does not delete HEAD , nor does it update it to indicate in another place (for example, SHA1, which new-branch used to indicate), because it should not be necessary - you should not delete the current branch. Thus, HEAD still refers to the remote branch, which means that HEAD no longer indicates a valid commit.

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

The problem you are encountering arises from how the reflog file is updated and how git reflog handles the updated reflog file. Each reflog entry contains "from" and "to" SHA1. When you switch from a non-existent new-branch to master , Git does not know what SHA1 is from. Instead of an error, it uses all-zeros SHA1 ( 0000000000000000000000000000000000000000 ). All-zeros SHA1 is also used when ref is created, so this last reflog entry makes it look like the HEAD just created when in fact it was never deleted. Apparently, the china git reflog stops reflowing when it encounters all-zeros of SHA1, even if there are more entries, so git reflog prints only one entry.

The following is shown:

 $ 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 reflog still has all the old entries - they just aren't showing git reflog . I believe this is a bug in Git.

Note: when you delete ref, the corresponding log is also deleted. I consider this a mistake, since there is no way to completely cancel the accidental deletion of links if you do not have a backup copy of the log.

+4
source

Remote branch and lost log

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

See commit 39ee4c6 , commit 893dbf5 , commit de92266 , commit 755b49a (February 21, 2017) by Kyle Mayer .
(Combined by Junio ​​C Hamano - gitster - to commit to c13c783 , February 27, 2017

branch : record the creation of a renamed branch in the HEAD log

Renaming the current branch adds the event to the current branch log and to the HEAD log.
However, registered entries are different.
A branch log entry represents the entire renaming operation (the old and new hash are identical), whereas a HEAD log entry represents only deletion (the new sha1 is null).

Extend replace_each_worktree_head_symref() , the only caller to branch_rename() to accept the argument to the reflog message.
This allows you to record a new recorder in the HEAD log.
As a result, the renaming event is represented by two entries (deletion and creation entry) in the HEAD log .

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

It would be advisable to move the branch log to a two-task form, but this will be due to changes in the way renaming is done and how the update and lock flags are processed for deletion, so this may not be worth the effort.

+1
source

Source: https://habr.com/ru/post/1202804/


All Articles