Skip to main content

Why Fresh Clones Are Required

Since git-filter-repo does irreversible rewriting of history, it’s critical to avoid making changes to a repository without a good backup. The primary defense is the fresh clone requirement.
History rewriting cannot be undone. Once you’ve rewritten history with git-filter-repo, the original commits are gone. Working from a fresh clone ensures you can always go back to the original by re-cloning.

The Safety Philosophy

While there’s no perfect way to check if a user has a good backup, git-filter-repo asks a related question that serves as an excellent proxy:
“Is this repository a fresh clone?”If yes, then the user has a backup (the original repository they cloned from).
This is a much more reliable safety mechanism than git filter-branch’s approach of writing copies of original refs to a special namespace (which most users don’t know how to use for recovery).

What Defines a “Fresh Clone”?

Git doesn’t provide a way to definitively answer “is this a fresh clone?” but git-filter-repo checks approximately a dozen conditions that are always true of brand new clones.

Fresh Clone Checks

git-filter-repo verifies that your repository:
A fresh clone has all objects in a single packfile, with no loose objects (or fewer than 100 if the repo is very small).Check: Number of packfiles must be 0-1, loose objects must be < 100
Fresh clones have one remote: origin, pointing to where you cloned from.Exception: A brand new bare repository with no packs and no remotes is also acceptable.
Fresh clones have only one entry in each reflog (the initial clone).Check: All files in .git/logs/ must have exactly one line.
Fresh clones don’t have any stashed changes.Check: refs/stash must not exist.
Fresh clones have a clean working directory.Check: git diff --staged --quiet and git diff --quiet both succeed.
Fresh clones don’t have any untracked files.Check: git ls-files -o returns nothing (excluding expected directories like .git/).
Fresh clones have standard Git directory structure.Check:
  • Bare repos: GIT_DIR must be .
  • Non-bare repos: GIT_DIR must be .git
On case-insensitive filesystems (macOS, Windows), refs must not differ only by case.Check: No refs like refs/heads/Main and refs/heads/main simultaneously.
On macOS and other normalizing filesystems, refs must not differ only in Unicode normalization.Check: No refs that normalize to the same NFC form.

False Positives and False Negatives

The fresh clone check is not perfect:

False Negatives (Good Backups Rejected)

Someone might have a perfectly good backup without it being a fresh clone. For example:
  • A repository where you’ve already made a backup elsewhere
  • A repository you’ve carefully maintained and want to filter
  • A bare repository you’ve prepared specifically for filtering
This is okay! You can use --force to override the check when you’re confident you have a backup.

False Positives (Bad Repos Accepted)

Someone could theoretically manipulate a repository to pass all the checks:
  • Repack everything into a single packfile
  • Remove all but one remote and rename it to origin
  • Truncate all reflogs to one entry
  • Clean the working directory
However:
  1. This requires significant effort
  2. It’s astronomically unlikely to happen by accident
  3. Someone going to this effort presumably knows what they’re doing
In practice, the safety checks are excellent at catching accidental runs on repositories you shouldn’t be filtering. They even caught the tool’s author once when running in the wrong directory!

How to Create a Fresh Clone

The proper way to create a fresh clone for filtering:
# For remote repositories
git clone https://github.com/user/repo.git repo-to-filter
cd repo-to-filter
Critical: When cloning local repositories, always use --no-local!
Wrong
git clone /path/to/repo new-repo
# Uses hardlinks, not a real copy
Correct
git clone --no-local /path/to/repo new-repo
# Creates real copies of objects

Using --force to Override

If you’re not in a fresh clone but want to proceed anyway, use --force:
Override the safety check
git filter-repo --path src/ --force
When it’s okay to use --force:
  • You have a verified backup elsewhere
  • You understand history rewriting is irreversible
  • You’re confident you’re in the right directory
  • You’re okay with git-filter-repo irreversibly rewriting the current repository
When it’s NOT okay to use --force:
  • “I don’t want to bother making a fresh clone”
  • You’re not sure if you have a backup
  • You’re recommending it to others without explanation
  • You habitually use it without thinking

Don’t Habitually Use --force

