---
title: "How to delete commit history in Git, all of it or just one commit"
description: "Three ways to get a leaked secret out of a Git branch: wipe the whole history with an orphan branch, drop a single commit with rebase, or scrub a file or password from every commit with git filter-repo."
url: "https://knng.de/blog/reset-git-history/"
published: "2026-01-28"
updated: "2026-07-02"
---

# How to delete commit history in Git, all of it or just one commit

## Preface

Sometimes you accidentally commit something that shouldn't be there, like an API key, a password or a whole `.env` file.  
Deleting the file in a new commit doesn't help, it's still in the history for anyone who clones the repo.

There are three ways to get rid of it, depending on how much history you want to keep:

| Method | What's left afterwards | When to use it |
|---|---|---|
| Orphan branch | one single commit | you don't care about the history at all |
| Drop one commit | everything except that commit | the secret came in with one commit and no later commit touched that file |
| git filter-repo | everything, minus the secret | the secret is in a file you still need, or in several commits |

> **Warning:** If the leaked thing is a password or token, change it first.  
> Rewriting the history hides it from the branch, but copies can already exist elsewhere. More on that [at the end](#what-rewriting-cant-fix).

---

### Wipe the whole history (orphan branch)

The simplest way: recreate the branch as an **orphan branch**, which removes all previous commits while keeping the current files.  
This works for any branch (`main`, `bleeding-edge`, `legacy`, etc.).

> **Note:** These steps assume the branch is named `main`. Replace it with your branch name if needed.

First, check out the branch you want to clean:  
```bash
git checkout main
```

Create an orphan branch (no history, working tree unchanged):
```bash
git checkout --orphan main-clean
```

Commit the current state as the new starting point:
```bash
git add .
git commit -m "cleanup"
```

Replace the old branch and force-push it:
```bash
git branch -D main
git branch -m main-clean main
git push origin main --force
```

After this, the branch contains only one commit, and the previous history is no longer referenced by the branch.

---

### Drop a single commit

If one commit brought the secret in and nothing after it touched that file again, you don't have to throw everything away.  
Order, messages and content of all other commits stay. Only the hashes of the commits after the dropped one change.

Find the commit first:
```bash
git log --oneline
```

In my test repo that looks like this, `0db33f9` is the one that added the `.env`:
```text
6c13408 add readme
ef463f6 update app
0db33f9 add config
a4ca0b7 add app
```

Start an interactive rebase from the commit right before it:
```bash
git rebase -i 0db33f9~1
```

Your editor opens with a list of commits.  
Change `pick` to `drop` in front of the one you want gone, then save and close:
```text
drop 0db33f9 add config
pick ef463f6 update app
pick 6c13408 add readme
```

> **Info:** If you'd rather skip the editor, this does the same in one line:  
> `git rebase --onto 0db33f9~1 0db33f9`

Check that it's gone. `-S` searches every commit on the branch for a string:
```bash
git log --oneline -S "supersecret123"
```

No output means no commit on the branch contains it anymore.  
Then push the rewritten branch:
```bash
git push --force-with-lease origin main
```

> **Note:** `--force-with-lease` only overwrites the remote if nobody else pushed in the meantime.  
> A plain `--force` would silently throw their work away.

If a later commit changed the same file, the rebase stops with a conflict.  
That usually means the secret is in those later commits too, and the next method is the better tool.

---

### Remove a file or a password from every commit

`git filter-repo` rewrites the whole history in one go, and it's what GitHub itself recommends for this.  
You'll also find BFG Repo-Cleaner in older guides, it does something similar, but I only tested filter-repo.

Install it:
```bash
pipx install git-filter-repo
```

Most distributions also package it under the same name.

filter-repo wants a fresh clone, so it can't touch anything you haven't pushed yet:
```bash
git clone https://github.com/<user>/<repo>.git
cd <repo>
```

**Option 1: remove a file from all commits**
```bash
git filter-repo --sensitive-data-removal --invert-paths --path .env
```

The file is gone from every commit.  
A commit that only added that file ends up empty, and filter-repo removes it entirely.

**Option 2: keep the file, but replace the secret everywhere**

Create a `replacements.txt` next to (not inside) the repo, one line per secret:
```text
hunter2secret==>REMOVED
```

Then run:
```bash
git filter-repo --sensitive-data-removal --replace-text ../replacements.txt
```

In my test, `config.yml` stayed in all three commits, just with `password: REMOVED` instead of the real value.

> **Note:** `--sensitive-data-removal` needs git-filter-repo 2.47 or newer.  
> It also fetches every branch and tag from the remote first, so nothing with the secret in it gets left behind.

Check that it's gone, this time across all branches:
```bash
git log --all --oneline -S "hunter2secret"
```

Then push everything back:
```bash
git push --force --mirror origin
```

> **Warning:** `--mirror` makes the remote look exactly like your local clone, including deleting remote branches that don't exist locally.  
> That's why this should only be run from the fresh clone that filter-repo just rewrote.

---

### What rewriting can't fix

After the force-push, the branch is clean. But in my test, the old commit was still on the server, it just wasn't part of any branch anymore:
```bash
git cat-file -t 0db33f9
git show 0db33f9:.env
```

```text
commit
API_KEY=supersecret123
```

Git only deletes commits that nothing points to during garbage collection, and on GitHub you don't control when that happens.  
On top of that:

* Everyone who cloned the repo before still has the old history.
* Forks keep their own copy, and you can't clean those.
* Pull requests on GitHub keep pointing at the old commits. According to GitHub's docs, only GitHub Support can clean those up, and only if changing the secret isn't enough.

So once a secret was pushed, CHANGE IT!

Cheers.

