Introduction
When working with many Git repositories daily, things can go wrong. This is especially true during a fast-paced working day. Although the name Git implies, its usage is simple, undoing things can be tricky.
With this blog post, I'd like to share how I undo various Git operations. The article was written based on git version 2.45.2.windows.1
. Also, I use posh git for the status prompt in PowerShell.
Undoing git commit
Scenario A: Adjusting a commit message
Goal
You'd like to change your last commit message or make additional changes to your last commit. You want to keep the changes you committed previously.
Pre-conditions
You've made changes and committed them to your local repository only and haven't pushed them to a remote yet. You are okay with a new commit hash replacing the previous one.
Solution
Use git commit --ammend
to change your last commit.
C:\repo [branch1]> git log --oneline -n 1
4f0c90b (HEAD -> branch1) adds file1
C:\repo [branch1]> echo "foo" >> file1.txt
C:\repo [branch1 +0 ~1 -0 !]> git commit -am "wrong commit message"
[branch1 81a502f] wrong commit message
1 file changed, 1 insertion(+)
C:\repo [branch1]> git log --oneline -n 2
81a502f (HEAD -> branch1) wrong commit message
4f0c90b adds file1
C:\repo [branch1]> git commit --amend -m "a better commit message"
[branch1 66e93d3] a better commit message
Date: Tue Jul 9 22:42:41 2024 +0200
1 file changed, 1 insertion(+)
C:\repo [branch1]> git log --oneline -n 2
66e93d3 (HEAD -> branch1) a better commit message
4f0c90b adds file1
Scenario B: Reverting a commit that hasn't been pushed yet
Goal
You've accidentally committed changes to your local repository that you'd like to undo. You'd like to keep your changes
Pre-conditions
You have committed the change to your local repository but haven't yet pushed them to a remote.
Solution
Use git reset --soft HEAD~1
to revert the previous commit.
C:\repo [branch1]> git log --oneline -n 1
66e93d3 (HEAD -> branch1) a better commit message
C:\repo [branch1]> echo "foo" >> file1.txt
C:\repo [branch1 +0 ~1 -0 !]> git commit -am "changes file1"
[branch1 39e5569] changes file1
1 file changed, 1 insertion(+)
C:\repo [branch1]> git log --oneline -n 2
39e5569 (HEAD -> branch1) changes file1
66e93d3 a better commit message
C:\repo [branch1]> git reset --soft HEAD~1
C:\repo [branch1 +0 ~1 -0 ~]> git log --oneline -n 1
66e93d3 (HEAD -> branch1) a better commit message
C:\repo [branch1 +0 ~1 -0 ~]> git status
On branch branch1
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: file1.txt
Undoing git merge
Scenario A: Revering a merge that is not pushed yet
Goal
You'd like to revert a prematurely made merge
Pre-conditions
Your local branch hasn't yet been pushed to a remote. There is nothing to commit on your local branch, and your working tree is clean
Solution
Use git reset --hard HEAD~1
to undo the merge and reset to the previous commit hash.
C:\repo [branch1 ≡]> git merge branch2
Updating 58bf207..cb93d0d
Fast-forward
file3.txt | 5 -----
1 file changed, 5 deletions(-)
delete mode 100644 file3.txt
C:\repo [branch1 ↑1]> git reset --hard HEAD~1
HEAD is now at 58bf207 changes file1
Undoing git push
Goal
You accidentally pushed to a remote and would like to undo the changes.
Pre-conditions
You ensured no other user of the repository is fetching the incorrect changes or is trying to build on top of the commit.
Solution
Use git push origin --force <hash>:<branch>
to reset the remote.
C:\repo [branch1 ↑1]> git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 275 bytes | 275.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/matthiasguentert/git-article.git
400c7dc..2d9687c branch1 -> branch1
C:\repo [branch1 ≡]> git log --oneline -n 2
2d9687c (HEAD -> branch1, origin/branch1) updates file2
400c7dc deletes file3
C:\repo [branch1 ≡]> git push origin -f 400c7dc:branch1
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To https://github.com/matthiasguentert/git-article.git
+ 2d9687c...400c7dc 400c7dc -> branch1 (forced update)
Undoing git pull
Goal
You'd like to undo a git pull
that merged changes from a remote into your working directory
Pre-conditions
You are okay to lose any staged changes
Solution
Use git reset --hard HEAD~1
to discard any local changes!
C:\repo [branch1 ↓1]> git pull
Updating 369c785..58bf207
Fast-forward
file1.txt | 1 +
1 file changed, 1 insertion(+)
C:\repo [branch1 ≡]> git reset --hard HEAD~1
HEAD is now at 369c785 some commit message
Undoing git add
Scenario A: Unstaging a staged file
Goal
You accidentally staged a file too many and would like to unstage it.
Solution
Use git restore --staged <file>
or git reset HEAD <file>
C:\repo [branch1 +1 ~1 -0 ~]> git status
On branch branch1
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: file1.txt
new file: file2.txt
# With git version >= 2.23.0 use git restore to unstage the file
C:\repo [branch1 +1 ~1 -0 ~]> git restore --staged file1.txt
# ... or git reset as an alternative
C:\repo [branch1 +1 ~1 -0 ~]> git reset HEAD file1.txt
C:\repo [branch1 +1 ~0 -0 | +0 ~1 -0 !]> git status
On branch branch1
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: file2.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: file1.txt
Unmodifying a modified file
Goal
You accidentally made changes to a file that you'd like to revert to the last commit
Pre-condition
You are aware and okay to lose your changes fully! The file you'd like to restore is in a tracked state and known to Git.
Solution
Use git restore <file>
or git checkout – <file>
C:\repo [branch1]> echo "foo" >> file1.txt
C:\repo [branch1 +0 ~1 -0 !]> git status
On branch branch1
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: file1.txt
no changes added to commit (use "git add" and/or "git commit -a")
C:\repo [branch1 +0 ~1 -0 !]> git restore file1.txt
C:\repo [branch1]> git status
On branch branch1
nothing to commit, working tree clean