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:
Clone the repository
git clone https://github.com/example/repo.git
cd repo
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:
- Keeps only
src/some-folder/some-feature/
- Renames it to
src/
- Removes everything else
Complex Reorganization Example
Restructure a repository layout:
Plan the reorganization
Original structure:Desired structure:src/
tests/
documentation/
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:
Create paths file
Create ../renames.txt:old/path/file1.txt==>new/path/file1.txt
src/old/==>src/new/
legacy/==>archived/legacy/
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/")
'
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:
Clone fresh
git clone https://github.com/example/module.git
cd module
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-
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"/")
'