watch-and-commit 1.0.0

A CLI tool that automatically stages and commits file changes using a filesystem watcher
# Async Git Auto-Commit File Watcher

A Rust-based file system watcher that automatically commits (and optionally pushes) changes to a Git repository whenever files are modified. Built with `tokio` for async I/O and `notify` for cross-platform file system events, it uses a debouncing strategy to batch rapid successive changes into a single commit.

---

## ✨ Features

- **Recursive file watching** — monitors the current directory and all subdirectories.
- **Smart debouncing** — waits for file system "event storms" to settle (default: 3 seconds) before committing, avoiding noisy commits during bulk operations like `npm install` or code formatting.
- **Auto-commit & push** — stages all changes, commits with a default message, and pushes to the configured remote (if any).
- **Pre-flight sanity checks** — verifies the repository is clean and in sync with its upstream before starting.
- **Bare repo auto-init** — if `GIT_DIR` and `GIT_WORK_TREE` are set and missing, they are created and initialised automatically.
- **Filters noise** — ignores `Access` events which would otherwise trigger unnecessary commits.

---

## 📦 Requirements

- [Rust]https://www.rust-lang.org/ (1.70+ recommended) with Cargo
- `git` available on your `PATH`
- A Git repository (or the ability to create one via `GIT_DIR`/`GIT_WORK_TREE` environment variables)

---

## 🚀 Installation

Clone the repository and build:

```bash
git clone <your-repo-url>
cd <your-repo>
cargo build --release
```

The compiled binary will be available at `target/release/<binary-name>`.

### Dependencies

This project uses the following crates (add to your `Cargo.toml` if setting up from scratch):

```toml
[dependencies]
tokio = { version = "1", features = ["full"] }
notify = "6"
```

---

## 🏃 Usage

### Basic usage

From inside any Git repository:

```bash
cargo run --release
```

The watcher will:
1. Run pre-flight checks on the current working directory.
2. Start monitoring the current directory (`.`) recursively.
3. Debounce events and auto-commit whenever the dust settles.

Press **Ctrl+C** to stop.

### Using a detached work tree

You can watch a directory while storing the Git metadata in a separate bare repository by setting the standard Git environment variables:

```bash
export GIT_DIR=/path/to/bare/repo.git
export GIT_WORK_TREE=/path/to/watched/files
cargo run --release
```

If either directory does not exist, it will be created, and a bare repository will be initialised at `GIT_DIR` automatically.

---

## 🔄 How It Works

```
┌─────────────────┐    events     ┌──────────────┐    batched    ┌───────────────┐
│  notify Watcher │──────────────▶│  Debouncer   │──────────────▶│ Event Handler │
│  (file system)  │   (mpsc tx)   │ (tokio task) │  (3s window)  │  + git commit │
└─────────────────┘               └──────────────┘               └───────────────┘
```

1. **`notify::RecommendedWatcher`** produces events for any change in the watched directory.
2. Events are sent over a bounded `tokio::sync::mpsc` channel (capacity: 100).
3. The **debouncer** receives the first event, then keeps collecting events until a quiet period of `debounce_duration` (3 seconds) has elapsed.
4. The accumulated batch is handed off to the **event handler**, which:
   - Logs each event.
   - Runs `git add .`.
   - Checks if anything was actually staged (skips empty commits).
   - Runs `git commit -m "Update"`.
   - Runs `git push` (only if a remote is configured).

---

## 🛡️ Pre-flight Checks

Before the watcher starts, it runs a series of checks to ensure a sane starting state:

| # | Check                                                             | Failure behaviour |
|---|-------------------------------------------------------------------|-------------------|
| 0 | Auto-create work tree & init bare repo if env vars are set        | Errors if creation fails |
| 1 | Current directory is inside a Git work tree                       | Aborts            |
| 2 | No staged or unstaged tracked changes                             | Aborts            |
| 3 | No untracked (non-ignored) files                                  | Aborts (shows up to 10 files) |
| 4 | `git fetch` succeeds (only if a remote is configured)             | Aborts            |
| 5 | `HEAD` matches upstream tracking branch (ahead/behind/diverged)   | Aborts with details |

If no remote is configured, sync checks are skipped with a friendly notice.

---

## ⚙️ Configuration

Currently, configuration is done by editing `src/main.rs`:

- **Debounce window**`Duration::from_secs(3)` in `main()`.
- **Watched path**`Path::new(".")` in `main()`.
- **Commit message**`"Update"` in `src/event_handler.rs::run_git_commit`.
- **Channel capacity**`mpsc::channel(100)` in `main()`.

---

## 📂 Project Structure

```
src/
├── main.rs            # Entry point, pre-flight checks, watcher setup
├── debouncer.rs       # Async debouncing logic over an mpsc::Receiver
└── event_handler.rs   # Event pretty-printing and git operations
```

---

## ⚠️ Caveats

- **Force-commits everything**: `git add .` stages _all_ changes in the work tree. Use a good `.gitignore`.
- **Fixed commit message**: every commit is `"Update"`. No change summarisation (yet).
- **No rate limiting on push**: if you make rapid-fire edits on a slow connection, `git push` may queue up.
- **Not recommended for public-facing branches**: this tool is designed for personal notes, scratch repos, and "save every change" workflows — not for shared production branches.

---

## 🧪 Example Output

```
--- Async File Watcher with 3-Second Debounce ---
Fetching from remote...
Fetch complete.
✅ Repository is clean and in sync with 'origin/main'.
Monitoring changes in: '.'
Press Ctrl+C to exit.
-> Event received. Starting debounce timer...
-> Event received. Resetting debounce timer...

=======================================================
✅ DEBOUNCED ACTION! Processing 2 events...
=======================================================
[MODIFY] File content changed: ./notes.md
[MODIFY] File content changed: ./notes.md
-------------------------------------------------------
🚀 Executing git auto-commit...
-> Running: git add .
[SUCCESS] Staged changes.
-> Running: git commit -m "Update"
[SUCCESS] Committed changes:
[main a1b2c3d] Update
 1 file changed, 3 insertions(+)
-> Running: git push
[SUCCESS] Pushed changes.
-------------------------------------------------------
```

---

## 📄 License

Add your preferred license here (e.g. MIT, Apache-2.0).

---

## 🙏 Acknowledgements

- [`notify`]https://crates.io/crates/notify — cross-platform file system notifications.
- [`tokio`]https://tokio.rs/ — asynchronous runtime for Rust.