如何用Python实现Git checkout并检测子目录分支差异及变更信息?
嘿,我刚好做过类似的需求,整理了一个完整的Python脚本方案,能完美搞定你要的Git分支检出对比、子目录差异扫描,以及文件元信息提取的全部操作。下面直接上干货:
Python实现Git分支对比与子目录扫描方案
核心思路
- 创建临时目录分别克隆仓库并检出两个目标分支,避免干扰本地工作区
- 用
git diff命令高效对比两个分支的文件差异(涵盖内容变更、文件新增/删除) - 通过
git log提取每个差异文件的最后修改者、时间戳及邮箱信息 - 按文件状态分类整理输出结构化结果
完整脚本
import os import subprocess import tempfile from typing import Dict, List def run_git_cmd(cmd: List[str], working_dir: str = None) -> str: """封装Git命令执行逻辑,处理输出和错误""" try: result = subprocess.run( cmd, cwd=working_dir, capture_output=True, text=True, check=True ) return result.stdout.strip() except subprocess.CalledProcessError as e: raise RuntimeError(f"Git命令执行失败: {' '.join(cmd)}\n错误详情: {e.stderr}") from e def get_file_modification_info(repo_path: str, file_path: str) -> Dict[str, str]: """获取指定文件的最后修改元信息""" log_output = run_git_cmd( ["git", "log", "-1", "--format=%an|%ae|%at", "--", file_path], cwd=repo_path ) author_name, author_email, timestamp = log_output.split("|") # 把时间戳转成可读格式 readable_time = subprocess.run( ["date", "-d", f"@{timestamp}"], capture_output=True, text=True ).stdout.strip() return { "作者名称": author_name, "作者邮箱": author_email, "时间戳": timestamp, "修改时间": readable_time } def compare_git_branches(repo_url: str, branch_a: str, branch_b: str) -> Dict: """对比两个分支的文件差异并返回结构化结果""" with tempfile.TemporaryDirectory() as temp_root: # 创建两个临时仓库目录 repo_a_path = os.path.join(temp_root, "branch_a_repo") repo_b_path = os.path.join(temp_root, "branch_b_repo") # 克隆并检出分支A print(f"正在准备分支 {branch_a} 的副本...") run_git_cmd(["git", "clone", repo_url, repo_a_path]) run_git_cmd(["git", "checkout", branch_a], cwd=repo_a_path) # 克隆并检出分支B print(f"正在准备分支 {branch_b} 的副本...") run_git_cmd(["git", "clone", repo_url, repo_b_path]) run_git_cmd(["git", "checkout", branch_b], cwd=repo_b_path) # 对比两个分支的文件状态(仅追踪的文件) print("正在扫描文件差异...") diff_result = run_git_cmd( ["git", "diff", "--name-status", branch_a, branch_b], cwd=repo_a_path # 在单个仓库内直接对比分支更高效 ) diff_lines = diff_result.split("\n") if diff_result else [] # 初始化结果字典 comparison_output = { "分支B新增文件": [], "分支B删除文件": [], "内容修改文件": [], "重命名文件": [] } for line in diff_lines: if not line: continue status_code, file_info = line.split("\t", 1) # 根据Git diff状态码分类处理 if status_code == "A": # 分支B新增的文件 file_meta = get_file_modification_info(repo_b_path, file_info) comparison_output["分支B新增文件"].append({ "文件路径": file_info, **file_meta }) elif status_code == "D": # 分支B删除的文件(分支A存在) file_meta = get_file_modification_info(repo_a_path, file_info) comparison_output["分支B删除文件"].append({ "文件路径": file_info, **file_meta }) elif status_code == "M": # 内容修改的文件,同时获取两个分支的修改信息 meta_a = get_file_modification_info(repo_a_path, file_info) meta_b = get_file_modification_info(repo_b_path, file_info) comparison_output["内容修改文件"].append({ "文件路径": file_info, f"{branch_a} 修改信息": meta_a, f"{branch_b} 修改信息": meta_b }) elif status_code == "R": # 文件重命名,拆分新旧路径 old_path, new_path = file_info.split("\t") comparison_output["重命名文件"].append({ "原路径": old_path, "新路径": new_path, "备注": "文件在分支B中被重命名" }) return comparison_output if __name__ == "__main__": # 替换成你的仓库地址和分支名 TARGET_REPO = "https://github.com/your-username/your-repo.git" BRANCH_1 = "main" BRANCH_2 = "feature/update-module" try: result = compare_git_branches(TARGET_REPO, BRANCH_1, BRANCH_2) # 打印格式化结果 print("\n=== Git分支对比结果 ===") for category, items in result.items(): if items: print(f"\n--- {category} ---") for item in items: for key, value in item.items(): if isinstance(value, dict): print(f"{key}:") for k, v in value.items(): print(f" {k}: {v}") else: print(f"{key}: {value}") print("-"*50) except Exception as e: print(f"执行过程出错: {str(e)}")
关键细节说明
- 临时目录自动清理:用
tempfile.TemporaryDirectory创建临时仓库,脚本结束后会自动删除,不会留下垃圾文件 - 差异范围控制:如果只需要扫描特定子目录,只需在
git diff命令末尾添加路径参数,比如-- "src/subdir" - 错误处理:封装的
run_git_cmd函数会捕获Git命令执行错误并抛出带详情的异常,方便调试 - 元信息准确性:直接从Git提交历史提取信息,比文件系统的修改时间更可靠(能反映代码仓库的提交记录)
扩展方向
- 可以把结果导出为JSON/CSV文件,方便后续分析或导入其他工具
- 若需要对比未追踪文件,可添加
git ls-files --others --exclude-standard命令获取未追踪文件列表再做对比 - 支持批量对比多个分支组合,只需循环调用
compare_git_branches函数即可
内容的提问来源于stack exchange,提问作者Maurice
相关产品推荐
相关产品推荐

