【Python】快速判断两个commit 是否存在cherry-pick 关系
判断两个提交是否有 cherry-pick 关系的 Python 脚本,可以基于以下三种常见情况进行优化:
-
Commit Hash 一致:如果两个提交的
hash
完全相同,那么它们是相同的提交。 -
Commit Title 存在关联:如果两个提交的
commit message
提及了相同的原始提交,例如cherry picked from commit <hash>
,可以通过解析提交信息来确定关联。 -
Commit 变更内容一致:即使
hash
和title
不同,如果两个提交的代码变更完全一致,则可能是 cherry-pick 关系。
另外,还有可能存在其他场景,比如提交的父节点不同,但内容一致,这也是一种 cherry-pick 的表现。
基于以上情况,下面是一个优化的 Python 脚本:
Python 脚本
V1版本
import subprocess
import re
def run_git_command(args):
"""
运行 Git 命令并返回输出结果。
"""
try:
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode != 0:
print(f"Error running command {args}: {result.stderr}")
return None
return result.stdout.strip()
except Exception as e:
print(f"An error occurred: {e}")
return None
def get_commit_message(commit_hash):
"""
获取提交信息(commit message)。
"""
return run_git_command(["git", "log", "-1", "--pretty=%B", commit_hash])
def get_diff(commit_hash):
"""
获取提交的代码差异(diff)。
"""
return run_git_command(["git", "diff-tree", "--no-commit-id", "--patch", "-r", commit_hash])
def check_cherry_pick_message(commit1_message, commit2_message):
"""
检查提交信息中是否提到 cherry-pick 的关联。
"""
cherry_pick_pattern = re.compile(r'cherry\s+picked\s+from\s+commit\s+([a-f0-9]{40})')
# 检查第一个提交信息是否提到 cherry-pick 的原始提交
match1 = cherry_pick_pattern.search(commit1_message)
match2 = cherry_pick_pattern.search(commit2_message)
if match1 and match2 and match1.group(1) == match2.group(1):
return True
# 或者检查一个提交是否提到另一个提交
if match1 and match1.group(1) in commit2_message:
return True
if match2 and match2.group(1) in commit1_message:
return True
return False
def compare_commit_diffs(commit1, commit2):
"""
比较两个提交的代码变更是否一致。
"""
diff1 = get_diff(commit1)
diff2 = get_diff(commit2)
if diff1 is None or diff2 is None:
return False
return diff1 == diff2
def compare_commits(commit1, commit2):
"""
综合比较两个提交是否有 cherry-pick 关系。
"""
# 1. 检查 commit hash 是否相同
if commit1 == commit2:
print(f"Commits {commit1} and {commit2} are identical (same hash).")
return True
# 2. 检查 commit message 是否提到 cherry-pick 关系
commit1_message = get_commit_message(commit1)
commit2_message = get_commit_message(commit2)
if commit1_message is None or commit2_message is None:
print("Failed to get commit messages.")
return False
if check_cherry_pick_message(commit1_message, commit2_message):
print(f"Commits {commit1} and {commit2} have a cherry-pick relation based on commit message.")
return True
# 3. 检查代码变更是否完全一致
if compare_commit_diffs(commit1, commit2):
print(f"Commits {commit1} and {commit2} have identical code changes (cherry-pick relation likely).")
return True
print(f"Commits {commit1} and {commit2} do not have a clear cherry-pick relation.")
return False
if __name__ == "__main__":
# 输入需要比较的两个 commit hash
commit_hash_1 = input("Enter the first commit hash: ")
commit_hash_2 = input("Enter the second commit hash: ")
if compare_commits(commit_hash_1, commit_hash_2):
print(f"Commits {commit_hash_1} and {commit_hash_2} are related (cherry-pick detected).")
else:
print(f"Commits {commit_hash_1} and {commit_hash_2} are not related (no cherry-pick detected).")
如果存在编码错误
可以使用下方脚本尝试
V2版本
import subprocess
import re
def run_git_command(args):
"""
运行 Git 命令并返回输出结果。
"""
try:
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8')
if result.returncode != 0:
print(f"Error running command {args}: {result.stderr}")
return None
return result.stdout.strip()
except Exception as e:
print(f"An error occurred: {e}")
return None
def get_commit_message(commit_hash):
"""
获取提交信息(commit message)。
"""
return run_git_command(["git", "log", "-1", "--pretty=%B", commit_hash])
def get_diff(commit_hash):
"""
获取提交的代码差异(diff)。
"""
try:
return run_git_command(["git", "diff-tree", "--no-commit-id", "--patch", "-r", commit_hash])
except Exception as e:
print(f"An error occurred while getting diff for {commit_hash}: {e}")
return None
def check_cherry_pick_message(commit1_message, commit2_message):
"""
检查提交信息中是否提到 cherry-pick 的关联。
"""
cherry_pick_pattern = re.compile(r'cherry\s+picked\s+from\s+commit\s+([a-f0-9]{40})')
match1 = cherry_pick_pattern.search(commit1_message)
match2 = cherry_pick_pattern.search(commit2_message)
if match1 and match2 and match1.group(1) == match2.group(1):
return True
if match1 and match1.group(1) in commit2_message:
return True
if match2 and match2.group(1) in commit1_message:
return True
return False
def compare_commit_diffs(commit1, commit2):
"""
比较两个提交的代码变更是否一致。
"""
diff1 = get_diff(commit1)
diff2 = get_diff(commit2)
if diff1 is None or diff2 is None:
return False
return diff1 == diff2
def compare_commits(commit1, commit2):
"""
综合比较两个提交是否有 cherry-pick 关系。
"""
if commit1 == commit2:
print(f"Commits {commit1} and {commit2} are identical (same hash).")
return True
commit1_message = get_commit_message(commit1)
commit2_message = get_commit_message(commit2)
if commit1_message is None or commit2_message is None:
print("Failed to get commit messages.")
return False
if check_cherry_pick_message(commit1_message, commit2_message):
print(f"Commits {commit1} and {commit2} have a cherry-pick relation based on commit message.")
return True
if compare_commit_diffs(commit1, commit2):
print(f"Commits {commit1} and {commit2} have identical code changes (cherry-pick relation likely).")
return True
print(f"Commits {commit1} and {commit2} do not have a clear cherry-pick relation.")
return False
def validate_commit_hash(commit_hash):
"""
验证提交 hash 是否是有效的 SHA-1 hash。
"""
if re.fullmatch(r'[a-fA-F0-9]{40}', commit_hash):
return True
else:
print(f"Invalid commit hash: {commit_hash}")
return False
if __name__ == "__main__":
commit_hash_1 = input("Enter the first commit hash: ")
commit_hash_2 = input("Enter the second commit hash: ")
if validate_commit_hash(commit_hash_1) and validate_commit_hash(commit_hash_2):
if compare_commits(commit_hash_1, commit_hash_2):
print(f"Commits {commit_hash_1} and {commit_hash_2} are related (cherry-pick detected).")
else:
print(f"Commits {commit_hash_1} and {commit_hash_2} are not related (no cherry-pick detected).")
else:
print("Please enter valid commit hashes.")
脚本的工作原理
-
Commit Hash 一致:脚本首先检查两个提交的
hash
是否完全相同。如果相同,它们肯定是相同的提交。 -
Commit Title 存在关联:通过正则表达式匹配
commit message
中的cherry picked from commit <hash>
字符串,检查一个提交是否从另一个提交进行了 cherry-pick。如果commit message
中存在这样的引用,则说明它们有 cherry-pick 关系。
-
Commit 变更内容一致:如果提交的
hash
和message
没有明显的关联,脚本通过获取两个提交的代码变更(git diff-tree
)并对比差异内容是否完全相同。如果两个提交的代码变更完全一致,它们很有可能是 cherry-pick 关系。
使用说明
-
将脚本保存为
compare_commits.py
。 -
在 Git 仓库的根目录执行以下命令:
python3 compare_commits.py
-
输入两个需要比较的提交哈希(
commit hash
)。
处理的场景
-
两个提交的
commit hash
完全相同(直接相同的提交)。 -
commit message
提到cherry-pick
的引用关系。 -
commit hash
和commit message
不同,但代码变更内容完全相同。
这个脚本可以帮助识别 cherry-pick 关系的多种常见情况,并可以扩展以处理更多特殊情况。你可以尝试运行这个脚本,看看能否正确检测出 cherry-pick 关系。