1. 概述
GitHub Actions 是一个灵活而强大的平台,用于自动化 GitHub 仓库中的工作流。在 CI/CD 流水线 中,一个常见的需求是检测 Pull Request(PR)或 Push 事件中哪些文件发生了修改。
本文将介绍如何在 PR 和 Push 事件中检测文件变更,并演示如何将这两种情况整合到一个统一的工作流中,确保逻辑在不同事件类型下都能稳定运行。
2. 在 Pull Request 中检测变更文件
当 PR 被打开或更新时,GitHub 会触发一个 pull_request
事件。这个事件提供了两个关键信息:base branch 和 head 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 会提供 before
和 after
两个 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 }}"
工作流说明:
before
和after
是 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_request
和push
事件; - 使用
if
条件判断事件类型,选择对应的 diff 逻辑; - 输出统一,便于后续步骤调用;
- 减少重复配置,提升可维护性。
✅ 建议:将 CHANGED_FILES
存入环境变量或缓存中,供后续 job 使用,实现更复杂的逻辑控制(如只运行受影响模块的测试)。
5. 小结
通过本文我们了解到,在 GitHub Actions 中检测文件变更是一个非常实用的功能,可以显著提升 CI/CD 的效率和智能程度。
- 对于 PR,我们使用
git diff
对比 base 和 head 分支; - 对于 Push,我们使用
before
和after
commit SHA 进行比对; - 我们可以将这两种逻辑统一到一个 workflow 中,实现事件驱动的智能检测;
- 通过合理使用
fetch-depth
和git diff
,确保逻辑稳定可靠。
掌握了这些技巧,你就可以根据文件变更来触发更精细的构建、测试或部署流程,真正做到“按需执行”。