Save before creating restore point: Before implementing auto-commit system
Auto-saved at 2025-07-29 14:24:49 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
174
AUTO-COMMIT-README.md
Normal file
174
AUTO-COMMIT-README.md
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
# Auto-Commit System for Timeshift Project
|
||||||
|
|
||||||
|
This system automatically saves your work to git to prevent losing changes when Claude gets restarted or when making experimental changes.
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### Start Auto-Watch (Recommended)
|
||||||
|
```bash
|
||||||
|
npm run watch-commit
|
||||||
|
```
|
||||||
|
This will automatically commit changes every 30 seconds when files are modified.
|
||||||
|
|
||||||
|
### Manual Commands
|
||||||
|
```bash
|
||||||
|
# Save current work immediately
|
||||||
|
npm run save "Your commit message"
|
||||||
|
|
||||||
|
# Create a restore point before major changes
|
||||||
|
npm run restore-point "Before major refactor"
|
||||||
|
|
||||||
|
# List all restore points
|
||||||
|
./auto-commit.sh list
|
||||||
|
|
||||||
|
# Clean old restore points (keeps last 10)
|
||||||
|
./auto-commit.sh cleanup
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 How It Works
|
||||||
|
|
||||||
|
### Automatic Commits
|
||||||
|
- **File Watcher**: Monitors `components/`, `app/`, `lib/`, `styles/` directories
|
||||||
|
- **Smart Delay**: Waits 30 seconds after last change before committing
|
||||||
|
- **Rate Limiting**: Max 10 commits per hour to avoid spam
|
||||||
|
- **File Types**: Watches `.tsx`, `.ts`, `.js`, `.jsx`, `.css`, `.scss`, `.json` files
|
||||||
|
|
||||||
|
### Restore Points
|
||||||
|
- **Branches**: Creates timestamped branches like `restore-point-20250129-143022`
|
||||||
|
- **Automatic**: Creates restore points on startup and every 30 minutes
|
||||||
|
- **Manual**: Create before risky changes with `npm run restore-point "description"`
|
||||||
|
|
||||||
|
## 🔧 Features
|
||||||
|
|
||||||
|
### Auto-Commit Script Features
|
||||||
|
✅ Automatic timestamped commits
|
||||||
|
✅ Descriptive commit messages
|
||||||
|
✅ Rate limiting to prevent spam
|
||||||
|
✅ Restore point creation
|
||||||
|
✅ Cleanup of old restore points
|
||||||
|
✅ Git status checking
|
||||||
|
|
||||||
|
### File Watcher Features
|
||||||
|
✅ Real-time file monitoring
|
||||||
|
✅ Smart batching of changes
|
||||||
|
✅ Graceful shutdown handling
|
||||||
|
✅ Periodic restore points
|
||||||
|
✅ Console logging for transparency
|
||||||
|
|
||||||
|
## 📚 Usage Examples
|
||||||
|
|
||||||
|
### Starting Your Work Session
|
||||||
|
```bash
|
||||||
|
# Start the auto-commit watcher
|
||||||
|
npm run watch-commit
|
||||||
|
|
||||||
|
# In another terminal, start development
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Before Making Risky Changes
|
||||||
|
```bash
|
||||||
|
# Create a restore point
|
||||||
|
npm run restore-point "Before implementing new feature"
|
||||||
|
|
||||||
|
# Make your changes...
|
||||||
|
# Files are auto-committed as you work
|
||||||
|
|
||||||
|
# If something breaks, restore with:
|
||||||
|
git checkout restore-point-20250129-143022
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Saves
|
||||||
|
```bash
|
||||||
|
# Quick save
|
||||||
|
npm run save
|
||||||
|
|
||||||
|
# Save with custom message
|
||||||
|
npm run save "Fixed spreadsheet column alignment"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Managing Restore Points
|
||||||
|
```bash
|
||||||
|
# List all restore points
|
||||||
|
./auto-commit.sh list
|
||||||
|
|
||||||
|
# Clean up old ones (keeps last 10)
|
||||||
|
./auto-commit.sh cleanup
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Benefits
|
||||||
|
|
||||||
|
1. **Never Lose Work**: Changes are saved every 30 seconds automatically
|
||||||
|
2. **Easy Recovery**: Restore points let you go back to working states
|
||||||
|
3. **Experiment Safely**: Try changes knowing you can always revert
|
||||||
|
4. **Claude Restarts**: No more losing progress when Claude session ends
|
||||||
|
5. **Version History**: Full git history of all changes with timestamps
|
||||||
|
|
||||||
|
## ⚙️ Configuration
|
||||||
|
|
||||||
|
### Modify Watch Settings
|
||||||
|
Edit `watch-and-commit.js`:
|
||||||
|
```javascript
|
||||||
|
const COMMIT_DELAY = 30000; // 30 seconds
|
||||||
|
const MAX_COMMITS_PER_HOUR = 10;
|
||||||
|
const WATCH_DIRS = ['components', 'app', 'lib', 'styles'];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modify Auto-Commit Behavior
|
||||||
|
Edit `auto-commit.sh` to change commit message format or add custom logic.
|
||||||
|
|
||||||
|
## 🛟 Recovery Scenarios
|
||||||
|
|
||||||
|
### Claude Gets Restarted
|
||||||
|
Your work is automatically saved! Just continue from where you left off.
|
||||||
|
|
||||||
|
### Experimental Changes Break Things
|
||||||
|
```bash
|
||||||
|
# List restore points to find a good one
|
||||||
|
./auto-commit.sh list
|
||||||
|
|
||||||
|
# Checkout the restore point
|
||||||
|
git checkout restore-point-20250129-143022
|
||||||
|
|
||||||
|
# Or continue from main with your auto-commits
|
||||||
|
git checkout main
|
||||||
|
git log --oneline -10 # See recent auto-commits
|
||||||
|
```
|
||||||
|
|
||||||
|
### Need to Go Back a Few Commits
|
||||||
|
```bash
|
||||||
|
# See recent commits
|
||||||
|
git log --oneline -10
|
||||||
|
|
||||||
|
# Reset to a specific commit (keep files)
|
||||||
|
git reset --soft HEAD~3
|
||||||
|
|
||||||
|
# Or create a new branch from specific commit
|
||||||
|
git checkout -b fix-attempt abc1234
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚦 Status Indicators
|
||||||
|
|
||||||
|
The watcher shows:
|
||||||
|
- 🔍 **Started**: Auto-commit watcher is running
|
||||||
|
- 📝 **File changed**: A file was modified
|
||||||
|
- 🔄 **Performing auto-commit**: About to save changes
|
||||||
|
- ✅ **Auto-commit successful**: Changes saved
|
||||||
|
- 🔖 **Creating restore point**: Making a backup branch
|
||||||
|
- ⏸️ **Rate limit reached**: Too many commits, pausing
|
||||||
|
|
||||||
|
## 🎮 Quick Reference
|
||||||
|
|
||||||
|
| Command | Purpose |
|
||||||
|
|---------|---------|
|
||||||
|
| `npm run watch-commit` | Start automatic file watching |
|
||||||
|
| `npm run save` | Immediate manual commit |
|
||||||
|
| `npm run restore-point` | Create backup branch |
|
||||||
|
| `./auto-commit.sh list` | Show restore points |
|
||||||
|
| `./auto-commit.sh cleanup` | Remove old restore points |
|
||||||
|
| `git checkout restore-point-XXXXXX` | Restore to backup |
|
||||||
|
| `git log --oneline -10` | See recent commits |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Pro Tip**: Always run `npm run watch-commit` when starting work with Claude to ensure nothing gets lost! 🛡️
|
||||||
106
auto-commit.sh
Executable file
106
auto-commit.sh
Executable file
@@ -0,0 +1,106 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Auto-commit script for timeshift project
|
||||||
|
# Usage: ./auto-commit.sh [message]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Function to auto-commit changes
|
||||||
|
auto_commit() {
|
||||||
|
local message="$1"
|
||||||
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
# Check if there are any changes
|
||||||
|
if [[ -n $(git status --porcelain) ]]; then
|
||||||
|
echo "🔄 Auto-committing changes at $timestamp"
|
||||||
|
|
||||||
|
# Add all changes
|
||||||
|
git add .
|
||||||
|
|
||||||
|
# Create commit message
|
||||||
|
if [[ -z "$message" ]]; then
|
||||||
|
message="Auto-commit: Save work in progress"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Commit with timestamp
|
||||||
|
git commit -m "$(cat <<EOF
|
||||||
|
$message
|
||||||
|
|
||||||
|
Auto-saved at $timestamp
|
||||||
|
|
||||||
|
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||||
|
|
||||||
|
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
|
||||||
|
echo "✅ Changes committed successfully"
|
||||||
|
|
||||||
|
# Show short log
|
||||||
|
git log --oneline -3
|
||||||
|
else
|
||||||
|
echo "📝 No changes to commit at $timestamp"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to create a safe restore point
|
||||||
|
create_restore_point() {
|
||||||
|
local description="$1"
|
||||||
|
local branch_name="restore-point-$(date '+%Y%m%d-%H%M%S')"
|
||||||
|
|
||||||
|
echo "🔖 Creating restore point: $branch_name"
|
||||||
|
|
||||||
|
# Commit current changes first
|
||||||
|
auto_commit "Save before creating restore point: $description"
|
||||||
|
|
||||||
|
# Create a new branch as restore point
|
||||||
|
git branch "$branch_name"
|
||||||
|
|
||||||
|
echo "✅ Restore point created: $branch_name"
|
||||||
|
echo "📋 To restore later, run: git checkout $branch_name"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to show available restore points
|
||||||
|
list_restore_points() {
|
||||||
|
echo "📋 Available restore points:"
|
||||||
|
git branch | grep "restore-point-" | sed 's/^..//' | sort -r
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to clean old restore points (keep last 10)
|
||||||
|
cleanup_restore_points() {
|
||||||
|
echo "🧹 Cleaning old restore points (keeping last 10)..."
|
||||||
|
git branch | grep "restore-point-" | sed 's/^..//' | sort -r | tail -n +11 | while read branch; do
|
||||||
|
git branch -D "$branch" 2>/dev/null && echo "🗑️ Deleted old restore point: $branch"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script logic
|
||||||
|
case "$1" in
|
||||||
|
"commit")
|
||||||
|
auto_commit "$2"
|
||||||
|
;;
|
||||||
|
"restore-point")
|
||||||
|
create_restore_point "$2"
|
||||||
|
;;
|
||||||
|
"list")
|
||||||
|
list_restore_points
|
||||||
|
;;
|
||||||
|
"cleanup")
|
||||||
|
cleanup_restore_points
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "📚 Auto-commit utility for timeshift project"
|
||||||
|
echo ""
|
||||||
|
echo "Usage:"
|
||||||
|
echo " $0 commit [message] - Auto-commit current changes"
|
||||||
|
echo " $0 restore-point [desc] - Create a restore point branch"
|
||||||
|
echo " $0 list - List available restore points"
|
||||||
|
echo " $0 cleanup - Clean old restore points"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 commit 'Fixed spreadsheet layout'"
|
||||||
|
echo " $0 restore-point 'Before major refactor'"
|
||||||
|
echo ""
|
||||||
|
auto_commit "$1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -6,7 +6,11 @@
|
|||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
|
"auto-commit": "./auto-commit.sh",
|
||||||
|
"watch-commit": "node watch-and-commit.js",
|
||||||
|
"save": "./auto-commit.sh commit",
|
||||||
|
"restore-point": "./auto-commit.sh restore-point"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-dialog": "^1.1.14",
|
"@radix-ui/react-dialog": "^1.1.14",
|
||||||
|
|||||||
181
watch-and-commit.js
Executable file
181
watch-and-commit.js
Executable file
@@ -0,0 +1,181 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
|
||||||
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
const WATCH_DIRS = ['components', 'app', 'lib', 'styles'];
|
||||||
|
const WATCH_EXTENSIONS = ['.tsx', '.ts', '.js', '.jsx', '.css', '.scss', '.json'];
|
||||||
|
const COMMIT_DELAY = 30000; // 30 seconds delay before auto-commit
|
||||||
|
const MAX_COMMITS_PER_HOUR = 10;
|
||||||
|
|
||||||
|
class AutoCommitWatcher {
|
||||||
|
constructor() {
|
||||||
|
this.commitQueue = new Set();
|
||||||
|
this.commitTimeout = null;
|
||||||
|
this.commitsThisHour = 0;
|
||||||
|
this.lastCommitTime = 0;
|
||||||
|
|
||||||
|
// Reset commit counter every hour
|
||||||
|
setInterval(() => {
|
||||||
|
this.commitsThisHour = 0;
|
||||||
|
}, 3600000);
|
||||||
|
|
||||||
|
console.log('🔍 Auto-commit watcher started');
|
||||||
|
console.log(`📁 Watching directories: ${WATCH_DIRS.join(', ')}`);
|
||||||
|
console.log(`⏱️ Commit delay: ${COMMIT_DELAY/1000}s`);
|
||||||
|
console.log(`🚦 Max commits per hour: ${MAX_COMMITS_PER_HOUR}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
WATCH_DIRS.forEach(dir => {
|
||||||
|
const fullPath = path.join(process.cwd(), dir);
|
||||||
|
if (fs.existsSync(fullPath)) {
|
||||||
|
this.watchDirectory(fullPath);
|
||||||
|
console.log(`👀 Watching: ${fullPath}`);
|
||||||
|
} else {
|
||||||
|
console.log(`⚠️ Directory not found: ${fullPath}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watchDirectory(dirPath) {
|
||||||
|
try {
|
||||||
|
fs.watch(dirPath, { recursive: true }, (eventType, filename) => {
|
||||||
|
if (!filename) return;
|
||||||
|
|
||||||
|
const filePath = path.join(dirPath, filename);
|
||||||
|
const ext = path.extname(filename);
|
||||||
|
|
||||||
|
// Check if file extension is in our watch list
|
||||||
|
if (WATCH_EXTENSIONS.includes(ext)) {
|
||||||
|
this.handleFileChange(filePath, eventType);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Error watching directory ${dirPath}:`, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFileChange(filePath, eventType) {
|
||||||
|
const relativePath = path.relative(process.cwd(), filePath);
|
||||||
|
console.log(`📝 File changed: ${relativePath} (${eventType})`);
|
||||||
|
|
||||||
|
// Add to commit queue
|
||||||
|
this.commitQueue.add(relativePath);
|
||||||
|
|
||||||
|
// Clear existing timeout and set new one
|
||||||
|
if (this.commitTimeout) {
|
||||||
|
clearTimeout(this.commitTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.commitTimeout = setTimeout(() => {
|
||||||
|
this.performAutoCommit();
|
||||||
|
}, COMMIT_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
async performAutoCommit() {
|
||||||
|
// Rate limiting
|
||||||
|
if (this.commitsThisHour >= MAX_COMMITS_PER_HOUR) {
|
||||||
|
console.log(`⏸️ Rate limit reached (${MAX_COMMITS_PER_HOUR} commits/hour). Skipping auto-commit.`);
|
||||||
|
this.commitQueue.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if enough time has passed since last commit
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - this.lastCommitTime < COMMIT_DELAY) {
|
||||||
|
console.log('⏸️ Too soon since last commit. Skipping.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.commitQueue.size === 0) {
|
||||||
|
console.log('📋 No files in commit queue');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if there are actually changes to commit
|
||||||
|
const { stdout: status } = await execAsync('git status --porcelain');
|
||||||
|
if (!status.trim()) {
|
||||||
|
console.log('📝 No actual changes to commit');
|
||||||
|
this.commitQueue.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🔄 Performing auto-commit...');
|
||||||
|
|
||||||
|
// Create commit message with changed files
|
||||||
|
const changedFiles = Array.from(this.commitQueue).slice(0, 5); // Limit to 5 files in message
|
||||||
|
const fileList = changedFiles.join(', ');
|
||||||
|
const moreFiles = this.commitQueue.size > 5 ? ` and ${this.commitQueue.size - 5} more` : '';
|
||||||
|
|
||||||
|
const message = `Auto-save: Updated ${fileList}${moreFiles}`;
|
||||||
|
|
||||||
|
// Use our auto-commit script
|
||||||
|
await execAsync(`./auto-commit.sh commit "${message}"`);
|
||||||
|
|
||||||
|
console.log('✅ Auto-commit successful');
|
||||||
|
this.commitsThisHour++;
|
||||||
|
this.lastCommitTime = now;
|
||||||
|
this.commitQueue.clear();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Auto-commit failed:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createRestorePoint(description = 'Auto restore point') {
|
||||||
|
try {
|
||||||
|
console.log('🔖 Creating restore point...');
|
||||||
|
await execAsync(`./auto-commit.sh restore-point "${description}"`);
|
||||||
|
console.log('✅ Restore point created');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Failed to create restore point:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle process termination gracefully
|
||||||
|
process.on('SIGINT', async () => {
|
||||||
|
console.log('\n🛑 Stopping auto-commit watcher...');
|
||||||
|
|
||||||
|
// Perform final commit if there are pending changes
|
||||||
|
try {
|
||||||
|
const { stdout: status } = await execAsync('git status --porcelain');
|
||||||
|
if (status.trim()) {
|
||||||
|
console.log('💾 Saving final changes...');
|
||||||
|
await execAsync('./auto-commit.sh commit "Final auto-save before shutdown"');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error during shutdown save:', error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('👋 Auto-commit watcher stopped');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the watcher
|
||||||
|
const watcher = new AutoCommitWatcher();
|
||||||
|
watcher.start();
|
||||||
|
|
||||||
|
// Create restore point on startup
|
||||||
|
setTimeout(() => {
|
||||||
|
watcher.createRestorePoint('Session start');
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
// Create periodic restore points (every 30 minutes)
|
||||||
|
setInterval(() => {
|
||||||
|
watcher.createRestorePoint('Periodic backup');
|
||||||
|
}, 30 * 60 * 1000);
|
||||||
|
|
||||||
|
console.log('🚀 Auto-commit system ready! Press Ctrl+C to stop.');
|
||||||
|
console.log('📋 Commands available:');
|
||||||
|
console.log(' ./auto-commit.sh commit [message]');
|
||||||
|
console.log(' ./auto-commit.sh restore-point [description]');
|
||||||
|
console.log(' ./auto-commit.sh list');
|
||||||
|
console.log(' ./auto-commit.sh cleanup');
|
||||||
Reference in New Issue
Block a user