返回

Windows 下提取 Git PR 变更文件到单独文件夹

windows

咋从 Git 仓库里只拿出 PR 修改过的文件?(Windows 版)

刚克隆了一个 Git 仓库到本地,分支不少。咱现在有个需求:只想把每个 Pull Request (PR) 里改动过的文件单独拎出来,放到一个单独的文件夹里。

举个例子,有个叫 test 的分支,某个 PR 改了这几个文件:

  • README.md
  • a.out
  • test.txt

我就想把这仨文件弄到一个单独的目录里。我这儿环境是 Windows 11,Git 版本是 2.47.1.windows.2。

之前试过这种命令:

git diff --name-only origin/master..origin/topic/bug_fixes > diff.txt

然后对着 diff.txt 手动把文件抠出来,太费劲了!有没有啥更方便的法子,能在 Windows 上只提取 PR 里变动的文件,然后复制到单独文件夹?或者有没有其他更好用的 Git 命令?

一、 问题的根源在哪?

Git 本身是记录文件版本的,它关注的是整个仓库的状态变化。而咱的需求是聚焦在“PR”这个层面上的文件变化。“PR”本质上是一组提交 (commits) 与目标分支的差异。 所以直接用一个命令搞定有点困难。

二、 几种解决办法

1. 利用 git diff 和 PowerShell (或者其他脚本)

这个方法的核心思路是:先用 git diff 找出差异文件的列表,然后通过脚本来复制这些文件。

原理:

  • git diff --name-only <commit1>..<commit2>:这个命令能列出两个提交 (commit) 之间的所有改动过的文件名。commit1commit2 可以是提交的哈希值、分支名或者其他能代表提交的引用。
  • --name-only:这个参数让 git diff 只输出文件名,不显示具体改动内容。
  • PowerShell (或其他脚本):负责读取 git diff 的输出,然后把对应的文件复制到目标文件夹。

步骤 (PowerShell):

  1. 获取 PR 的起始和结束提交:
    一般情况下,PR 是从一个特性分支合并到主分支 (比如 main 或者 master)。我们需要找到特性分支开始的那个提交 (也就是和主分支分叉的那个点) 以及 PR 的最后一个提交。 可以用git log 去找,也可以通过Github,Gitlab 网页查看PR的信息直接获取。

    假设我们找到了起始提交 start_commit 和 结束提交end_commit.

  2. 运行 PowerShell 脚本:

    # 目标文件夹 (请自行修改)
    $targetDir = "C:\ExtractedFiles"
    
    # 确保目标文件夹存在
    if (!(Test-Path -Path $targetDir)) {
        New-Item -ItemType Directory -Path $targetDir
    }
    
    # 获取差异文件列表
    $changedFiles = git diff --name-only start_commit..end_commit
    
    # 复制每个文件
    foreach ($file in $changedFiles) {
       if(Test-Path -LiteralPath $file){ #检查文件是否存在,防止删除的文件也被尝试复制
            # 构建目标文件的完整路径
            $targetFile = Join-Path -Path $targetDir -ChildPath $file
    
            #如果存在目录结构,则创建
             $dir = Split-Path -Parent $targetFile
              if (!(Test-Path -Path $dir)) {
                New-Item -ItemType Directory -Path $dir -Force
            }
            # 复制文件
            Copy-Item -Path $file -Destination $targetFile -Force
        }
        else{
            Write-Host "文件 '$file' 已被删除,跳过复制。"
        }
    }
    

    注意:start_commit end_commit替换为真实的值

    直接运行该脚本就可以将差异文件提取到目标文件夹,同时保留文件路径。

安全建议:

  • 这个脚本没啥特别的安全风险,但是要注意目标文件夹的权限,别把文件复制到不该放的地方。

进阶用法:

你可以把这个脚本封装成一个函数,或者保存成一个 .ps1 文件,以后直接调用,省得每次都敲一遍。

2. 使用 git merge-basegit diff 结合

如果你知道 PR 是合并到哪个目标分支(例如 main),可以更方便地找到分叉点。

原理:

  • git merge-base <branch1> <branch2>:这个命令可以找出两个分支的共同祖先(分叉点)。

步骤 (PowerShell):

  1. 假设你的 PR 分支是 feature-branch,目标分支是 main

  2. 运行以下命令:

    # 目标文件夹 (请自行修改)
    $targetDir = "C:\ExtractedFiles"
    
    # 确保目标文件夹存在
    if (!(Test-Path -Path $targetDir)) {
        New-Item -ItemType Directory -Path $targetDir
    }
    
    # 获取分叉点
    $baseCommit = git merge-base feature-branch main
    
    # 获取差异文件列表
    $changedFiles = git diff --name-only $baseCommit feature-branch
    
    # 复制每个文件 (和上面一样)
       foreach ($file in $changedFiles) {
       if(Test-Path -LiteralPath $file){ #检查文件是否存在,防止删除的文件也被尝试复制
            # 构建目标文件的完整路径
            $targetFile = Join-Path -Path $targetDir -ChildPath $file
    
            #如果存在目录结构,则创建
             $dir = Split-Path -Parent $targetFile
              if (!(Test-Path -Path $dir)) {
                New-Item -ItemType Directory -Path $dir -Force
            }
            # 复制文件
            Copy-Item -Path $file -Destination $targetFile -Force
        }
        else{
            Write-Host "文件 '$file' 已被删除,跳过复制。"
        }
    }
    

注意:feature-branch 替换成你的特性分支名称.

直接运行该脚本就可以将差异文件提取到目标文件夹,同时保留文件路径。

这样就不用手动去找起始提交了。

3. 使用 git archive (有限制)

git archive 可以把某个提交或者分支打包成一个压缩文件。 但是,它不能直接只打包差异文件。 如果你的 PR 只涉及少量文件的修改,并且你不介意把整个分支打包,可以考虑这个方法。

原理:

  • git archive:把 Git 仓库的某个部分打包成压缩文件。

步骤:
* 找到PR的最后一个提交
* 使用git archive -o <压缩文件名>.zip <commit> 打包文件

这方法比较简单, 但是不符合本问题的需求(需要全部打包, 而不是增量差异文件)

4. 高级方案:自定义 Git 脚本和 Hook(进阶)

如果你经常需要处理这类需求,并且想做到极致的自动化。可以考虑使用Git提供的底层命令以及Hooks.

例如可以在一个.git/hooks/post-merge钩子下运行一个你写好的脚本。该脚本将在每次进行git merge时,且合并后被执行。 该脚本能够根据你提供的源分支以及目标分支自动查找PR相关的提交, 并执行差异文件提取。这样能大幅减少重复性操作, 将PR变更管理和差异提取整合到你平时Git工作流之中.

这属于自定义Git的高级内容了,实现起来稍微有些难度,并且更考验对Git内部原理的理解.

三、 总结

处理只提取 Git PR 变更文件的需求, 核心思路就是使用 git diff 找到差异文件列表,再借助脚本复制. 使用git merge-base可以让这个流程更便捷. git archive有一定的局限性. 如果要更高度的自动化,自定义Git的底层操作和Hooks是更高级的做法。