git提取当前分支指定文件历史版本

git提取当前分支指定文件历史版本

场景:排查某个文件变化历史

复制代码
import os
import subprocess

# ================= 配置 =================
# 注意:前面加 r 是为了防止报错,保持不动
TARGET_FILE = r"C://test//a.txt"

# 提取多少个版本
COUNT = 5
# 导出到哪个文件夹 (会自动创建在当前脚本目录下)
OUTPUT_DIR = "history_versions"


# =======================================

def run_cmd(cmd, cwd):
    """
    cmd: git 命令
    cwd: 执行命令的目录 (关键修复点)
    """
    # 这里的 cwd 参数告诉 Python:去那个文件夹里运行 git
    return subprocess.check_output(cmd, shell=True, cwd=cwd).decode('utf-8').strip()


def main():
    if not os.path.exists(TARGET_FILE):
        print(f"❌ 找不到文件: {TARGET_FILE} (请检查路径)")
        return

    # 1. 自动获取目标文件所在的目录
    # Git 命令必须在这个目录下(或其父目录)运行,才能找到 .git 仓库
    repo_work_dir = os.path.dirname(TARGET_FILE)

    # 2. 准备输出目录
    if not os.path.exists(OUTPUT_DIR):
        os.makedirs(OUTPUT_DIR)

    print(f"📂 切换工作目录至: {repo_work_dir}")
    print(f"🔍 正在查找最近 {COUNT} 次修改记录...")

    # 3. 获取日志
    # 注意:cmd 里依然使用绝对路径的文件名,这样最稳妥
    cmd_log = f'git log -n {COUNT} --pretty=format:"%h|%ad|%s" --date=format:"%Y%m%d_%H%M%S" -- "{TARGET_FILE}"'

    try:
        # 关键修改:把 repo_work_dir 传进去
        logs = run_cmd(cmd_log, repo_work_dir).split('\n')
    except subprocess.CalledProcessError as e:
        print("\n❌ Git 执行失败!可能是因为该目录不是 Git 仓库,或者没有安装 Git。")
        print(f"错误信息: {e}")
        return

    for i, log in enumerate(logs):
        if not log: continue
        try:
            commit_hash, date, msg = log.split('|')
        except ValueError:
            continue  # 跳过格式不对的行

        # 4. 构造导出文件名
        ext = os.path.splitext(TARGET_FILE)[1]
        # 清洗文件名中的非法字符
        safe_msg = "".join([c for c in msg if c.isalnum() or c in " -_"])[:30]
        out_name = f"v{i + 1}_{date}_{commit_hash}_{safe_msg}{ext}"
        out_path = os.path.join(OUTPUT_DIR, out_name)

        # 5. 提取文件内容
        # 使用 git show hash:绝对路径 (Windows下可能需要转义,这里最简单的办法是只给文件名,由 cwd 控制上下文)
        # 但为了稳妥,我们使用 git show hash:./文件名 (相对路径) 或者处理一下路径

        # 技巧:git show <hash>:<path> 需要 path 是相对于仓库根目录的路径。
        # 直接用绝对路径在 git show <hash>:... 中经常会报错。
        # 最稳妥的方法是:git show <hash> -- <绝对路径> 这样通常打印不出来内容到stdout
        # 替代方案:既然我们已经在工作区了,用 git show <hash>:<文件名> (相对路径)

        file_name_only = os.path.basename(TARGET_FILE)

        # 尝试获取相对于 git 根目录的路径(这是最严谨的写法)
        try:
            git_root = run_cmd("git rev-parse --show-toplevel", repo_work_dir).replace('/', '\\')
            rel_path = os.path.relpath(TARGET_FILE, git_root).replace('\\', '/')
        except:
            # 如果获取失败,尝试直接用文件名(仅当文件在根目录时有效,这步是保底)
            rel_path = file_name_only

        cmd_show = f'git show {commit_hash}:"{rel_path}"'

        try:
            content = run_cmd(cmd_show, repo_work_dir)
            with open(out_path, 'w', encoding='utf-8') as f:
                f.write(content)
            print(f"✅ 已导出: {out_name}")
        except Exception as e:
            print(f"⚠️ 导出 {commit_hash} 失败。可能原因:路径转义问题或文件在该版本不存在。")
            print(f"   尝试命令: {cmd_show}")

    print("-" * 30)
    print(f"🎉 完成!文件已保存在脚本所在目录的 '{OUTPUT_DIR}' 文件夹中。")


if __name__ == "__main__":
    main()
相关推荐
玉梅小洋1 天前
Git 使用技巧——查看 Commit 修改文件的概要
git·github
Howie Zphile2 天前
Git 拉 NocoBase 2.0 beta(next 分支),并“每天自动更新 + 自动编译 + 自动重启”
大数据·git·elasticsearch
吕司2 天前
Git分支管理
git
黑屋里的马2 天前
GitExtension下载、安装
git·gitextension
Geoking.2 天前
Git 中的 Rebase 与 Merge:原理、区别与最佳实践
git
invicinble2 天前
一文了解git
大数据·git·elasticsearch
我命由我123452 天前
Git 初始化本地仓库并推送到远程仓库解读
运维·服务器·经验分享·笔记·git·学习·学习方法
爱码小白2 天前
Git学习笔记
笔记·git·学习
skywalk81632 天前
sudo apt upgrade git 报错
git
_运维那些事儿2 天前
GitLabCI/CD语法
linux·服务器·git·ci/cd·gitlab·运维开发·devops