Beginner · Git in depth · topic 12 of 53 · 35 min
Rebase, undo and rescue: rebase, reset, revert, reflog and stash
Explanation
What rebase does
Merging keeps both lines of history and joins them with a merge commit. Rebasing does something different: it takes the commits that are only on your branch, temporarily removes them, moves your branch to the tip of the other branch, and then replays your commits one by one on top. The result looks as if you had started your work from the latest main.
# before: main: A - B - C feature: A - B - X - Y
git switch feature
git rebase main
# after: feature: A - B - C - X' - Y' (X' and Y' are NEW commits)Interactive rebase: tidy up before you share
While working, you often make commits like "wip", "fix typo" and "really fix it". Before opening a pull request, interactive rebase lets you clean them up. git rebase -i HEAD~4 opens an editor listing the last four commits, oldest first, each with an action word.
pick 1a2b3c4 Add coupon field to checkout
fixup 5d6e7f8 fix typo
squash 9a8b7c6 Validate coupon expiry date
drop 0f1e2d3 Temporary debug logging
reword 4c5d6e7 add tests| Action | What it does |
|---|---|
| pick | Keep the commit as it is |
| reword | Keep the changes, edit the message |
| squash | Melt into the previous commit and combine both messages |
| fixup | Melt into the previous commit and throw this message away |
| drop | Remove the commit completely |
| edit | Stop at this commit so you can change it |
If a replayed commit conflicts, Git stops. Fix the files, git add them, and run git rebase --continue. git rebase --abort returns everything to how it was.
The golden rule
Merge or rebase?
| Merge | Rebase | |
|---|---|---|
| History | True record, with merge commits | Straight line, easy to read |
| Safe on shared branches | Yes | No (rewrites commits) |
| Conflicts | Solved once, in the merge | May be solved commit by commit |
| Typical use | Bringing a finished branch into main | Updating your own branch, cleaning up before a PR |
Cherry-pick: copy one commit
git cherry-pick <id> applies the changes of one commit onto your current branch as a new commit. It is useful to bring a single bug fix from main into a release branch, without merging everything else.
Undoing: reset versus revert
git reset moves the current branch pointer back to an earlier commit. The three modes differ in what happens to the changes from the commits you step back over.
git reset --soft HEAD~1 # undo the last commit, keep its changes STAGED
git reset --mixed HEAD~1 # (default) undo the commit, keep changes in your files, unstaged
git reset --hard HEAD~1 # undo the commit AND throw away its changes (dangerous)
git revert 5e2c8d1 # make a NEW commit that undoes 5e2c8d1; history is keptreset rewrites history, so it follows the golden rule: use it only on commits nobody else has. On a shared branch use git revert, which adds a new commit with the opposite changes. Everyone simply pulls it.
reflog: the safety net
Git writes down every place HEAD has pointed to in the reflog (reference log). Even after a reset --hard or a bad rebase, the old commits usually still exist for a while (by default, unreachable entries are kept about 30 days). Find the ID and bring it back.
git reflog
# 7d41c2e HEAD@{0}: reset: moving to HEAD~3
# a19f0b3 HEAD@{1}: commit: Add UPI QR payment option
git branch rescue a19f0b3 # a new branch pointing at the "lost" commitstash: park unfinished work
git stash push -m "half-done coupon form" # save changes and clean the working directory
git stash list # stash@{0}: On feature: half-done coupon form
git stash apply stash@{0} # bring it back (the stash stays in the list)
git stash pop # bring back the latest and remove it from the list
git stash push -u -m "with new files" # -u also stashes untracked filesCommon mistakes and how to fix them
- Ran reset --hard and lost work: check git reflog straight away and create a branch at the old commit. Uncommitted changes that were never staged or committed cannot be recovered this way.
- Rebased main and force-pushed: tell the team at once. Restore the old tip from someone's clone or the reflog, and protect main so force pushes are blocked.
- Squash in the first line of the todo list fails, because there is no earlier commit to melt into. The first line must be pick or reword.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Rebase, undo and rescue: rebase, reset, revert, reflog and stash · 1/9
This lesson covers rewriting and undoing history safely: rebase, reset, revert, reflog and stash.
Prefer a video by a teacher? Find free videos on “git rebase interactive reset revert reflog explained” on YouTube ↗
In real life: A hotfix at a Bengaluru fintech
A wrong interest rate went live in a loan app. The on-call engineer does not reset main, because twenty developers have already pulled it. She runs git revert on the bad commit, which adds one new commit putting the old rate back, and the pipeline deploys it in minutes. The same fix is needed in the release/2.4 branch used by the older Android app, so she runs git cherry-pick with the revert commit's ID on that branch. History stays honest, and nobody's clone breaks.
git switch main
git revert 8c1d2e3 # new commit: Revert "Update loan interest rate"
git push
git switch release/2.4
git cherry-pick 4f5a6b7 # copy that revert into the release branchPractice (0/4 done)
You committed too early and want to undo the last commit but keep all its changes staged. Which command?
A bad commit is already on the shared main branch that the whole team pulls. What is the safe way to undo it?
Simulate an interactive rebase todo list. Write apply_todo(lines) where each line is "action id message", e.g. "pick 1a2b3c4 Add coupon field". Actions: pick keeps the commit; drop removes it; squash melts it into the previous resulting commit and joins the messages with " / " (real Git opens an editor here); fixup melts it into the previous commit and discards its own message. Return the list of resulting commit messages in order. If squash or fixup has no previous commit, raise ValueError, just like Git refuses.
In a practice repository, make four commits on a branch called feature/coupon: "Add coupon field", "wip", "fix typo" and "Add coupon tests". Use interactive rebase to turn them into two clean commits. Then simulate a disaster: run git reset --hard HEAD~2 and use the reflog to get the commits back. Finally, stash a small change, switch branches, and bring it back.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Collaboration: remotes, pull requests and branching strategiesGit in depth · same module
- Git and GitHubFrontend Web Development · Async JavaScript, modules and tools
- GitOps and platform engineeringGitOps, platform engineering and DevOps careers
- Secrets managementDevSecOps
- Transactions: all or nothingSQL and Databases · Changing data, transactions and ACID
- Your portfolio projectFrontend Web Development · Build, deploy and get hired
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Why does rebasing change commit IDs?
Answer: The replayed commits have new parents, and the parent is part of the hash
A commit's ID is a hash that includes its parent. New parent, new ID.
In interactive rebase, which action melts a commit into the previous one and discards its message?
Answer: fixup
fixup combines the changes but keeps only the previous commit's message; squash keeps both messages.
What does git reset --mixed HEAD~1 (the default mode) do?
Answer: Undoes the last commit and leaves its changes in your files, unstaged
Mixed moves the branch back and resets the staging area, but your working files keep the changes.
You need one bug-fix commit from main in the release/3.1 branch, and nothing else. Which command fits?
Answer: git cherry-pick <fix-commit-id>
cherry-pick applies just that commit's changes as a new commit on the current branch.
After git reset --hard, which command helps you find the commit you lost?
Answer: git reflog
The reflog records every position HEAD has had, including before the reset.
Why prefer git push --force-with-lease over git push --force after rebasing your own PR branch?
Answer: It refuses to overwrite the remote if someone else pushed new commits
force-with-lease checks that the remote is still where you last saw it, so you do not wipe out a teammate's commits.
main has no new commits since feature was created. What does git merge feature (run on main) do by default?Related
Answer: Moves main forward to the tip of feature (fast-forward)
When main is an ancestor of feature, Git just moves the pointer. Use --no-ff if you want a merge commit anyway.
Which commit message is best?Related
Answer: Fix PIN code check for codes starting with 0
A good subject says what the commit does, as a short command, so the history explains itself.
From Git and GitHub
You ran git add on a file. Which command shows the changes that will go into the next commit?Related
Answer: git diff --staged
git diff --staged compares the staging area with the last commit. Plain git diff shows only unstaged changes.
In trunk-based development, how do teams merge unfinished features safely?Related
Answer: They hide unfinished work behind feature flags
Feature flags let half-built code be merged and deployed while switched off, so branches stay short-lived.
From Collaboration: remotes, pull requests and branching strategies
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Git in depth” before its exam: Collaboration: remotes, pull requests and branching strategies, Semantic versioning, tags and releases.
- Module 3 exam: Git in depth · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Docker. 53 topics left in the course before the final exam.