Skip to main content

Undoing Mistakes with Git: Mastering Reverting and Resetting changes

Everyone makes mistakes, including accidentally committing changes in Git repositories. Fortunately, Git provides functionalities to revert these mistakes and return your project to a previous state. This blog post dives deep into git revert and git reset, explaining their differences, advantages, and when to use them.


Understanding git revert

git revert reverses the effects of a specific commit. It creates a new commit that contains the opposite changes of the commit you want to undo. This nullifies the original commit's changes, keeping your project as intended.

To use git revert, specify the commit hash you want to revert. For example, to undo changes in commit a1b2c3d:


git revert a1b2c3d

This command creates a new commit reversing the changes in a1b2c3d and adds it to your branch. You can customize the revert with options like --no-commit to stage the changes instead of creating a new commit.


Benefits of git revert

  • Preserves Git history: The original commit remains in history, and the reverted changes have a new commit reversing them. This provides a clear audit trail for future reference.
  • Maintains branch stability: Reverting with git revert doesn't change your branch's history. This keeps the branch in sync with other collaborators, avoiding the need to repush the entire branch after reverting.
  • Reverts unmerged commits: git revert can handle reverting commits that haven't been merged into the main branch yet. This is helpful for working on feature branches and undoing changes before integrating them into the main codebase.


Mastering git reset

git reset is more powerful and potentially riskier than git revert. It allows you to move the current HEAD pointer to a specific commit, effectively changing your working directory and index to that point in time.

To use git reset:


git reset <commit-hash>

This command moves the HEAD pointer to the specified commit, resetting your working directory and index to reflect that commit's state. Be cautious, as this could potentially lose uncommitted changes if you haven't staged them before executing the git reset command.


Understanding git reset options

git reset offers options depending on the desired outcome:

  • --hard: Discards all uncommitted changes in the working directory and index, forcing an exact match to the target commit. Use this option with caution as it cannot be undone.
  • --mixed (default): Resets the index but leaves the working directory untouched. Unstaged changes remain in the working directory, but staged changes are discarded.
  • --soft: Resets the HEAD pointer but leaves both the working directory and index unchanged. This is useful for undoing a git commit that hasn't been pushed yet.


Deciding between git revert and git reset

Choosing between git revert and git reset depends on the specific situation and desired outcome:

  • Revert a specific commit without changing history: Use git revert if you want to undo the effects of a specific commit without rewriting history and maintaining a clear audit trail.
  • Completely discard a commit: Use git reset --hard if you want to completely remove a commit from your branch history and delete any associated changes. Be very careful with this option as it cannot be undone.
  • Modify history for minor tweaks: Use git reset --soft to move the HEAD pointer without affecting your working directory and index. This can be helpful for making minor adjustments before committing final changes.


Real-world examples with code

Let's put our theoretical knowledge into practice with some realistic scenarios:


Scenario 1: Fixing a Broken Build

You've pushed a commit that introduced a bug, breaking the build process. You need to fix this urgently, but you don't want to lose the other changes you made in the same commit.


# Identify the commit that broke the build

git log --oneline


# Revert the specific commit while preserving the other changes

git revert HEAD^


# Push the revert commit to the remote branch

git push origin HEAD

This sequence of commands identifies the problematic commit, reverts it using git revert HEAD^ (referring to the previous commit), and immediately pushes the fix to the remote branch.


Scenario 2: Undoing Accidental File Deletion

You accidentally deleted a crucial file and committed the change. Thankfully, you realize the mistake before pushing. Here's how to recover the lost file:


# Identify the commit where the deletion occurred

git log --oneline


# Use git reset to undo the deletion without affecting other changes

git reset HEAD~ -- file-name.txt


# Stage the recovered file and commit the recovery

git add file-name.txt

git commit -m "Recovered accidentally deleted file"

This approach identifies the commit with the deletion, uses git reset HEAD~ to undo that commit, and then specifically recovers the file using -- file-name.txt. Finally, it stages and commits the recovered file.


Scenario 3: Discarding Unnecessary Changes

You've been working on a feature branch and made some changes you later realize are irrelevant or unnecessary. You want to discard those changes without affecting other parts of the branch.


# Identify the commit where the unwanted changes were introduced

git log --oneline


# Use git reset --soft to move the HEAD pointer without altering the working directory

git reset --soft HEAD~2


# Stage the desired changes and commit them

git add file1.txt file2.txt

git commit -m "Committed necessary changes"


# Delete the unwanted commit from the local branch history

git branch -d unwanted-branch

This example uses git reset --soft to move the HEAD pointer back two commits, leaving the working directory untouched. Then, it stages the desired files and commits them separately. Finally, it deletes the unwanted commit from the local branch history.


Scenario 4: Reverting a Merged Commit with Conflicts

You merged a feature branch into the main branch, but the merge resulted in conflicts that you accidentally committed without resolving them properly. You need to revert the merge and fix the conflicts before committing again.


# Identify the merge commit

git log --merges


# Revert the merge commit using git revert

git revert -m 1 HEAD


# Resolve the conflicts manually and stage the resolved files

git add file1.txt file2.txt


# Commit the conflict resolution

git commit -m "Resolved conflicts and committed merge"

This approach identifies the merge commit, reverts it using git revert -m 1 HEAD, and then allows you to manually resolve the conflicts. Finally, you stage the resolved files and commit the fix with a clear message.


Conclusion

By understanding these real-world scenarios and practicing with code examples, you'll be well-equipped to handle various situations and effectively navigate the sometimes-complex world of Git history.
Remember: Always verify the outcome of these commands before pushing your changes to a shared remote repository.


Comments

Topics

Show more