1. 概述

GitHub Actions 是一个灵活而强大的平台,用于自动化 GitHub 仓库中的工作流。在 CI/CD 流水线 中,一个常见的需求是检测 Pull Request(PR)或 Push 事件中哪些文件发生了修改。

本文将介绍如何在 PR 和 Push 事件中检测文件变更,并演示如何将这两种情况整合到一个统一的工作流中,确保逻辑在不同事件类型下都能稳定运行。

2. 在 Pull Request 中检测变更文件

当 PR 被打开或更新时,GitHub 会触发一个 pull_request 事件。这个事件提供了两个关键信息:base branchhead branch。通过对比这两个分支的差异,我们就能知道 PR 中修改了哪些文件。这在只想对修改部分运行测试或代码检查时非常有用。

下面是一个基础的 PR 文件检测工作流示例:

name: Detect PR Changes

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  detect-changes:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout the code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # 确保获取完整历史以便比较

      - name: Get changed files
        id: changes
        run: |
          git fetch origin ${{ github.base_ref }}
          CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
          echo "changed=$CHANGED_FILES" >> $GITHUB_OUTPUT

      - name: Print changed files
        run: echo "Changed files: ${{ steps.changes.outputs.changed }}"

工作流说明:

  • 使用 pull_request 事件,并监听 opened, synchronize, reopened 三种状态变化;
  • fetch-depth: 0 确保获取完整的 Git 历史;
  • 使用 git diff --name-only 对比 base 分支与当前 HEAD;
  • 使用 ...(三连点)语法来获取两个分支之间的完整差异;
  • 最后将结果写入 GITHUB_OUTPUT,供后续步骤使用。

踩坑提醒:如果 fetch-depth 设置为 1(默认值),会导致 git diff 无法获取完整差异。

3. 在 Push 事件中检测变更文件

除了 PR,有时我们也会直接向分支提交代码。此时可以使用 push 事件来检测变更文件。GitHub Actions 会提供 beforeafter 两个 commit SHA,分别代表推送前和推送后的提交。

这是一个 Push 事件的检测工作流示例:

name: Detect Push Changes

on:
  push:
    branches:
      - main
      - develop

jobs:
  detect-push-changes:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout the code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Get changed files on push
        id: push_changes
        run: |
          git fetch origin ${{ github.ref_name }}
          CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }})
          echo "changed=$CHANGED_FILES" >> $GITHUB_OUTPUT

      - name: Print changed files
        run: echo "Changed files: ${{ steps.push_changes.outputs.changed }}"

工作流说明:

  • beforeafter 是 Push 事件自带的两个 commit;
  • 使用 git diff --name-only 对比这两个 commit 的差异;
  • 同样使用 fetch-depth: 0 确保完整历史;
  • 最后输出变更文件列表。

⚠️ 注意:Push 事件只能检测到当前分支的变更,无法跨分支比较。

4. 统一处理 Pull Request 与 Push 事件

在实际项目中,我们通常希望用一个统一的工作流来同时支持 PR 和 Push 事件。这样可以避免重复代码,也更便于维护。

我们可以根据 github.event_name 来判断事件类型,并执行不同的 diff 逻辑:

name: Detect File Changes

on:
  push:
    branches:
      - main
      - develop
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  detect-changed-files:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Determine event type
        id: event_type
        run: echo "Event is ${{ github.event_name }}"

      - name: Get changed files (PR)
        if: github.event_name == 'pull_request'
        id: pr_changes
        run: |
          git fetch origin ${{ github.base_ref }}
          CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
          echo "changed=$CHANGED_FILES" >> $GITHUB_OUTPUT

      - name: Get changed files (Push)
        if: github.event_name == 'push'
        id: push_changes
        run: |
          git fetch origin ${{ github.ref_name }}
          CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }})
          echo "changed=$CHANGED_FILES" >> $GITHUB_OUTPUT

      - name: Print changed files
        run: |
          if [ "${{ github.event_name }}" = "pull_request" ]; then
            echo "Changed files: ${{ steps.pr_changes.outputs.changed }}"
          else
            echo "Changed files: ${{ steps.push_changes.outputs.changed }}"
          fi

工作流亮点:

  • 同时监听 pull_requestpush 事件;
  • 使用 if 条件判断事件类型,选择对应的 diff 逻辑;
  • 输出统一,便于后续步骤调用;
  • 减少重复配置,提升可维护性。

建议:将 CHANGED_FILES 存入环境变量或缓存中,供后续 job 使用,实现更复杂的逻辑控制(如只运行受影响模块的测试)。

5. 小结

通过本文我们了解到,在 GitHub Actions 中检测文件变更是一个非常实用的功能,可以显著提升 CI/CD 的效率和智能程度。

  • 对于 PR,我们使用 git diff 对比 base 和 head 分支;
  • 对于 Push,我们使用 beforeafter commit SHA 进行比对;
  • 我们可以将这两种逻辑统一到一个 workflow 中,实现事件驱动的智能检测;
  • 通过合理使用 fetch-depthgit diff,确保逻辑稳定可靠。

掌握了这些技巧,你就可以根据文件变更来触发更精细的构建、测试或部署流程,真正做到“按需执行”。


原始标题:Get Files Modified in a PR in GitHub Actions