有没有办法从 GitHub 上删除分支的 PR 中获取代码?

Is there a way to grab code from a PR with a deleted branch on GitHub?

在 GitHub,我通常克隆某人的分支或将其添加为远程以下载他们的更改。但是,如果 branch/fork 被完全删除,这不是一个选项。

但是,如果您单击 PR 中的 "Files changed" 选项卡,您仍然可以在 PR 中查看更改的文件,即使 fork/branch 已删除并且 PR 已关闭。

有没有办法在我的系统上本地获取包含这些更改的分支?一个想法是通过复制粘贴网站上的更改来手动重新创建所有内容,但这种方法似乎不是最优的。

是的,比较简单:

git fetch <remote-or-url> refs/pull/1234/head:refs/heads/pr1234

例如会将他们的(GitHub 的)pull-request-#1234 提交转入您的 pr1234 分支。

背景

其工作方式很简单:

  1. Git 中的所有名称1refsreferences。也就是说,分支名称 master 实际上只是 refs/heads/master;标签名称 v1.2 实际上只是 refs/tags/v1.2;您的远程跟踪 origin/master 实际上只是 refs/remotes/origin/master分支名称 的名称以 refs/heads/ 开头。 Git 通常会去掉任何名称的前导部分,假设您知道 master 是一个分支,v1.2 是一个标签,而 origin/master 是一个远程跟踪名称。

  2. 当您 运行 git fetch 时,您的 Git 通过名称获得 他们的 提交(和其他对象)他们的 Git 显示给您的 Git 的 -and-hash-ID 对。然后它复制所需的任何提交(和其他对象),以便您的 Git 可以更新您的远程跟踪名称 and/or 将条目写入您的 Git 的 .git/FETCH_HEAD 文件等等。最后,它做了几件事:

    • 按照指示更新您自己的参考文献;和
    • 写入.git/FETCH_HEAD.


    FETCH_HEAD文件留给git pull代码,主要是;现在 git pull 已用 C 重写,这有点像历史文物了。

关于 refs 的好处是,被分成 namespaces,您或任何人都可以发明自己的。 refs/heads/refs/tags/refs/remotes/ space 被采用,refs/bisect/refs/replace/ 以及其他一些,但是 Git Hub 能够使用 refs/pull/,而 Git 本身不会,来存储 他们的 名字。

在此 refs/pull/ space、GitHub 中插入 PR 或问题编号(这些编号是每个存储库的,否则是存储库的全局编号),然后是 /head 用于提示提交,/merge 用于执行无人值守的自动测试 git merge 的结果。如果自动合并 失败 ,GitHub 就不会创建 merge ref。因此,如果有编号为 1234 的 PR,则 refs/pull/1234/head 存在;如果该 PR 成功测试合并,refs/pull/1234/merge 也存在,如果不存在,则不存在。

请注意,从他们的(GitHub's)branch 名称到您的 remote-tracking 名称的转换是由你自己的 remote.origin.fetch 配置行,默认为:

+refs/heads/*:refs/remotes/origin/*

这是一个 refspec: 一对 ref,用冒号分隔,可以选择使用加号作为前缀 +(通常用于这种特殊情况) .这匹配他们所有的 branch 名称,因为左侧或 source 模式匹配分支名称。它会导致您的 Git 将其分支名称替换为您的远程跟踪名称,因为右侧或 destination 模式将 refs/heads/ 替换为 refs/remotes/origin/ (保持 * 匹配的所有内容完好无损)。

这意味着您可以添加:

+refs/pull/*/head:refs/heads/pr*

到你的 remote.origin.fetch refspecs,在你的 .git/config 中,这样 git fetch 将自动并且几乎总是,2 创建或更新您的 pr<em>number</em> 个分支机构。但是,它可能会破坏您自己创建的任何个人 pr<em>number</em> 分支,因此请注意,如果您这样做,你必须避免命名分支 pr-something(至少是数字,但要小心 p运行ing;见脚注 2)。


1这里的例外是HEADMERGE_HEADCHERRY_PICK_HEAD等。虽然 FETCH_HEAD 有时像参考一样工作,但 FETCH_HEAD 文件特别特殊,因为它可以包含多行和一些额外的注释;其他的仅包含(并且恰好)一个哈希 ID 或一个符号引用。

2"Always" 太强了,甚至我那略微狡猾的措辞 almost almost always 也可能是:任何时候你 运行 git fetch 带有一些 refspec 参数,这将覆盖此默认值。您配置的 remote.origin.fetch 设置只有在您没有覆盖它们时才会被使用。记住它是如何与 fetch.prunegit fetch -p 交互的:目标中的 * 现在意味着 Git 将自动 删除 匹配的引用不是由 fetch 操作写入的。

pr* 而不是 pr/* 的能力取决于您的 Git 年份。在一些旧版本的 Git 中,refspecs 中类似 glob 的 * 模式只能以 "whole name component" 方式使用,例如,refs/heads/*:refs/remotes/origin/* 在所有 [=161= 中总是可行的] 版本,无论多么古老,但有时 refs/heads/a*:refs/remotes/origin/a* 不是。