Renaming files is a common maintenance task—cleaning up naming, fixing typos, or reorganizing a project. Here’s the fastest, least-surprising way to rename files in Git from the command line, plus a few gotchas to save you time.
TL;DR
|
1 2 3 4 |
git mv <old> <new> git commit -m "Rename <old> → <new>" git push |
For case‑only changes on macOS/Windows, rename via a temporary filename first.
Prerequisites
- Git is installed and the repo is already initialized and up to date.
- You’re on the branch where you want the rename to happen.
|
1 2 3 4 |
# Good hygiene before you start git status git pull --ff-only |
The 10‑second rename
Use git mv so Git tracks the rename automatically (stages the change for commit):
|
1 2 3 4 5 6 7 8 9 10 |
# Syntax git mv <old/path/filename.ext> <new/path/filename.ext> # Example git mv src/utils/helpers.js src/utils/string-helpers.js # Commit and push git commit -m "Rename helpers.js → string-helpers.js" git push |
That’s it. git mv performs a file move plus a stage in one step. (Under the hood it’s equivalent to mv + git add -A + git rm.)
Case‑only renames (macOS/Windows gotcha)
Most macOS and Windows file systems are case‑insensitive. A rename that only changes letter case (e.g., Readme.md → README.md) may seem to do nothing.
Fix: do a two‑step rename via a temporary name
|
1 2 3 4 5 |
git mv README.md README.tmp git mv README.tmp README.md git commit -m "Case-only rename: Readme.md → README.md" |
Tip: If your repo is shared across different OSes, always use the two‑step approach for case-only renames.
Renaming into a new folder (create missing dirs)
If the destination folder doesn’t exist yet:
|
1 2 3 4 5 |
mkdir -p src/components/common git mv src/components/Button.jsx src/components/common/Button.jsx git commit -m "Move Button into components/common" |
Rename multiple files
You can combine git mv with shell expansion or loops.
Add a prefix to all .spec.ts files:
|
1 2 3 4 5 6 7 |
for f in tests/*.spec.ts; do base=$(basename "$f") git mv "$f" "tests/spec-$base" done git commit -m "Prefix test files with spec-" |
Move a whole directory:
|
1 2 3 4 |
git mv packages/legacy-lib packages/legacy-lib-old git commit -m "Archive legacy-lib" |
Oops — undo before commit
If you haven’t committed yet, you can unstage and restore the old path:
|
1 2 3 4 5 6 7 8 9 |
# Show staged changes git status # Unstage the rename git restore --staged <new/path/file> # Put the file back (working tree) git mv <new/path/file> <old/path/file> |
After committing — revert the rename
If the rename is already committed and pushed, make a follow‑up commit to move it back:
|
1 2 3 4 |
git mv <new/path/file> <old/path/file> git commit -m "Revert rename of <file>" git push |
Verifying Git sees the rename
|
1 2 3 4 5 6 |
git status git log --name-status -1 # Look for lines like: # R100\told/path/file\tnew/path/file |
R means Git detected a rename; the number is the similarity score.
Common errors & fixes
- fatal: destination exists — Choose a different destination or remove/rename the existing target first.
- Pathspec did not match any files — Check the source path; ensure the file is tracked (
git ls-files | grep <name>). - Case-only rename didn’t stick — Use the two‑step temp filename workaround above.
FAQ
Do I have to use git mv?
No—Git detects renames heuristically. But git mv is clearer in history and avoids forgetting to stage deletions.
Will history be preserved?
Yes. Git tracks content, not file IDs. Use git log --follow <file> to trace history across renames.
What about submodules or LFS?
git mv works the same. For submodules, you’re moving the gitlink entry, not the nested repo content.




