👻

GitHub Actionsで高速にgit diffを取る

に公開

GitHub Actionsで git diff を使って差分を取ろうとしたとき、shallow cloneだと取得できません。しかし全部fetchすると時間がかかります。

今回は、最低限のfetchだけを取得して、ちゃんと差分を取れるようにする設定を紹介します。

やりたいこと

  • GitHub ActionsでCIを回すときに git diff を使って差分を取得したい
  • でもクローンは最小限(fetch-depth: 1)にしたい

解決方法

まずは shallow clone。

- name: Checkout code with shallow clone
  uses: actions/checkout@v4
  with:
    fetch-depth: 1
    fetch-tags: false

これだけだと、ベースブランチとの共通祖先(merge base)が手元にないので、このあと追加で最低限のコミットだけ取得します。

- name: Fetch necessary commits
  run: |
    # Get base and head commit SHAs from the pull_request event payload
    BASE_SHA="${{ github.event.pull_request.base.sha }}"
    HEAD_SHA="${{ github.event.pull_request.head.sha }}"
    echo "Base SHA: ${BASE_SHA}"
    echo "Head SHA: ${HEAD_SHA}"
    # Use the GitHub Compare API to get the merge base commit SHA
    MERGE_BASE_SHA=$(curl -s \
      -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
      -H "Accept: application/vnd.github+json" \
      "https://api.github.com/repos/${{ github.repository }}/compare/${BASE_SHA}...${HEAD_SHA}" | jq -r '.merge_base_commit.sha')
    # Exit if the merge base SHA is not found
    if [ "$MERGE_BASE_SHA" = "null" ] || [ -z "$MERGE_BASE_SHA" ]; then
      echo "Error: Could not retrieve merge base commit SHA."
      exit 1
    fi
    echo "Merge base commit SHA: ${MERGE_BASE_SHA}"
    echo "MERGE_BASE_SHA=${MERGE_BASE_SHA}" >> $GITHUB_ENV
    # Fetch the merge base commit if it is not already present in the shallow clone
    git fetch --depth=1 origin ${MERGE_BASE_SHA}

やっていることは以下です。

  • BASE_SHAHEAD_SHAをPull Requestの情報から取得
  • GitHubのCompare APIを叩いて、merge baseを特定
  • そのmerge baseだけをピンポイントでfetch

git diffを取得する

この後は以下のようにすればgit diffで差分を取得できます。

MERGE_BASE_SHA=${{ env.MERGE_BASE_SHA }}
git diff ${MERGE_BASE_SHA} ${HEAD_SHA}

まとめ

必要な差分だけfetchすれば、CIが速くなりつつ、使い慣れたgit diffで差分を取得できます。

今回紹介した方法はGitHub Actionsで完結するので便利です。

Discussion