8000
Skip to content

Repository files navigation

clearedforpush simulating a merge before a push: a clean branch clears for takeoff, a conflicting one is held back with exit code 1

Cleared for Push

Know before you push. Catch merge conflicts locally in about a second, instead of 20 minutes later in CI.

Crates.io CI Downloads License

Install  ·  Usage  ·  Git hook  ·  Configuration  ·  How it works  ·  FAQ


Why

Conflicts almost never surface when you create them. They surface after the push, in a CI log or a reviewer's comment, once the code has gone cold and a teammate is already blocked. A two-minute fix turns into a thirty-minute detour.

clearedforpush simulates the merge before you push and tells you immediately.

Clear

╭──────────────────────────────╮
│      CLEAR FOR TAKEOFF       │
│                              │
│  No conflicts. Safe to push! │
╰──────────────────────────────╯

Exit code 0. Push with confidence.

Conflicts ahead

╭──────────────────────────────╮
│  HOLD FOR CLEARANCE          │
│                              │
│  Conflicts detected.         │
╰──────────────────────────────╯
  ✗ src/auth.rs
  ✗ src/main.rs

Exit code 1. Fix it while it's fresh.

It is strictly read-only. Your working directory, index, and branches are never touched.

Install

Linux and macOS

curl -fsSL https://raw.githubusercontent.com/sanjayrohith/clearedforpush/main/scripts/install.sh | sh

Windows (PowerShell)

irm https://raw.githubusercontent.com/sanjayrohith/clearedforpush/main/scripts/install.ps1 | iex

No Rust toolchain required. The installer detects your platform, installs to ~/.local/bin (or %LOCALAPPDATA%\Programs\clearedforpush on Windows), and tells you if that directory is not on your PATH.

With Cargo, or from source
cargo install clearedforpush
git clone https://github.com/sanjayrohith/clearedforpush
cd clearedforpush
cargo install --path .

cargo install places the binary in ~/.cargo/bin. If clearedforpush: command not found follows a successful install, that directory is not on your PATH:

echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.bashrc   # bash
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.zshrc    # zsh
fish_add_path "$HOME/.cargo/bin"                           # fish
Installer options
Variable Default Purpose
CFP_VERSION latest release Install a specific tag, e.g. v0.1.1
CFP_INSTALL_DIR ~/.local/bin Install somewhere else
curl -fsSL .../install.sh | CFP_INSTALL_DIR="$HOME/bin" sh
$env:CFP_INSTALL_DIR = "D:\tools"; irm .../install.ps1 | iex

Prefer to read before you run? See install.sh and install.ps1.

Manual download

Grab an archive from Releases, extract it, and move the binary onto your PATH.

Platform Asset
Linux (x86_64) clearedforpush-vX.Y.Z-x86_64-unknown-linux-musl.tar.gz
macOS (Apple Silicon) clearedforpush-vX.Y.Z-aarch64-apple-darwin.tar.gz
macOS (Intel) clearedforpush-vX.Y.Z-x86_64-apple-darwin.tar.gz
Windows (x86_64) clearedforpush-vX.Y.Z-x86_64-pc-windows-msvc.zip

On Linux prefer the musl archive. It is statically linked and runs on any distro regardless of glibc version.

On macOS, a binary downloaded through a browser is quarantined by Gatekeeper and will refuse to open. Clear the flag with xattr -d com.apple.quarantine clearedforpush, or use the one-line installer, which handles it for you.

Requires Git 2.38 or newer, which introduced git merge-tree --write-tree. Check with git --version.

Usage

cd your-repo
clearedforpush check

That is the whole thing. If a conflict exists you get the exact file list and a non-zero exit code.

Command What it does
check Conflict detection against the auto-detected base, PR-aware
check --base develop Check against a specific base branch
check --stats Add ahead/behind counts, files changed, line diffs
check --diff Print the conflicting diff hunks
check --skip-prs Skip checking against open pull requests
check --format <fmt> Emit text, json, or compact
install-hook [--force] Install the pre-push hook
uninstall-hook Remove the pre-push hook
init Write a .clearedforpush.toml template

