angler 0.1.0

Efficient git hooks management
# (WIP) Angler: Manage git hooks and run them fast

## Plan

- [x] Order steps below
- [ ] Simple pre-commit hook
- [ ] Shell script hook
- [ ] `xargs` on staged files
- [ ] Multiple hooks (sequential)
- [ ] Rust-based hook (system version)
- [ ] From repository
- [ ] YAML config
- [ ] `install` and `run` commands
- [ ] Store
- [ ] `uninstall`
- [ ] Python-based hook (system version)
- [ ] Python-based hook (dedicated venv)
- [ ] Multithread `xargs`
- [ ] Display
- [ ] More hook types
- [ ] Multithread hooks (non-modifying)
- [ ] Lock config
- [ ] More languages

## Key features

- Fast
  - Rust binary, no intermediate VM
  - Concurrency across and within hooks
  - Pre-computed exact commands to run
- Simple
  - Human-readable expansion of each hook
  - Local / global install
  - No need for a repo-side manifest
  - Display only failing hooks
  - Monorepo support (?) - Check pre-commit issues
  - Sourceable multi-part config (?) - Check pre-commit issues on the matter
  - CLI add/remove/stats + autocompletion (?)

## Parallel execution

### First round: full parallelism

#### Snapshot Stage

- Take a snapshot of all files to be modified before any hooks run
- This serves as the "base state" that can be restored if needed

#### Isolated Execution Environment

- Each hook operates on a temporary copy of the files
- Modifications are made to these copies, not the original files
- This prevents partial modifications from being visible to other hooks

#### Change Recording

- Track all modifications made by each hook
- Store these as "proposed changes" rather than applying them directly

#### Validation Phase

- After a hook completes, validate that its changes are compatible with changes from other hooks
- Detect potential conflicts (same line modified differently by multiple hooks)

### Second round: Sequential Fallback

When conflicts are detected in parallel execution, rather than simply failing or discarding all changes, fall back to sequential.
This preserves the automatic fixing behavior users expect.

### Smart Ordering

- Learn from conflict patterns to optimize future runs
- Store information about which hooks conflict with each other
- Automatically adjust execution strategy on subsequent runs

## Similar tools

### Husky

```
package.json    # Modified on running `husky init` to auto-install `husky`
.husky/
    .gitignore      # Initialized with `*`
    pre-commit      # Human-writable shell script, initialized with `npm test`
    _/              # Configured git.config.core.hooksPath
        h               # Runs `.husky/<script>` if it exists
        pre-commit      # Only runs `./husky/_/h`, auto-created
        pre-push        # Only runs `./husky/_/h`, auto-created
```

- Configures core hooks path to `.husky/_/`
- Writes dummy files for all possible hooks
- Redirects all git hooks to a single dispatcher `h`, that runs the appropriate human-written script
- Purely shell-based
- Can be disabled using the `HUSKY` environment variable

### Overcommit

TODO

### Pre-commit

```
# project-level
pre-commit-config.yaml
.git/hooks/
    pre-commit      # Templated, runs `<python> -m pre-commit hook-impl <config> type=pre-commit`
    pre-push        # Templated, runs `<python> -m pre-commit hook-impl <config> type=pre-push`
```

```
# global
~/.cache/pre-commit/
    db.db                       # Store, sqlite DB, tables `repos` [repo, ref, path] and `configs`
    repo-<cloned_repo_id>/      # Cached across configs, referenced in the Store
        <repo_content>              # Cloned from source
        .pre-commit-hooks.yaml      # Manifest describing how to run the hook, part of the source
        <venv>                      # Contains hook dependencies, created during install
```

- Structure
  - Git hook-level templated block that invokes a dispatcher
  - Project-level YAML config
  - Global-level cached repo hooks + venv
  - Global-level `config -> repos -> path` store for cleanup
  - Repo-level manifest (for hook-defining repos)
- Support
  - Many languages
  - Hooks can require additional dependencies
  - Extensible
  - Hook-defining repos must expose a manifest
  - Local hooks possible
- Install
  - Global cache is populated
  - Adds templated redirects to a shared dispatcher
  - Auto-install possible with template-dir
- Run
  - Dispatcher `<python> -m pre-commit hook-impl <config> type=<hook-type>` runs
    - Python process
    - Loads config, finds hooks
    - Installs missing hooks
    - Builds hook commands
    - Runs hooks within subprocesses (Python `xargs`)
  - Concurrency
    - `xargs` runs in multithreading
    - Serial execution can be explicitly required
  - Directly invokable with `pre-commit run`
  - Targeted skips possible with `SKIP=` environment variable