【问题标题】:How to list branches that contain an equivalent commit如何列出包含等效提交的分支
【发布时间】:2013-04-24 15:35:35
【问题描述】:

在之前的问题中,有人提供了查找包含 EXACT 提交的分支的答案:

How to list branches that contain a given commit

接受的答案强调这仅适用于 EXACT 提交 id,不适用于相同的提交。进一步指出,Git Cherry 可以用来解决这个问题。

Git cherry 似乎是为反向而准备的;查找未推送到上游的提交。如果我不知道哪个分支创建了它以及上游是什么,这将毫无用处。所以我看不出它将如何帮助解决这个问题。

有人可以解释/提供一个示例,说明如何使用 git cherry 查找包含特定提交的“等效”的所有分支吗?

【问题讨论】:

  • 我建议编写一个使用git rev-listgit patch-id 的脚本来确定这一点。您可能还需要解析 git cherry-pick 在提交消息中留下的注释,因为补丁 ID(也是 git cherry 的基础)并不完美,如果您解决了任何冲突,它将中断。
  • 无意 +1 评论:我不知道你说了什么或者它对我有什么帮助。我用它们来生成什么列表?让我们假设有人解决了冲突,并且我的队友不够聪明,无法使用cherrypick -x,因为我不得不向他们指出。
  • 好吧,那你就完蛋了。如果你想确切地知道哪些分支包含哪些提交,只使用合并而不是挑选。您可以假设具有相同提交消息的提交可能是相同的,并对它们的差异做一些时髦的启发式方法来验证该假设。但是这个过程很容易出错,你必须自己编写代码。
  • 听起来其他页面的海报是错误的。这主要是出于好奇,并验证我决定在请求合并之前强制队友变基,以便我们所有的合并都是 FF,因此没有樱桃选择和冲突等。我感谢你的帖子!现在我的意思是两个+1 :-)
  • 这是 OT,但我认为仍然很重要:这似乎是对 rebase 的常见误解。如果您希望分支易于合并,只需让所有者将 master 合并到其中即可。当你变基时,你不仅会以线性历史告终,还会以谎言告终。中间提交甚至可能不再起作用,因为在变基期间没有人测试。这可能会让你的历史变得毫无用处。另一方面,真正的合并会告诉您哪些部分是独立开发的,这也很有用。 Git 不是 svn,git 根本不需要线性。

标签: git version-control


【解决方案1】:

在您回答哪些分支包含等效提交的问题之前,您必须确定“哪些提交是等效的”。一旦你有了它,你只需在每个提交上使用git branch --contains

很遗憾,没有 100% 可靠的方法来确定等效提交。

最可靠的方法是检查提交引入的变更集的补丁ID。这就是 git cherrygit log --cherrygit log --cherry-mark 所依赖的。在内部,他们都打电话给git patch-id。补丁 id 只是标准化更改差异的 SHA1。任何引入相同更改的提交都将具有相同的补丁 ID。此外,任何引入大部分相同更改(仅在空格或它们在文件中应用的行号不同)的提交都将具有相同的补丁 ID。如果两个提交具有相同的补丁 ID,则几乎可以保证它们是等效的——您几乎不会通过补丁 ID 得到误报。但是,假阴性经常发生。任何时候你做git cherry-pick 并且必须手动解决合并冲突,你可能会在变更集中引入差异。即使是 1 个字符的更改也会导致生成不同的补丁 id。

按照 Chronial 的建议,检查补丁 ID 需要编写脚本。首先使用类似

的方式计算原始提交的补丁 id

(注意 - 脚本未经测试,但应该相当接近工作)

origCommitPatchId=$(git diff ORIG_COMMIT^! | git patch-id | awk '{print $1}')

现在您将不得不搜索历史中的所有其他提交并计算它们的补丁 ID,并查看它们是否相同。

for rev in $(git rev-list --all)
do
   testPatchId=$(git diff ${rev}^1..${rev} | git patch-id | awk '{print $1}')
   if [ "${origCommitPatchId}" = "${testPatchId}" ]; then
      echo "${rev}"
   fi
done

现在您有了 SHA 列表,您可以将它们传递给 git branch -a --contains

如果由于合并冲突,上述方法对您不起作用怎么办?

嗯,您还可以尝试其他一些方法。通常,当您选择提交时,会保留提交中的原始作者姓名、电子邮件和日期字段。所以你会得到一个新的提交,但是作者信息是一样的。

因此您可以使用

从原始提交中获取此信息
git log -1 --pretty="%an %ae %ad" ORIG_COMMIT

