Skip to main content
git-filter-repo makes it easy to rename files, directories, and entire directory structures throughout your repository’s history.

Simple Path Renaming

Rename a single file throughout history:
git filter-repo --path-rename old-name.txt:new-name.txt

Rename Directories

Move or rename an entire directory:
# Rename a directory
git filter-repo --path-rename old-dir/:new-dir/

# Move a directory to a different location
git filter-repo --path-rename src/components/:lib/ui/
Include the trailing slash when renaming directories to ensure all files within are renamed correctly.

Multiple Renames

Perform several rename operations at once:
git filter-repo \
    --path-rename src/:lib/ \
    --path-rename tests/:spec/ \
    --path-rename README.txt:README.md

Moving Root to Subdirectory

Make the entire repository root become a subdirectory:
1

Clone the repository

git clone https://github.com/example/repo.git
cd repo
2

Move to subdirectory

git filter-repo --to-subdirectory-filter my-module
All files that were at the root (like README.md) will now be at my-module/README.md.

Combining Extraction and Renaming

Extract a subdirectory and rename it:
git filter-repo \
    --path src/some-folder/some-feature/ \
    --path-rename src/some-folder/some-feature/:src/
This:
  1. Keeps only src/some-folder/some-feature/
  2. Renames it to src/
  3. Removes everything else

Complex Reorganization Example

Restructure a repository layout:
1

Plan the reorganization

Original structure:
app/
test/
docs/
Desired structure:
src/
tests/
documentation/
2

Apply multiple renames

git filter-repo \
    --path-rename app/:src/ \
    --path-rename test/:tests/ \
    --path-rename docs/:documentation/

Using Paths File for Renames

For many renames, use a paths file with ==> syntax:
1

Create paths file

Create ../renames.txt:
old/path/file1.txt==>new/path/file1.txt
src/old/==>src/new/
legacy/==>archived/legacy/
2

Apply renames

git filter-repo --paths-from-file ../renames.txt

Advanced: Callback-Based Renaming

For complex renaming logic, use Python callbacks:

Rename by pattern

git filter-repo --filename-callback '
    # Add a prefix to all files
    return b"legacy-" + filename
'

Conditional renaming

git filter-repo --filename-callback '
    # Move all .js files to src/
    if filename.endswith(b".js"):
        return b"src/" + filename
    return filename
'

Replace path components

git filter-repo --filename-callback '
    # Replace "old" with "new" in all paths
    return filename.replace(b"/old/", b"/new/")
'

Renaming with Extraction

Combine path selection with renaming:
# Keep only src/ and rename it to lib/
git filter-repo --path src/ --path-rename src/:lib/

Tag Renaming

Rename tags to avoid conflicts when merging:
# Add prefix to all tags
git filter-repo --tag-rename '':'my-project-'

# This renames:
# v1.0 -> my-project-v1.0
# v2.0 -> my-project-v2.0

Complete Example: Repository Merge Preparation

Prepare a repository for merging by reorganizing and prefixing:
1

Clone fresh

git clone https://github.com/example/module.git
cd module
2

Extract and reorganize

git filter-repo \
    --path src/ \
    --to-subdirectory-filter modules/extracted \
    --tag-rename '':'extracted-module-'
This:
  • Keeps only the src/ directory
  • Moves everything to modules/extracted/src/
  • Prefixes all tags with extracted-module-
3

Verify changes

ls -la
git log --oneline --all
git tag

Handle Special Cases

Rename preserving directory structure

# Move all Python files to a src/ directory while preserving their relative paths
git filter-repo --filename-callback '
    if filename.endswith(b".py"):
        return b"src/" + filename
    return filename
'

Case-sensitive rename

# Rename README to readme (careful on case-insensitive filesystems)
git filter-repo --path-rename README.md:readme.md
On case-insensitive filesystems (macOS, Windows), renaming files that differ only in case requires extra care. git-filter-repo handles this, but be cautious when pushing to remotes.

Verify Renames

After renaming, verify the changes:
# Check that old paths don't exist
git log --all --full-history -- old/path/

# Verify new paths have history
git log --follow new/path/file.txt

# Compare file count
git ls-tree -r HEAD --name-only | wc -l

Common Patterns

Flatten directory structure

# Move all files from nested directories to root
git filter-repo --filename-callback '
    import os
    return os.path.basename(filename)
'
Flattening can cause filename conflicts if multiple files have the same name in different directories.

Add date prefix to directories

git filter-repo --path-rename src/:2024-01-15-src/

Normalize path separators

# Convert backslashes to forward slashes (from Windows)
git filter-repo --filename-callback '
    return filename.replace(b"\\", b"/")
'

Build docs developers (and LLMs) love