You ran git rebase main on your feature branch, hit a conflict, then ran git rebase --abort or completed the rebase — and now git log --oneline only shows the base-branch commits. Your five working commits are gone. Or the rebase finished but the branch points to one squashed commit instead of the three you expected.
Fastest fix: your commits are almost never deleted. When a rebase starts, Git saves the old branch tip in ORIG_HEAD. If you have not run another rebase, merge, or git reset since, this one line puts the branch back exactly where it was:
git reset --hard ORIG_HEAD
If ORIG_HEAD was already overwritten, the reflog still holds every state the branch passed through, kept for 90 days by default (30 days for entries no longer reachable, as of June 2026). The rest of this guide shows how to find the right SHA, verify it holds your work, and restore the branch safely.
First, figure out which bucket you are in
Run git reflog show HEAD | head -20 and match what you see against the table.
| What you observe | Likely cause | Jump to |
|---|---|---|
| Rebase just finished, no other commands run since | Branch moved, ORIG_HEAD is intact | Fastest fix above |
rebase (start) then rebase (finish) entries; some commits not in git log | Interactive drop/squash collapsed them | Cause 1 |
rebase --skip appears in the reflog | A conflicting commit was discarded, not resolved | Cause 2 |
Commit count dropped after git pull --rebase | Commit deemed “already applied” upstream | Cause 3 |
Your commits exist locally but git fetch shows different ones | Teammate force-pushed over the branch | Cause 4 |
git rebase --onto was used, commits nowhere in git log | Wrong range arguments left them dangling | Cause 5 |
Common causes
Ordered by hit rate, highest first.
1. Interactive rebase with drop or squash collapsed commits unintentionally
You ran git rebase -i HEAD~5, accidentally hit d on a line you meant to keep, deleted a pick line entirely, or used squash/fixup where you wanted pick. The collapsed commits still exist as dangling objects.
How to spot it: git reflog show HEAD | head -20. You will see an entry like HEAD@{3}: rebase (finish): returning to refs/heads/feature; the SHA on the line immediately before rebase (start) is your branch tip from before the rebase touched anything.
2. A conflicting commit was discarded with git rebase --skip
When a rebase stops on a conflict, git rebase --skip does not “skip this conflict step” — it drops the entire commit being applied and moves on. People run it expecting to resolve the conflict later, and the commit silently disappears from the result.
How to spot it: git reflog | grep -i 'rebase.*skip', or look for an abort/finish entry with fewer commits than you started with. The pre-rebase tip is still in the reflog just before rebase (start).
3. git pull --rebase dropped a commit it considered “already applied”
git pull --rebase replays your local commits on top of the fetched remote tip. If a local commit is a clean cherry-pick of something already upstream (for example a teammate cherry-picked your fix), Git treats it as redundant and drops it. With the merge backend this is the --empty=drop default.
How to spot it: count commits before and after. git log origin/main..HEAD --oneline shows what is local-only; if a commit is missing, check whether its change already landed upstream with git log origin/main --oneline | grep "<message snippet>".
4. A teammate force-pushed over the branch after your rebase
Your local reflog still shows your commits, but the remote branch now points elsewhere, so it looks like your commits vanished after git fetch.
How to spot it: git log HEAD..origin/feature --oneline is non-empty and lists commits you never authored, while git log origin/feature..HEAD --oneline shows the commits you thought were lost.
5. git rebase --onto used incorrect range arguments
In git rebase --onto <newbase> <oldbase> <branch>, a wrong <oldbase> moves a different range than you intended and leaves some commits as dangling objects, reachable from no branch.
How to spot it: git fsck --lost-found lists dangling commit objects (also written to .git/lost-found/commit/). Inspect each with git show <sha>.
Shortest path to fix
If the fastest fix (git reset --hard ORIG_HEAD) already restored your branch, you are done — skip to “How to confirm it’s fixed.” Otherwise, recover through the reflog.
Step 1: Find the pre-rebase SHA in the reflog
git reflog --date=iso | head -30
Look for the line just before rebase (start) (or rebase -i (start)). The SHA in the left column is your branch tip immediately before the rebase touched anything. Note it down as PREBASE_SHA.
Step 2: Create a recovery branch pointing to that SHA
Branch first instead of resetting — it is non-destructive and gives you a safe place to inspect.
git branch recovery/feature-prebase PREBASE_SHA
Verify the recovery branch holds your commits:
git log --oneline recovery/feature-prebase | head -10
Step 3: Verify the commits contain your actual work
git diff main recovery/feature-prebase -- src/
Confirm you see your real changes, not just rebase metadata or an empty diff.
Step 4: Reset your feature branch to the recovery point
# Only after Step 3 shows the right diff
git checkout feature
git reset --hard recovery/feature-prebase
Step 5: Redo the rebase carefully if you still need it
# --empty=keep keeps commits that would otherwise be dropped for becoming empty;
# --keep-empty keeps commits that were intentionally empty to begin with
git rebase --empty=keep main
If conflicts appear, resolve each file and run git rebase --continue — never --abort or --skip unless you genuinely want to discard that commit.
Step 6: Push the restored branch
# Force is required because history was rewritten
git push --force-with-lease origin feature
--force-with-lease refuses the push if someone else pushed to the branch since your last fetch, so you cannot silently overwrite a teammate’s work the way plain --force can.
How to confirm it’s fixed
git log --oneline -10shows all the commits you expected, in order.git diff recovery/feature-prebase(orgit diff ORIG_HEAD) prints nothing — the working tree matches the recovered tip exactly.git statusis clean and on the right branch.- After pushing,
git log origin/feature..HEADis empty, confirming the remote matches local.
Once you confirm, delete the scratch branch: git branch -D recovery/feature-prebase.
Prevention
- Reach for
ORIG_HEADfirst after any rebase:git log ORIG_HEAD -1 --onelineshows your old tip, andgit reset --hard ORIG_HEADundoes the rebase as long as no other history-moving command ran since. - When unsure of the range, use a count instead of a branch name:
git rebase -i HEAD~3is safer thangit rebase -i main. - Set
git config --global rebase.missingCommitsCheck error(default isignore) so interactive rebase stops if you delete apick/dropline, instead of silently dropping the commit. - Always push with
--force-with-lease, never bare--force; it aborts when the remote moved unexpectedly. - Keep reflog history longer:
git config --global gc.reflogExpire 180andgit config --global gc.reflogExpireUnreachable 90(defaults are 90 and 30 days). - Before a risky rebase, tag the current tip:
git tag backup/before-rebase HEAD. A tag is a permanent reference, so the commits survive evengit gc. - For shared branches, prefer a merge over rebase so you never rewrite public history.
FAQ
Q: Does git rebase --abort undo a rebase that already finished?
A: No. --abort only works while a rebase is paused mid-conflict. For a completed rebase, use git reset --hard ORIG_HEAD, or reset to the pre-rebase SHA you find in git reflog.
Q: My reflog is empty on a freshly cloned repo — can I still recover?
A: The reflog is local only and starts fresh on clone. If the remote was force-pushed, the original commits survive only in someone else’s clone. Ask a teammate to run git reflog or git log <branch> on their machine and re-push the SHA.
Q: Can I recover commits after git gc ran?
A: Possibly. If the commits were unreachable and older than gc.reflogExpireUnreachable (30 days by default), they may be pruned. Otherwise run git fsck --lost-found — dangling commits are listed and written to .git/lost-found/commit/. Inspect each SHA with git show.
Q: I squashed several commits and now they look gone — is the work lost?
A: No. Squash merges the changes into one commit; nothing is deleted. git log -p <squashed-sha> shows every change. To split them back out, git reset HEAD~1 and re-commit in pieces, or recover the originals from the reflog as above.
Q: After I rebased and force-pushed, how should a teammate sync without losing their work?
A: They should git fetch origin then git reset --hard origin/feature (after stashing or committing anything local) — not git pull, which would try to merge the rewritten history and create a tangle.