Interactive Rebase, Merge, or Cherry-Pick: Choosing How to Clean Up Local Git History
Decide between interactive rebase, merge, and cherry-pick based on one constraint — whether the branch is shared — then squash a feature branch safely and verify the result with log, diff, and reflog.
15 Dec 2025, 19:08 UTC

Your feature branch works, but the history is a mess: three "fix typo" commits, a half-finished experiment, and a debugging commit that should never see a pull request. The decision you face is not whether to clean it up, but which Git operation fits your situation — because picking the wrong one on a shared branch can break your teammates' clones.
The short answer: use git rebase -i to reorganize commits on branches only you have pushed (or haven't pushed at all), use git merge when the history is already shared, and use git cherry-pick when you only need one or two commits moved somewhere else.
The decision and its one hard constraint
Every option below either preserves commit IDs or rewrites them. Rebase and cherry-pick create new commits with new hashes; merge leaves existing commits untouched. That single fact drives the whole decision:
- If anyone else has fetched the commits you want to change, rewriting them forces those teammates into a diverged-history mess they must repair by hand.
- If the branch is private — local only, or a topic branch you alone push to — rewriting is safe and gives you a clean, reviewable history.
So the first question is never "which command is better" but "has anyone else pulled this branch?"
Comparing the three supported options
| Operation | What it does | History result | Safe on shared branches? | Best for |
|---|---|---|---|---|
git rebase -i | Reorders, squashes, edits, or drops a contiguous range of commits | Linear, rewritten (new hashes) | No — private/topic branches only | Polishing a feature branch before opening a PR |
git merge | Combines two branches, preserving both histories | True history plus a merge commit | Yes | Integrating shared work; branches others depend on |
git cherry-pick | Copies individual commits onto the current branch | New commits duplicating the originals | Yes for the target; source untouched | Moving a hotfix or single commit without the rest of its branch |
Trade-offs that matter in practice
Interactive rebase: clean history, real risks
Rebase gives reviewers a story: one commit per logical change, no noise. The cost is that you are editing history, so two things can go wrong. First, conflicts can reappear at each commit being replayed, not just once — a five-commit rebase can mean five conflict resolutions. Second, if you drop or mangle a commit by mistake, the branch is altered; recovery depends on git reflog, which only retains entries for a limited time (90 days by default for reachable entries).
Merge: honest but noisy
Merging main into your feature branch (instead of rebasing onto it) never rewrites anything, so it is always safe. The downside is merge commits piling up in the PR, which some teams find unreadable. Many teams compromise: merge freely while collaborating, then squash at PR merge time via the hosting platform.
Cherry-pick: surgical, but duplicates
Cherry-pick is the right tool when a bugfix on one branch is also needed on a release branch. The trade-off is that the same change now exists as two different commits, which can produce trivial conflicts later when the branches eventually merge.
Concrete implementation: squashing a feature branch
Assume you are on feature/login, branched from main, with five local commits you want to collapse into two. Run these in your local clone; no special permissions are needed beyond write access to the repo.
# 1. Record the current state so you can verify and recover
git log --oneline main..HEAD
# 2. Start the interactive rebase over your branch's commits
git rebase -i main
Your editor opens with the oldest commit at the top. Change pick to squash (or s) on commits you want folded into the one above, reword to fix a message, or drop to delete a commit. Save and close; Git replays the commits in order.
If a conflict stops the rebase, Git tells you which files conflict. Fix them, then:
git add <resolved-files>
git rebase --continue
# or bail out entirely and return to the pre-rebase state:
git rebase --abort
If you already pushed the branch once and must push the rewritten version, use git push --force-with-lease origin feature/login. The --force-with-lease flag refuses the push if someone else updated the remote branch since your last fetch — a meaningful safeguard over plain --force, but it still rewrites the remote, so only do this on branches you own.
Verifying the result — and recovering from mistakes
After the rebase, confirm the graph looks the way you intended:
git log --oneline main..HEAD # expect your two cleaned-up commits
git status # expect "nothing to commit, working tree clean"
git diff main...HEAD --stat # sanity-check the total change is unchanged
The diff against the merge base should match what the branch contained before cleanup; if the stat output changed unexpectedly, a conflict resolution likely dropped or altered content.
If you dropped a commit by accident, git reflog lists every position HEAD has occupied. Find the entry just before the rebase (e.g., HEAD@{3}) and run git reset --hard HEAD@{3} to restore the branch, or git cherry-pick <hash> to recover a single lost commit. Note that reset --hard discards uncommitted working-tree changes, so commit or stash first.
Limitations
Interactive rebase operates on a contiguous range of commits on the current branch — it cannot easily edit commits scattered across merged branches. It also cannot rewrite merge commits without the --rebase-merges flag, which adds complexity beyond most cleanup jobs. Behavior described here applies to Git 2.x; the editor-based todo list has been stable for years, but check git rebase --help for your installed version if a flag behaves differently.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.