Git Push --force vs --force-with-lease: Key Differences Explained for Developers

Git has revolutionized collaboration in software development, enabling teams to track changes, experiment, and merge code seamlessly. A common task in Git workflows is pushing local changes to a remote repository. But what happens when your local branch history diverges from the remote—for example, after rewriting commits with git commit --amend or rebasing? This is where force pushing enters the picture.

Force pushing (git push --force) is a powerful but risky command that overwrites the remote branch with your local history. While it’s occasionally necessary, it can easily erase teammates’ work if misused. Enter git push --force-with-lease: a safer alternative that adds a critical safety check before overwriting the remote.

In this blog, we’ll demystify both commands, explore their inner workings, highlight key differences, and guide you on when to use each. By the end, you’ll understand how to avoid catastrophic mistakes and push changes confidently—even when rewriting history.

Table of Contents#

  1. Understanding Git Push Basics
  2. What is git push --force?
  3. The Risks of git push --force
  4. What is git push --force-with-lease?
  5. How --force-with-lease Works Under the Hood
  6. Key Differences: --force vs --force-with-lease
  7. When to Use Each Command
  8. Best Practices for Safe Force Pushing
  9. Real-World Scenarios and Examples
  10. Conclusion
  11. References

Understanding Git Push Basics#

Before diving into force pushing, let’s recap how normal git push works. When you run git push <remote> <branch>, Git attempts to update the remote branch with your local commits. For this to succeed without force, your local branch must be a fast-forward of the remote branch. In other words:

  • The remote branch has no new commits that your local branch lacks.
  • Your local branch has new commits added on top of the remote’s history.

If the remote branch has diverged (e.g., a teammate pushed commits while you were working), Git rejects the push with a "non-fast-forward" error:

! [rejected]        main -> main (non-fast-forward)  
error: failed to push some refs to 'https://github.com/your/repo.git'  
hint: Updates were rejected because the tip of your current branch is behind  
hint: its remote counterpart. Integrate the remote changes (e.g.  
hint: 'git pull ...') before pushing again.  

To resolve this, you’d typically git pull to fetch and merge the remote changes, then push again. But what if you intentionally rewrote your local history (e.g., to clean up commits with git rebase -i or git commit --amend)? In that case, your local history no longer aligns with the remote, and a normal push will fail. This is where force pushing comes into play.

What is git push --force?#

git push --force (or -f for short) is a command that unconditionally overwrites the remote branch with your local branch’s history. It ignores the "non-fast-forward" check and replaces the remote’s version of the branch with yours.

How It Works:#

When you run git push --force <remote> <branch>, Git tells the remote: "Discard your current history for this branch and replace it with mine."

Example:#

Suppose you commit a typo, amend it locally, and try to push:

# Make a commit  
git add .  
git commit -m "Add login feature"  
 
# Realize there's a typo; amend the commit  
git commit --amend -m "Add login feature (fix typo)"  
 
# Normal push fails (history diverged)  
git push origin main  
# ! [rejected]        main -> main (non-fast-forward)  
 
# Force push to overwrite remote  
git push --force origin main  
# Enumerating objects: 5, done.  
# Counting objects: 100% (5/5), done.  
# ...  
# To https://github.com/your/repo.git  
# + a1b2c3d...e4f5g6h main -> main (forced update)  

When It’s Tempting to Use:#

  • Rewriting commits in a personal branch (no collaborators).
  • Cleaning up a messy commit history before merging to main.
  • Undoing a previous push (e.g., accidental sensitive data exposure).

The Risks of Using git push --force#

While --force is powerful, it’s also dangerous. Here’s why:

1. Overwriting Teammates’ Work#

If a teammate pushed commits to the remote branch after you last pulled, --force will delete their commits. For example:

  • Alice pushes commit A to main.
  • Bob pulls A, works, and pushes commit B (remote now has A → B).
  • Alice, unaware of B, amends A to A' and runs git push --force.
  • Remote main is now A'—Bob’s commit B is erased!

2. Losing Critical History#

Force pushing replaces the remote’s history, making it hard to recover lost commits unless someone else has a local copy. Even with git reflog, recovery is time-consuming and error-prone.

3. Breaking CI/CD Pipelines#

If the remote branch is linked to CI/CD, overwriting history can invalidate builds, rollbacks, or deployments tied to old commit hashes.

What is git push --force-with-lease?#

git push --force-with-lease is a safer alternative to --force that prevents accidental overwrites of remote changes you haven’t fetched. It acts as a "conditional force push": it will only overwrite the remote branch if no one else has pushed new commits since you last fetched.

How It Works at a High Level:#

Before overwriting the remote, --force-with-lease checks: "Does the remote branch have any commits I don’t have locally?" If yes, it aborts the push. If no, it proceeds with the force push.

Example:#

Using the earlier typo scenario, but with --force-with-lease:

# Amend the commit (same as before)  
git commit --amend -m "Add login feature (fix typo)"  
 
# Safely force push with lease check  
git push --force-with-lease origin main  
# Enumerating objects: 5, done.  
# ...  
# To https://github.com/your/repo.git  
# + a1b2c3d...e4f5g6h main -> main (forced update)  

If a teammate had pushed new commits in the meantime:

git push --force-with-lease origin main  
# To https://github.com/your/repo.git  
# ! [rejected]        main -> main (stale info)  
# error: failed to push some refs to 'https://github.com/your/repo.git'  
# hint: Updates were rejected because the remote contains work that you do  
# hint: not have locally. This is usually caused by another repository pushing  
# hint: to the same ref. You may want to first integrate the remote changes  
# hint: (e.g., 'git pull ...') before pushing again.  

