Suggestions
← TIL
~2 min read
#git#devops#workflow#tooling#senior-dev

git absorb: the automatic fixup you didn't know you needed

You have 5 commits on a branch. The reviewer found bugs in 3 different ones. The ritual: git rebase -i, find each commit, mark it as fixup, don't mess up the order. Ten minutes to fix three lines.

git absorb does all of that automatically. Not just git commit --fixup SHA — it also figures out the right SHA for you.

Terminal
# Install
brew install git-absorb       # macOS
cargo install git-absorb      # any platform

# The flow

git add -p # selectively stage your fixes
git absorb # detects and creates fixup! commits automatically
git rebase -i --autosquash # applies everything in one pass

What it generates in your history:

git log --oneline
$ git log --oneline
f3a1b2c fixup! feat: add form validation
9e4d5a1 fixup! fix: typo in Astro config
a7c8e0f feat: add form validation
3b2d1e9 fix: typo in Astro config

After rebase --autosquash, those fixup! commits disappear. Clean history without opening the rebase editor.

How it works: for each staged hunk, it checks whether the change commutes with each recent commit. When it finds one it doesn't commute with, that's the target; it creates a fixup! pointing exactly there. Default range: last 10 commits. If the hunk commutes with all of them, no candidate was found: it leaves the hunk staged and shows a warning. It doesn't guess.

The trick is git add -p: stage exactly the changes that belong to each fix. The more atomic the staging, the more precise the detection.

Tip: git absorb --and-rebase runs the rebase in one step. Or set git config --global rebase.autoSquash true to skip the --autosquash flag every time.

When it fails: two concrete cases. If you stage completely new code with no context in recent commits, no candidate is found: a warning appears and the hunk stays staged.

The quieter case: two commits touched the same region. Commit A introduces expiry=3600 (the bug). Commit B renames that same variable afterward. Your fix (3600 → 86400) doesn't commute with B → git absorb targets it, even though the bug came from A. The history looks clean but is semantically wrong. Manual rebase for that one.

Useful tools don't announce themselves. Install this one before your next PR turns into commit surgery.

References

Link copied to clipboard