Get modified files in Github actions

Issue

I have 2 Github Actions workflows in my repository and one of the steps requires getting all the files that have been modified (except deleted files) in a PR. I use this in the first one:

on:
  pull_request:
    branches: [ main ]


jobs:
  get_files:
    name: run_on_pr
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/[email protected]
        with:
          fetch-depth: 0

      - name: Modified files
        run: |
          git fetch origin main:main
          git diff --name-only --diff-filter=d main~ main

This one works okay and I am able to get a list of all the files that have been modified. However, in the second workflow, which is supposed to run when the PR is merged, this does not work.

on:
  push:
    branches: [ main ]


jobs:
  get_files:
    name: run_when_pr_is_merged
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/[email protected]
        with:
          fetch-depth: 0

      - name: Modified files
        run: |
          git fetch origin main:main
          git diff --name-only --diff-filter=d main~ main

I get the error
fatal: refusing to fetch into branch 'refs/heads/main' checked out at '/home/runner/....'. I think the error is coming from the git fetch origin main:main as the workflow is running off the main branch and I am trying to do a fetch in there. I have removed that but still didn’t get the list I needed. Any help or better way to get the list of modified files in a PR in both workflows?

Solution

It’s a bit more complicated than this.

For PR’s GitHub action/checkout is created a detached head which is simulating a merge of the PR into the target branch. You can see it in the logs of the checkout action itself. You can alter this behavior by using a different ref, but I don’t recommend it – it’s actually making things easier, especially for forked PRs.

To get a list of changed files in PR, you just have to checkout with fetch-depth: 2 to get previous commits and then get files modified by a merge:

- name: Checkout
  uses: actions/[email protected]
  with:
     fetch-depth: 2
- name: Get changes
  run: git diff --name-only -r HEAD^1 HEAD

For push events, it’s also a bit more complicated as you can have multiple commits in single push, so here you have to fetch-depth: 0 and then use the GitHub context values to figure out the difference for the push:

- name: Checkout
  uses: actions/[email protected]
  with:
     fetch-depth: 0
- name: Get changes
  run: git diff --name-only ${{ github.event.before }} ${{ github.event.after }}

If you want a single workflow to handle both, you can do something like this:

- uses: actions/[email protected]
  with:
      fetch-depth: ${{ github.event_name == 'pull_request' && 2 || 0 }}
- name: Get changed files
  id: changed-files
  run: |
      if ${{ github.event_name == 'pull_request' }}; then
          echo "changed_files=$(git diff --name-only -r HEAD^1 HEAD | xargs)" >> $GITHUB_OUTPUT
      else
          echo "changed_files=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }} | xargs)" >> $GITHUB_OUTPUT
      fi
- name: List changed files
  run: |
      for file in ${{ steps.changed-files.outputs.changed_files }}; do
          echo "$file was changed"
      done

Answered By – Grzegorz Krukowski

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published