Git Reset and Force Push: Rewriting History Safely

Master git reset --hard and force push to rewrite Git history. Learn when these powerful commands are appropriate, understand the dangers, and follow best practices to avoid losing work.

10 min read
PreviousNext

Sometimes you need to completely remove commits from history - not just undo their effects, but erase them as if they never existed. This is where git reset --hard and force push come in. These are powerful tools that can save you from embarrassing mistakes, but they can also cause serious problems if misused.

🚨

Warning: The commands in this guide can permanently delete commits and cause problems for team members. Only use them when you fully understand the consequences!

💡

What You'll Learn: In this comprehensive guide, you'll master:

  • Understanding git reset modes (soft, mixed, hard)
  • Using git reset --hard to remove commits
  • When and how to force push safely
  • The relationship between local and remote after reset
  • Recovering from mistakes with git reflog
  • Best practices for rewriting history

Understanding Git Reset Modes

Git reset has three modes that affect different areas:

+-------------------+-------------------+-------------------+
|    REPOSITORY     |   STAGING AREA    | WORKING DIRECTORY |
|    (commits)      |     (index)       |    (files)        |
+-------------------+-------------------+-------------------+
         |                   |                    |
   --soft resets       --mixed resets      --hard resets
   (this only)         (this + above)      (all three)

The Three Reset Modes

ModeRepositoryStaging AreaWorking DirectoryUse Case
--softResetKeepKeepRedo commit message
--mixedResetResetKeepUnstage files
--hardResetResetResetComplete undo
⚠️

Key Point: --hard is the only mode that affects your actual files. It will discard all uncommitted changes!

Scenario: Removing Commits with Reset

Let's walk through a real example. Here's our starting state:

git log

Output:

commit 6ba2cf9d542e08e35c3a44d18dc30d803e081acf (HEAD -> master)
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date:   Mon Jan 26 15:00:47 2026 +0500

    updated first.txt

commit ca0a0bc990527f72ae92d8a7f0cece1461d7684b
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date:   Mon Jan 26 14:59:58 2026 +0500

    updated third.txt

commit a2464fe62296925ce69366ea3c7d9de4fb95e29a
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date:   Mon Jan 26 14:59:03 2026 +0500

    added fourth.txt

commit ce0024b5fc0365ab9830c94eee7be465b141a617 (hello/master)
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date:   Mon Jan 26 14:42:05 2026 +0500

    Revert "added fourth.txt"

Current State Visualized

                                    hello/master
                                         |
    ce0024b ----> a2464fe ----> ca0a0bc ----> 6ba2cf9
       |            |              |            |
    "Revert      "added        "updated     "updated
    fourth.txt"  fourth.txt"   third.txt"   first.txt"
                                               |
                                              HEAD
                                            (master)

We want to go back to a2464fe (added fourth.txt), removing the last two commits.

Step 1: Perform the Reset

git reset --hard a2464fe

Output:

HEAD is now at a2464fe added fourth.txt

What Just Happened

Before reset --hard:

    ce0024b ----> a2464fe ----> ca0a0bc ----> 6ba2cf9  (HEAD)
                                               |
                                      These commits exist

After reset --hard:

    ce0024b ----> a2464fe  (HEAD)
                     |
                   master
                                ca0a0bc ----> 6ba2cf9
                                        |
                               These commits are ORPHANED
                               (still exist, but unreachable)

Step 2: Verify the Reset

git log

Output:

commit a2464fe62296925ce69366ea3c7d9de4fb95e29a (HEAD -> master)
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date:   Mon Jan 26 14:59:03 2026 +0500

    added fourth.txt

commit ce0024b5fc0365ab9830c94eee7be465b141a617
Author: Owais Abbasi <owais.abbasi9@gmail.com>
Date:   Mon Jan 26 14:42:05 2026 +0500

    Revert "added fourth.txt"

The two commits (ca0a0bc and 6ba2cf9) are gone from the visible history!

Check the Status

git status

Output:

On branch master
Your branch is behind 'hello/master' by 2 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

nothing to commit, working tree clean
💡

Important: Git says we're "behind" the remote. This is because the remote still has the commits we just removed locally!

The Local vs Remote Problem

After reset, your local and remote are out of sync:

Your Local:           GitHub (Remote):

    a2464fe (HEAD)        ce0024b
       |                     |
     master               a2464fe
                             |
                          ca0a0bc
                             |
                          6ba2cf9  <-- hello/master

If you try a normal push:

git push

Output:

To https://github.com/owais-io/secondproject.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/owais-io/secondproject.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.

Git refuses because pushing would lose commits on the remote.

Step 3: Force Push

To override the remote with your local state, use force push:

git push -f hello master

Or the longer form:

git push --force hello master

Output:

Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To https://github.com/owais-io/secondproject.git
 + 6ba2cf9...a2464fe master -> master (forced update)

After Force Push

Your Local:           GitHub (Remote):

    a2464fe (HEAD)        a2464fe  <-- hello/master
       |                     |
     master               (same!)

    The remote now matches your local.
    The old commits are GONE from both places.

Force Push Options

CommandDescriptionSafety Level
git push -fForce push, overwrites remoteDangerous
git push --forceSame as -fDangerous
git push --force-with-leaseForce push only if remote hasn't changedSafer

The Safer Option: --force-with-lease

git push --force-with-lease origin master

This only succeeds if no one else has pushed to the branch since your last fetch. It prevents accidentally overwriting a teammate's work.

Recovering Lost Commits with Reflog

Made a mistake? Git keeps a log of all HEAD movements:

git reflog

Output:

a2464fe (HEAD -> master) HEAD@{0}: reset: moving to a2464fe
6ba2cf9 HEAD@{1}: commit: updated first.txt
ca0a0bc HEAD@{2}: commit: updated third.txt
a2464fe (HEAD -> master) HEAD@{3}: commit: added fourth.txt

To recover the lost commits:

# Go back to the state before reset
git reset --hard 6ba2cf9

Lifesaver: Reflog entries are kept for about 90 days by default. If you accidentally reset, you can usually recover!

When to Use Reset + Force Push

Appropriate Situations

SituationSafe to Use?
Personal branch, only you use itYes
Removing sensitive data (passwords, keys)Yes (then rotate credentials!)
Cleaning up messy commits before PR reviewYes
Feature branch before mergeYes

Dangerous Situations

SituationSafe to Use?
Shared branch (main, master, develop)NO!
After others have pulled your commitsNO!
Production branchNO!
When you're unsureNO!

Complete Workflow Example

Here's the full workflow from the terminal logs:

# 1. Check current state
git log --oneline
# 6ba2cf9 (HEAD -> master) updated first.txt
# ca0a0bc updated third.txt
# a2464fe added fourth.txt
# ce0024b (hello/master) Revert "added fourth.txt"

# 2. Push current state to remote (so both are in sync)
git push hello master

# 3. Now both local and remote have all commits
git log --oneline
# 6ba2cf9 (HEAD -> master, hello/master) updated first.txt

# 4. Reset to remove last 2 commits
git reset --hard a2464fe
# HEAD is now at a2464fe added fourth.txt

# 5. Local is now behind remote
git status
# Your branch is behind 'hello/master' by 2 commits

# 6. Normal push fails
git push
# ! [rejected] (non-fast-forward)

# 7. Force push to update remote
git push -f hello master
# + 6ba2cf9...a2464fe master -> master (forced update)

# 8. Verify both match
git log --oneline
# a2464fe (HEAD -> master, hello/master) added fourth.txt

Reset vs Revert: Decision Guide

                    Need to undo commits?
                           |
           +---------------+---------------+
           |                               |
     Already pushed?                 Only local?
           |                               |
    +------+------+                  Use git reset
    |             |                  (simpler)
  Shared       Personal
  branch?      branch?
    |             |
Use revert    Can use either
(safe)        (reset is cleaner)

Best Practices

1. Always Check Before Resetting

# See what you're about to lose
git log HEAD..origin/master --oneline

# Create a backup branch
git branch backup-before-reset

2. Communicate with Your Team

Before force pushing:

  • Announce in your team channel
  • Make sure no one has unpushed work
  • Consider if revert is a better option

3. Use --force-with-lease

# Instead of
git push -f origin branch

# Use
git push --force-with-lease origin branch

4. Know Your Escape Route

# If something goes wrong
git reflog  # Find the commit you want
git reset --hard <commit>  # Go back

Quick Reference

# Reset modes
git reset --soft HEAD~1   # Undo commit, keep changes staged
git reset --mixed HEAD~1  # Undo commit, unstage changes (default)
git reset --hard HEAD~1   # Undo commit, discard all changes

# Reset to specific commit
git reset --hard <commit-hash>

# Force push
git push -f origin branch
git push --force origin branch
git push --force-with-lease origin branch  # Safer

# Recovery
git reflog                    # See all HEAD movements
git reset --hard HEAD@{n}     # Go back n steps

Summary

git reset --hard and force push are powerful tools for rewriting history:

  • Reset --hard: Moves HEAD and discards commits (locally)
  • Force push: Overwrites remote with your local history
  • Use with caution: Can cause problems for teammates
  • Prefer revert: For shared branches, use git revert instead
  • Know recovery: Use git reflog if you make mistakes

Remember: With great power comes great responsibility. These commands can save you, but they can also cause chaos. Always think twice before rewriting shared history!

Thank you for reading!

Published on December 24, 2025

Owais

Written by Owais

I'm an AIOps Engineer with a passion for AI, Operating Systems, Cloud, and Security—sharing insights that matter in today's tech world.

I completed the UK's Eduqual Level 6 Diploma in AIOps from Al Nafi International College, a globally recognized program that's changing careers worldwide. This diploma is:

  • ✅ Available online in 17+ languages
  • ✅ Includes free student visa guidance for Master's programs in Computer Science fields across the UK, USA, Canada, and more
  • ✅ Comes with job placement support and a 90-day success plan once you land a role
  • ✅ Offers a 1-year internship experience letter while you study—all with no hidden costs

It's not just a diploma—it's a career accelerator.

👉 Start your journey today with a 7-day free trial

Related Articles

Continue exploring with these handpicked articles that complement what you just read

9 min read

Git Rebase: Creating a Clean Linear History

Master git rebase to create clean, linear commit histories. Learn how rebase works, when to use it instead of merge, and follow best practices to avoid common pitfalls with practical examples.

#git#version-control+4 more
Read article

More Reading

One more article you might find interesting