Git Revert and Reset

Understand how to revert (undo) changes made in commits using 'git revert'. Learn the different types of reset (soft, mixed, hard) and their effects using 'git reset'.


Git Reset - Hard Mode

Let's talk about the most forceful tool in the git reset arsenal: git reset --hard. This command is powerful but comes with a significant warning: Data Loss. Use it with extreme caution.

Detailed Explanation: git reset --hard

git reset --hard is used to completely reset your branch's head to a specified commit. Unlike git reset --soft or git reset --mixed (the default), --hard not only moves the branch pointer but also discards all changes in both the staging area (index) and the working directory.

Effect on the Staging Area (Index)

All changes that were staged (i.e., added with git add) but not yet committed are permanently removed. They are effectively undone and are no longer tracked by Git.

Effect on the Working Directory

git reset --hard also overwrites all changes in your working directory to match the specified commit. This means any modifications, additions, or deletions you've made to files that haven't been staged or committed will be irreversibly lost. This is the primary reason for the "Data Loss" warning.

How it Works

  1. Branch Pointer Movement: First, the branch pointer (e.g., HEAD for your current branch) is moved to the specified commit.
  2. Staging Area Reset: The staging area is then cleared and updated to match the files as they existed in that commit.
  3. Working Directory Reset: Finally, the files in your working directory are overwritten to match the files in the staging area (which now matches the specified commit).

Example

Let's say you have the following commits:

 A -> B -> C (HEAD is at C) 

And you run:

 git reset --hard B 

Here's what happens:

  • The branch pointer (HEAD) now points to commit B.
  • The staging area is reset to the state of commit B.
  • Your working directory is overwritten to match the files in commit B. Any changes you had in the staging area or working directory since commit B are gone.

Your history now looks like this:

 A -> B (HEAD)  C [Commit C is now orphaned and may be garbage collected eventually] 

When to Use (and When to Avoid) git reset --hard

  • Use: When you've made a series of experimental commits that you want to completely discard, and you are absolutely certain you don't need them. A common scenario is when working on a local branch and experimenting with features before pushing.
  • Avoid: When you have important, uncommitted changes that you want to keep. Before using git reset --hard, consider alternatives like:
    • Stashing: Use git stash to temporarily save your changes.
    • Creating a new branch: Create a new branch with git checkout -b new-branch-name to preserve your changes on a separate branch.
    • Committing: Commit your changes, even if they are incomplete, to at least preserve them in your local history. You can always amend or squash them later.
  • Never use on shared/public branches: Using git reset --hard on a branch that others are collaborating on will rewrite history and cause significant problems for your team.

WARNING: Data Loss

This cannot be stressed enough. git reset --hard is a destructive operation. Ensure you have backed up or stashed any important changes before running this command. There is no easy "undo" after using git reset --hard, especially if the commits you're resetting to are not recent.

Consider adding git reset --hard to your .gitconfig file with an alias that provides an extra layer of safety, such as:

 [alias]
          force-reset = !sh -c 'echo "Are you absolutely sure you want to hard reset? (y/N)" && read -r answer && if [[ "$answer" == "y" ]]; then git reset --hard "$@"; else echo "Reset aborted."; fi' 

This will prompt for confirmation before executing the command.