Search code examples
gitgithubgithub-actions

Get modified files in Github actions


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/checkout@master
        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/checkout@master
        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/checkout@v3
      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/checkout@v3
      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/checkout@v3
      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