Git detects the remote has new commits and blocks the push—saving you from overwriting your teammate’s work!

How --force-with-lease Works Under the Hood#

To understand --force-with-lease, we need to peek at how Git tracks remote branches.

Git’s "Lease" Check:#

When you clone or fetch a repository, Git stores the last known state of remote branches in your local .git directory (e.g., refs/remotes/origin/main). This is your "lease" on the remote branch’s history.

--force-with-lease compares your local tracking branch (e.g., origin/main) with the actual remote branch. If they match (no new commits on the remote), Git allows the force push. If they don’t (remote has new commits), it aborts.

Technical Deep Dive:#

  • FETCH_HEAD: When you run git fetch, Git records the remote’s latest commit hash in FETCH_HEAD.
  • Lease Validation: --force-with-lease uses FETCH_HEAD (or your local tracking branch) to check if the remote has changed. If the remote’s current commit hash matches FETCH_HEAD, the lease is valid; otherwise, it’s "stale."

You can even customize the lease check (e.g., specify a specific expected commit hash), but the default behavior works for most cases.

Key Differences: --force vs --force-with-lease#

Featuregit push --forcegit push --force-with-lease
SafetyUnsafe: Overwrites remote unconditionally.Safe: Overwrites only if remote is unchanged.
Remote Change CheckNone. Ignores new remote commits.Checks for new remote commits (via lease).
Behavior on Diverged RemoteOverwrites remote, deleting new commits.Aborts push with an error.
Risk of Data LossHigh (especially in shared repos).Low (prevents accidental overwrites).
Use CasePersonal repos with no collaborators.Shared repos; any scenario with team members.

When to Use Each Command#

Use git push --force Only If:#

  • You’re the sole contributor to the repository/branch (no teammates will push to it).
  • You’re absolutely certain no one has pushed changes since you last fetched (e.g., a private side project).
  • You’ve explicitly coordinated with your team to overwrite the remote (e.g., rolling back a production incident).

Use git push --force-with-lease (Almost) Always:#

  • Shared repositories: Even if you "think" no one else pushed, --force-with-lease adds a safety net.
  • Rewriting history in team branches: After rebasing or amending commits in a shared feature branch.
  • As a default: Many developers alias git push --force-with-lease to git pushf for convenience (see Best Practices below).

Best Practices for Safe Force Pushing#

  1. Always Fetch Before Force Pushing
    Run git fetch (or git pull --rebase) to ensure you have the latest remote changes before using --force or --force-with-lease. This minimizes the chance of divergence.

  2. Alias --force-with-lease for Convenience
    Make --force-with-lease your go-to by setting a global alias:

    git config --global alias.pushf 'push --force-with-lease'  

    Now you can run git pushf origin main instead of typing the full command.

  3. Communicate with Your Team
    If you need to force push to a shared branch, announce it in your team chat (e.g., "Force pushing to feature/login to clean up commits—pull after 5 minutes!").

  4. Avoid Force Pushing to Protected Branches
    Most Git hosting platforms (GitHub, GitLab) let you mark branches like main as "protected," blocking force pushes entirely. Use this to enforce safety.

  5. Consider Alternatives to Force Pushing
    For shared branches, git revert (creates a new commit undoing changes) is often safer than rewriting history. Reserve force pushing for cases where history cleanup is critical.

Real-World Scenarios and Examples#

Scenario 1: Solo Project (Safe with --force, but --force-with-lease Still Better)#

You’re working alone on a personal blog repo. You amend a commit and want to push:

# Amend the commit  
git commit --amend -m "Fix typo in about page"  
 
# Push with --force (tempting, but unnecessary risk)  
git push -f origin main  
 
# Better: Use --force-with-lease (no downside here)  
git push --force-with-lease origin main  

Even solo, --force-with-lease protects against accidental pushes if you forgot you fetched changes from another device (e.g., your laptop and desktop).

Scenario 2: Team Collaboration (Disaster with --force, Saved by --force-with-lease)#

  • Team Setup: You and your teammate, Priya, are working on feature/payment.
  • Timeline:
    1. Priya pushes commit A to feature/payment.
    2. You pull A, work, and push commit B (remote: A → B).
    3. Priya, unaware of B, amends A to A' (local history: A').
    4. Priya runs git push --force: Remote becomes A', deleting your commit B. Disaster!
    5. If Priya used git push --force-with-lease: Git detects B (which Priya doesn’t have), aborts the push, and Priya is prompted to pull first.

Scenario 3: Recovering from a Failed --force-with-lease#

If --force-with-lease aborts, here’s how to fix it:

# 1. Fetch the remote changes  
git fetch origin feature/payment  
 
# 2. Rebase your changes on top of the remote (to integrate new commits)  
git rebase origin/feature/payment  
 
# 3. Now push with --force-with-lease (safe, since you’ve integrated remote changes)  
git push --force-with-lease origin feature/payment  

Conclusion#

Force pushing is a powerful tool for managing Git history, but it comes with significant risks—especially in collaborative environments. git push --force is an unconditional overwrite that can erase teammates’ work, while git push --force-with-lease adds a critical safety check to prevent accidental data loss.

The golden rule: Use --force-with-lease by default, reserve --force for solo projects, and always communicate with your team before force pushing to shared branches. With these practices, you’ll keep your repository history clean and your teammates happy.

References#