【问题标题】:Git: Is there a way to figure out where a commit was cherry-pick'ed from?Git:有没有办法找出一个提交是从哪里挑选出来的?
【发布时间】:2021-05-25 01:32:48
【问题描述】:

如果我从多个分支中挑选,是否有一种简单的方法可以确定提交的来源(例如原始提交的 sha)?

示例:
- 在主分支
- 从开发分支中挑选提交 A
- A 在主分支变成 D

之前:

* B (master) Feature Y
| * C (dev) Feature Z
| * A Feature X
|/
* 3
* 2
* 1  

之后:

* D (master) Feature X
* B Feature Y
| * C (dev) Feature Z
| * A Feature X
|/
* 3
* 2
* 1  

是否有可能找出 D 是从 A 中挑选出来的(除了搜索提交消息)?

编辑:
尽管我会使用 daggy-fixes(请参阅 VonCs 答案),但我接受了 Chris Johnsens 的答案,因为它更接近实际问题。谢谢大家。

【问题讨论】:

  • 这就是为什么许多 DVCS 工作流程(尤其是 Mercurial 经常听到这一点)倾向于使用 daggy-fixes 而不是精挑细选的修复。
  • 我已经更新了我的答案以包含“daggy-fix”技术。

标签: git version-control


【解决方案1】:

如果我按照你的图表,你想知道你是否可以确定 D(不是 B)是挑选 A 的结果。

理论上,如“How to list git branches that contain a given commit?”所示,如果 D 实际上与 A 是相同的提交(SHA1),则可以搜索提交:

git branch --contains <commit>

但作为Jefromi cmets,D 在这种情况下不能具有相同的 SHA1。
剩下的就是寻找一个共同的提交信息:见Jefromi's answer


正如Ken Bloom 在问题的 cmets 中提到的那样,对于这样的本地樱桃采摘,daggy-fix 技术(如 monotonemercurial)更合适,因为它会留下清晰的合并痕迹。

Daggy 修复意味着使用而不是丢失祖先图中错误和修复之间的真实来源和关系。

由于 [Git] 提供了在任何修订之上进行提交的能力,从而产生了一个微小的匿名分支,一个可行的替代樱桃采摘的方法如下:

  • 使用 bisect 来识别出现错误的版本;
  • 查看该版本;
  • 修复错误;
  • 并将修复作为引入该错误的修订的子版本提交。

这个新的变化可以很容易地合并到任何有原始错误的分支中,而不需要任何粗略的挑选樱桃的滑稽动作。
它使用修订控制工具的正常合并和冲突解决机制,因此它比樱桃采摘(其实现几乎总是一系列怪诞的黑客攻击)可靠得多。

(这里是 Mercurial 图,但很容易应用于 Git)

并不适合所有人。
直接针对引入错误的修订版开发修复程序并不总是那么容易。

  • 也许直到其他一些更新的代码以暴露该错误的方式使用它时才发现该错误;如果没有其他代码,将很难调试和找到修复程序。
  • 或许当时根本没有意识到修复的重要性或范围。

另见article for more on daggy-fix

Monotone 的作者将这种追溯历史修复错误,然后将修复合并到现代分支中的技术命名为“daggy fix”,这是一个有影响力的分布式修订版- 控制系统。
这些修复被称为 daggy,因为它们利用了项目历史被构造为 directed acyclic graph, or dag
虽然这种方法可以与 Subversion 一起使用,但与分布式工具相比,它的分支是重量级的,这使得 daggy-fix 方法不太实用。这强调了这样一种理念,即工具的优势将影响其用户所采用的技术。

【讨论】:

  • 我以前从未听说过“daggy fix”这个词。如果您想使用 DAG 来跟踪错误修复是否已合并到多个分支中,它肯定封装了一个很好的实践(我知道 Git 的维护者喜欢在修复旧版本 Git 中发现的错误时使用该技术)。对于那些对 Darcs 感兴趣的人:在我看来,“daggy”的概念可以用来简洁地描述 Darcs 与基于 DAG 的系统的不同之处:Darcs 自动使补丁尽可能“daggy”,而无需手动工作平分和管理一个小的临时分支机构。
【解决方案2】:

默认情况下,有关原始“樱桃”提交的信息不会记录为新提交的一部分。

在提交消息中记录源提交

如果您可以强制使用特定的工作流程/选项, git cherry-pick-x 选项:

在记录提交时,在原始提交消息中附加一条注释,指出此更改是从哪个提交中挑选出来的。

