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:

  1. Monitor a folder for changes, including files within subdirectories, excluding hidden files.
  2. Add only the relevant (non-ignored) files to Git.
  3. 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.
  4. 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 in YYYY-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.