然后像以前一样,您必须检查历史中的每个提交,打印相同的信息并进行比较。那可能会给你一些额外的匹配。

您还可以使用git log --grep=ORIG_COMMIT 来查找在提交消息中引用 ORIG_COMMIT 的任何提交。

如果这些都不起作用,您可以尝试查找随镐引入的特定行,或者可以git log --grep 查找提交消息中可能唯一的其他内容。

如果这一切听起来很复杂,那么,确实如此。这就是为什么我告诉人们尽可能避免使用cherry-pick。 git branch --contains 非常有价值且易于使用且 100% 可靠。其他解决方案都没有。

【讨论】:

  • 谢谢,这正是我所期望和正在寻找的东西
【解决方案2】:

以下似乎有效(但尚未经过太多测试)。它运行git cherryfor each local git branch,如果git cherry 没有将提交列为从分支中丢失,则打印分支名称。

# USAGE: git-cherry-contains <commit> [refs]
# Prints each local branch containing an equivalent commit.
git-cherry-contains() {
    local sha; sha=$(git rev-parse --verify "$1") || return 1
    local refs; refs=${2:-refs/heads/}
    local branch
    while IFS= read -r branch; do
        if ! git cherry "$branch" "$sha" "$sha^" | grep -qE "^\+ $sha"; then
            echo "$branch"
        fi
    done < <(git for-each-ref --format='%(refname:short)' $refs)
}

请参阅Andrew C's post,详细了解git cherry 的实际工作原理(使用git patch-id)。

【讨论】:

  • 谢谢!该脚本运行良好,对其进行了更新以允许指定要搜索的 ref 文件夹(因此我们可以搜索上游 refs):git-cherry-contains 0adbcd refs/remotes/origin/.
  • 正是我想要的。这是 .gitconfig 文件的等效别名:cherry-contains = "!f(){ local sha=$(git rev-parse --verify \"$1\") || return 1; local refs=${2:-refs/heads/}; local branch; git for-each-ref --format='%(refname:short)' $refs | while IFS= read -r branch; do if ! git cherry \"$branch\" \"$sha\" \"$sha^\" | grep -qE \"^\\+ $sha\"; then echo \"$branch\"; fi; done; };f"
【解决方案3】:

命令

使用以下 Bash 命令(将 &lt;COMMIT HASH&gt; 替换为您要搜索的提交哈希):

PATCH_ID=$(git show <COMMIT HASH> | git patch-id | cut -d' ' -f1) \
&& ALL_MATCHING_COMMIT_HASHES=$(git log --all -p | git patch-id | grep $PATCH_ID | cut -d' ' -f2) \
&& for HASH in $ALL_MATCHING_COMMIT_HASHES; do echo "$(git branch -a --contains $HASH) (commit $HASH)"; done 

示例输出

user@host test_cherry_picking $ PATCH_ID=$(git show 59faabb91cfc8e449737f93be8c7df3825491674 | git patch-id | cut -d' ' -f1) \
&& ALL_MATCHING_COMMIT_HASHES=$(git log --all -p | git patch-id | grep $PATCH_ID | cut -d' ' -f2) \
&& for HASH in $ALL_MATCHING_COMMIT_HASHES; do echo "$(git branch -a --contains $HASH) (commit $HASH)"; done

* hotfix (commit 59faabb91cfc8e449737f93be8c7df3825491674)
master (commit bb5fa0d16931fa1d5fa9f5e9ee5c27634fad7da8)

user@host test_cherry_picking $

说明

计算给定 GIT REVISION PARAMETER 的 PATCH ID(例如,提交的哈希)。然后使用计算的 PATCH ID 查找所有提交。最后,包含这些提交的所有分支名称都会打印到控制台。

这当然只适用于所有(樱桃采摘的)提交的 PATCH ID 相同的情况。任何时候您挑选并必须手动解决合并冲突时,您可能会在变更集中引入差异。这将导致不同的 PATCH ID。

【讨论】:

  • 我发现 git show &lt;COMMIT_HASH&gt; 不会为相同的精选更改产生相同的补丁 ID 输出。使用来自stackoverflow.com/a/45373500/150447 的建议,我尝试使用git diff-tree -p 作为替代,我得到了确定性相同的结果。
【解决方案4】:
$ for i in `git rev-list --all --grep="something unique in the commit message"`; do git branch --all --contains $i; done | sort | uniq

【讨论】:

    猜你喜欢
    • 2010-11-28
    • 2016-05-02
    • 2021-11-06
    • 1970-01-01
    • 1970-01-01
    • 2015-03-30
    • 2015-08-12
    • 2012-05-02
    相关资源
    最近更新 更多