简体   繁体   English

Git:删除当前分支并丢失了reflog

[英]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. 我之前遇到了一个不寻常的git问题,我已经解决了,但我仍然很好奇它为什么会发生。

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 . 通常git不会允许这样做,但是由于OSX上的大小写不敏感,我让自己陷入了一种情况,我认为我有两个分支,一个名为feature/ONE ,另一个名为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 . 认为这些是两个独立的分支(来自一个主要是linux /区分大小写的背景),我正在使用feature / ONE,我试图使用git branch -D删除feature / one。

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' . 我很快注意到我做了什么,试图从git reflog找回丢失的工作,这给了我fatal: bad default revision 'HEAD'的错误fatal: bad default revision 'HEAD' I attempted to get back into a normal state using git checkout -f develop , which worked. 我尝试使用git checkout -f develop恢复正常状态,这有效。 However, when I looked at git reflog after this unfortunately it only had one entry stating checkout: moving from feature/ONE to develop . 然而,当我在此之后查看git reflog ,它只有一个条目表明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: 我已经通过检查git-fsck找到了丢失的提交,但我的问题是:

Why did this sequence of actions break reflog? 为什么这一系列动作会破坏reflog? Shouldn't reflog still know the history of the HEAD ref, even though the branch was deleted? 难道reflog仍然不知道HEAD ref的历史,即使分支被删除了吗?

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). 在正常情况下, HEAD指向SHA1(在这种情况下称为分离 )或指向现有分支引用(在这种情况下,命名分支被视为已检出)。

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 ). 当你检查new-branchHEAD指向refs/heads/new-branch )然后以某种方式设法删除new-branch分支时,Git只删除分支的ref文件( .git/refs/heads/new-branch )和分支的reflog文件( .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. Git 不会删除HEAD ,也没有更新指向其它地方(如该SHA1 new-branch用来指向),因为不应该有必要-你不应该是能够删除目前的分支。 So HEAD still references the now-deleted branch, which means that HEAD no longer points to a valid commit. 所以HEAD仍然引用了现在删除的分支,这意味着HEAD不再指向有效的提交。

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. 如果你然后执行git checkout -f master ,Git会更新HEAD以指向refs/heads/master ,会在HEAD的reflog文件( .git/logs/HEAD )中添加一个新条目,检出文件,然后索引已更新。 All of this is normal—this is what Git always does when you check out another branch. 所有这一切都很正常 - 这就是Git在您查看另一个分支时总是会做的事情。

The issue you encountered arises from how the reflog file is updated and how git reflog processes the updated reflog file. 您遇到的问题源于reflog文件的更新方式以及git reflog如何处理更新的reflog文件。 Each reflog entry contains a "from" and "to" SHA1. 每个reflog条目都包含一个“from”和“to”SHA1。 When you switch from the non-existent new-branch branch to master , Git doesn't know what the "from" SHA1 is. 当您从不存在的new-branch分支切换到master分支时,Git不知道“来自”SHA1是什么。 Rather than error out, it uses the all-zeros SHA1 ( 0000000000000000000000000000000000000000 ). 它使用全零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. 创建ref时也会使用全零SHA1,因此这个最新的reflog条目使它看起来像刚刚创建的HEAD ,实际上它从未被删除过。 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. 显然,即使有更多条目, git reflog porcelain命令也会在遇到全零SHA1时停止遍历reflog,这就是为什么git reflog只打印一个条目。

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 . 正如你所看到的, HEAD的reflog仍然包含所有旧条目 - 它们只是没有被git reflog I consider this to be a bug in Git. 我认为这是Git中的一个错误。

Side note: When you delete a ref, the corresponding log is also deleted. 附注:删除ref时,也会删除相应的日志。 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. 我认为这是一个错误,因为除非您备份日志,否则无法完全撤消意外的ref删除。

Deleted current branch and lost reflog 删除当前分支并丢失reflog

Two years later, this issue should be alleviated in Git 2.13 (Q2 2017). 两年后,这个问题应该在Git 2.13(2017年第二季度)中得到缓解。

See commit 39ee4c6 , commit 893dbf5 , commit de92266 , commit 755b49a (21 Feb 2017) by Kyle Meyer ( kyleam ) . 请参阅Kyle Meyer( kyleam 提交的39ee4c6commit 893dbf5commit de92266commit 755b49a (2017年2月21日
(Merged by Junio C Hamano -- gitster -- in commit c13c783 , 27 Feb 2017) (由Junio C gitster合并- gitster -提交c13c783 ,2017年2月27日)

branch : record creation of renamed branch in HEAD's log branch :在HEAD的日志中记录重命名分支的创建

Renaming the current branch adds an event to the current branch's log and to HEAD's log. 重命名当前分支会将事件添加到当前分支的日志和HEAD的日志中。
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). 分支日志中的条目表示整个重命名操作(旧和新散列相同),而HEAD日志中的条目仅表示删除(新sha1为空)。

Extend replace_each_worktree_head_symref() , whose only caller is branch_rename() , to take a reflog message argument. 扩展replace_each_worktree_head_symref() ,其唯一调用者是branch_rename() ,以获取reflog消息参数。
This allows the creation of the new ref to be recorded in HEAD's log. 这允许创建新的ref以记录在HEAD的日志中。
As a result, the renaming event is represented by two entries (a deletion and a creation entry) in HEAD's log . 因此,重命名事件由HEAD日志中的两个条目(删除和创建条目)表示

It's a bit unfortunate that the branch's log and HEAD's log now represent the renaming event in different ways. 有点不幸的是,分支的日志和HEAD的日志现在以不同的方式表示重命名事件。
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. 将分支的日志移动到双条目形式是有意义的,但这将涉及更改重命名的执行方式以及如何处理更新标志和reflog以进行删除,因此可能不值得付出努力。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM