Undoing Mistakes in Git

1. Discarding Uncommitted Changes

a) git restore <file>

  • Discards uncommitted modifications in the specified file.
  • Irreversible: once applied, changes cannot be recovered.

b) git restore -p <file>

  • The -p (patch) option lets you interactively choose hunks to discard.
  • Useful for selectively reverting parts of a file.

c) git restore .

  • Discards all uncommitted changes in the working directory since the last commit.

2. Amending the Last Commit

git commit --amend -m "New Commit Message"

  • Updates the message (and/or contents) of the last commit.
  • Do not amend commits that have already been shared with others.

3. Reverting a Specific Commit

git revert <SHA>

  • Creates a new commit that inverts the changes of the specified commit.
  • Safe way to “undo” a change without rewriting history.
  • Obtain the SHA from git log, e.g. 74e3b2b.

4. Resetting to an Earlier Commit

a) git reset --hard <SHA>

  • Moves HEAD and current branch to the given commit, discarding all subsequent commits and uncommitted changes.
  • Warning: unpushed commits are permanently lost.

b) git reset --mixed <SHA> (default)

  • Similar to --hard, but preserves changes from discarded commits as unstaged modifications.

5. Restoring a File from a Past Commit

git restore --source <SHA> -- <file>

  • Replaces the working copy of <file> with its state at the given commit.
  • Only affects the specified file.

6. Using the Reflog to Recover Lost Commits

git reflog

  • Records updates to HEAD (including resets) for a limited time.
  • Find the SHA of a lost state and then create a branch or reset to it.
Restoring After a Hard Reset
  • Locate the prior HEAD reference in the reflog.
  • Use git branch <new-branch> <reflog-SHA> to recover.
Recovering a Deleted Branch
  1. Identify the branch tip SHA via git reflog.
  2. Recreate the branch: git branch <branch-name> <SHA>.

7. Moving Commits to Another Branch

a) To a New Branch

git branch <new-branch>
git reset --hard HEAD~1
  • Creates <new-branch> at the current HEAD, then removes the last commit from the original branch.

b) To an Existing Branch

git checkout <target-branch>
git cherry-pick <SHA>

git checkout <original-branch>
git reset --hard HEAD~1
  • Cherry-picks the commit into <target-branch>, then removes it from the source branch.

8. Interactive Rebase for History Rewriting

Use with caution: Interactive rebase rewrites commit history, which can break shared branches.

git rebase -i HEAD~N

  • Opens an editor listing the last N commits before HEAD.
  • For each commit, you can choose an action:
CommandDescription
pickKeep the commit as-is.
rewordChange the commit message.
editPause during rebase to edit the commit (e.g., amend files).
squashCombine with the previous commit, combining messages.
fixupCombine with the previous commit, discarding current message.
dropRemove the commit from history.

Typical Use Cases:

  • Rewriting sloppy commit messages: Use reword.
  • Cleaning up small incremental commits: Use squash or fixup.
  • Reordering commits for clarity: Move lines up/down.
  • Removing unwanted changes: Use drop.

Workflow Example:

git rebase -i HEAD~3
  1. An editor opens with the last 3 commits.
  2. Modify the commands next to each commit.
  3. Save and exit.
  4. Git walks through the commits, applying the actions in order.

If a conflict occurs, Git will pause and let you resolve it:

  • Fix the conflict.
  • git add <file>
  • git rebase --continue

To abort the rebase at any time:

git rebase --abort

Note: Never rebase public/shared branches. Use on local feature branches only