|
| 1 | +<h1 align="center">diffx</h1> |
| 2 | + |
| 3 | +<p align="center">A CLI utility for easily diffing Git changes across your working tree, local refs, remote refs, GitHub PRs, GitLab MRs, or any Git URLs.</p> |
| 4 | + |
| 5 | +## Quick Start |
| 6 | + |
| 7 | +### Install |
| 8 | + |
| 9 | +```bash |
| 10 | +# npm |
| 11 | +npm install -g @jaydenfyi/diffx |
| 12 | + |
| 13 | +# bun |
| 14 | +bun add -g @jaydenfyi/diffx |
| 15 | + |
| 16 | +# no install |
| 17 | +npx @jaydenfyi/diffx --help |
| 18 | +``` |
| 19 | + |
| 20 | +### Most useful commands |
| 21 | + |
| 22 | +```bash |
| 23 | +# 1) Show all current local changes (tracked + untracked) |
| 24 | +diffx |
| 25 | + |
| 26 | +# 2) Compare two refs |
| 27 | +diffx main..feature |
| 28 | + |
| 29 | +# 3) Diff a GitHub PR |
| 30 | +diffx https://github.com/owner/repo/pull/123 |
| 31 | + |
| 32 | +# 4) Generate an apply-able patch |
| 33 | +diffx main..feature --mode patch |
| 34 | + |
| 35 | +# 5) Quick status view |
| 36 | +diffx --name-status |
| 37 | +``` |
| 38 | + |
| 39 | +## `diffx` vs `git diff` |
| 40 | + |
| 41 | +| Capability | `diffx` | `git diff` | |
| 42 | +| ---------- | ------- | ---------- | |
| 43 | +| Full working tree snapshot (tracked + untracked) | ✅ | ❌ | |
| 44 | +| Direct GitHub PR and GitLab MR diffing | ✅ | ❌ | |
| 45 | +| Cross-remote and fork comparisons | ✅ | ❌ | |
| 46 | +| Include/exclude glob filtering | ✅ | ❌ | |
| 47 | +| `git diff` compatibility | ✅ | ✅ | |
| 48 | + |
| 49 | +## Command |
| 50 | + |
| 51 | +```bash |
| 52 | +diffx [range-or-url] [options] [-- <pathspec...>] |
| 53 | +``` |
| 54 | + |
| 55 | +Use `--index` for strict `git diff` compatibility (index vs worktree behavior). |
| 56 | + |
| 57 | +## Input Formats |
| 58 | + |
| 59 | +### No range argument (default behavior) |
| 60 | + |
| 61 | +```bash |
| 62 | +# Uses current worktree first (tracked + untracked) |
| 63 | +diffx |
| 64 | + |
| 65 | +# If there are no local changes, falls back to inferred base..HEAD |
| 66 | +``` |
| 67 | + |
| 68 | +### Local ranges |
| 69 | + |
| 70 | +```bash |
| 71 | +diffx main..feature |
| 72 | +diffx abc123..def456 |
| 73 | +diffx refs/heads/main..refs/tags/v1.0 |
| 74 | +``` |
| 75 | + |
| 76 | +### Remote shorthand ranges |
| 77 | + |
| 78 | +```bash |
| 79 | +diffx owner/repo@main..owner/repo@feature |
| 80 | +diffx owner/repo@main..feature |
| 81 | +``` |
| 82 | + |
| 83 | +### Git URL ranges (SSH/HTTPS, any host) |
| 84 | + |
| 85 | +```bash |
| 86 | +diffx git@github.com:owner/repo.git@main..feature |
| 87 | +diffx https://github.com/owner/repo.git@v1.0..v2.0 |
| 88 | +diffx git@github.com:owner/repo.git@main..git@gitlab.com:owner/fork.git@feature |
| 89 | +``` |
| 90 | + |
| 91 | +### GitHub refs and URLs |
| 92 | + |
| 93 | +```bash |
| 94 | +# GitHub ref shorthand |
| 95 | +diffx github:owner/repo@main..feature |
| 96 | + |
| 97 | +# PR reference |
| 98 | +diffx github:owner/repo#123 |
| 99 | + |
| 100 | +# PR URL |
| 101 | +diffx https://github.com/owner/repo/pull/123 |
| 102 | + |
| 103 | +# PR vs PR |
| 104 | +diffx github:owner/repo#123..github:owner/repo#456 |
| 105 | + |
| 106 | +# Commit URL |
| 107 | +diffx https://github.com/owner/repo/commit/abc123 |
| 108 | + |
| 109 | +# PR changes URL |
| 110 | +diffx https://github.com/owner/repo/pull/123/changes/abc123..def456 |
| 111 | + |
| 112 | +# Compare URL (same repo or cross-fork) |
| 113 | +diffx https://github.com/owner/repo/compare/main...feature |
| 114 | +diffx https://github.com/owner/repo/compare/main...other-owner:other-repo:feature |
| 115 | +``` |
| 116 | + |
| 117 | +### GitLab refs |
| 118 | + |
| 119 | +```bash |
| 120 | +# GitLab shorthand range |
| 121 | +diffx gitlab:owner/repo@main..feature |
| 122 | + |
| 123 | +# Merge request ref |
| 124 | +diffx gitlab:owner/repo!123 |
| 125 | +``` |
| 126 | + |
| 127 | +## Output Modes |
| 128 | + |
| 129 | +`diffx` defaults to `diff` mode. |
| 130 | + |
| 131 | +- `diff`: unified diff output. |
| 132 | +- `patch`: patch output (for `git apply` style use). |
| 133 | +- `stat`: per-file histogram + summary line. |
| 134 | +- `numstat`: tab-delimited additions/deletions per file. |
| 135 | +- `shortstat`: one summary line only. |
| 136 | +- `name-only`: changed filenames only. |
| 137 | +- `name-status`: status + filename (e.g. `M`, `A`, `D`, `U`). |
| 138 | +- `summary`: structural summary (`create mode`, `rename`, etc.), equivalent to `git diff --summary`. |
| 139 | + |
| 140 | +Examples: |
| 141 | + |
| 142 | +```bash |
| 143 | +diffx main..feature --mode patch |
| 144 | +diffx https://github.com/owner/repo/pull/123 --stat |
| 145 | +diffx --name-status |
| 146 | +``` |
| 147 | + |
| 148 | +## Filtering |
| 149 | + |
| 150 | +```bash |
| 151 | +# Include only TypeScript files |
| 152 | +diffx main..feature --include "src/**/*.ts" |
| 153 | + |
| 154 | +# Exclude tests |
| 155 | +diffx main..feature --exclude "**/*.test.ts" |
| 156 | + |
| 157 | +# Combine include + exclude |
| 158 | +diffx main..feature --include "src/**" --exclude "**/*.spec.ts" |
| 159 | + |
| 160 | +# Repeat include/exclude flags (matches any include; excludes any exclude) |
| 161 | +diffx --include "*.ts" --include "*.tsx" --exclude "*.js" --exclude "*.jsx" |
| 162 | +``` |
| 163 | + |
| 164 | +## Pager behavior |
| 165 | + |
| 166 | +- Diff/patch output auto-pages on TTY (git-like behavior). |
| 167 | +- Honors `GIT_PAGER`, `core.pager`, `PAGER`. |
| 168 | +- Use `--pager` to force paging. |
| 169 | +- Use `--no-pager` to disable paging. |
| 170 | + |
| 171 | +## Options Reference |
| 172 | + |
| 173 | +| Option | Short | Description | |
| 174 | +| --------------------- | ----- | ---------------------------------------------------------------------------------------------------------- | |
| 175 | +| `--mode <mode>` | | Select output mode: `diff`, `patch`, `stat`, `numstat`, `shortstat`, `name-only`, `name-status`, `summary` | |
| 176 | +| `--stat` | | Shortcut for stat output | |
| 177 | +| `--numstat` | | Shortcut for numstat output | |
| 178 | +| `--summary` | | Structural summary (native `git diff --summary`) | |
| 179 | +| `--shortstat` | | Shortcut for shortstat output | |
| 180 | +| `--name-only` | | Show filenames only | |
| 181 | +| `--name-status` | | Show status + filename | |
| 182 | +| `--include <pattern>` | `-i` | Include only files matching glob (repeatable) | |
| 183 | +| `--exclude <pattern>` | `-e` | Exclude files matching glob (repeatable) | |
| 184 | +| `--pager` | | Force pager | |
| 185 | +| `--no-pager` | | Disable pager | |
| 186 | +| `--index` | | Strict `git diff` compatibility mode | |
| 187 | +| `--help` | `-h` | Show help | |
| 188 | +| `--version` | `-v` | Show version | |
| 189 | + |
| 190 | +## Git Pass-through Compatibility |
| 191 | + |
| 192 | +`diffx` forwards unknown/standard `git diff` flags to git when possible, including pathspec support after `--`. |
| 193 | + |
| 194 | +```bash |
| 195 | +diffx main..feature -U10 --word-diff |
| 196 | + |
| 197 | +diffx --stat -- src/cli src/utils |
| 198 | +``` |
| 199 | + |
| 200 | +This allows existing `git diff` habits while still using `diffx` input resolution and workflows. |
| 201 | + |
| 202 | +## Exit Codes |
| 203 | + |
| 204 | +| Code | Meaning | |
| 205 | +| ---- | ---------------------------------------- | |
| 206 | +| `0` | Success | |
| 207 | +| `1` | No files matched filters | |
| 208 | +| `2` | Invalid input / unsupported range format | |
| 209 | +| `3` | Git execution/fetch error | |
| 210 | + |
| 211 | +## Development |
| 212 | + |
| 213 | +This repository uses `bun`. |
| 214 | + |
| 215 | +```bash |
| 216 | +bun install |
| 217 | +bun run build |
| 218 | +bun run test |
| 219 | +bun run test:e2e |
| 220 | +bun run lint |
| 221 | +bun run typecheck |
| 222 | +``` |
| 223 | + |
| 224 | +## License |
| 225 | + |
| 226 | +MIT |
0 commit comments