Ledger is opinionated about common git paper cuts. Rather than showing cryptic git errors, we handle common situations automatically.
Git's CLI is conservative and shows warnings/errors for situations that rarely cause real problems. Ledger is smarter about these cases:
- Try the smart thing first
- Only fail on real conflicts
- Provide clear options when intervention is needed
The Problem: Git refuses to pull if you have ANY uncommitted changes, even if those changes don't conflict with incoming changes.
error: cannot pull with rebase: You have unstaged changes.
error: please commit or stash them.
Ledger's Solution: Auto-stash, pull, then restore.
Pull requested with uncommitted changes
↓
Stash changes (including untracked files)
↓
Pull with rebase
↓
Pop stash to restore changes
↓
✓ "Pulled 3 commits and restored your uncommitted changes"
When it fails: Only if there's an actual conflict between your uncommitted changes and the pulled changes. In that case, Ledger will tell you exactly what happened.
Implementation: pullCurrentBranch() in git-service.ts
The Problem: You stage changes and commit, only to realize origin has moved ahead. Now you need to pull, deal with merge commits, or rebase. Normal git just commits and leaves you to sort out the mess later.
Ledger's Solution: Before committing, fetch and check if you're behind origin. If so, offer to "leapfrog" over the incoming commits by pulling first, then committing on top.
User clicks "Commit"
↓
Fetch origin (silent)
↓
Are we behind? → Yes → Show prompt:
┌─────────────────────────────────────┐
│ ⚠️ Origin has 3 new commits │
│ │
│ [Pull & Commit] [Commit Anyway] [X] │
└─────────────────────────────────────┘
↓ No
Commit normally
Options:
- Pull & Commit (Leapfrog) — Pull first (with auto-stash magic), then commit on top of the latest
- Commit Anyway — Commit behind origin (creates diverged history you'll need to reconcile later)
- Cancel — Think about it
When it aborts: If the pull causes conflicts with your uncommitted changes, the leapfrog aborts and asks you to resolve conflicts first. Your changes are preserved.
Implementation: commitChanges(message, description, force) in git-service.ts
The Problem: You have a branch and want to know "is this branch still useful?" Standard git diffs are confusing:
git diff master..branch— Shows everything different between them, including noise from master moving forwardgit diff master...branch— Shows all changes made on the branch, but some may be obsolete or already superseded
Neither answers the real question: "What would this branch actually contribute if merged right now?"
Ledger's Solution: PR Preview simulates a merge and shows only the unique contribution.
User views branch detail panel
↓
"PR Preview" tab (default)
↓
git merge-tree --write-tree master branch
↓
Diff the merge result against master
↓
✓ Shows exactly what a PR would contribute
(Same as GitHub's PR diff view)
Three Diff Views:
| View | Git Equivalent | Use Case |
|---|---|---|
| PR Preview | merge-tree + diff |
"Is this branch still useful?" |
| Branch Diff | master..branch (two-dot) |
Raw current state comparison |
| Branch Changes | master...branch (three-dot) |
Historical view of work done |
Conflict Detection: If the branch has merge conflicts with master, PR Preview shows:
⚠️ badge with conflict count- Tooltip listing conflicting files
- Still shows the non-conflicting diffs
Why this matters:
- Stale branches that were forked ages ago often show huge diffs vs master
- After master moves forward, it's hard to tell if the branch has unique value
- PR Preview cuts through the noise to show exactly what matters
Implementation: getBranchMergePreview() in git-service.ts
The Problem: Git's stash apply only works on your current branch. If you stashed changes on branch A but you're now on branch B, you have to checkout A first - which risks conflicts with your current work.
error: Your local changes would be overwritten by checkout.
Please commit your changes or stash them before you switch branches.
Ledger's Solution: Apply a stash directly to its original branch without switching, using worktrees:
You're on: feature/new-thing
Stash was from: feature/old-thing
Standard git: checkout old → apply → checkout new (risky!)
Ledger: "Apply to feature/old-thing" (via worktree)
How it works:
| Scenario | What Ledger Does |
|---|---|
| Target branch has an existing worktree | Apply stash directly to that worktree. Changes remain uncommitted. |
| Target branch has no worktree | Create temp worktree → apply stash → auto-commit → clean up |
The Flow:
User clicks "Apply to feature/old-thing"
↓
Does feature/old-thing have a worktree?
↓ Yes ↓ No
Apply stash there Create temp worktree at
(uncommitted) .git/ledger-temp-worktrees/
↓ ↓
Done! Apply stash
↓
Auto-commit: "Apply stash: <message>"
↓
Remove temp worktree
↓
Done!
Benefits:
- Never lose your current context
- Safe - no risk of conflicts blocking your checkout
- Changes are either uncommitted (existing worktree) or auto-committed (temp worktree)
- Temp worktrees are automatically cleaned up
This is "parallel git": Worktrees let you work on multiple branches simultaneously. Ledger extends this to operations like stash apply that git normally requires branch switching for.
Implementation: applyStashToBranch() in git-service.ts
Things we might handle automatically in the future:
Fetch origin when the app gains focus so you always see up-to-date behind/ahead counts.
When a branch is merged and deleted on origin, offer to clean up the local branch.
Before switching branches, warn if you have uncommitted changes that would conflict.
When auto-stashing, use meaningful names like ledger-auto-stash-for-pull so you can identify Ledger's stashes.
Currently, these behaviors are always on. In the future, we may add settings to disable specific behaviors for users who prefer vanilla git.
Found a git paper cut that Ledger should handle better? Open an issue describing:
- The git error/situation
- Why it's annoying
- What the smart behavior should be