【问题标题】:Compare old and new versions of force-pushed GitHub pull request比较新旧版本的强制推送 GitHub 拉取请求
【发布时间】:2017-02-02 17:30:28
【问题描述】:

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

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

我想我可以在首次提出 PR 时执行 git pullgit checkout $BRANCH_NAME,然后在 PR 更新后执行 git fetchgit diff $BRANCH_NAME..origin/$BRANCH_NAME - 但这也会显示已引入到base 分支(通常是 master)并通过 rebase 引入 PR。是否有可能消除这些噪音,只显示 PR 本身的变化?

【问题讨论】:

  • 实际上,如果我们都使用 TopGit,并且也推送我们的 TopGit 分支,我们就不会有这个问题,因为我们可以看看彼此的 TopGit 分支,看看发生了什么变化——而且也可以解决变基问题,因为 TopGit 会合并而不是变基,然后在发布 PR 时导出(这就像一个不会改变历史的压缩变基)。不过,这需要我的同事改变他们的 git 工作流程。
  • 虽然,再想一想,这并不总能解决问题,因为任何有争议的合并决议都可能隐藏在合并提交中。
  • 旁注:GitHub 现在支持“rebase on merge”合并策略,因此您实际上不再需要 rebase 拉取请求
  • 重新基于合并可能会破坏构建。当然,非平凡的合并也可以。我建议在合并之前重新定位和重建。
  • 公平起见。我不确定你问的是可能的。变基会重写 Git 历史记录,因此除非您在本地拥有分支,否则您将无法将其与变基版本进行比较

标签: git github pull-request git-diff


【解决方案1】:

旧版公关

您可以直接在 GitHub 上执行此操作:请参阅“Find committer of a force push on GitHub

点击“force-pushed”链接会在两次提交之间显示一个two dot comparison


原答案:2016

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

hat 还将显示已引入基础分支(通常是 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)。
但是如果没有共同的祖先路径,你不能轻易比较两个分支。

【讨论】:

  • GitLab 上是否有此 GitHub 功能 Clicking the “force-pushed” link will show a two dot comparison between the two commits 的等价物?编辑:我刚刚发现在强制推送拉取请求时有Compare with previous version 链接。
【解决方案2】:

结帐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>

这个想法是 commit-tree 将伪造 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。

【讨论】:

  • 这似乎很聪明(并且比我的回答更有潜力)+1
  • 您的回答解释了 reflog 的概念,无论如何这都是有用的:)我没有写它,因为您的解释已经足够好了;)
  • checkout -b 命令上,可以在此之前澄清您的 HEAD 位置。 tempmaster 指向同一个地方,还是在哪里?
  • 没关系。 git commit-tree 命令是一个低级命令,它创建一个具有自己历史记录的新提交。特别是与 2 个父母的提交,即您指定的父母(upstreamoldcommit
  • 其实你说对了,换分支也没用。我正在编辑答案
【解决方案3】:

从 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 Web 界面在“...强制将 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

【讨论】:

    【解决方案4】:

    我相信这根本不可能获得此票所建议的旧版本的拉取请求:https://github.com/isaacs/github/issues/999 在该存储库中发布的用户是advised to contact GitHub support,因此 GitHub 支持可能已经回复说不是在创建该票证时可能。

    这是 Gerrit 拥有的 GitHub 拉取请求中缺少的一个主要功能,但 Gerrit 用户非常错过。

    【讨论】:

      【解决方案5】:

      以下方法有效,但对于大型 repo 效率低下,因为下载了整个 repo 工作树:

      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 支持allow git fetching revs by their SHA1,这可能会更简单、更精简,但事实并非如此 还没有。

      【讨论】:

      • GitHub 现在允许通过其 SHA 获取修订。我想他们是几个月前开始的。
      • 这忽略了问题的重点:您正在比较两个版本的最终状态,但这包括旧起点和新起点之间的差异。
      猜你喜欢
      • 2017-12-29
      • 2018-10-12
      • 2013-11-08
      • 1970-01-01
      • 1970-01-01
      • 2013-03-09
      • 2021-08-29
      • 1970-01-01
      • 2015-02-16
      相关资源
      最近更新 更多