Automating Git Commits with inotify: Never Forget to Push Again
Build a real-time file watcher that automatically commits and pushes your changes to GitHub
Introduction
As developers and technical writers, we’ve all been there: you spend hours writing documentation, blog posts, or code, only to realize hours (or days) later that you forgot to commit and push your changes. Or worse, you lose work because it was never backed up to a remote repository. If you have a homelab, you are likely working with a Proxmox instance via SSH. In that case, you are working with a Linux kernel, and it’s easy to forget to commit and push your changes.
What if your files could automatically commit and push themselves to GitHub the moment you save them? No manual git add, git commit, git push dance. Just save your file, and seconds later it’s safely on GitHub.
In this guide, I’ll show you how to build an automated Git commit system using Linux’s inotify file monitoring system and systemd services. This setup watches a directory for changes and automatically commits and pushes to GitHub in real-time.
What We’re Building
By the end of this tutorial, you’ll have:
- ✅ Real-time file monitoring that detects when files change
- ✅ Automatic Git commits with timestamps
- ✅ Automatic pushes to GitHub within seconds of saving
- ✅ Background daemon that runs continuously via systemd
- ✅ Debouncing logic to prevent commit spam during active editing
- ✅ Smart exclusions for temporary files and Git metadata
- ✅ Secret detection that aborts commits if credentials are staged
- ✅ Concurrency protection via lock files to prevent race conditions
- ✅ Comprehensive logging via journald for troubleshooting
Use Cases
This automation is perfect for:
- Blog posts and documentation – Never lose writing progress
- Configuration files – Automatically version control your dotfiles
- Notes and knowledge bases – Keep Obsidian vaults or markdown notes backed up
- Small projects – Continuous backup without thinking about it
- Learning journals – Document your learning with automatic version history
Not recommended for:
- Large collaborative projects (too many automatic commits)
- Code requiring peer review before commits
- Projects where commit messages need careful crafting
Prerequisites
What You’ll Need
System Requirements:
- Linux system (Ubuntu, Debian, Proxmox, or similar)
- Git installed and configured
- SSH key set up with GitHub (or HTTPS with credential caching)
- Sudo/root access for installing packages
Knowledge Prerequisites:
- Basic Git concepts (commit, push, remote)
- Basic Linux terminal usage
- Understanding of file permissions and executables
Verify Git Setup
Before we begin, ensure Git is configured:
# Check Git installation
git --version
# Check Git configuration
git config --global user.name
git config --global user.email
# If not set, configure them:
git config --global user.name "Your Name"
git config --global user.email "your-email@example.com"
Verify GitHub Authentication
Test that you can push to GitHub:
# For SSH (recommended)
ssh -T git@github.com
# Should see: "Hi username! You've successfully authenticated..."
If authentication fails, you need to set up SSH keys or HTTPS credentials before proceeding.
Architecture Overview
Let’s understand how this system works:
┌─────────────────┐
│ You save a │
│ file in the │
│ watched dir │
└────────┬────────┘
│
▼
┌─────────────────────┐
│ inotifywait │
│ detects change │
│ (modify/create/ │
│ delete/move) │
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ Debounce timer │
│ (5 second wait) │
│ Allows multiple │
│ saves to batch │
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ auto-commit.sh │
│ - git add -u │
│ - secret check │
│ - git commit │
│ - git push │
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ GitHub │
│ Changes appear │
│ in repository │
└─────────────────────┘
Key Components:
- inotify – Linux kernel subsystem that monitors filesystem events
- inotifywait – Command-line tool to watch for file changes
- auto-commit.sh – Script that performs Git operations
- watch-and-commit.sh – Wrapper that monitors files and triggers commits
- systemd service – Background daemon that runs the watcher continuously
Step 1: Create Your Git Repository
First, let’s set up the directory you want to auto-commit.
Initialize Git Repository (this tutorial walks through a workflow of maintaining a repo of blog posts)
# Create your directory (or use an existing one)
mkdir -p ~/blogposts
cd ~/blogposts
# Initialize Git
git init
# Rename default branch to 'main'
git branch -m main
# Configure Git for this repository
git config user.name "Your Name"
git config user.email "your-email@example.com"
Create .gitignore
Exclude files you don’t want to commit:
cat > .gitignore << 'EOF'
# macOS
.DS_Store
.AppleDouble
.LSOverride
._*
# Editor backup files
*~
*.swp
*.swo
*.swn
.vscode/
.idea/
# Logs from our automation
*.log
# Temporary files
*.tmp
*.temp
# Automation scripts (keep outside version control)
auto-commit.sh
watch-and-commit.sh
# Security - never auto-commit these
.env
.env.*
*.pem
*.key
*.p12
*.pfx
*.gpg
credentials*
secrets*
id_rsa*
id_ed25519*
*.secret
# Drafts (optional - uncomment if you want to exclude drafts)
# drafts/
# private/
EOF
Create README
cat > README.md << 'EOF'
# My Blog Posts
A collection of technical writing and blog posts, automatically backed up to GitHub.
## Auto-Commit System
This repository uses an automated commit system that watches for file changes and pushes to GitHub automatically.
See [automated-git-commits-with-inotify.md](./automated-git-commits-with-inotify.md) for setup details.
EOF
Create Initial Commit
git add .
git commit -m "Initial commit: Set up repository"
Step 2: Create GitHub Repository
Now we need a remote repository to push to.
- Go to GitHub.com and log in
- Click the + icon (top right) → New repository
- Configure:
- Repository name:
blogposts(or your preferred name) - Description: “Automated blog post backups”
- Visibility: Public or Private
- DO NOT initialize with README (we already have one)
- Repository name:
- Click Create repository
Link Local to Remote
# Add GitHub as remote (use SSH - recommended)
git remote add origin git@github.com:your-username/blogposts.git
# Or use HTTPS if you prefer
# git remote add origin https://github.com/your-username/blogposts.git
# Verify remote
git remote -v
# Push initial commit
git push -u origin main
If the push fails due to authentication, ensure your SSH key is added to GitHub or set up credential caching for HTTPS.
Step 3: Install inotify-tools
The inotify-tools package provides inotifywait, which monitors file changes.
# Update package list
sudo apt update
# Install inotify-tools
sudo apt install -y inotify-tools
# Verify installation
inotifywait --version
What is inotify?
inotify is a Linux kernel subsystem that provides file system event monitoring. It’s incredibly efficient because it operates at the kernel level rather than polling for changes. When a file is modified, created, or deleted, inotify immediately notifies interested programs.
★ Insight ─────────────────────────────────────
Unlike polling-based file monitoring (checking every few seconds), inotify uses kernel-level filesystem events, making it extremely efficient. It only triggers when actual changes occur, consuming minimal CPU. This is the same technology used by editors like VS Code to detect external file changes and by build tools like webpack for hot reloading.
─────────────────────────────────────────────────
Step 4: Create Auto-Commit Script
This script performs the actual Git operations.
cat > ~/blogposts/auto-commit.sh << 'EOF'
#!/bin/bash
set -euo pipefail
# Auto-commit script for blogposts
# This script checks for changes and commits them automatically
: "${HOME:?HOME is not set}"
REPO_DIR="$HOME/blogposts"
cd "$REPO_DIR" || exit 1
# Prevent concurrent runs with a lock file
LOCK_FILE="/tmp/blogpost-autocommit.lock"
exec 200>"$LOCK_FILE"
flock -n 200 || { echo "$(date): Another commit in progress, skipping"; exit 0; }
# Check if there are any changes
if [[ -n $(git status --porcelain) ]]; then
echo "$(date): Changes detected in blogposts"
# Stage only tracked files (safer than 'git add .' which could commit secrets)
git add -u
# Also add new .md and .txt files explicitly (safe allowlist)
git add -- '*.md' '*.txt' '.gitignore' 2>/dev/null || true
# Safety check: abort if staged files look like secrets
if git diff --cached --name-only | grep -qiE '.(env|pem|key|p12|pfx|gpg|secret|credentials)'; then
echo "$(date): ERROR - Potential secrets detected in staged files. Aborting commit."
git reset HEAD
exit 1
fi
# Create commit message with timestamp
COMMIT_MSG="Auto-commit: Update blog posts - $(date '+%Y-%m-%d %H:%M:%S')"
# Commit changes
git commit -m "$COMMIT_MSG"
# Pull any remote changes first to avoid non-fast-forward errors
git pull --rebase origin main 2>/dev/null || true
# Push to GitHub (main branch)
if git push origin main; then
echo "$(date): Changes committed and pushed successfully"
else
echo "$(date): Error pushing to GitHub"
exit 1
fi
else
echo "$(date): No changes detected"
fi
EOF
# Make executable
chmod +x ~/blogposts/auto-commit.sh
Test the Script Manually
# Create a test file
echo "# Test" > ~/blogposts/test.md
# Run the script
~/blogposts/auto-commit.sh
# Check GitHub - the file should appear!
Step 5: Create File Watcher Script
This script uses inotifywait to monitor for changes and trigger commits.
cat > ~/blogposts/watch-and-commit.sh << 'EOF'
#!/bin/bash
set -euo pipefail
# Real-time file watcher for blogposts
# This script watches for file changes and automatically commits them
: "${HOME:?HOME is not set}"
WATCH_DIR="$HOME/blogposts"
SCRIPT_DIR="$HOME/blogposts"
# Colors for output
GREEN=' 33[0;32m'
YELLOW=' 33[1;33m'
NC=' 33[0m' # No Color
echo -e "${GREEN}🔍 Blogpost Auto-Commit Watcher Started${NC}"
echo "Watching: $WATCH_DIR"
echo "Excluding: .git/, logs, temp files"
echo "Press Ctrl+C to stop"
echo "---"
# Track last commit time to debounce rapid changes
LAST_COMMIT_TIME=0
DEBOUNCE_SECONDS=5
inotifywait -m -r -e modify,create,delete,move
--format '%w%f'
--exclude '(.git|.swp|.log$|auto-commit.log|watcher.log|~$|.DS_Store)'
"$WATCH_DIR" |
while IFS= read -r filepath; do
# Extract filename from full path
filename=$(basename "$filepath")
# Skip if it's the auto-commit script itself
if [[ "$filename" == "auto-commit.sh" ]] || [[ "$filename" == "watch-and-commit.sh" ]]; then
continue
fi
CURRENT_TIME=$(date +%s)
TIME_DIFF=$((CURRENT_TIME - LAST_COMMIT_TIME))
if [ $TIME_DIFF -lt $DEBOUNCE_SECONDS ]; then
echo -e "${YELLOW}⏳ Waiting for more changes (${TIME_DIFF}s ago)...${NC}"
continue
fi
echo -e "${GREEN}📝 Change detected:${NC} $filename"
echo "⏰ $(date '+%Y-%m-%d %H:%M:%S')"
echo "Waiting ${DEBOUNCE_SECONDS} seconds for additional changes..."
sleep $DEBOUNCE_SECONDS
echo "Running auto-commit..."
"$SCRIPT_DIR/auto-commit.sh" || echo "Auto-commit exited with error (will retry on next change)"
LAST_COMMIT_TIME=$(date +%s)
echo "---"
done
EOF
# Make executable
chmod +x ~/blogposts/watch-and-commit.sh
Understanding Debouncing
Why debounce?
Without debouncing, every single save would create a commit. If you’re actively editing a file, your editor might save every few seconds, creating dozens of commits per minute!
How it works:
The script tracks when the last commit occurred. If a change is detected within 5 seconds of the last commit, it skips that change. After you stop editing (5 seconds of inactivity), it commits all accumulated changes in one commit.
Test the Watcher Manually
# Run in one terminal
~/blogposts/watch-and-commit.sh
# In another terminal, make changes
echo "More content" >> ~/blogposts/test.md
# Watch the first terminal - you'll see it detect the change and commit!
Press Ctrl+C to stop the watcher when done testing.
Step 6: Create Systemd Service
Running the watcher manually is tedious. Let’s make it a background service that starts automatically.
Create Service File
# Create systemd user directory
mkdir -p ~/.config/systemd/user
# Create service file
cat > ~/.config/systemd/user/blogpost-watcher.service << 'EOF'
[Unit]
Description=Blogpost Auto-Commit Watcher
After=network.target
[Service]
Type=simple
ExecStart=%h/blogposts/watch-and-commit.sh
Restart=always
RestartSec=10
[Install]
WantedBy=default.target
EOF
Note: The %h specifier in systemd automatically resolves to your home directory, so no manual path editing is needed. Logs are handled by journald — view them with journalctl --user -u blogpost-watcher.service -f.
Understanding the Service Configuration
| Setting | What It Does |
|---|---|
Type=simple |
Service runs in foreground (doesn’t fork/daemonize) |
ExecStart=%h/... |
Command to run (%h = your home directory) |
Restart=always |
Automatically restart if it crashes |
RestartSec=10 |
Wait 10 seconds before restarting |
WantedBy=default.target |
Start when user logs in |
★ Insight ─────────────────────────────────────
Systemd user services run in your user session, not as root. They start when you log in and stop when you log out (unless you enable “linger”). The %h specifier automatically resolves to your home directory, so you never need to hardcode paths. Logs go to journald by default — view them with journalctl --user -u blogpost-watcher.service. The Restart=always directive means if the watcher crashes, systemd automatically restarts it after 10 seconds, making the system self-healing.
─────────────────────────────────────────────────
Enable and Start Service
# Reload systemd to recognize new service
systemctl --user daemon-reload
# Enable service (start on login)
systemctl --user enable blogpost-watcher.service
# Start service now
systemctl --user start blogpost-watcher.service
# Check status
systemctl --user status blogpost-watcher.service
You should see output showing:
● blogpost-watcher.service - Blogpost Auto-Commit Watcher
Loaded: loaded
Active: active (running)
Step 7: Test the Complete System
Let’s verify everything works end-to-end.
Create a Test File
# Create a new blog post
cat > ~/blogposts/my-first-auto-commit-post.md << 'EOF'
# My First Auto-Committed Post
This post was automatically committed to GitHub!
## How It Works
As soon as I save this file, the inotify watcher detects the change,
waits 5 seconds to see if I make more changes, then automatically:
1. Commits the file to Git
2. Pushes to GitHub
No manual `git add`, `git commit`, or `git push` needed!
EOF
Watch the Logs
# View real-time logs via journald
journalctl --user -u blogpost-watcher.service -f
You should see output like:
🔍 Blogpost Auto-Commit Watcher Started
Watching: /home/your-username/blogposts
---
📝 Change detected: my-first-auto-commit-post.md
⏰ 2025-11-05 14:23:15
Waiting 5 seconds for additional changes...
Running auto-commit...
Changes detected in blogposts
Changes committed and pushed successfully
---
Verify on GitHub
- Go to https://github.com/your-username/blogposts
- You should see your new file!
- Click on “Commits” to see the auto-generated commit message
Managing the Service
View Service Status
systemctl --user status blogpost-watcher.service
View Live Logs
# View real-time logs via journald
journalctl --user -u blogpost-watcher.service -f
# View recent log entries
journalctl --user -u blogpost-watcher.service --since "1 hour ago"
Stop the Service
systemctl --user stop blogpost-watcher.service
Restart the Service
systemctl --user restart blogpost-watcher.service
Disable Auto-Start
systemctl --user disable blogpost-watcher.service
Re-enable Auto-Start
systemctl --user enable blogpost-watcher.service
Advanced Configurations
Adjust Debounce Time
If 5 seconds is too short or too long for your workflow, edit the watcher script:
nano ~/blogposts/watch-and-commit.sh
# Find this line:
DEBOUNCE_SECONDS=5
# Change to your preferred value (in seconds):
DEBOUNCE_SECONDS=10 # Wait 10 seconds
DEBOUNCE_SECONDS=30 # Wait 30 seconds
DEBOUNCE_SECONDS=2 # Wait 2 seconds (fast!)
# Save and restart the service
systemctl --user restart blogpost-watcher.service
Watch Multiple Directories
To watch multiple directories, create separate services for each:
# Copy the watcher script
cp ~/blogposts/watch-and-commit.sh ~/notes/watch-and-commit.sh
# Edit to point to new directory
nano ~/notes/watch-and-commit.sh
# Change WATCH_DIR and SCRIPT_DIR
# Create new service file
cp ~/.config/systemd/user/blogpost-watcher.service
~/.config/systemd/user/notes-watcher.service
# Edit service to point to new script
nano ~/.config/systemd/user/notes-watcher.service
# Enable and start
systemctl --user daemon-reload
systemctl --user enable notes-watcher.service
systemctl --user start notes-watcher.service
Custom Commit Messages
To customize commit messages, edit auto-commit.sh:
nano ~/blogposts/auto-commit.sh
# Find the COMMIT_MSG line and modify:
COMMIT_MSG="Auto-save: $(date '+%Y-%m-%d %H:%M:%S')"
# Or
COMMIT_MSG="📝 Updated $(git diff --name-only --cached | wc -l) file(s)"
# Or include changed filenames
CHANGED_FILES=$(git diff --name-only --cached | tr 'n' ', ')
COMMIT_MSG="Auto-commit: Updated $CHANGED_FILES"
Exclude Specific File Patterns
To exclude additional files from being watched:
nano ~/blogposts/watch-and-commit.sh
# Find the --exclude line and add patterns:
--exclude '(.git|.swp|.log$|~$|.DS_Store|drafts/.*|private/.*)'
Common patterns to exclude:
drafts/.*– Ignore drafts folderprivate/.*– Ignore private folder.*.draft.md– Ignore files ending in .draft.mdTODO.md– Ignore specific files
Enable Linger (Run After Logout)
By default, user services stop when you log out. To keep them running:
# Enable linger for your user
sudo loginctl enable-linger $USER
# Now the service runs even after logout!
Troubleshooting
Issue: Service Not Starting
Check service status:
systemctl --user status blogpost-watcher.service
Common causes:
- Script path wrong: Verify paths in service file match your actual paths
- Script not executable:
chmod +x ~/blogposts/watch-and-commit.sh - inotify-tools not installed:
sudo apt install inotify-tools
Issue: Changes Not Being Committed
Check if watcher is running:
systemctl --user status blogpost-watcher.service
View logs for errors:
journalctl --user -u blogpost-watcher.service --since "1 hour ago" --no-pager
Test auto-commit script manually:
# Make a change
echo "test" >> ~/blogposts/test.md
# Run script
~/blogposts/auto-commit.sh
# Check for errors
Common causes:
- Git authentication failed: Test
git pushmanually - File excluded by .gitignore: Check
.gitignorepatterns - Debounce timer active: Wait 5+ seconds after last change
Issue: Push to GitHub Fails
Test authentication:
# For SSH
ssh -T git@github.com
# For HTTPS
git push origin main # Should not prompt for password
Fix SSH authentication:
# Generate SSH key if needed (you will be prompted for a passphrase -
# use one for security, especially on shared/multi-user systems)
ssh-keygen -t ed25519 -C "your-email@example.com"
# Start ssh-agent and add your key (avoids re-entering passphrase)
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
# Add to GitHub
cat ~/.ssh/id_ed25519.pub
# Copy output and add to GitHub Settings → SSH Keys
Fix HTTPS authentication:
# Cache credentials in memory for 1 hour (safer than 'store' which saves in plaintext)
git config --global credential.helper 'cache --timeout=3600'
# Next push will prompt for credentials, then cache them in memory
Security note: Avoid
credential.helper store— it writes your GitHub credentials to~/.git-credentialsin plaintext. On shared or multi-user systems, any user who can read that file gets your GitHub access. Usecache(memory-only) or SSH keys instead.
Issue: Too Many Commits
If you’re getting commits too frequently:
- Increase debounce time: Edit
DEBOUNCE_SECONDSin watcher script - Switch to scheduled commits: Use cron instead (see Alternative Approaches below)
- Manually commit instead: Disable service and run
auto-commit.shwhen ready
Issue: Service Stops After Logout
Enable linger:
sudo loginctl enable-linger $USER
Issue: High CPU Usage
Check inotify watch limit:
# View current limit
cat /proc/sys/fs/inotify/max_user_watches
# If watching many files, increase limit
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Alternative Approaches
Option 1: Cron-Based (Scheduled)
If real-time commits are too frequent, use cron for scheduled commits:
# Edit crontab
crontab -e
# Add line to run every hour (with SSH agent for key-based auth)
0 * * * * export HOME=$(getent passwd $(whoami) | cut -d: -f6) && export PATH=/usr/local/bin:/usr/bin:/bin:$PATH && eval "$(ssh-agent -s)" > /dev/null && ssh-add $HOME/.ssh/id_ed25519 2>/dev/null && $HOME/blogposts/auto-commit.sh >> $HOME/blogposts/cron.log 2>&1
# Or every 30 minutes
*/30 * * * * export HOME=$(getent passwd $(whoami) | cut -d: -f6) && $HOME/blogposts/auto-commit.sh >> $HOME/blogposts/cron.log 2>&1
# Or daily at 6 PM
0 18 * * * export HOME=$(getent passwd $(whoami) | cut -d: -f6) && $HOME/blogposts/auto-commit.sh >> $HOME/blogposts/cron.log 2>&1
Note: Cron runs in a minimal environment where
$HOME,PATH, and SSH agent may not be available. The examples above set these explicitly. If using SSH authentication, the hourly example shows how to startssh-agentin the cron context.
Pros: Batches changes, predictable schedule
Cons: Not real-time, might lose work between runs
Option 2: Git Hooks
Use Git’s post-save hooks (requires editor integration):
# Create post-commit hook
cat > ~/blogposts/.git/hooks/post-commit << 'EOF'
#!/bin/bash
git push origin main
EOF
chmod +x ~/blogposts/.git/hooks/post-commit
Pros: Pushes after every manual commit
Cons: Requires manual commits, no automation
Option 3: Editor Integration
Many editors can run commands on save:
VS Code (settings.json):
{
"runOnSave.commands": [
{
"match": ".*\.md$",
"command": "~/blogposts/auto-commit.sh",
"runIn": "terminal"
}
]
}
Vim (.vimrc):
autocmd BufWritePost ~/blogposts/* !~/blogposts/auto-commit.sh
Security Considerations
Sensitive Files
Never auto-commit:
- API keys or credentials
- Private personal information
- Files with secrets
Our .gitignore already blocks common secret file patterns (.env, *.pem, *.key, credentials*, etc.), and the auto-commit.sh script includes a pre-commit check that aborts if it detects staged files matching secret patterns. These are defense-in-depth layers — if one fails, the other catches it.
To add more exclusions:
echo "my-custom-secret.yaml" >> ~/blogposts/.gitignore
Private Repositories
For sensitive content, use private GitHub repositories:
- Create repository as Private on GitHub
- Only you can see the content
- Auto-commits still work the same way
SSH Key Security
Best practices:
- Use passphrase-protected SSH keys
- Use
ssh-agentto avoid entering passphrase repeatedly - Limit SSH key permissions:
chmod 600 ~/.ssh/id_ed25519 - Different SSH keys for different purposes
Performance Considerations
Resource Usage
The watcher service is extremely lightweight:
- CPU: Nearly 0% when idle, <1% during commits
- Memory: ~5-10 MB
- Disk I/O: Minimal (only during actual commits)
Scalability
This system works well for:
- ✅ Hundreds of small files (< 1 MB each)
- ✅ Frequent small changes
- ✅ Text files (markdown, code, configs)
Not recommended for:
- ❌ Large binary files (videos, images > 10 MB)
- ❌ Thousands of files changing simultaneously
- ❌ Generated build output (node_modules, dist/)
Git Repository Size
Monitor repository size:
cd ~/blogposts
git count-objects -vH
If repository gets too large:
# Clean up old commits (advanced - be careful!)
git gc --aggressive --prune=now
Real-World Use Cases
Use Case 1: Daily Blog Writing
Scenario: Writing technical blog posts daily.
Configuration:
- Debounce: 10 seconds (allows for thought between paragraphs)
- Excludes:
drafts/folder for work-in-progress - Result: Every blog post automatically backed up within seconds
Use Case 2: Configuration Management
Scenario: Managing dotfiles and system configs.
Setup:
# Watch home directory configs
WATCH_DIR="$HOME"
# Only monitor specific files
--include '(.bashrc|.vimrc|.gitconfig)'
Result: Configuration changes automatically version controlled
Use Case 3: Note-Taking System
Scenario: Using Obsidian or similar markdown notes.
Configuration:
- Watch Obsidian vault directory
- Debounce: 30 seconds (longer gaps between note sessions)
- Exclude:
.obsidian/folder (Obsidian metadata)
Result: Personal knowledge base continuously backed up
Use Case 4: Documentation Wiki
Scenario: Team wiki or documentation site.
Setup:
- Private GitHub repository
- Multiple users with SSH keys
- Each user runs their own watcher
Result: Distributed documentation with automatic sync
Comparison with Other Solutions
vs. Manual Git Commits
| Feature | Auto-Commit | Manual |
|---|---|---|
| Effort | Zero | High |
| Frequency | Real-time | When you remember |
| Commit Quality | Generic messages | Descriptive messages |
| Best For | Personal projects | Collaborative projects |
vs. Cloud Sync (Dropbox, Google Drive)
| Feature | Auto-Commit | Cloud Sync |
|---|---|---|
| Version History | Full Git history | Limited versions |
| Branching | Yes | No |
| Offline Work | Yes | Limited |
| Conflicts | Git merge | Auto-resolve (may lose data) |
| Cost | Free (GitHub) | Paid tiers |
vs. Backup Software
| Feature | Auto-Commit | Backup Software |
|---|---|---|
| Granularity | File-level | Often full disk |
| Restoration | Any commit | Backup points |
| Remote Access | GitHub (anywhere) | Backup location only |
| Speed | Instant | Scheduled |
Conclusion
You’ve now built a fully automated Git commit system that:
- ✅ Watches files for changes in real-time
- ✅ Automatically commits with timestamps
- ✅ Pushes to GitHub within seconds
- ✅ Runs as a background service
- ✅ Restarts automatically if it crashes
- ✅ Detects and blocks accidental secret commits
- ✅ Prevents concurrent commit race conditions
- ✅ Logs all activity via journald for troubleshooting
Key Takeaways
- inotify is powerful: Linux’s filesystem monitoring is efficient and reliable
- Debouncing matters: Prevents commit spam during active editing
- Systemd is your friend: User services make background tasks simple
- Automation reduces friction: Remove barriers to backing up your work
Next Steps
Now that you have this working, consider:
- Set up multiple watchers: Different directories for different projects
- Customize commit messages: Add more context about what changed
- Monitor via notifications: Send alerts when commits happen
- Integrate with CI/CD: Trigger builds or deployments on commits
Further Resources
Questions or Issues? The most common problems are:
- Authentication (ensure SSH key is added to GitHub)
- Service not starting (check paths in service file)
- Too many commits (increase debounce time)
Happy auto-committing! 🚀
This guide was written based on a real-world implementation for a personal blog post repository. All configurations have been tested and verified working on Ubuntu 22.04 with Git 2.34+ and inotify-tools 3.22+.