
Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: August 20, 2025
GitHub Actions provides a flexible and powerful platform for automating workflows within a GitHub repository. One common use case in continuous integration (CI) workflows is the ability to detect which files have changed in a pull request (PR) or push event.
In this article, we’ll explore how to detect changed files in a PR and on push using GitHub Actions. We’ll also demonstrate how to combine both scenarios into a single workflow, ensuring that file detection logic works reliably for both event types.
When a PR is opened or updated, GitHub triggers a pull_request event. This event provides access to two important pieces of information: The base branch and the head branch. By comparing these two branches, we can detect which files have changed. This is especially useful when we want to run a specific job, like tests or linters, only for the parts of the codebase that were modified in the PR.
Let’s begin with a pull_request workflow that checks out the code and uses git diff to detect modified files:
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 # Ensures we have the full history for diff
- 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 }}"
The workflow is configured to trigger on pull_request events, specifically for actions such as opened, synchronize, and reopened. Within the job, in the first step, we use the actions/checkout@v4 action with fetch-depth: 0 to ensure that we retrieve the full commit history before comparing the difference.
Next, we fetch the base branch of the PR using github.base_ref in the second step. Then, we compare it against the current HEAD using git diff –name-only. This triple-dot syntax allows us to see the complete set of files that have changed between the branches. Finally, we store the result in an output variable so that subsequent steps in the workflow can access the list of changed files.
Lastly, we use echo to print the result and verify the output during the workflow execution.
While PRs are common for collaborative development, direct pushes to branches are still relevant in many workflows. As another option, we can detect changed files when pushing new code to our branches. When using the push event, GitHub Actions provides access to the commit SHA before and after the push.
This approach is straightforward and effective for detecting changes in workflows triggered by direct branch updates. It’s especially useful for scenarios like production deployments, validations, or testing workflows that need to respond to updates.
We can use these SHAs to detect changes:
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 }}"
As discussed, GitHub’s push event provides two important commit SHAs: before and after, representing the state of the branch before and after the push. By using git diff with these two SHAs, we can determine exactly which files were modified.
Similar to the pull request approach, we retrieve the full commit history using the actions/checkout@v4 action with fetch-depth: 0.
In the second step of the workflow, we compare the before and after SHAs using git diff to get a list of changed files.
Finally, we print the result to verify our changes during the workflow execution.
In practice, we may want a unified workflow that handles both PRs and pushes. Rather than duplicating logic across separate workflows, we can combine support for both event types into a single, unified GitHub workflow.
We can achieve this by adding conditional logic based on the event_name value:
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
This workflow listens to both push and pull_request events, allowing it to handle file change detection in a unified way. It begins by checking out the code with full history using actions/checkoutv4, ensuring that all commits needed for diff comparisons are available.
Depending on the event type, determined by github.event_name, the workflow executes the appropriate set of steps: for pull requests, it compares the base branch to the head using git diff, while for pushes, it compares the before and after commit SHAs. Then, we store each result in an output variable. A final step prints the list of changed files, using conditional logic to select the correct output.
This approach reduces maintenance overhead while also providing consistent file change detection, no matter how updates are made to the repository.
In this article, we explored how detecting file changes in GitHub Actions is a powerful technique that enables more intelligent and efficient CI/CD workflows.
Detecting file changes in GitHub Actions is a powerful technique that enables more intelligent and efficient CI/CD workflows. Whether we’re working with PRs, direct pushes, or both, we can tailor our workflow to respond only to relevant changes, either for specific events or a combination of them.