记录一次git patch解析的问题

发布时间 2023-04-27 23:14:30作者: 淡如逝水

因为工作需要对git patch内容进行解析,解析成文件及对应修改行、删除行的数据结构。
git patch大概内容:

点击查看代码
commit message 1
commit message 2
diff --git a/file1.txt b/file1.txt
index 1234567..abcdefg 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1,3 +1,5 @@
 line1
 line2
 line3
+diff --git line4
+line5
+
diff --git a/file2.txt b/file2.txt
index 2345678..bcdefgh 100644
--- a/file2.txt
+++ b/file2.txt
@@ -1,3 +1,4 @@
 line1
 line2
-diff --git line3
+line3.1
+line3.2
 line4

原本使用split('diff')的方式分割成每个文件的修改内容,然后在对每一段内容进行匹配,比如匹配修改文件:
re.search(r'--git\s+a/(.*?)\s+b/', diff_string)
刚开始没有问题,后来遇到一种场景,如果一笔提交中包含了一个patch.diff文件的修改就会导致我分割的内容不准确(看以patch案例,会有+diff --git 或-diff --git的行),不是以一个文件的修改内容来分割。
初步考虑使用split('\ndiff'),但用diff分割后,匹配修改文件名时以上内容还是会造成干扰
最后调整为
diff = re.findall(r'\ndiff --git.*?(?=\n(?:diff --git|\Z))', git_patch, re.DOTALL)
使用正则的前视断言及开头的\ndiff来作为分隔符,使用\Z匹配字符串末尾。

在 Python 的 re 模块中,$ 符号和 \Z 符号都用于匹配字符串的结尾。但是,它们之间有一个细微的区别,这个区别取决于是否使用 re.MULTILINE(或 re.M)标志。
如果使用了 re.MULTILINE 标志,则 $ 符号可以匹配每一行的结尾。也就是说,当多行模式被启用时,$ 可以匹配每一行的末尾,而不仅仅是字符串的最后一个字符。
相反,\Z 符号只匹配字符串的末尾,无论是否使用多行模式。它始终匹配字符串的结尾位置,而不考虑是否有换行符。
综上所述,如果你需要匹配多行字符串中每一行的结尾,应该使用 $ 符号并启用 re.MULTILINE 标志;如果你只需要匹配整个字符串的结尾,则使用 \Z 符号即可。

python官网中的说明:
image