我正在尝试找出如何在两个标签之间打印出 git 日志,排除在一个特定 merge 提交中 merge 的所有提交。
例如。如果我的提交日志看起来像这样:
TAGA TAGB
| C---F---H---K Q---T---V |
v / \ / \ v
A---B---D---G---J---L---M---O---R---U---W---X
\ / \ /
E---I N---P---S
而且我想要排除在 merge 提交 L 时引入主干的所有提交。 (所以在这个例子中,我想排除 C,F,H,K)。
目前我的 git 行看起来像这样:
git log --oneline --no-merges TAGB ^TAGA
这将列出两个标签之间的所有提交(注意,我排除了 merge 提交本身,这就是 J、L、U 和 W 不存在的原因。):
A,B,C,D,E,F,G,H,I,K,M,N,O,P,Q,R,S,T,V,X
有谁知道排除 C,F,H & K 的方法吗?即:
A,B,D,E,G,I,M,N,O,P,Q,R,S,T,V,X
如有任何帮助,我们将不胜感激。
最佳答案
没有便宜的、一体化的 Git 表达式方法可以做到这一点。图行走代码要么遵循 --first-parent
仅(如 Vimsha's answer )或跟随 merge 的所有父级。因此,排他性 ^K
(或 ^L^2
或以其他方式命名提交 K
)具有修剪 所有 可从 K
访问的提交的副作用,而不仅仅是 C--F--H--K
顺序。
我们仍然可以获得我们想要的列表,只是需要更多的工作。首先,生成有趣哈希的完整列表:
tempfile1=$(mktemp)
git rev-list --no-merges TAGB ^TAGA > $tempfile1
(请注意 git log
和 git rev-list
几乎是相同的命令,并且是从相同的来源构建的;它们之间的主要区别在于 rev-list
只打印完整的哈希 ID)。接下来,生成要排除的完整哈希列表:
tempfile2=$(mktemp)
git rev-list --no-merges K ^B > $tempfile2
(你是怎么找到 B
的?好吧,我想知道,你一开始是怎么找到 K
的?——但是一旦你找到了 K
,并且知道它比如说,L^2
,B
是L^
和L^2
之间的 merge 基础,所以你可以用git merge-base
和这两个名称找到它的ID。)我们真的不需要--no-merges
在此示例中,但在更复杂的示例中,我们可能需要它和/或 --first-parent
以及。 (编辑以添加注释:当然我们永远不需要 --no-merges
:这是一个排除列表, merge 已被排除。)
现在我们需要“在临时文件 1 中找到的所有哈希值,不包括在临时文件 2 中的任何哈希值”,执行此操作的 Unix 工具集命令是 comm
. (Linux 变体要求对输入进行排序并实际检查,因此您需要 --nocheck-order
来打败它。comm
使用的底层算法只需要文件的行是按照相同的顺序,情况就是这样。或者,您可以对两个文件进行排序:例如 sort -o $tempfile1 $tempfile1
。但这样做既昂贵又不必要。)
The
comm
utility reads file1 and file2, which should be sorted lexically, and produces three text columns as output: lines only in file1; lines only in file2; and lines in both files.
我们需要“仅在 file1 中的行”,即丢弃出现在 file2 中的行。那将是“文本第 1 列”,这似乎需要更多的后处理,但幸运的是:
-1
Suppress printing of column 1.
-2
Suppress printing of column 2.
-3
Suppress printing of column 3.
所以我们取消第 2 列和第 3 列:
comm -23 $tempfile1 $tempfile2
这会在标准输出上生成我们想要的 SHA-1 的完整列表 git log
显示。
最后一步是将此输出 Hook 到 git log --oneline
,这很简单:
git log --oneline --stdin --no-walk
--no-walk
防止 git log
(或 git rev-list
)完全不进行提交图遍历,这正是我们想要的,因为我们为它提供了要访问的每个提交的完整列表。 --stdin
提供我们从 comm
获得的修订.
我们现在需要的只是一个小的 shell 打包来清理临时文件:
#! /bin/sh -e
# note the -e option -- this avoids the need for a lot of || exit
# clauses (to handle any early failures)
tempfile1=$(mktemp)
trap "rm -f $tempfile1" 0 1 2 3 15
tempfile2=$(mktemp)
trap "rm -f $tempfile1 $tempfile2" 0 1 2 3 15
git rev-list --no-merges TAGB ^TAGA > $tempfile1
git rev-list --no-merges K ^B > $tempfile2
comm -23 $tempfile1 $tempfile2 | git log --oneline --stdin --no-walk
(当然,这都是未经测试的,它省略了您想要查找或获取参数的地方,这两个标签和 merge 提交 L
您希望单边排除,以及边号为 1 或 2。根据需要想象一下。)
关于Git 日志 : excluding commits from a specific merge,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40384934/