How to Retrieve Only the File Differences in a Specific Folder from a Pull Request Using a Command

This article is a translated version of my original post on Qiita. Original (Japanese): https://qiita.com/segur/items/14170af605225119be08

How to Retrieve Only the File Differences in a Specific Folder from a Pull Request Using a Command

I intend to create a GitHub Actions workflow that triggers build, test, and deploy only when there are differences in specific folders. I think you've encountered such a situation before!

To solve this issue, I explored scripts to get file differences in a branch. Here's the script I finally came up with!

# Get the base commit of the branch
baseCommit=$( \
  { diff -u <(git rev-list --first-parent ${BRANCH_NAME}) <(git rev-list --first-parent ${TARGET_BRANCH}) || true; } | \
  sed -ne 's/^ //p' | \
  head -1 \
)

# Get the head commit of the branch
headCommit=`git rev-parse ${BRANCH_NAME}`

# Compare commits and get file differences
git diff ${headCommit} ${baseCommit} --name-only --relative=${DIRECTORY}

Let me explain this script step by step.

Command to Get the Base Commit of a Branch

The following command allows you to find the commit where the branch diverged.

baseCommit=$( \
  { diff -u <(git rev-list --first-parent ${BRANCH_NAME}) <(git rev-list --first-parent ${TARGET_BRANCH}) || true; } | \
  sed -ne 's/^ //p' | \
  head -1 \
)

Let's break this down.

First, you can retrieve the commit log of a specified branch using git rev-list, like this:

$ git rev-list --first-parent origin/${BRANCH_NAME}

62d58bf10bd6dd391142311de5f0122fcc13669a
4336e8bed15c4c991703abe67769562c9ea8d9fa
c56fc286b45c61d00e3616c619555fbb21bce95a
108f8ebbf6233ae2d63d088c620611f5b1bb4daa
71b752b3aed0d6ec8b73fe620eb343cad3af469a

(Note: The commit hashes above are fictional.)

Next, we use the diff command to compare the commit logs.

The exit code may result in an error, which can be problematic when using GitHub Actions, so we ensure it always exits with true.

$ { diff -u <(〇〇〇) <(〇〇〇) || true; }

--- /dev/fd/63  2022-04-15 16:49:28.000000000 +0900
+++ /dev/fd/62  2022-04-15 16:49:28.000000000 +0900
@@ -1,4 +1,4 @@
+fddb596c92b46d87dae58969c6b9aa0881a17bda
 4336e8bed15c4c991703abe67769562c9ea8d9fa
 c56fc286b45c61d00e3616c619555fbb21bce95a
 108f8ebbf6233ae2d63d088c620611f5b1bb4daa

Then, using the sed command, you extract lines that do not start with +, -, or @. This fetches a list of common commits between the two branches, as shown below.

$ 〇〇〇 | sed -ne 's/^ //p'

4336e8bed15c4c991703abe67769562c9ea8d9fa
c56fc286b45c61d00e3616c619555fbb21bce95a
108f8ebbf6233ae2d63d088c620611f5b1bb4daa

Finally, using the head command, you obtain the top commit from the list of common commits. This is considered the branch point (i.e., the root of the PR branch).

$ 〇〇〇 | head -1

4336e8bed15c4c991703abe67769562c9ea8d9fe

With this, you've successfully identified the base commit where the branch diverged!

Command to Get the Head Commit of a Branch

Execute the following command to retrieve the hash of the latest commit.

headCommit=`git rev-parse ${BRANCH_NAME}`

Command to Fetch File Differences Between Two Commits

By executing the following command, you can list the file differences between two commits.

git diff ${headCommit} ${baseCommit} --name-only

Command to Fetch File Differences in a Specific Folder Between Two Commits

Adding the relative option to the above command allows you to list only those related to a specific folder!

git diff ${headCommit} ${baseCommit} --name-only --relative=${DIRECTORY}

Bonus: Command to Count the Number of File Differences

To count the number of file differences, use the command below!

count=$( \
  git diff ${headCommit} ${baseCommit} --name-only --relative=${DIRECTORY} | \
  wc -l \
)

If ${count} is 0, there are no file differences; if it's 1 or more, then there are file differences!

You can integrate this into a GitHub Actions expression to create workflows that execute build, test, and deploy only when there are differences in the specified folder.

Conclusion

In preparing this article, I referred to the following resources. Thank you for the helpful insights.