Git is a powerful tool for managing code, and one of its most important features is the ability to "reset" changes. Resetting in Git refers to moving the current branch backward to a specific commit, effectively "undoing" certain changes. However, Git provides different levels of reset, allowing you to adjust whether you want to change just the commit history, the working directory, or both. The git reset command is often used to undo mistakes, clean up commit history, or remove changes from a staging area.
In this chapter, we’ll take a deep dive into resetting changes in Git, explore various forms of git reset
, and examine different scenarios where it can be applied. We will provide detailed examples along with explanations of what happens in each case.
Git reset is a versatile command with three main modes:
The purpose of git reset
is to move the HEAD (current commit pointer) to a specific commit while controlling how far you “reset” your work. Unlike git revert
, which creates a new commit that undoes the changes, git reset
rewrites the history by deleting commits and changes.
The basic syntax of git reset
is:
$ git reset []
Where:
<mode>
can be one of --soft
, --mixed
, or --hard
.<commit>
refers to the commit hash or reference point to which you want to reset.The commit can be specified as a commit hash, or as relative references like HEAD~1
(one commit before HEAD) or HEAD~3
(three commits before HEAD).
A soft reset moves the HEAD pointer to a previous commit but does not affect the index (staging area) or working directory. This is often used to modify commit history while keeping all changes staged.
Imagine you have made three commits, but realize you want to go back and make some changes before the latest commit:
$ git log --oneline
a1b2c3d Third commit
9e8f7g6 Second commit
5d4f3e2 First commit
To reset the repository back to the second commit, run:
$ git reset --soft 9e8f7g6
Explanation: This command moves the HEAD pointer back to 9e8f7g6
(the second commit), and all changes from a1b2c3d
(the third commit) remain in the staging area, ready to be recommitted.
After running the soft reset, the log will show:
$ git log --oneline
9e8f7g6 Second commit
5d4f3e2 First commit
The changes from the third commit (a1b2c3d
) are now in the staging area, waiting to be committed again if needed.
A mixed reset is the default mode of git reset
. It moves the HEAD to a previous commit and resets the index (staging area) but leaves your working directory unchanged. This means your changes remain in the working directory, but they are no longer staged for commit.
Let’s say you’ve made some changes and staged them, but now you realize that you don’t want to commit them just yet. Here’s the commit history:
$ git log --oneline
a1b2c3d Third commit
9e8f7g6 Second commit
5d4f3e2 First commit
To reset the repository to the second commit without losing your changes in the working directory, run:
$ git reset 9e8f7g6
Explanation: This command resets the HEAD pointer to the second commit, removes changes from the staging area (index), but leaves them in the working directory.
$ git status
On branch main
Changes not staged for commit:
modified: file.txt
Your changes are still present but now appear as untracked or modified files that are no longer staged.
A hard reset is the most drastic form of reset. It moves the HEAD to a specific commit and removes both staged and unstaged changes from the index and working directory. Essentially, it completely erases the changes made after the reset point.
Imagine you made several commits, but now you want to completely remove all the changes and reset to an earlier state:
$ git log --oneline
a1b2c3d Third commit
9e8f7g6 Second commit
5d4f3e2 First commit
To remove all changes after the second commit:
$ git reset --hard 9e8f7g6
Explanation: This command moves the HEAD pointer back to 9e8f7g6
and deletes all changes made after this commit from both the staging area and working directory.
$ git status
On branch main
nothing to commit, working tree clean
The working directory and commit history have been reset to the state of the second commit.
Warning: Hard resets can result in permanent loss of changes. It’s advisable to use this command with caution.
One of the most common use cases for git reset
is undoing a commit that hasn’t been pushed to a remote repository. In this case, using git reset --soft
allows you to undo the commit while keeping your changes staged.
For example, if you committed changes by mistake, simply run:
$ git reset --soft HEAD~1
This will undo the last commit while keeping the changes staged, so you can amend or modify the commit message.
You can use git reset
to unstage files without deleting the changes. This is especially useful when you want to review or modify files before committing them.
To unstage all changes, use:
$ git reset HEAD
This resets the staging area but keeps your working directory intact.
Understanding the difference between git reset
, git revert
, and git checkout
is crucial as they all modify the state of your repository, but in different ways.
git reset
: Moves the HEAD pointer to a specified commit and can modify the staging area and working directory depending on the mode.git revert
: Creates a new commit that undoes changes introduced by a previous commit, preserving the commit history.git checkout
: Temporarily switches branches or views a previous state without changing the commit history.Suppose you have the following commit history:
A---B---C---D (HEAD)
git reset --hard B
: Moves the HEAD to commit B
and discards all changes in commits C
and D
.
git revert C
: Creates a new commit that undoes the changes in commit C
, keeping C
in the history but negating its effects.
git checkout B
: Temporarily switches to commit B
, but no changes are made to the history. You can explore the state of the repository at commit B
without altering future commits.
--hard
Reset on Shared BranchesA hard reset rewrites history and can cause problems for other collaborators if they are working on the same branch. Only use git reset --hard
on local or private branches.
To avoid needing drastic resets, commit your work often. This creates multiple checkpoints, allowing you to reset or revert more granularly if mistakes are made.
When you need to amend or fix mistakes in your most recent commits, a soft reset is useful. It allows you to uncommit changes while keeping them staged, which is ideal for modifying commit messages or combining multiple commits. For example, if you want to fix a typo in your commit message, you could use:
$ git reset --soft HEAD~1
This command will move the HEAD to the commit before the last one, leaving the changes in the staging area. You can then modify the changes or update the commit message before recommitting.
Before performing a reset, especially a hard reset, it is prudent to create a backup branch. This allows you to preserve the current state of your branch and easily recover if needed.
To create a backup branch before a hard reset:
$ git branch backup-branch
$ git reset --hard HEAD~3
In this example, backup-branch
saves the state before resetting the branch three commits back. If something goes wrong, you can switch back to backup-branch
:
$ git checkout backup-branch
Always review the changes that will be affected by a hard reset. Use git status
and git diff
to inspect the state of your working directory and staging area before running a hard reset:
$ git status
$ git diff
This ensures you’re aware of any uncommitted changes that might be lost.
If you accidentally perform a hard reset and lose valuable work, you can try to recover it using git reflog
. The reflog records changes to the HEAD, allowing you to find the commit you reset away from.
To find the lost commit:
$ git reflog
This will show a list of recent commits and resets. You can use the commit hash from the reflog to restore your lost work:
$ git checkout
If you reset to an earlier commit and have conflicts with uncommitted changes, resolve these conflicts as you would in a normal merge. Use git status
to see which files are conflicting and edit them manually or use a merge tool.
To resolve conflicts:
$ git status
Edit the conflicting files, stage the resolved changes:
$ git add
And then commit the resolution:
$ git commit
Key takeaways:
Resetting changes in Git provides powerful tools for managing your project’s commit history and working directory. By understanding the different types of resets—soft, mixed, and hard—you can effectively manage your repository and undo changes according to your needs. Happy coding !❤️