5 minutes
Automating File Change Detection and Git Integration in Bash
In software development, automating routine tasks can significantly boost productivity. One common scenario is monitoring changes to files within a directory, automatically committing those changes to version control, and pushing them to a remote server. In this article, we’ll walk through how to create a Bash script that detects file changes, integrates with Git to track changes, commits those changes, and pushes them to an FTP server using Git-FTP.
The Goal
We want to build a Bash script that:
- Monitors a directory for any changes to files, including deep subdirectories.
- Excludes hidden files (e.g.,
.env
,.gitignore
) from monitoring. - Checks if a file is ignored by Git before adding it to the repository.
- Automatically commits changes only when there are files staged for commit.
- Pushes the changes to a remote FTP server using
git-ftp
.
Let’s dive into how we can achieve this step-by-step.
The Core Bash Script
At the core of the solution is a Bash script that uses a combination of built-in tools (find
, stat
, etc.) and Git commands to detect changes in files and handle version control.
#!/bin/bash
folder="/root/work"
declare -A file_times
# 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
file_times["$file"]=$(stat -c %Y "$file")
done < <(find "$folder" -type f -print0)
# Continuously check for changes
while true; do
change_detected=false
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
current_time=$(stat -c %Y "$file")
if [[ ${file_times["$file"]} != "$current_time" ]]; then
echo "File changed: $(basename "$file")"
file_times["$file"]=$current_time
change_detected=true
# 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
# Check if the file is ignored by Git
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)
# If changes were detected, execute the git commands
if [ "$change_detected" = true ]; then
echo "Changes detected, updating repository..."
# Add all tracked files with changes
git add -u
# Check if there are any staged changes to commit
if ! git diff --cached --exit-code >/dev/null; then
# Commit the changes if there are files to be committed
git commit -m "[updated] files have been changed"
else
echo "No changes to commit."
fi
# Push changes to the FTP server
git ftp push
fi
sleep 2
done
Breaking Down the Script
1. Monitoring File Changes
The script uses the find
command to recursively locate all files in the target directory. It initializes the modification times of these files using stat -c %Y "$file"
, which retrieves the last modification time.
file_times["$file"]=$(stat -c %Y "$file")
It then continuously checks if the modification time of any file has changed. If it has, the script detects this as a change and sets a flag change_detected=true
.
2. Excluding Hidden Files
Hidden files (those whose names start with a dot, e.g., .env
, .gitignore
) are excluded from monitoring. This is achieved by checking the file’s basename and skipping it if it starts with a dot:
if [[ $(basename "$file") == .* ]]; then
echo "Skipping hidden file: $(basename "$file")"
continue
fi
This ensures that no hidden files are added or tracked by Git, and no changes in hidden files trigger further actions.
3. Checking If a File Is Tracked by Git
Before adding any changed file to Git, the script checks whether the file is already tracked using the following command:
if git ls-files --error-unmatch "$file" >/dev/null 2>&1; then
echo "File is already tracked: $(basename "$file")"
else
# File is not tracked
fi
This prevents redundant git add
commands on files that are already being tracked.
4. Checking If a File Is Ignored by Git
If a file is untracked, the script then checks whether it’s listed in .gitignore
or any global ignore rules using:
if git check-ignore "$file" >/dev/null 2>&1; then
echo "File is ignored: $(basename "$file")"
else
git add "$file"
fi
This ensures that ignored files are not added to Git.
5. Committing Changes Only When Necessary
To avoid unnecessary commits, the script checks if there are any staged changes using:
if ! git diff --cached --exit-code >/dev/null; then
git commit -m "[updated] files have been changed"
else
echo "No changes to commit."
fi
This prevents empty commits, ensuring the script only commits when there are actual changes.
6. Pushing Changes via Git-FTP
Once changes are committed, the script pushes them to an FTP server using git ftp push
. This assumes git-ftp
has already been configured for the repository.
Benefits of the Script
- Automation: The script runs continuously, detecting changes and automating the process of committing and pushing updates.
- Efficiency: By checking if files are tracked, ignored, or hidden, the script efficiently handles only the relevant files, reducing unnecessary Git operations.
- No Empty Commits: The script intelligently commits changes only when there are staged modifications, preventing redundant commits.
Conclusion
This script provides an efficient way to monitor file changes, handle version control using Git, and automate the process of pushing updates to an FTP server. Whether you’re managing a development environment or deploying changes frequently, this approach can save time and reduce manual intervention. By leveraging Bash’s powerful tools and Git integration, we can create a robust and scalable solution to streamline our workflow.