2025-03-15 Productivity, Programming
Undoing Changes & Recovering Lost Work in Git
By O. Wolfson
Working with Git can sometimes lead to mistakes like unwanted changes or lost commits. Fortunately, Git offers robust tools to revert changes and recover lost work. This article will guide you through key commands for undoing changes and reverting to previous states.
Three commands are often used to undo changes and recover lost work:
Each command has different purposes and behaviors.
Command | Purpose | Affects Working Directory? | Affects Staging Area (Index)? | Affects Commit History? | Common Use Case |
---|---|---|---|---|---|
git restore | Restore file(s) to a previous state | Yes (optional) | Yes (optional) | No | Discard changes in the working directory or unstage changes |
git reset | Move HEAD and optionally update index and working directory | Optional | Yes | Yes (in some modes) | Unstage changes, discard commits, or move HEAD |
git revert | Create a new commit that undoes a previous commit | No | No | Yes | Safely undo a commit in public history |
1. git restore
- Introduced in Git 2.23 to improve usability.
- Used to discard changes in the working directory or unstage changes from the index (staging area).
- Does not affect commit history.
Examples:
bashgit restore file.txt # Discard changes in working directory (reverts to last committed state)
git restore --staged file.txt # Unstage file but keep changes in working directory
git restore --source=HEAD~1 file.txt # Restore file to state from one commit ago
2. git reset
- Moves the HEAD pointer to a different commit.
- Optionally modifies the staging area and working directory depending on the mode:
--soft
: Moves HEAD, keeps changes staged.--mixed
(default): Moves HEAD, unstages changes, keeps working directory.--hard
: Moves HEAD, unstages and discards changes in working directory.
Examples:
bashgit reset --soft HEAD~1 # Undo last commit, keep changes staged
git reset --mixed HEAD~1 # Undo last commit, unstage changes, keep working directory
git reset --hard HEAD~1 # Undo last commit, discard changes from working directory
3. git revert
- Creates a new commit that undoes the changes from a specific previous commit.
- Safe for public/shared branches because it preserves history.
- Does not modify past commits or move HEAD back.
Examples:
bashgit revert HEAD # Revert last commit (create a new commit that undoes it)
git revert <commit-hash> # Revert a specific commit
Key Differences Summary:
Feature | git restore | git reset | git revert |
---|---|---|---|
Undo Working Directory Changes | ✔️ | ✔️ (with --hard ) | ❌ |
Unstage Files | ✔️ | ✔️ | ❌ |
Undo Commits | ❌ | ✔️ (moves HEAD) | ✔️ (creates new commit) |
Preserves History | ✔️ | ❌ (except --soft ) | ✔️ |
Safe for Public History | ✔️ | ❌ | ✔️ |
When to Use:
Situation | Recommended Command |
---|---|
Discard changes in working directory | git restore file.txt |
Unstage a file | git restore --staged file.txt |
Undo the last commit, keep changes staged | git reset --soft HEAD~1 |
Undo the last commit, keep changes in working directory | git reset --mixed HEAD~1 |
Undo the last commit, discard changes entirely | git reset --hard HEAD~1 |
Undo a specific commit safely in shared history | git revert <commit> |
Building on the project we created earlier called my_git_project
. Let’s continue using this project to simulate common scenarios, like accidental deletions, bad commits, and recovery, so you can learn how to get back on track when things go wrong.
Recap: Setting Up my_git_project
If you haven’t already, follow these steps to create a simple project:
bashmkdir my_git_project
cd my_git_project
git init
Create and commit a file:
bashecho "Hello, Git!" > hello.txt
git add hello.txt
git commit -m "Added hello.txt with a greeting message"
Make a second change:
bashecho "Welcome to version control." >> hello.txt
git add hello.txt
git commit -m "Updated hello.txt with an additional message"
Now, we have a small history to work with.
🔄 Reverting to Previous Versions
Let’s say you realize that the second commit was a mistake. You want to go back to the first version.
Temporary Switch to an Earlier Commit (Checkout)
You can view the state of the project at the initial commit without changing your branch:
-
Get the commit hash for the initial commit:
bashgit log --oneline
Example output:
text9e2b4d2 Updated hello.txt with an additional message 4f6b3c2 Added hello.txt with a greeting message
-
Temporarily switch to the first commit:
bashgit checkout 4f6b3c2
You are now in "detached HEAD" state, viewing the project as it was at the initial commit. To return to the latest state:
bashgit checkout main
Permanently Go Back (Reset)
If you want to undo the second commit and erase it from history:
bashgit reset --hard 4f6b3c2
This will delete the second commit and any changes after it. Use this with caution—it’s permanent!
Safe Rollback Without Losing History (Revert)
Instead of erasing history, you can undo the effects of a specific commit by creating a new commit:
bashgit revert 9e2b4d2
This will create a new commit that undoes the changes made in the second commit. The history remains intact, which is safer for shared projects.
🛠️ Undoing Commits & Changes
Sometimes, you need to undo recent work without deleting your progress.
Undo the Last Commit but Keep Changes (Soft Reset)
Let’s say you committed changes, but realized you forgot to add something. You can undo the last commit while keeping your changes staged:
bashgit reset --soft HEAD~1
This moves the commit back, but your changes stay in the staging area. You can modify files and commit again.
Undo the Last Commit and Discard Changes (Hard Reset)
If you want to completely remove the last commit and all changes:
bashgit reset --hard HEAD~1
This is irreversible—use with care.
Discard Uncommitted Changes (Restore)
If you made changes to hello.txt
but want to discard them:
bashgit restore hello.txt
This reverts the file to the last committed state. To discard all files:
bashgit restore .
🕵️ Recovering Lost Commits with git reflog
What if you made a hard reset and realize you lost important work?
- View the history of HEAD movements:
bash
git reflog
Example output:
text3f9d2a1 HEAD@{0}: reset: moving to HEAD~1 9e2b4d2 HEAD@{1}: commit: Updated hello.txt with an additional message 4f6b3c2 HEAD@{2}: commit: Added hello.txt with a greeting message
-
Find the lost commit (e.g.,
9e2b4d2
), and recover it:bashgit checkout 9e2b4d2
-
Save it by creating a new branch:
bashgit branch recovered-work
-
Switch to this branch:
bashgit checkout recovered-work
This brings your lost work back into the project.
💻 Hands-on Exercise: Simulating Accidental Deletions and Recovery
- Open
my_git_project
. - Add another line to
hello.txt
:bashecho "Oops, this might be a mistake." >> hello.txt git add hello.txt git commit -m "Added a potentially bad line"
- Realize it’s a mistake and remove the last commit:
bash
git reset --hard HEAD~1
- Panic—you needed that line!
- Use
git reflog
to find the commit hash. - Recover it:
bash
git checkout <commit_hash>
- Create a branch to save it:
bash
git branch recovered-work git checkout recovered-work
This exercise teaches you that even when things seem lost, git reflog
is your safety net.
✨ Key Takeaways
Command | Purpose |
---|---|
git checkout <commit> | Temporarily view an old commit. |
git reset --hard <commit> | Permanently move branch to a previous commit (destructive). |
git revert <commit> | Create a new commit to undo a previous commit (safe). |
git reset --soft HEAD~1 | Undo last commit but keep changes staged. |
git reset --hard HEAD~1 | Undo last commit and discard changes. |
git restore <file> | Discard changes in a file. |
git reflog | View the history of HEAD movements to recover lost commits. |
Final Thought
Mistakes are part of development. Knowing how to undo changes and recover lost work will boost your confidence in using Git. Experiment with these commands in my_git_project
to get comfortable before working on larger projects.