Statistics

clearedforpush check --stats
clearedforpush check --stats
Symbol Meaning
Commits you are ahead of base
Commits base is ahead of you
Files changed
± Insertions and deletions

In CI and scripts

Exit codes are stable: 0 when clean, 1 when conflicts exist.

clearedforpush check && git push

--format json emits a versioned schema suited to parsing:

{
  "version": 1,
  "current_branch": "feature-x",
  "base_branch": "main",
  "has_conflicts": false,
  "exit_code": 0,
  "conflicted_files": [],
  "conflict_diffs": [],
  "stats": { "ahead": 3, "behind": 1, "files_changed": 5 },
  "pr_conflicts": []
}

--format compact reduces it to one line: OK: no conflicts or CONFLICT: file1.rs, file2.rs.

Git hook

clearedforpush install-hook

Every push now runs a conflict check first, and a push carrying conflicts is blocked.

git push --no-verify     # bypass when you need to

An existing pre-push hook is never clobbered. You get a warning, and --force chains onto it rather than overwriting:

clearedforpush install-hook --force
clearedforpush uninstall-hook      # removes only our section
clearedforpush install-hook

Configuration

clearedforpush init
# Base branch, auto-detected when unset
base = "develop"

# Check open PRs for conflicts (default: true)
check_prs = true

# Default output format: "text", "json", or "compact"
format = "text"

stats = true
diff = false

# Paths to ignore when reporting conflicts
ignore = ["*.lock", "docs/**", "*.generated.*"]

[github]
# Alternative to the GITHUB_TOKEN env var
token = "ghp_..."

CLI flags always override config-file values.

How it works

clearedforpush shells out to Git's own merge-tree --write-tree plumbing to simulate the merge in memory. Nothing on disk is modified.

sequenceDiagram
    autonumber
    participant You
    participant CFP as clearedforpush
    participant Git

    You->>CFP: clearedforpush check
    CFP->>Git: detect current and base branch
    CFP->>Git: fetch base branch (read-only)
    CFP->>Git: merge-tree --write-tree
    Git-->>CFP: simulated merge tree
    CFP->>CFP: parse conflicts, check open PRs
    CFP-->>You: CLEAR (exit 0) or HOLD (exit 1)
Loading

Why not git merge --no-commit? That mutates your index and can strand you in a half-merged state. The lower-level plumbing leaves the repository provably untouched: no working-directory changes, no index writes, no branch updates, no stashing.

FAQ

Does it modify my repository?
No. It only reads. Your working directory, index, HEAD, and branches are untouched.
What Git version do I need?
Git 2.38.0 or later (October 2022), which introduced --write-tree for merge-tree.
Can I check against a branch other than main?
Yes. clearedforpush check --base develop works with any branch, and you can set base in .clearedforpush.toml to make it the default.
Does it need network access?
It fetches the base branch from origin so you compare against current remote state, so yes for check. Pull request awareness additionally needs either the gh CLI authenticated or a GITHUB_TOKEN; use --skip-prs to turn it off.
Is it fast enough to run on every push?
Yes. It is designed to finish in under two seconds on typical repositories, which is why the pre-push hook is practical.
Can I use it in CI?
Yes. Use --format json for a stable, versioned schema, or rely on the exit codes in a shell script.

Roadmap

  • Core conflict detection
  • Branch statistics
  • Pre-push hook integration
  • GitHub pull request awareness
  • Diff hunks, JSON and compact output
  • .clearedforpush.toml configuration
  • Prebuilt binaries and one-line installers
  • GitHub Actions and GitLab CI templates

Contributing

Contributions are welcome. See CONTRIBUTING.md to get started.

License

Licensed under either of MIT or Apache-2.0, at your option.


Report a bug  ·  Request a feature


Clear skies and clean merges.

About

Catch merge conflicts before you push.ClearedForPush tells you about merge conflicts before you push. Not during CI, not in PR review. Locally, in under 2 seconds.

Topics

Resources

Contributing

Stars

10 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

0