git 获取强制推送历史

git get force push history

这个问题可能重复,但我没有找到完全相同的问题。

在我们 QA 团队的 git 存储库中,我经常发现我之前添加的测试用例丢失了。从主分支的历史中,我再也找不到我的提交了。

有一次对比我个人分支的日志和主分支的日志找到了根本原因,是有人强行推送的,那家伙承认他对git merge不是很熟悉但是赶紧推送所以使用强制选项。

但是如果没有这样的分支来备份这些提交历史怎么办?每个团队成员都在几天后退出,这样提交历史就永远消失了。

而且即使有这样的备份分支,要比较和了解谁强行推送也是非常耗时的。

有什么方法可以获取强制推送日志吗?请注意,作为一名普通员工,我不是 git 服务器的 owner/admin,但我关心我的提交并希望删除它们的人得到警告。

git 本身并没有真正可靠的方法来检索此类信息。 (有可能某些 git 托管软件可能会保留强制推送的类似 reflog 的记录,但我不知道有任何;如果您使用特定的托管包,您可以查阅其文档。)

除了找到尚未接受新历史记录的克隆之外,您还可以从克隆的引用日志中获取所需的信息,即使它已经接受了新历史记录 - 但引用日志是本地的并且暂时的,所以不能保证。

(根据您的远程主机的托管方式,您 可能 也可以参考其 reflogs,如果它保留它们并且您可以访问它们。但即使所有这些ifs 打断你的路,不能保证,因为 reflogs 是临时的。)

这就是为什么 git push -f 是一个危险的工具;冒着直言不讳的风险,如果开发人员对 git 的理解还没有发展到他们理解它有多危险的程度,那么不应授予该开发人员强制推送到与其他人共享的 refs 的权限开发人员。

您可以配置一个 git 遥控器来拒绝所有非快进推送(即使是强制的);请参阅 git config 文档中的 receive.denyNonFastForwards

如果您需要更细粒度的控制,您将不得不再次依赖托管软件来提供它。

您将无法获得强制推送的历史记录,但您至少可以窥探自己被破坏的提交。在本地克隆中,使用 git fsck:

 (master) :~/repo$ git fsck --lost-found
Checking object directories: 100% (256/256), done.
dangling blob 6edc1e217eaab8f72f67bcec2e4d1dfde299971e
dangling commit 9847c5942bf477989112ece202988bc3f8caad05
 (master) :~/repo$ git merge 9847c59
     // ... conflicts likely appear ... //

从这里,您可以解决冲突并在适当的时候推迟您的更改。如果您在与提交提交的系统不同的系统上,这将不起作用,即使这样,如果经过足够的时间,您的本地提交也可能会由于修剪而丢失。

这个 "hacky" 解决方案可能有助于找到强制推送的修改提交,但我不确定它是否适用于所有可能的情况。

假设您在远程(裸)存储库上,您可以打印出存储在特定分支的 reflog 中的所有提交,并将它们与普通 git log 的提交进行比较。差异应该会给你强制推送提交。

请注意,对于裸回购,reflogs must be enabled

举个例子,假设您已经提交了 master、pushed、然后 amended 和 force pushed - 两次。 那么你总共会有四次提交(如 reflog 所见),但在你的实际日志中只有两次:

$ git log -g master | grep ^commit # Show reflog of master
commit 3f52cea357aaa6ba9db86c1526b025a7ee2906c1 (refs/remotes/origin/master, refs/heads/master)
commit 5afa0cfe6e16d4d45088aeb226ed052a5ad72b87
commit 0334c6f8ba13c1465c855cf0b7cf6c79df487740
commit 8bb40a9d6da838e151c8653b8d6be2b7afeb1902

$ git log master | grep ^commit    # Show log of master
commit 3f52cea357aaa6ba9db86c1526b025a7ee2906c1 (refs/remotes/origin/master, refs/heads/master)
commit 0334c6f8ba13c1465c855cf0b7cf6c79df487740

$ git reflog                       # for reference - HEAD@{0, 2} were force pushes
3f52cea (HEAD -> refs/heads/master) HEAD@{0}: push
5afa0cf HEAD@{1}: push
0334c6f HEAD@{2}: push
8bb40a9

在 bash 中,我可以显示两者之间的差异(使用进程替换):

$ diff <(git log -g master | grep ^commit) <(git log master | grep ^commit) | grep "^<"
< commit 5afa0cfe6e16d4d45088aeb226ed052a5ad72b87
< commit 8bb40a9d6da838e151c8653b8d6be2b7afeb1902

我正在区分上面的两个输出并通过管道传输到 grep,这样我就可以看到修改后的提交。