我想pom.xml
使用正则表达式过滤文件日志的内容。
我创建它于正则表达式它使用 PCRE 工作;这是我的正则表达式:
commit \K[a-z0-9]*(?=[\s\S]*\+.*<version>1.2.0)
然后我尝试使用以下命令运行:
git log --full-history -p pom.xml | grep -P "commit \K[a-z0-9]*(?=[\s\S]*\+.*<version>1.2.0)"
和
git log --full-history -p pom.xml | perl -nle 'print \$1 if /commit \K[a-z0-9]*(?=[\s\S]*\+.*<version>1.2.0)/'
但它们都不起作用(似乎没有任何匹配)。
我肯定错过了一些东西,但我不知道是什么。
编辑:
为了澄清这一点,这是一个例子git log
:
commit a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471f
Author: Author <[email protected]>
Date: Wed Mar 30 15:04:29 2022 +0100
commit message
diff --git a/pom.xml b/pom.xml
index 93df07e..5f82fd2 100755
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.organization.project</groupId>
<artifactId>ProjectName</artifactId>
- <version>1.1.1</version>
+ <version>1.2.0</version>
<name>ProjectName</name>
<description>Description of project</description>
我想选择version
更改为某个值的提交的哈希值(1.2.0
在我编写的正则表达式中)。
显然,这是在某个存储库上完成的所有提交的日志,也可能有多个提交。
在此输入上使用上述正则表达式应输出提交哈希:
a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471f
这就是使用 regexr 实际发生的情况。
答案1
对于保存到文件中的示例提交日志gitlog
,GNUgrep
命令会提取
% < gitlog ggrep -Pzo 'commit \K[a-z0-9]*(?=[\s\S]*\+.*<version>1.2.0)'
a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471fmessage%
% < gitlog ggrep -Pzo 'commit \K[a-z0-9]*(?=[\s\S]*\+.*<version>1.2.0)' | od -c
0000000 a 1 3 5 7 f 4 e 1 c b 2 c 3 4 a
0000020 a 1 a 1 3 5 7 f 4 e 1 c b 2 c 3
0000040 4 a a 1 4 7 1 f \0 m e s s a g e
0000060 \0
0000061
考虑到误报,这可能并不理想。此外,PCRE 标志的文档-P
提到了一些关于进行实验的内容-z
。使用基于行的正则表达式,我们可以用来^commit
限制匹配开始的位置,但-z
会阻止这种情况发生,除非 GNUgrep
有一个标志来修改^
匹配的位置,就像 Perl 那样:
% < gitlog perl -0777 -nE 'say $1 if m/^commit (\S+).*<version>1.2.0/ms'
a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471f
这-0777
是 GNUgrep
-z
标志的一个实验性较低的版本(-0777
吸收整个输入),正ms
则表达式标志可以^
在任何地方匹配换行符并.
跳过换行符。但是,这可能效率极低,因为正则表达式可能必须为每次提交多次搜索整个日志,并且可能找不到所需的版本号。或者,它可以多次匹配提交,因为没有任何内容限制版本信息仅与之前的提交行匹配。
另一种方法是记住最后一次提交,并在找到版本号时使用该值。这允许逐行解析:
% < gitlog perl -nle 'if (m/^commit (\S+)/) {$commit=$1} if(m/<version>1.2.0/) {print $commit}'
a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471f
{print $commit;exit}
如果您不关心输入的其余部分(可能很多),这可能会在找到匹配版本时停止搜索。
% < gitlog perl -nle 'if (m/^commit (\S+)/) {$commit=$1} if(m/<version>1.2.0/) {print $commit;exit}'
a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471f
为了获得更快的速度,可以将其编写为awk
,我通常只有在用 Perl 编写后才能弄清楚:
% < gitlog awk '/^commit/{c=$2};/<version>1.2.0/{print c;exit}'
a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471f
理想情况下,您的测试输入(或多个输入)应该执行许多不同的可能性:目标之前和之后的多个提交记录、重复记录等,特别是如果此代码要以任何形式的无人值守方式使用,而无需人工干预。健全性检查结果:
% < gitlog
blah blah blah
commit a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471f
commit eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
- <version>1.1.1</version>
+ <version>1.2.0</version>
commit ffffffffffffffffffffffffffffffffffffffff
+ <version>1.2.0</version>
答案2
没关系,我自己找到了解决方案。
事实证明我已经完成了一半,但我遗漏了一些细节。
作为Stack Overflow 上的这个答案解释说,有必要传递一些其他参数才能grep
使其按预期工作;此外,使用单引号而不是双引号。
因此,第一个命令变为:
git log --full-history -p pom.xml | grep -Pzo 'commit \K[a-z0-9]*(?=[\s\S]*\+.*<version>1.2.0)'
答案3
使用乐(以前称为 Perl_6)
<version>1.2.0
如果在文本后面找到以下代码,则从 gitlog 提交中提取第一行:
raku -e 'put $_.split("\n")[0] if m/ \<version\>1\.2\.0 / given slurp();'
#OR
raku -e 'put lines[0] if m/ \<version\>1\.2\.0 /;'
返回:
commit a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471f
您可以按如下方式删除“commit”文本:
raku -e 'put $_.split("\n")[0].subst("commit ") if m/\<version\>1\.2\.0/ given slurp();'
返回:
a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471f
在 Raku 中,正则表达式的转义规则非常简单:只需反斜杠每个非<alnum>
字符即可将其理解为文字(即假设non-<alnum>
s 具有特殊含义 - 就像.
代表任何字符的点)。或者引用您正在搜索的文本,即m/ "<version>1.2.0" /
:
raku -e 'put .split("\n")[0].subst("commit ") if m/"<version>1.2.0"/ given slurp();'
#OR
raku -e 'put .[0].subst("commit ") if m/"<version>1.2.0"/ given lines();'
返回:
a1357f4e1cb2c34aa1a1357f4e1cb2c34aa1471f