Git:有没有办法弄清楚提交是从哪里挑选出来的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2922652/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 04:19:41  来源:igfitidea点击:

Git: Is there a way to figure out where a commit was cherry-pick'ed from?

gitversion-control

提问by EricSchaefer

If I cherry-pick from multiple branches, is there a simple way to figure out where the commit was coming from (e.g. the sha of the original commit)?

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

Example:
- at master branch
- cherry pick commit A from a dev branch
- A becomes D at the master branch

示例:
- 在 master 分支
- 从 dev 分支挑选提交 A
- A 在 master 分支变成 D

Before:

前:

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

After:

后:

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

Is it possible to figure out that D was cherry-picked from A (aside from searching for the commit message)?

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

Edit:
Although I will go with daggy-fixes (see VonCs answer) I accepted Chris Johnsens answer because it is closer to the actual question. Thanks guys.

编辑:
虽然我会使用 daggy-fixes(参见 VonCs 答案),但我接受了 Chris Johnsens 的答案,因为它更接近实际问题。谢谢你们。

回答by Chris Johnsen

By default, the information about the original, “cherry” commit is not recorded as part of the new commit.

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

Record the Source Commit in the Commit Message

在提交消息中记录源提交

If you can force the use of particular workflows/options, git cherry-pickhas the -xoption:

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

When recording the commit, append to the original commit message a note that indicates which commit this change was cherry-picked from.

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

This is obviously useless if you can not rely on the cherry pickers using the option. Also, since the recorded information is just plain text—not an actual reference as far as Git is concerned—even if you use -x, you still have to take steps to make sure that the original commit is kept alive (e.g. is is part of the DAG of a tag or a non-rewinding branch).

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

git cherryand git patch-id

gitcherrygit patch-id

If you can restrict your search to two particular branches of the history DAG, then git cherrycan find both “unpicked” and “picked” cherries.

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

Note: This command (and the related git patch-id) can only identify conflict-free cherries that were individually picked without extra changes. If there was a conflict while picking the cherry (e.g. you had to slightly modify it to get it to apply), or you used -n/--no-committo stage extra changes (e.g. multiple cherries in a single commit), or the content of the commit was rewritten after the picking, then you will have to rely on commit message comparison (or the -xinformation if it was recorded).

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

git cherryis not really designed to identify the origin of picked cherries, but we can abuse it a bit to identify single cherry pairs.

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

