git rebase:重新提交的提交仍然在索引?

时间:2014-01-27 15:28:02

标签: git git-rebase

当我读到git-rebase时,我理解重新提交的提交应该丢失。我说应该是因为我注意到了,知道重新设定的sha,我可以回想起来 假设我有以下三个提交

A -> B -> C

其中C的sha是cshaid。然后,如果我以fixing-up交互式地将C B变为git rebase -i HEAD~2,然后使用git log检查结果,我会获得预期结果,这意味着

A -> B'

其中B'的sha与B的sha不同 但是,再次显示git log cshaid会显示

A -> B -> C

问题:这是一种已知行为吗?我尝试阅读git rebase --help但找不到相关信息。为什么重新提交承诺并不是简单的忘记?我的意思是,只有当你知道自己在做什么并且能够做到这一点时,rebase才是一种危险的操作,这就是拥有脏索引(或者保存这些无用提交的地方)的重点?我错过了什么吗? 重现步骤(并更好地理解我的怀疑)。如果您愿意重现这种情况,请尝试:

  1. mkdir sampledir && cd sampledir && git init
  2. touch file && git add -A . && git commit -m "Initial"
  3. 编辑文件,然后git commit -am "First modification"
  4. 编辑文件,然后git commit -am "Second modification"
  5. git log,您会看到三次提交,请记住Second modification
  6. 的sha
  7. git rebase -i HEAD~2fixup Second modification加入First modification
  8. git log,您会看到两个提交,其中First modification的sha现在与第5步不同
  9. 但是,git log sha-for-"Second modification"将显示与此列表中第5点完全相同的树

4 个答案:

答案 0 :(得分:4)

是的,这是预期的行为。未引用的提交最终将被垃圾收集,从而从磁盘中清除。它们被保存了好几天(默认情况下为14天),但在此之前的14天计时器甚至开始计时,对象必须已经从reflog中过期(默认情况下,无法访问的对象在30天后过期)。

相关的StackOverflow问题:

答案 1 :(得分:3)

  

......我知道重新提交的提交应该丢失

他们不是迷失,他们(故意)“被遗弃”(我的任期)。

确实rebase复制了旧提交的内容。实际上,除了特殊的优化等之外,它基本上与执行git cherry-pick相同(并且交互式rebase脚本对每个“pick”操作使用git cherry-pick,并且对“squash”和“modash”提交修改样式提交修理“操作”。

然后,存储库中的提交何时以及是否可见完全取决于其他内容。通常git log以您所在分支的名称开头,如HEAD中所记录的那样(.git目录中有一个名为HEAD的文件,其中包含字符串{ {1}},这就是git知道你是“在分支主人身上”的原因。) 1

给定一个分支名称,git通过“读取引用”将其转换为(单个)提交:

ref: refs/heads/master

然后$ git rev-parse master # note: you can also rev-parse HEAD directly 676699a0e0cdfd97521f3524c763222f1c30a094 命令可以通过SHA-1读取提交对象。该提交对象有一些父SHA-1,log也读取它们,依此类推,直到它到达没有父项的提交(“root”提交)。

因此,给定根提交git log,第二次和第三次提交AB - 加上标签C,指向master

C

(这里的箭头表示谁指向谁,它的方式与你的绘图相反!),git可以从A <-- B <-- C <-- HEAD=master 开始A找到(到达)提交C并且向后工作。

rebase复制C并在B折叠,按预期给出C

B'

A <-- B <-- C ^ \ B' 显示B'的原因是标签git log被“剥离”了提交master并且“粘贴到”提交{{1} }}。更确切地说,分支CB' 2 的文件将被master的新SHA-1重写:

.git/refs/heads/master

正如我所听到的那些答案所指出的那样,“废弃的”提交(以及存储库中的任何其他被遗弃的对象)最终被“垃圾收集器”B'删除。

然而,声称“没有”标签的说法有点过分了。隐藏在“reflog”中的至少一个标签会使提交A <-- B <-- C [no label, "abandoned"] ^ \ B' <-- HEAD=master 不被垃圾收集。并且,如果您创建引用git gc的分支或代码标签,则在C移动C标签之前或之后, 标签也会保留存储库中的rebase,可通过“普通”名称访问,您将看到master(查看所有分支和标记名称,而不仅仅是C中的名称)


1 git log --all文件可以包含原始SHA-1。在这种情况下,你有git称之为“分离的HEAD”:你的SHA-1提交而不是它的分支名称。

2 分支和标记名称(实际上,任何引用都可以)“打包”,在这种情况下,单独的文件会消失。这节省了空间,并且您不应该依赖于单独文件的存在。但是,一旦分支变为“活动” - 进行了大量更新 - 单独的文件将重新出现,因为更新该文件比更新packed-refs文件更快更容易。

答案 2 :(得分:2)

与其他以破坏性方式更改历史记录的命令一样,

git rebase会删除对过时提交的引用,但不会导致立即删除它。在正常操作过程中定期自动执行的git gc将(最终)从.git/objects中删除实际的提交数据(尽管reflog将保留对提交的活动的引用一段时间)。

这是一项安全功能;它使用git实际丢失数据变得非常困难。如果你真的想确保某些东西不见了 - 例如,如果你不小心提交了一个巨大的文件并且想要恢复磁盘空间 - 你需要使reflog条目到期并手动运行git gc

git reflog expire --expire=now --all
git gc --aggressive --prune=now

答案 3 :(得分:1)

  

我知道重新提交的提交应该丢失。

不。提交不要迷路。在繁忙的回购中,git最终将垃圾收集任何参考文件完全无法访问的内容并且已经持续了一个月或更长时间,但除了git gc之外,git操作只会添加到历史记录图中。

移动标签对您的仓库中的实际历史记录完全没有影响。