5 minutes
Automating File Change Detection and Smart Git Committing Using Bash
In software development, automating repetitive tasks like monitoring file changes and committing those changes to version control can save time and reduce human error. This article walks through a Bash script that not only detects changes in files within a directory but also integrates smart Git behavior: either amending the last commit or creating a new one depending on whether a commit was already made today.
This enhanced approach ensures you maintain a clean Git history while automating mundane tasks such as pushing updates to an FTP server via Git-FTP.
What We Want to Achieve:
- Monitor a folder for changes, including files within subdirectories, excluding hidden files.
- Add only the relevant (non-ignored) files to Git.
- Commit the changes if any were detected, but only create a new commit if the last commit wasn’t made today. Otherwise, amend the last commit.
- Push changes to an FTP server using Git-FTP.
The Solution: A Bash Script
Here’s the step-by-step implementation of the script with detailed explanations:
Full Bash Script
#!/bin/bash
# Folder we want to monitor
folder="/root/work"
# Declare an associative array to store modification times of each file
declare -A file_times
# Step 1: Initialize file modification times (excluding hidden files and subdirectories)
while IFS= read -r -d '' file; do
# Skip hidden files (those starting with a dot)
if [[ $(basename "$file") == .* ]]; then
echo "Skipping hidden file: $(basename "$file")"
continue
fi
# Store each file's modification time in the file_times array
file_times["$file"]=$(stat -c %Y "$file")
done < <(find "$folder" -type f -print0)
# Step 2: Continuously check for changes in the folder
while true; do
change_detected=false
while IFS= read -r -d '' file; do
# Skip hidden files
if [[ $(basename "$file") == .* ]]; then
echo "Skipping hidden file: $(basename "$file")"
continue
fi
# Get the current modification time of the file
current_time=$(stat -c %Y "$file")
# Compare with the stored modification time
if [[ ${file_times["$file"]} != "$current_time" ]]; then
echo "File changed: $(basename "$file")"
file_times["$file"]=$current_time
change_detected=true
# Step 3: Check if the file is tracked by Git
if git ls-files --error-unmatch "$file" >/dev/null 2>&1; then
echo "File is already tracked: $(basename "$file")"
else
# Step 4: Check if the file is ignored by Git (e.g., via .gitignore)
if git check-ignore "$file" >/dev/null 2>&1; then
echo "File is ignored: $(basename "$file")"
else
echo "File is not tracked, adding to Git: $(basename "$file")"
git add "$file"
fi
fi
fi
done < <(find "$folder" -type f -print0)
# Step 5: If any changes were detected, proceed to Git operations
if [ "$change_detected" = true ]; then
echo "Changes detected, updating repository..."
# Add all tracked files that have changed
git add -u
# Step 6: Check if there are staged changes to commit
if ! git diff --cached --exit-code >/dev/null; then
# Get the date of the last commit in YYYY-MM-DD format
last_commit_date=$(git log -1 --format=%cd --date=short)
# Get today's date in the same format
today_date=$(date +%Y-%m-%d)
# Step 7: Decide whether to amend the last commit or create a new one
if [[ "$last_commit_date" == "$today_date" ]]; then
echo "Amending the last commit (made today)..."
git commit --amend -m "[updated] files have been changed"
else
echo "Creating a new commit..."
git commit -m "[updated] files have been changed"
fi
else
echo "No changes to commit."
fi
# Step 8: Push changes to the FTP server using Git-FTP
git ftp push
fi
# Wait for 2 seconds before checking for changes again
sleep 2
done
Breakdown of the Script:
Step 1: Initialize File Modification Times
while IFS= read -r -d '' file; do
if [[ $(basename "$file") == .* ]]; then
continue
fi
file_times["$file"]=$(stat -c %Y "$file")
done < <(find "$folder" -type f -print0)
find
: Recursively finds all files in the specified folder.stat -c %Y
: Retrieves the last modification time of the file.- Hidden files: Files whose names start with a dot (e.g.,
.env
) are skipped.
Step 2: Continuously Check for Changes
while true; do
change_detected=false
while IFS= read -r -d '' file; do
if [[ $(basename "$file") == .* ]]; then
continue
fi
current_time=$(stat -c %Y "$file")
if [[ ${file_times["$file"]} != "$current_time" ]]; then
file_times["$file"]=$current_time
change_detected=true
fi
done
sleep 2
done
- The script enters an infinite loop, continuously checking for changes.
- The file’s current modification time is compared to the stored value in
file_times
. - If a change is detected, the script proceeds to handle it.
Step 3-4: Check if the File is Tracked or Ignored by Git
if git ls-files --error-unmatch "$file" >/dev/null 2>&1; then
echo "File is already tracked"
else
if git check-ignore "$file" >/dev/null 2>&1; then
echo "File is ignored"
else
git add "$file"
fi
fi
git ls-files --error-unmatch "$file"
: Checks if the file is tracked by Git.git check-ignore "$file"
: Checks if the file is ignored by Git according to.gitignore
.
Step 5-7: Commit Changes (Amend or Create a New Commit)
last_commit_date=$(git log -1 --format=%cd --date=short)
today_date=$(date +%Y-%m-%d)
if [[ "$last_commit_date" == "$today_date" ]]; then
git commit --amend -m "[updated] files have been changed"
else
git commit -m "[updated] files have been changed"
fi
git log -1 --format=%cd --date=short
: Retrieves the date of the last commit inYYYY-MM-DD
format.date +%Y-%m-%d
: Gets today’s date.- If the last commit date is today, the script amends the previous commit to avoid creating multiple commits. Otherwise, it creates a new commit.
Step 8: Push Changes to the FTP Server
git ftp push
git ftp push
: Pushes the committed changes to the FTP server. This requires Git-FTP to be configured for your repository.
Conclusion
This Bash script provides an efficient way to automate file change detection, version control, and deployment via Git-FTP. By intelligently checking the last commit date, the script ensures that your Git history remains clean—either amending the last commit if changes were made the same day or creating a new commit for the next day’s changes. With this setup, you can streamline your development workflow and focus more on coding rather than manually managing commits and deployments.
By running this script in the background, you can maintain an up-to-date repository with minimal effort, ensuring changes are always committed and pushed to your FTP server whenever files in your project are modified.