比较新旧版本的强制推送 GitHub 拉取请求

Compare old and new versions of force-pushed GitHub pull request

我的同事经常会对开放的拉取请求进行一些更改,将他们的本地分支重新设置为基础分支 - 通常也会将他们的更改压缩到以前的提交中 - 然后强制推送。

如何查看旧版 PR 和新版 PR 之间的变化?

我想我可以在第一次提出 PR 时做 git pullgit checkout $BRANCH_NAME,然后 git fetch 然后在 PR 更新后 git diff $BRANCH_NAME..origin/$BRANCH_NAME - 但是还将显示已引入基本分支(通常是 master)并通过 rebase 引入 PR 的更改。是否可以消除噪音并仅显示 PR 本身发生的变化?

the old version of the PR

您可以直接在 GitHub 上这样做:请参阅“Find committer of a force push on GitHub

Clicking the “force-pushed” link will show a two dot comparison between the two commits.


原答案:2016

这将仅在远程仓库的 reflog 中可用,其中将包括强制推送的分支的前一个 HEAD。
由于远程回购是 GitHub 一个,您仍然可以通过查看推送事件来推断旧提交:请参阅“”。

hat will also show changes that have been introduced into the base branch (typically master)

更准确地说,您将始终与共同祖先存在差异(这将包括来自基础分支的提交,例如 master

What are the differences between double-dot ".." and triple-dot "..." in Git diff commit ranges?

所以在你的情况下,你的强制推送分支在远程仓库上看起来像这样:

      x--x--x        (old branch in reflog)
     /
 m--M0--M--M   (master)
            \
             X--X--X (new branch forced push)

差异 old_HEAD..newHEAD 将包括来自基本分支的少数 M 提交,因为它们是共同祖先 (M0) 路径的一部分。

因此您可以比较强制推送的分支(前提是您正在监视 pushEvents 并且知道该分支的前一个 HEAD)。
但是你不能轻易比较两个没有共同祖先路径的分支。

签出 this 另一个问题的答案,该问题想要做的事情与您想要实现的目标非常相似。

它描述了你的情况:

newcommit -> the new pull request commit
oldcommit -> the old pull request commit
upstream -> the base branch commit the new pull request is based on

现在这样做:

git commit-tree newcommit^{tree} -p oldcommit -p upstream -m "message"
git show <commit id returned by previous command>

这个想法是提交树将伪造 oldcommitupstream 之间的合并,生成 newcommit 树,从而包含 newcommit 的代码。 它根本不会修改您当前的分支,它会创建一个新的无头提交并为您提供它的 ID。 这意味着 git show 会将所有修改列为冲突解决方案,这是新 PR 与旧 PR 之间的确切区别。

为了能够做到这一点,你需要在你的 git 存储库的某个地方有以前的 PR(如果强制推送已经执行,git 历史已经被重写并且不能已恢复,除非您的电脑上有它或您有权访问服务器 reflog)。检查 VonC 答案以获取有关此的详细信息。

假设:

  • 基础分支:master
  • 你在本地有旧的 PR 分支:$BRANCH_NAME
  • 远程分支中的新 PR:origin/$BRANCH_NAME

你可以这样做:

# fetch locally upstream changes (origin/$BRANCH_NAME)
git fetch
# produce the fake merge commit
git commit-tree origin/$BRANCH_NAME^{tree} \
         -p $BRANCH_NAME \
         -p `git merge-base master origin/$BRANCH_NAME` \
         -m "message"
# see "fake" conflict resolution = difference between the two PR
git show <commit id returned by previous command>

git merge-base 用于查找两个分支之间的共同祖先,在本例中是在基础分支中查找新 PR 所基于的提交,如果您愿意,可以编写提交直接ID。

我相信这根本不可能获得旧版本的拉取请求,如这张票所建议的那样:https://github.com/isaacs/github/issues/999 Users who post in that repository are advised to contact GitHub support,所以很可能 GitHub 支持已经回复说这是不可能的该票已创建。

这是 Gerrit 的 GitHub 拉取请求中缺少的一个主要功能,而 Gerrit 用户非常想念这些功能。

下面的方法有效,但对于大型回购来说效率低下,因为整个回购工作树都被下载了:

repo=rossant/awesome-math
a=61e250
b=2b53ad
tmp=$(mktemp -d -p /tmp)
mkdir -p $tmp/{a,b}
curl -SL https://github.com/$repo/archive/$a.tar.gz | tar xz --strip-components=1 -C $tmp/a
curl -SL https://github.com/$repo/archive/$b.tar.gz | tar xz --strip-components=1 -C $tmp/b
git diff --no-index $tmp/{a,b}
rm -r $tmp

如果 Github 允许 git fetch 通过 他们的 SHA1,但事实并非如此 还没。

从 Git 2.19 开始,git range-diff 提供了一种比较重新设置历史记录的好方法。当新的历史记录具有相同的提交结构且只有很小的更改时,它会很好地工作(但如果新版本的拉取请求既有新的基础又有不同的提交细分,它可能对你没有多大帮助)。它比较每个提交中的补丁并显示差异,并以相当好的突出显示来区分上下文更改和补丁更改。

假设拉取请求已从 c1 变基到 c2,目标分支为 main 并且您有一个指向 myrepo 的本地工作目录使用拉取请求到 GitHub 存储库。您可以下载提交并比较历史记录,如下所示:

git fetch myrepo c1 c2
git range-diff main c1 c2

请注意,您需要拉取请求的旧提示的本地副本。 I don't know how to obtain the commit ID automatically;这就是 GitHub 网络界面在“...强制将 mywork 分支从 c1 推到 c2”中显示的内容。在我撰写本文时,我认为 GitHub 始终允许通过 ID 获取提交,即使它们仅存在于旧版本的拉取请求中(过去并非如此)。如果您不希望它被从您的工作副本中垃圾收集,请为提交指定一个分支名称。

如果在您已审阅的部分之后有新提交,将变基部分与新提交分开的简便方法是搜索您审阅的最后一次提交的提交消息。在 bash/zsh 语法中:

git range-diff main {c1,c2}'^{/This is the commit message}'

对于更复杂的情况(例如,如果某些旧提交因为合并了同一子功能的不同实现而失去了相关性),您可以指定不同的起点:git range-diff OLD_START..OLD_END NEW_START..NEW_END