DevOps
Part 5 of 6 · GitCherry-pick, Revert, Reset & Reflog Recovery
Cherry-pick copies one commit's changes onto another branch as a new commit. Revert adds an inverse commit. Reset moves a branch pointer. Reflog is the local record of where HEAD used to point, and it is how you get a commit back after a bad rebase or reset.
- 1Gist
- 2Maps
- 3Q&A
- 4Sandbox
Voice readout needs Web Speech Synthesis in this browser.
Question ladder
L1
Soft versus hard reset in one line?
Answer
Soft moves the ref only. Hard moves the ref and makes the index and working tree match the target, which destroys uncommitted work.
L2
Why is revert the undo on main?
Answer
It adds a commit. Everyone can fast-forward. Reset would require a non-fast-forward push.
L3
How do you revert a merge commit?
Answer
git revert -m 1 MERGE chooses the first parent, usually main, and records the inverse of that merge.
L4
When does cherry-pick beat merging the branch?
Answer
When you need one hotfix and not the rest of the branch. It loses when many dependent commits should move together.
L5
Where does the reflog live, and who can see it?
Answer
Under .git/logs on your clone. It is not pushed. Teammates do not have your reflog.
L6
Can you recover a file that was never committed after reset --hard?
Answer
Not from the reflog. Uncommitted bytes were never an object. Stash or commit before a hard reset.
L7
What is the team protocol if main really must be rewritten?
Answer
Branch protection blocks force-push. An incident needs multi-party approval, a pause, one lease push, and everyone resetting to the new tip. Prefer revert.
Failure modes
reset --hard plus force on main
You erase other people's commits and break every SHA pinned in tickets and CI.
Hard reset of uncommitted work
Those bytes are not in the object database. Reflog cannot restore them.
Cherry-pick that later merges too
The same change arrives twice when the original branch lands, unless you planned for that.
Revert of a merge with the wrong parent
-m picks which side is the mainline. The wrong parent inverts the wrong tree.
Waiting until the reflog expires
Unreachable commits are kept for a shorter window. Garbage collection then drops them.
Misconceptions
Reset deletes the commit object immediately.
Reset moves the ref. The object remains while the reflog, or any other ref, can see it.
Revert rewrites the bad commit.
Revert adds a new commit. The bad commit stays in history, which is why shared branches can pull it.
The reflog is on the remote.
It is local. A teammate's lost commit is in their reflog, or in yours only if you had that tip.
Interviewer traps
Undoing a production commit with reset --hard and a force-push.
revert, then push a fast-forward. Name -m 1 if the bad commit is a merge.
Saying reflog will restore an unsaved file after a hard reset.
Reflog restores commits. Stash or a private commit is what saves uncommitted work.
Design scenario
Same prompt for every reader.
Requirements
Undo main without a force-push. Recover the private tip. Port one commit to the release branch.
Traffic / scale
One shared main. One release branch. One developer recovering a local rebase.
Latency
The main undo should be a pull everyone can fast-forward the same day.
Consistency
main's new tip is a revert commit. The recovered private branch points at the pre-rebase id. The release branch gets a new cherry-pick id.
Availability
The old private tip is recoverable only while the local reflog entry exists.
Failure assumptions
- The bad main commit is a merge, so revert needs a parent number.
- The lost work includes uncommitted files that hard reset already deleted.
Constraints
- No force-push to main.
- Cherry-pick only the hotfix, not the whole feature branch.
Prompt
A bad deploy commit is on main. Separately, a private rebase went wrong and the old tip seems gone. A one-commit hotfix must also reach a release branch.
API
Which commands undo main, recover the private tip, and port the hotfix?
Data
Which objects are new, and which old id does the reflog still name?
Architecture
What does branch protection block, and what stays local in .git/logs?
A bad commit is already on main, and your private rebase threw the tip away
Prefer
Revert main, reflog the private branch
main grows an inverse commit everyone can pull. Your old private tip is still a reflog entry. Point a branch at it.
- No force-push. Tickets that name the bad SHA stay valid.
- The old private objects are still in your object database.
- A release branch gets a cherry-pick, not a merge of unfinished work.
Alternative
reset --hard on main and force-push
You move a shared ref backwards and delete other people's starting point.
- Their next push is rejected or overwrites you, depending on flags.
- CI pins and deploy records now name a commit that is not the tip.
- Uncommitted files you reset locally are simply gone.
Three recoveries, three commands
Revert adds. Reset moves a private ref. Reflog names the commit you think you lost.
- 1
Shared main
pull --ff-only, revert the bad id, push. Use -m 1 when the bad commit is a merge. - 2
Private rewind
reset --soft keeps the index. Mixed keeps the edits unstaged. Hard matches the target and drops uncommitted work. - 3
Lost tip
reflog, then switch -c recover OID, or reset the private branch to that entry. - 4
One hotfix
cherry-pick the commit onto the release branch. Continue or abort like a rebase.
Overview
Seniors separate an undo that adds history from an undo that moves a pointer. Cherry-pick is how one snapshot's diff rides onto another branch. Reflog is why a "deleted" commit is often still on disk.
soft, mixed, and hard
| Mode | HEAD | Index | Working tree | Typical use | Failure if wrong |
|---|---|---|---|---|---|
--soft | Moves | Unchanged | Unchanged | Undo the commit, keep the staging | Soft across the wrong range |
--mixed (default) | Moves | Matches the target | Unchanged | Undo the commit, keep edits unstaged | Believing the files were deleted |
--hard | Moves | Matches the target | Matches the target | Throw away local WIP and the tip | Destroys uncommitted work |
git reset --soft HEAD~1
git reset HEAD~1
git reset --hard HEAD~1The third command is the dangerous one. It makes the index and the working tree match the target.
Flow
- 1
C1
- nextC2
- 2
C2
- nextC3 current tip
- 3
C3 current tip
- nextreset moves the branch
- 4
reset moves the branch
- nextC2 is the new tip
- 5
C2 is the new tip
- nextC3 stays in the reflog
- 6
C3 stays in the reflog
Lesson map
Cherry-pick, Revert, Reset & Reflog Recovery
Cherry-pick copies one commit's changes onto another branch as a new commit. Revert adds an inverse commit. Reset moves a branch pointer. Reflog is the local record of where HEAD used to point, and it is how you get a commit back after a bad rebase or reset.
Architecture. Architecture
Select a node to see why it exists, or an edge to see the protocol, direction, effect, and consequence.
Mermaid export
flowchart TB dev["Developer"] ref["Branch ref"] log["Reflog"] obj["Object DB"] dev -->|1. A bad rebase| ref ref -->|2. Reflog| log dev -->|4. git reflog| log dev -->|5. New branch or| ref
C3 is not deleted. It is unreachable from the branch and still reachable from the reflog.
Revert versus reset
| Goal | Prefer | Why it beats the other |
|---|---|---|
| Undo a commit on shared main | git revert OID | Additive. Others pull a fast-forward |
| Remove a tip you have not pushed | git reset | No extra revert commit |
| Undo a merge on main | git revert -m 1 MERGE | Records the inverse of the first-parent tree |
| Rewrite a private feature | reset or rebase | Cleaner than a stack of reverts |
Failure if you choose wrong: reset --hard plus force on main erases other people's work and breaks SHAs in tickets and CI.
git switch main
git pull --ff-only
git revert abc1234
git push origin main
git revert -m 1 mergeOid-m 1 means the first parent is the mainline you want to keep as the baseline. Later commits that touched the same hunks can still conflict. Resolve them like a merge. Reverting a revert re-applies the original change. That is the usual way to land a feature again after an emergency undo.
Cherry-pick
git switch release/2.1
git cherry-pick abc1234
git add PATHS
git cherry-pick --continue
git cherry-pick --abort
git cherry-pick A^..B| Compared with merging the branch | Cherry-pick wins when | Cherry-pick loses when |
|---|---|---|
| Port one hotfix | The surface stays small | Many dependent commits belong together |
| Skip unrelated commits | You can name the one SHA | A later merge duplicates those changes |
-x adds a "cherry picked from" trailer. Use it when the audit trail matters. Cherry-picking a merge commit needs -m to choose which parent diff to apply. That is rare. Prefer an ordinary commit when you can.
Reflog
git reflog
git switch -c recover abc1234
git reset --hard feature@{2}The hard reset to a reflog entry is only for a private branch whose working tree you are willing to match. Creating a new branch at the old id is the safer first move.
Sequence
- 1
Developer → Branch ref
1. A bad rebase moves the tip
- 2
Branch ref → Reflog
2. Reflog records the old id
- 3
Object DB
3. Old commits stay stored
- 4
Developer → Reflog
4. git reflog finds the id
- 5
Developer → Branch ref
5. New branch or reset to it
The reflog is local. It is not on the remote. Entries expire. A common default is on the order of 90 days for reachable tips and a shorter window for unreachable ones. git fsck --lost-found is the last resort after the reflog is gone and before garbage collection. Do not promise it.
Stash has its own reflog. git fsck and git reflog show stash are the places to look after a dropped stash, depending on the Git version. Know that the stash reflog exists.
Uncommitted work is not a commit
A hard reset deletes working-tree edits that were never hashed. The reflog does not have them. Mitigations are a stash, a private checkpoint commit, or editor local history.
git stash push -u -m "wip before reset"
git reset --hard origin/main
git stash popShared main stays protected
- Branch protection blocks force-push to
main. - A rewrite is an incident. It needs more than one person's approval.
- Prefer revert, then a forward fix.
- If a rewrite is unavoidable, announce it, pause automation, force-with-lease once, and have everyone reset to the new tip.
Which undo
Press Run. Snippets must be self-contained — no network, files, or native modules.
Press Run. Snippets must be self-contained — no network, files, or native modules.
Interview Q&A
Soft versus hard in one line?
Answer
Soft moves the ref and leaves the index and working tree. Hard moves the ref and makes the index and working tree match the target. That destroys uncommitted work.
Can revert conflict?
Answer
Yes. Later commits may have touched the same hunks. You resolve the markers the way you would in a merge, then commit the revert.
What is a revert of a revert?
Answer
A new commit that puts the original changes back. Teams do this when a feature was reverted in an incident and is ready to land again.
How do you cherry-pick a merge commit?
Answer
Pass -m and a parent number so Git knows which side's diff to apply. It is easy to pick the wrong parent. Prefer cherry-picking the non-merge commits when they still exist.
Where does the reflog live?
Answer
In .git/logs/ on your clone. It is not pushed with the branch.
The commit is not in the reflog. Now what?
Answer
git fsck may still list a dangling commit if garbage collection has not deleted the object. After that, it is gone. Uncommitted files were never candidates.
Why is reset a bad undo for a published commit?
Answer
It rewinds a tip other people have based work on. The next push is not a fast-forward, so you are coordinating a rewrite instead of shipping an undo.
How do you undo a dropped stash?
Answer
Look at the stash reflog (git reflog show stash) and at unreachable commits from git fsck. The exact invocation depends on the Git version. The point is that stash has a reflog too.
Pitfalls
reset --hardto "clean up" before you have committed or stashed.revertwithout-mon a merge commit. Git will refuse, and guessing the parent number inverts the wrong side.- Cherry-picking a range that includes a merge (
A^..Bneeds care). - Treating
fsckas a backup product. It is a last look at objects that have not been pruned.
Say the command for a bad commit on main, the command for a private tip you have not pushed, and the command that finds the tip a rebase just abandoned. Name the one case where none of them restore a file.