【问题标题】:After a git reset, unreachable commit not removedgit reset 后,未删除无法访问的提交
【发布时间】:2017-05-14 05:06:23
【问题描述】:

我有一个小仓库,里面有几个提交:

* a0fc4f8 (HEAD -> testbranch) added file.txt  
* e6e6a8b (master) hello world now  
* f308f53 Made it echo  
* f705657 Added hello  
* 08a2de3 (tag: initial) initial  

还有:

$ git status  
On branch testbranch  
nothing to commit, working directory clean  

我无法理解以下行为。在这种状态下,我运行: $ git reset initial
我现在看到了:

* e6e6a8b (master) hello world now  
* f308f53 Made it echo  
* f705657 Added hello  
* 08a2de3 (HEAD -> testbranch, tag: initial) initial  

我所期待的:提交 a0fc4f8 将被删除,因为它无法访问。
发生了什么:
1) 做git show a0fc4f8 仍然显示提交
2) 执行 git status 会将提交 a0fc4f8 添加的 file.txt 显示为未跟踪,提交 f705657 添加的文件 hello 也显示为未跟踪。
3) 运行 git gcgit gc --prune=all 不会删除 a0fc4f8,尽管它不再可访问并且没有与之关联的名称/标签。
为什么会出现这些情况?

更新:

$ git fsck  
Checking object directories: 100% (256/256), done.  
Checking objects: 100% (15/15), done.    

更新 2:

$ git log --all --decorate --graph --oneline  
* e6e6a8b (master) hello world now  
* f308f53 Made it echo  
* f705657 Added hello  
* 08a2de3 (HEAD -> testbranch, tag: initial) initial  

$ git gc --force  
Counting objects: 15, done.  
Delta compression using up to 4 threads.  
Compressing objects: 100% (8/8), done.  
Writing objects: 100% (15/15), done.   
Total 15 (delta 1), reused 15 (delta 1)   

$ git log --all --decorate --graph --oneline  
* e6e6a8b (master) hello world now  
* f308f53 Made it echo  
* f705657 Added hello  
* 08a2de3 (HEAD -> testbranch, tag: initial) initial  

$ git show a0fc4f8 仍然显示提交

更新 3:

$ git reflog testbranch  
08a2de3 testbranch@{0}: reset: moving to initial  
a0fc4f8 testbranch@{1}: commit: added file.txt  
e6e6a8b testbranch@{2}: branch: Created from HEAD  

【问题讨论】:

  • git fsckgit gc --prune=all 之后说了什么?
  • @MichałWalenciak:请检查更新
  • 如果您运行git reset (--mixed) initial,那么您的工作目录不会受到影响,这就是为什么您的所有更改都包含在a0fc4f8 提交中的原因。
  • @C1sc0:我没有跑(--mixed)
  • @Jim --mixed 是默认值

标签: git git-branch git-reset git-gc


【解决方案1】:

1) 执行git show a0fc4f8 仍然显示提交

这是设计使然。由于以下几个原因,无法立即删除无法访问的对象:

  • 也许您错误地运行了最后一个命令(或为其提供了错误的参数),您意识到了错误并想回到之前的状态;
  • 与完成操作所需的工作量相比,删除无法访问的对象(节省一些磁盘空间)的收益太小了。

不时自动执行修剪不可访问的对象。它也由一些 git 命令执行(fetchpush 是其中的一些)。

2) 执行git status 会将提交a0fc4f8 添加的file.txt 显示为未跟踪,提交f705657 添加的文件hello 也显示为未跟踪。

您在没有指定模式的情况下运行git reset。默认模式是--mixed,这意味着:

  • 分支被移动到命令中指定的提交(本例中为initial);
  • 索引被重置以匹配分支指向的新提交;
  • 工作树未修改。

这解释了为什么文件在目录中(第三个项目符号)以及为什么它们未被跟踪(第二个项目符号;索引与 initial 提交匹配,但这些文件在创建时甚至不存在)。

3) 运行 git gcgit gc --prune=all 不会删除 a0fc4f8,尽管它不再可访问并且没有与之关联的名称/标签。

git gc 还会检查分支 reflogs 中的引用。如果您的testbranch 分支启用了reflog,则reflog 中的最新条目指向提交a0fc4f8(这是testbranch 分支在您运行git reset 之前所在的位置)。您可以通过运行git reflog testbranch 检查是否为分支testbranch 启用了引用日志。如果它打印了一些东西,你会在第二行的位置testbranch@{1} 上找到提交a0fc4f8。符号name@{n} 表示分支name 的先前nth 值(它指向的提交,n 移动过去)。

您可以在documentation 中找到有关git gc 工作方式的更多信息。

Notes 部分中写道:

git gc 非常努力地确保它收集的垃圾的安全。特别是,它不仅会保留当前分支和标签集引用的对象,还会保留索引引用的对象、远程跟踪分支、git filter-branchrefs/original/ 中保存的引用或引用日志(可能引用提交在后来被修改或重绕的分支中)。

如果您希望收集某些对象而实际上没有,请检查所有这些位置并确定在您的情况下删除这些引用是否有意义。

【讨论】:

  • 我在 reflog 中看到 a0fc4f8 但它不是最近的条目,是吗?请参阅更新的 OP
  • 你是对的,这是第二个条目。第一个条目(testbranch@{0},最近的)是分支的当前位置。 testbranch@{1} 是它之前的位置(即在git reset 移动它之前的位置。)
  • 那么如果 reflog 引用了某些东西 git gc 会忽略它吗?但这意味着 git gc 永远不会删除提交?该提交不总是会成为 reflog 历史记录的一部分吗?
  • 对于 reflog 中提到的无法访问的提交,有一个可配置的过期阈值(默认为 30 天)。从数据库中删除过期的提交,并更新引用它们的 reflog。阅读文档(在您的控制台中的 git-scm.com/docs/git-gcgit help gc 上),那里描述了配置条目。
  • 除了 reflog 条目(使对象保持活动状态,直到 reflog 条目本身被删除)之外,所有 对象在被修剪之前获得宽限期,默认为 14 天。宽限期旨在允许各种命令写入临时对象,然后使其永久化:只要它们在开始后的两周内完成,它们就可以工作。这是您(即@Jim)用--prune=all 覆盖的东西。如果您的 reflog 过期,您可以让对象消失。
猜你喜欢
  • 1970-01-01
  • 2017-06-04
  • 2012-08-11
  • 1970-01-01
  • 1970-01-01
  • 2017-10-04
  • 2018-11-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多