Do not get in the habit of always specifying --force!If you do, one day you will run your command in the wrong directory (it happens to everyone), and you won’t have the safety check to bail you out. You’ll irrevocably destroy a repository you didn’t mean to.

Don’t Recommend --force Carelessly

When helping others on forums, Q&A sites, or in emails:
Never recommend --force without first:
  1. Explaining that it means putting their repository’s data at risk
  2. Confirming they have a backup
  3. Ensuring they understand history rewriting is irreversible
People who suggest --force when it’s clearly not needed are needlessly putting other people’s data at risk.

Common Error Messages

When the fresh clone check fails, you’ll see errors like:
Aborting: Refusing to destructively overwrite repo history since
this does not look like a fresh clone.
  (expected freshly packed repo)
Please operate on a fresh clone instead. If you want to proceed
anyway, use --force.
Each error tells you exactly which check failed.

Special Case: Cloning Local Repositories

When the repository you’re cloning is on the local filesystem, you must use --no-local:
Required flag for local clones
git clone --no-local /path/to/original repo-to-filter
If you forget, you might see:
Error message
Note: when cloning local repositories, you need to pass
      --no-local to git clone to avoid this issue.

Aborting: Refusing to destructively overwrite repo history since
this does not look like a fresh clone.
  (expected freshly packed repo)
...
Why --no-local is required:By default, git clone uses hardlinks when cloning local repositories for efficiency. This means objects aren’t really copied—they’re shared via filesystem links.This causes two problems:
  1. The clone doesn’t look “fresh” (shared objects appear as loose objects)
  2. Filtering the clone can actually corrupt the original (since they share objects)
Using --no-local forces Git to copy objects, creating a true independent clone.
The safe, recommended workflow for using git-filter-repo:
1

Clone the repository

git clone --no-local /path/to/original repo-to-filter
cd repo-to-filter
(Use --no-local for local repos, omit for remote repos)
2

(Optional) Analyze the repository

git filter-repo --analyze
less .git/filter-repo/analysis/path-all-sizes.txt
This helps you understand what to filter.
3

Run git-filter-repo

git filter-repo --path src/ --path docs/
No --force needed—it detects this is a fresh clone.
4

Verify the results

git log --all --oneline
git ls-files
# Check that only desired files remain
5

If satisfied, push to new location

git remote add origin https://github.com/user/new-filtered-repo.git
git push -u origin --all
git push -u origin --tags
6

If not satisfied, delete and try again

cd ..
rm -rf repo-to-filter
# Start over from step 1
This is why working from a fresh clone is safe!

Real-World Examples

Example 1: Safe filtering

Success case
# Clone fresh
git clone https://github.com/myorg/myrepo.git myrepo-filtered
cd myrepo-filtered

# Filter (no --force needed)
git filter-repo --path important-code/
# ✓ Works perfectly

Example 2: Caught by safety check

Prevented mistake
# Working in my main repo (not a fresh clone)
cd ~/projects/myrepo

# Try to filter
git filter-repo --path src/
# ✗ Error: expected one remote, origin
# ✓ Safety check prevented accidental data loss!

Example 3: Legitimate --force usage

With backup
# Made a backup manually
cp -r myrepo myrepo-backup
cd myrepo

# Filter with --force (I have a backup)
git filter-repo --path src/ --force
# ✓ Acceptable: explicit --force with known backup

FAQ

Because if something goes wrong, you have no way to recover. History rewriting is irreversible. A fresh clone ensures you can always go back to the original.
You don’t—use --force. But be absolutely sure your backup is complete and current.
No, and you shouldn’t want to. The check exists to protect you. Use --force when you’re certain, but don’t disable the safety net.
--force doesn’t make the filtering itself unsafe—it just disables the check that you have a backup. The filtering is just as safe; you just might not have a way to recover if something goes wrong.
In CI/CD, you’re typically cloning fresh on each run, so the check should pass. If your CI setup doesn’t look like a fresh clone, you may need to use --force or adjust your CI configuration to do a proper clone.

Next Steps

Quick Start

Start using git-filter-repo with step-by-step examples

How It Works

Understand the technical architecture

Design Rationale

Learn about all 12 design goals

Common Tasks

See real-world filtering examples

Build docs developers (and LLMs) love