如果您不能依靠使用该选项的樱桃采摘者,这显然是无用的。 此外,由于记录的信息只是纯文本——就 Git 而言不是实际的参考——即使你使用-x,你仍然必须采取措施确保原始提交保持活动状态(例如标记或非回绕分支的 DAG 的一部分)。

git cherrygit patch-id

如果您可以将搜索限制在历史 DAG 的两个特定分支,那么git cherry 可以同时找到“未采摘”和“采摘”樱桃。

注意:此命令(以及相关的git patch-id)只能识别单独采摘的无冲突樱桃,无需额外更改。如果在挑选樱桃时发生冲突(例如,您必须稍微修改它以使其应用),或者您使用 -n/--no-commit 进行额外的更改(例如,单个提交中的多个樱桃),或者提交的内容在挑选后被重写,那么您将不得不依赖提交消息比较(或 -x 信息,如果它被记录)。

git cherry 并不是真正旨在识别采摘樱桃的来源,但我们可以稍微滥用它来识别单个樱桃对。

鉴于以下历史 DAG(如原始海报示例中所示):

1---2---3---B---D  master
         \
          A---C    dev
# D is a cherry-picked version of C

你会看到这样的:

% git cherry master dev
+ A
- C
% git cherry dev master
+ B
- D

(A、B、C 和 D 是真实输出中的完整 SHA-1 哈希)

由于我们在每个列表中看到一个樱桃(- 行),它们必须形成一对樱桃。 D 是从 C 中挑选出来的(反之亦然;你不能仅通过 DAG 来判断,尽管提交日期可能会有所帮助)。

如果您要处理多个潜在的樱桃,则必须“滚动自己的”程序来进行映射。任何语言中的代码都应该很容易使用关联数组、散列、字典或等价物。在 awk 中,它可能看起来像这样:

match_cherries() {
    a="$(git rev-parse --verify "$1")" &&
    b="$(git rev-parse --verify "$2")" &&
    git rev-list "$a...$b" | xargs git show | git patch-id |
    awk '
        { p[$1] = p[$1] " " $2 }
    END { 
            for (i in p) {
                l=length(p[i])
                if (l>41) print substr(p[i],2,l-1)
            }
        }'
}
match_cherries master dev

有两个采摘樱桃的扩展示例:

1---2---3---B---D---E  master
         \
          A---C        dev
# D is a cherry-picked version of C
# E is a cherry-picked version of A

输出可能如下所示:

match_cherries master dev
D C
E A

(A、C、D 和 E 是真实输出中的完整 SHA-1 哈希)

这告诉我们 C 和 D 代表相同的变化,而 E 和 A 代表相同的变化。和以前一样,除非您还考虑(例如)每个提交的提交日期,否则无法判断每一对中的哪一个是“第一个”。

提交消息比较

如果您的樱桃不是用-x 采摘的,或者它们是“脏的”(有冲突,或添加了其他更改(即--no-commit 加上临时额外更改,或git commit --amend 或其他“历史重写”机制)),那么您可能不得不依赖于比较不可靠的提交消息比较技术。

如果您可以找到一些可能是提交所独有的提交消息,并且不太可能在由于挑选樱桃而导致的提交中发生更改,则此技术最有效。效果最好的部分取决于项目中使用的提交消息的样式。

一旦您选择了消息的“识别部分”,您就可以使用git log 来查找提交(在 Jefromi 的回答中也有演示)。

git log --grep='unique part of the commit message' dev...master

--grep 的参数实际上是一个正则表达式,因此您可能需要转义任何正则表达式元字符 ([]*?.\)。

如果您不确定哪些分支可能保存原始提交和新提交,您可以使用 --all,如 Jefromi 所示。

【讨论】:

  • 有没有办法将-x 设置为git cherry-pick 的默认行为?
  • @dubek 创建一个别名,例如 git config --global alias.cp -x. I made an alias even before I discovered -x` 以避免将所有这些字母与破折号一起键入。
【解决方案3】:

新创建的提交中没有嵌入有关原始提交的信息,因此没有直接的方法可以判断。您的建议(搜索提交消息)可能是最好的方法 - 它肯定比搜索具有相同差异的提交要容易得多:

git log --grep="<commit subject>" --all

当然,除非提交不再可以从分支中访问...可能你会想查看git fsck 的输出。

【讨论】:

  • 非常感谢您的回答
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-12
  • 2012-04-08
  • 1970-01-01
  • 2014-10-04
相关资源
最近更新 更多