Given the following history DAG (as in the original poster's example):

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

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

you will see something like this:

你会看到这样的事情:

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

(A, B, C, and D are full SHA-1 hashes in the real output)

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

Since we see one cherry (the -lines) in each list, they must form a cherry pair. D was a cherry picked from C (or vice versa; you can not tell by the DAG alone, though the commit dates might help).

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

If you are dealing with more than one potential cherry, you will have to “roll your own” program to do the mapping. The code should be easy in any language with associative arrays, hashes, dictionaries, or equivalent. In awk, it might look like this:

如果您要处理不止一种潜在的樱桃,则必须“推出自己的”程序来进行映射。代码在任何具有关联数组、散列、字典或等价物的语言中都应该很容易。在awk 中,它可能如下所示:

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

With an extended example that has two picked cherries:

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

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

The output might look like this:

输出可能如下所示:

match_cherries master dev
D C
E A

(A, C, D, and E are full SHA-1 hashes in the real output)

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

This tells us that C and D represent the same change and that E and A represent the same change. As before, there is no way to tell which of each pair was “the first” unless you also consider (e.g.) the commit dates of each commit.

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

Commit Message Comparison

提交消息比较

If your cherries were not picked with -x, or they are “dirty” (had conflicts, or other changes added to them (i.e. with --no-commitplus staging extra changes, or with git commit --amendor other “history rewriting” mechanism)), then you may have to fall back on less the less reliable technique of comparing commit messages.

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

This technique works best if you can find some bit of the commit message that is likely to be unique to the commit and is unlikely to have changed in the commit that resulted from the cherry pick. The bit that would work best would depend on the style of commit messages used in your project.

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

Once you have picked out an “identifying part” of the message, you can use git logto find commits (also demonstrated in Jefromi's answer).

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

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

The argument to --grepis actually a regular expression, so you might need to escape any regexp metacharacters ([]*?.\).

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

If you are not sure which branches might hold the original commit and the new commit, you can use --allas Jefromi showed.

如果您不确定哪些分支可能包含原始提交和新提交,您可以使用--allJefromi 所示。

回答by VonC

If I follow your diagram, you want to know if you can determine than D (not B) is the result of cherry-picking A.

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

In theory, as illustrated in "How to list git branches that contain a given commit?", you can search for a commit, if D is actually the same commit (SHA1) than A:

理论上,如“如何列出包含给定提交的 git 分支?”中所述,您可以搜索一个提交,如果 D 实际上是与 A 相同的提交(SHA1):

git branch --contains <commit>

But as Jefromicomments, D cannot have the same SHA1 in this case.
That leaves the search for a common commit message: see Jefromi's answer.

但正如Jefromi评论的那样,在这种情况下,D 不能具有相同的 SHA1。
这就留下了对常见提交消息的搜索:请参阅Jefromi 的回答



As Ken Bloommentions in the comments of the question, for such a local cherry-picking, a daggy-fix technique (like in monotoneor mercurial) is more appropriate, because it will leave a clear trace of the merge.

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

Daggy fixes mean using rather than losing the true origin and relationship between bugs and fixes in the ancestry graph.

Since [Git] offers the ability to make a commit on top of any revision, thereby spawning a tiny anonymous branch, a viable alternative to cherry-picking is as follows:

  • use bisectto identify the revision where a bug arose;
  • check out that revision;
  • fix the bug;
  • and commit the fix as a child of the revision that introduced the bug.

This new change can easily be merged into any branch that had the original bug, without any sketchy cherry-picking antics required.
It uses a revision-control tool's normal merge and conflict-resolution machinery, so it is far more reliable than cherry-picking (the implementation of which is almost always a series of grotesque hacks).

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

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

  • 使用bisect来确定出现错误的修订版;
  • 检查该修订;
  • 修复错误;
  • 并将修复作为引入错误的修订的子项提交。

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

https://storage.googleapis.com/google-code-attachments/rainforce/issue-4/comment-5/Hg-dag-6-daggy-fix.png Hg DaggyFox

https://storage.googleapis.com/google-code-attachments/rainforce/issue-4/comment-5/Hg-dag-6-daggy-fix.png Hg DaggyFox

(here a Mercurial diagram, but easily applied to Git)

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

Doing daggy fixes all the time isn't for everyone.
It's not always so easy to develop a fix directly against the revision where the bug was introduced.

  • Perhaps the bug wasn't discovered until some other more recent code used it in ways that exposed the bug; it would be hard to debug and find the fix without this other code around.
  • Or perhaps the importance or scope of the fix simply hadn't been realised at the time.

并不是每个人都经常进行笨拙的修复。
直接针对引入错误的修订版开发修复程序并不总是那么容易。

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


See also this article for more on daggy-fix:

有关daggy-fix 的更多信息,另请参阅这篇文章

This technique of going back in history to fix a bug, then merging the fix into modern branches, was given the name "daggy fixes" by the authors of Monotone, an influential distributed revision-control system.
The fixes are called daggy because they take advantage of a project's history being structured as a directed acyclic graph, or dag.
While this approach could be used with Subversion, its branches are heavyweight compared with the distributed tools, making the daggy-fix method less practical. This underlines the idea that a tool's strengths will inform the techniques that its users bring to bear.

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

回答by Cascabel

No information about the original commit is embedded in the newly created commit, so there's no direct way to tell. What you suggest (searching for the commit message) is probably the best way - it's certainly a lot easier than searching for a commit with the same diff:

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

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

Unless of course the commit's no longer reachable from a branch... probably then you'd want to look at the output of git fsck.

除非当然不再可以从分支访问提交......否则你可能想要查看git fsck.