Documentation
GitChop is a Mac-native visual driver for git rebase -i. This page covers everything in the app — what each verb does, how the split-commit flow works, and how to recover when something goes sideways.
Installation
Download the DMG, drag GitChop into /Applications, eject. The app is signed and notarized so first launch goes straight through Gatekeeper, no right-click Open dance.
GitChop runs the git already on your $PATH (typically the Xcode command-line tools or Homebrew's). Anything your shell git can do — including custom config, mergetool, and any version-specific behavior — applies to GitChop too.
Opening a repo
⌘O or the folder button in the toolbar opens any directory inside a git repo. GitChop walks up to the nearest .git, just like git itself.
Each open repo gets its own tab. Open paths persist between launches and restore on next start; tabs that point at a repo that's been moved or deleted get silently dropped.
The sample repo
If you grabbed the DMG, it includes a Sample Project folder with a generator script. Run bash "Sample Project/init.sh" from a terminal — it creates a self-contained 26-commit demo repo at Sample Project/repo/. Open that folder in GitChop and you'll have a real-looking history with deliberate candidates for every verb.
The script is destructive-by-design: it wipes and regenerates the repo each time, so you can chop, mess up, and reset by re-running.
Verbs
Every commit in the plan has a verb chip on the left. Click it to pick a different verb. The verb determines what happens at Apply time.
| Chip | What it does |
|---|---|
| • pick | Keep this commit as-is. The default for every row. |
| ✎ reword | Open a modal to edit the commit's full message (subject + body). Save rewrites it at Apply time. |
| ✂ edit | Pause the rebase here so you can split this commit's hunks into multiple commits. See Split a commit. |
| ↑ squash | Combine into the previous commit, keeping both messages. |
| ⤴ fixup | Combine into the previous commit, dropping this commit's message. |
| ✕ drop | Remove this commit entirely. The row gets a soft red wash and strikethrough so it's obvious it's going away. |
Squash and fixup chains
When a row's verb is squash or fixup, it folds into the row above. A small +N badge appears on the absorbing row showing how many commits will fold in. Drops in between don't break the chain — they just disappear, and the next squash/fixup attaches to whatever pick or edit came before.
Reword availability
Reword is available on every commit. The new message is stored in memory until Apply; canceling the modal reverts the chip back to pick.
Edit availability
Edit needs at least 2 hunks to produce multiple commits. On a single-hunk commit the menu shows "Edit — Not available (commit has only 1 hunk)" and the entry is disabled. Use Reword instead if you only want to change the message.
Reordering
Grab any commit by its drag handle (left-most column) and drop it elsewhere in the list. Reordering rewrites the rebase TODO at Apply time.
The list is in git-rebase-i order — oldest at the top, newest at the bottom. That matches the file git itself shows you when you git rebase -i from a terminal, so the mental model carries over.
Choosing how many commits
The count pill in the list header (12 commits / 12 of 24 commits) is also the depth picker. Click it to load the last 12 / 25 / 50 / 100 / All commits.
The default is 12 because that's about the size of a typical pre-push tail — most rebases are local cleanup before sharing. Bigger histories work fine, just slower to load.
Custom base
Right-click any commit and pick Use as base. The clicked commit becomes the rebase foundation (excluded from the plan, matching git rebase -i <sha>) and everything newer than it becomes the editable plan.
The header pill switches to N commits from abc1234 so the pinned base is visible. The depth menu collapses to a single Switch to depth-based loading option until you revert.
Reword
Click the verb chip and pick Reword. A modal opens with the commit's full message preloaded — subject on the first line, blank line, then body. Edit, click Save.
pick so the chip doesn't lie about what'll happen at Apply.
Already-reworded rows can be re-edited via the chip menu's Edit message… entry without toggling the verb.
How it works under the hood
At Apply, GitChop writes each new message to a per-rebase scratch file under /tmp/gitchop-reword-<uuid>/<sha> and points $GIT_EDITOR at a tiny helper script. When git invokes the editor for a reword line, the helper reads .git/rebase-merge/done to find which commit is in flight and copies the matching scratch file into git's COMMIT_EDITMSG. No interactive editor pop-ups during the rebase.
Split a commit
The headline feature. Mark a commit edit in the chip menu — the split sheet auto-opens. Inside:
- Hunks — every hunk in the commit, grouped by file. Each hunk has a chevron menu on the right to assign it to a bucket.
- Buckets — initially two empty buckets. Each will become a separate commit. Type a subject, drag hunks (via the menu) into the bucket, watch the count update. + Add for more buckets, trash icon to remove them.
Save when every hunk is assigned and every bucket has both at least one hunk and a non-empty subject. The status line in the footer tracks what's still missing.
Back in the main list, the edit row expands with ghost rows — one per bucket, with a colored numbered badge, a — placeholder hash (the new commits don't exist yet), and the bucket subject. They mirror the buckets' colors so you can match each bucket card to its preview row.
How it works under the hood
At Apply, when the rebase reaches the edit commit, GitChop runs git reset HEAD^ to uncommit it (working tree intact), then for each bucket: re-parses the live diff, reassembles only that bucket's hunks into a patch, applies it to the index, and commits with the bucket subject. The re-parse-per-bucket means line numbers always match the working tree — splits are robust to upstream rebase steps that shift other commits' content.
Squash & fixup
Both fold the current commit into the row above. The difference:
- Squash opens git's combined-message editor. GitChop sets
$GIT_EDITOR=:so the default merged message is accepted without a popup. - Fixup drops the current commit's message entirely; the absorbing commit's message is kept as-is.
If you set squash/fixup at the very top of the plan or with no pick/edit/reword above it, git will error at Apply time and the rebase rolls back. The chip menu doesn't pre-validate, but the result sheet surfaces git's error message.
Drop
Drop removes the commit from history. The row is grayed out, the subject struck-through, and the row gets a soft red wash so it's visually obvious it's going away.
Drop is destructive — the commit's content is gone after Apply. The backup ref is your safety net (see Apply & backup).
Apply & backup
The Apply button (sealed checkmark icon, top right) is enabled the moment any plan edit exists. Click it to open the confirmation sheet — it summarizes the changes ("3 commits will be dropped, 2 commits' messages will be rewritten") and shows the resulting commit count.
Before any history rewrite, GitChop creates a backup ref:
refs/gitchop-backup/<timestamp>
pointing at the pre-Apply HEAD. The result sheet shows the ref so you can copy it.
To restore manually after a successful Apply you don't like:
git update-ref HEAD refs/gitchop-backup/<timestamp>
git reset --hard HEAD
Backups stack up over time. To clean old ones:
git for-each-ref refs/gitchop-backup --format='%(refname)' \
| xargs -n1 git update-ref -d
Resolving conflicts
If git stops on a merge conflict during Apply, GitChop pauses the rebase and opens the Resolve conflicts sheet. The left side lists every unmerged file with two affordances per row:
- Open — opens the file in your default editor (whatever
open path/to/filewould do). - Reveal — selects the file in Finder.
Resolve the conflict markers in the file, save it. Click Refresh in the sheet to re-check git's unmerged-paths list — files that no longer have unmerged regions drop off automatically.
Once the list is empty, Continue is enabled. Click it to run git rebase --continue and resume the rebase. The next commit may itself conflict — same sheet just updates with the new file list.
Other actions:
- Skip commit runs
git rebase --skip. The conflicting commit is dropped; rebase continues with the rest of the plan. - Abort runs
git rebase --abortand restores HEAD from the backup ref. You're back to where you were before clicking Apply.
--skip or --allow-empty; GitChop detects this and runs --skip automatically, logging the action.
Mid-rebase reorder
The right side of the conflict sheet shows the remaining commits — everything git hasn't processed yet, parsed live from .git/rebase-merge/git-rebase-todo. Drag to reorder; the change is in-memory until you click Continue or Skip, at which point GitChop writes the new ordering back to disk so git picks it up.
Useful when you realize a different commit should go next, or when you want to defer a problematic commit by moving it later in the queue.
Recovery
If GitChop crashes or you force-quit mid-rebase, the next time you click Apply, GitChop detects the orphaned .git/rebase-merge directory and runs git rebase --abort to clean it up before starting a new rebase. The backup ref from the previous (interrupted) Apply is still on disk — restore manually with the commands in Apply & backup if needed.
The Reset toolbar button (left of Apply) discards in-flight plan edits without touching git. Verb changes, reordering, configured splits, and reword messages all revert to defaults. Asks for confirmation before discarding.
Preferences
Open with ⌘, from the GitChop menu. Three tabs:
General
Default depth — how many commits a freshly-opened repo loads. Defaults to 12; pick from 12 / 25 / 50 / 100 / All. Each repo's depth menu still overrides per-tab.
Git
Path to git — leave blank to use whatever git shows up first on your shell's $PATH (Xcode command-line tools, Homebrew, etc.). Set an absolute path to force a specific binary — useful for asdf, MacPorts, or custom builds. The "Currently resolves to:" line below the field shows what GitChop would actually invoke right now.
Editor
How Open in the conflict sheet launches a file. Three modes:
- macOS default —
NSWorkspace.open, same as a Finder double-click. Works with no setup. - Specific app — pick a
.appbundle (Visual Studio Code.app, Cursor.app, Sublime Text.app, etc.). Every conflicted file opens in that app. - Shell command — run a command per file with
$FILEsubstituted to the absolute path. Great for editor CLIs that block-and-wait, likecode --wait $FILE,cursor $FILE, orsubl --wait $FILE.
Keyboard
| Shortcut | Action |
|---|---|
| ⌘, | Preferences |
| ⌘O | Open repo (adds a new tab) |
| ⌘W | Close active tab (prompts to confirm if there are unsaved plan edits) |
| ⌘↩ | Apply (when there are pending changes) |
| ↩ | Save (in any modal sheet) |
| esc | Cancel (in any modal sheet) |
Requirements
- macOS 14 Sonoma or later
- Apple Silicon (M-series Mac)
giton your$PATH— typically already there from Xcode command-line tools or Homebrew