git merge --rebase:如何查找"前/前sha1"

时间:2017-10-18 11:31:13

标签: git

想象一下,我有分支develop和分支feature

feature,提交shaX

有人将develop feature--rebase合并并推送,因此功能提交现在为shaY,历史记录已被重写。

目前,我只有shaY。但我想从feature重新创建原始shaX分支。

当我只知道shaX时,如何获得shaY

我可以(如果需要)接受在dev的计算机上完成的解决方案,但是为了我自己的知识,我希望能够在任何机器上完成解决方案(如果可能的话),并且因为那个开发人员可能已经离开或他的机器被破坏

1 个答案:

答案 0 :(得分:1)

作为Mort said in a comment,您无法在任何计算机上执行此操作。

实际上,我们必须考虑不是计算机而是存储库。问题变成:哪些​​存储库具有或者具有 X ,我们如何找到它?只要我们记住,我们称之为("谁"或者#34;什么"或者#34;存储库"或其他什么)并不重要一旦我们找到这个集合,它就是一个单独的存储库列表。

谁有提交?

由于Git是分发的,因此可能存在许多存储库副本。每个副本可能有点不同。 一些份数 - 虽然数量可能 0 - 拥有原始提交,而某些赢得了<\ n> / em>的。第一个数字可能为零的原因正是您所说的:

  

......开发人员可能已经离开或他的[复制]被破坏

如果他是唯一拥有可行副本的人,并且现在已经不复存在了,那么你可以归结为原件的零拷贝和运气不好。但是由于他在他的Git存储库中执行了原始操作,我们知道他必须在某些时候使用hash X 进行原始提交。所以问题扩展到:还有谁 - 其他存储库可能有 X?其次,对于那些可能拥有它的人,我们如何找到它?

第一个问题的答案始于以下几点:任何选择它的人,都会在某个时候接受它。如果是这样,他们可能仍然拥有它。如果没有,他们显然没有。

大多数人提交提交的方式通常是运行git fetch。 (请注意,git clone是一个包装器,一旦创建了新的存储库,就会运行git fetch,所以即使git clone也是git fetch的情况。)你运行git fetch,你指示你的Git连接到另一个Git。你的Git问另一个Git:你有什么分支和标签名称?他们的Git向你发送一个列表,然后你的Git选择它喜欢的名字并要求他们的Git发送任何可达提交和其他符合这些名称的对象,你的Git还没有。

一旦你的Git拥有这些对象,你的Git就会采用这些名称并(通常)以某种方式更改它们,并将这些名称放入您自己的存储库中。如果他们有一个名为feature的分支,则您的Git会将其名称feature转换为您自己的名称origin/feature。然后,您的Git会将自己的引用 refs/remotes/origin/feature设置为其分支feature的提示提交的哈希ID。

(对于标记名称,您自己的Git通常会使 no 更改,这就是标记名称为#34;比分支名称更全局&#34 ;.

通常情况下,大多数运行git fetch的人只会带一个分支,例如,如果我运行git pull origin master(运行git fetch origin master)我最终会指示我的Git连接到你的Git,向你的Git询问你有哪些分支和标签,但是然后只带你的master,然后把我叫origin/master - 或带上所有分支机构。因此,任何将git fetch运行到另一个拥有&#34; good&#34;的Git的Git当时以feature名称提交,并将feature带回来,将带来&#34;良好提交 X &#34;。

这为您提供了候选人池:谁可能最初选择 X ?任何运行git fetch的人(包括由git fetchgit clone运行的git pull - 虽然后者倾向于限制提取,但却运行到 X的存储库,而该存储库有 X

需要考虑另外一组存储库,因为还有另一种获取提交的方法:git push可以发送提交。 git push操作有点像git fetch,但角色是相反的。而不是让我的Git与你的Git交谈,以便我的Git能够从你的提交提交,我让我的Git与你的Git交谈,这样我的Git就可以向你提交提交。我提供了提交对象和任何其他对象,然后我发送了一个请求:请将您的分支或标记名称(例如refs/heads/feature)设置为此哈希ID。使用--force我发送命令:将您的名字设置为此哈希ID!强制标志覆盖默认值&#34;是快进&#34;检查在接收端完成(但在接收端强制执行的任何其他检查:命令仍然可以被拒绝)。

因此,git push获得良好提交的任何人都可以拥有它。即使后续的force-push覆盖了会导致良好提交 X 的名称,仍然可以,但默认情况下,它不是,因为原因在下一节中给出。

如果你有 X ,你还有 X 吗?

现在我们已将所有候选存储库排成一行,我们需要检查它们。要查看的地方是 reflogs reflog 是Git用来保留参考文件的历史记录。

我们上面已经使用&#34;参考&#34;没有正确定义它。引用只是以refs/开头的名称。 Git通过这些引用发现了几乎所有内容。例如,分支名称是以refs/heads/开头的引用,标记名称是以refs/tags/开头的引用。每个引用都存储一个(单个)哈希ID。

只要您的Git 更新引用 - 将当前哈希ID替换为新哈希ID,您就可以选择让您的Git 保存 值,在日志条目中。这些日志条目是您的 reflogs

每个引用都有一个reflog,特殊名称HEAD也有一个reflog。每个reflog都有一个可能无限长度-Git只是在每次引用更新时添加它 - 所以为了防止reflog消耗无限空间,Git也为每个reflog条目分配一个时间戳。然后Reflog条目最终到期。

任何给定的reflog条目的到期时间默认为30天或90天。这部分有点棘手,我没有时间在这里写下来,所以让我们来看看30天的短时间。虽然reflog条目仍处于活动状态,但它会保留早期的哈希ID,并且通过保留它们,它会保留这些对象&#34; live&#34;在存储库数据库中。

与此同时,还有另外两个棘手的部分:

  • 默认情况下,接受git push请求的服务器将 禁用。这使得他们不太可能保留良好的提交 X ,如果他们已经推迟了错误的替换。
  • 客户端 - 更有可能 X - 已经重命名 featureorigin/feature

因此,查看是否可以找到具有良好哈希 X 的提交的位置在 reflogs 中,可能是origin/feature的那些,在每个运行git fetch的客户端。

在任何给定的Git存储库上查看reflog的命令是git reflog reference。 (这实际上会运行git log -g,因此受到用户配置git log选项的影响。)这是我的副本git reflog origin/mastergit reflog origin/pu的摘要Git的Git存储库:

3dc57ebfb refs/remotes/origin/master@{2}: fetch: fast-forward

ca0964be6 refs/remotes/origin/pu@{1}: fetch: forced-update

我展示这些是为了说明更正常的提取之间的区别,这种提取会带来新的提交而不会丢弃旧提交:origin/master@{2}指向提交3dc57ebfb,以及何时更新发生了,这是一个正常的风格更新;但是origin/pu@{1}指向ca0964be6,当更新发生时,这是一次强制更新&#34;。

如果有人选择了提交 X ,然后feature被重新定位并强行推送到origin,并且有人选择了新的错误 commit chain,他们的origin/feature将经历强制更新。这意味着您将在reflog中看到强制更新,这反过来意味着早期引用有可能指向良好提交 X ,或者其历史记录引导的提交好的提交 X

它是否实际直接指向 X X 的某些后代是偶然的,您必须手动检查这些提交以找出。< / p>