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

发布于 2024-09-03 08:48:57 字数 506 浏览 3 评论 0原文

如果我从多个分支中挑选,是否有一种简单的方法可以找出提交来自哪里(例如原始提交的 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的答案,因为它更接近实际的问题。谢谢你们。

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)?

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

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)?

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

瀞厅☆埖开 2024-09-10 08:48:57

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

在提交消息中记录源提交

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

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

如果您不能依赖使用该选项的樱桃采摘者,这显然是没有用的。
另外,由于记录的信息只是纯文本——就 Git 而言并不是实际的引用——即使您使用 -x,您仍然需要采取措施来确保原始提交是保持活动状态(例如,它是标签或非倒带分支的 DAG 的一部分)。

gitcherrygit patch-id

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

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

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

给定以下历史 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< /em> 查找提交(也在 Jefromi 的答案中进行了演示)。

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

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

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

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-pick has the -x option:

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 cherry and git patch-id

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

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-commit to 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 -x information if it was recorded).

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

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

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)

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).

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:

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

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)

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.

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-commit plus staging extra changes, or with git commit --amend or other “history rewriting” mechanism)), then you may have to fall back on less the less reliable technique of comparing commit messages.

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 log to find commits (also demonstrated in Jefromi’s answer).

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

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

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

同尘 2024-09-10 08:48:57

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

理论上,如“如何列出包含给定提交的 git 分支?",您可以搜索提交,如果 D实际上与 A 具有相同的提交 (SHA1):

git branch --contains <commit>

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


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

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

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

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

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

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

(这里是 Mercurial 图,但是轻松应用于 Git)

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

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

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

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

If I follow your diagram, you want to know if you can determine than D (not B) is the result of cherry-picking 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 branch --contains <commit>

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


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

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 bisect to 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).

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)

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:

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.

陌生 2024-09-10 08:48:57

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

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文