Tracking a file's history across renames
A renamed file looks like a brand new file to git log. With --follow the full history stays visible, but only under certain conditions.
Table of Contents
- 1. Why git log stops at a rename
- 2. How Git actually detects renames
- 3. git log --follow: basics and syntax
- 4. Combining follow with patch output
- 5. Limits of --follow
- 6. git blame and rename detection
- 7. Adjusting the similarity threshold
- 8. Practical example: a file renamed multiple times
- 9. Alternatives and complementary tools
- 10. Summary
- 11. FAQ
1. Why git log stops at a rename
When a file is renamed or moved into another directory, its path formally changes as far as Git is concerned. A plain call to git log with the current file path as the argument afterward only shows commits created under that path.
Every commit before the rename disappears from the output, even though the content of the file stayed continuous throughout. For anyone trying to trace the full history of a central class, that is a real problem.
The reason lies in how Git works internally: renames are not stored as a distinct operation, they are only detected afterward, when displaying history, through a similarity comparison between deleted and newly added files.
# Shows only commits since the rename, older history is missing
git log -- app/code/Vendor/Module/Model/PriceCalculator.php
# The previous file had a different name, it does not show up here
git log -- app/code/Vendor/Module/Model/Calculator.php
2. How Git actually detects renames
Git stores the full state of the repository at every commit, not a chain of diffs. There is no internal field that explicitly marks a rename as such, unlike in some other version control systems.
When generating a diff between two commits, Git instead compares every deleted file against every newly added file and computes a similarity index. If that index exceeds a threshold, Git reports the two files as a rename, even though technically one file was deleted and another was newly created.
This after the fact detection is why rename tracking does not behave the same everywhere. Tools that do not perform a similarity comparison only ever see a deletion and a creation, never the rename itself.
# Show the similarity index for a commit
git show --stat -M <commit-sha>
# R087 Model/Calculator.php Model/PriceCalculator.php
# R087 means 87 percent similarity between the old and new file
3. git log --follow: basics and syntax
The --follow option tells Git to keep searching a single file's history across detected renames. As soon as Git finds a past commit that marks the file as a rename, the search automatically jumps to the previous path and continues from there.
The important restriction is that it works with exactly one file path. --follow only functions when filtering by a single file, not multiple paths or directories at once. If more than one path is given, Git silently ignores the option.
The result is one continuous history across every detected rename, including commits that predate the very first rename, all the way back to the original creation of the file under any of its earlier names.
# Full history including every detected rename
git log --follow -- app/code/Vendor/Module/Model/PriceCalculator.php
# More compact, one line per commit
git log --follow --oneline -- app/code/Vendor/Module/Model/PriceCalculator.php
4. Combining follow with patch output
Plain commit metadata is often not enough when tracing how a file's content actually changed. Combining --follow with -p additionally shows the full diff for every commit, even across rename boundaries.
At the point where the rename happened, the diff shows both the old and new file name, often alongside content changes made in the same commit. That reveals whether a rename happened in isolation or as part of a larger refactoring.
For very long histories, using --stat instead of the full patch first helps to see only the size of each change and then select individual commits for the full diff.
# Full diff across every detected rename
git log --follow -p -- app/code/Vendor/Module/Model/PriceCalculator.php
# Just the stats per commit, a compact overview
git log --follow --stat -- app/code/Vendor/Module/Model/PriceCalculator.php
5. Limits of --follow
The most important limitation was already mentioned: --follow only works with exactly one file path. Anyone tracing the history of several moved files at once has to run the command separately for each one.
For merge commits, rename detection can become unreliable, especially when a file was renamed on one branch and heavily rewritten on the other. The similarity index then falls below the threshold and Git no longer sees a connection.
Multiple renames happening close together, combined with substantial content changes at the same time, can also break the chain. The more similar the file content stays across versions, the more reliably --follow works.
# Does NOT work: multiple paths at once with --follow
git log --follow -- Model/PriceCalculator.php Model/TaxCalculator.php
# --follow is silently ignored here
# Run separately instead
git log --follow -- Model/PriceCalculator.php
git log --follow -- Model/TaxCalculator.php
6. git blame and rename detection
While --follow applies to git log, git blame has its own approach. By default, blame only detects renames within the currently viewed file's history in a limited way, but tracks them actively via the -C option.
The -C option tells blame to also search for lines that originally lived in a different file, for example when code was copied or moved from one file to another. Passing -C -C extends the search further, to every file touched in the same commit.
For simply tracking a rename chain, a single -C combined with -M, which explicitly enables rename detection within a file, is usually enough, even when additional content changes happened in the same commit.
# Blame with rename and copy detection enabled
git blame -C -M app/code/Vendor/Module/Model/PriceCalculator.php
# Blame for a specific line, including the commit before the rename
git blame -C -M -L 42,42 app/code/Vendor/Module/Model/PriceCalculator.php
7. Adjusting the similarity threshold
The default threshold for rename detection is 50 percent similarity. For most cases that is a reasonable compromise, but for files that were heavily reworked at the same time they were renamed, the value can be too high.
Using -M with an explicit percentage lets you lower the threshold manually, for example to 30 percent, to also catch renames combined with more extensive content changes. Too low a value, though, produces false positive rename matches between completely unrelated files.
The parameter can be set for git log, git diff, and git blame alike, and directly affects the quality of rename detection in each of those outputs.
# Lower the threshold to 30 percent, more renames get detected
git log --follow -M30% -- app/code/Vendor/Module/Model/PriceCalculator.php
# Show the threshold directly in a diff
git diff -M30% HEAD~5 HEAD
8. Practical example: a file renamed multiple times
In a typical Magento module, a central model class often moves more than once: first renamed within the same directory, later moved into a new subdirectory during a refactor. --follow reconstructs that entire chain with a single command.
In practice you start from the current path of the file and let Git find the older history on its own, instead of manually searching for earlier file names. Only when --follow hits a detectable boundary, for example because the similarity index for a particular rename is too low, do you need to manually search for the suspected earlier name.
A proven approach combines --follow for the automated part with a targeted git log --diff-filter=D using a name pattern, to manually find deleted files with a similar name when automatic detection reaches its limits.
# Reconstruct the full chain, current path as the starting point
git log --follow --oneline --name-status -- app/code/Vendor/Module/Model/PriceCalculator.php
# If the chain breaks: search deliberately for deleted similar files
git log --diff-filter=D --oneline --name-only -- '*Calculator*'
9. Alternatives and complementary tools
For the history of an entire directory rather than a single file, --follow offers no solution. The only option here is a manual reconstruction using several git log --diff-filter=R calls that deliberately search for rename commits within the relevant timeframe.
git log --all --full-history does show every commit that ever touched a file, but it does not automatically follow renames and therefore has to be combined with the correct path valid at each point in time.
For a visual view, GUI tools like GitKraken or the history view in PhpStorm can help, since they often render rename chains as a single continuous line, which makes understanding complex file movements much easier.
# Every commit marked as a rename across the whole repository
git log --all --diff-filter=R --summary --oneline
# Full history without follow, with an explicit full-history flag
git log --full-history --oneline -- app/code/Vendor/Module/
| Command | Rename detection | Use case | Limitation |
|---|---|---|---|
| git log | None | Current history from the latest path | Stops at every rename |
| git log --follow | Automatic, one path | Complete file history | Only one path at a time |
| git blame -C -M | Active, line based | Trace the origin of individual lines | Slower on large repositories |
| git log --diff-filter=R | Lists rename commits | Manually searching for renames | No automatic following |
Mironsoft
Git workflows, branching strategies, and CI hooks
Chaotic Git history and unclear branching rules across the team?
We set up clean Git workflows, clarify branching strategies for the team, and automate quality checks via Git hooks and CI pipelines so the history stays traceable.
Workflow Audit
Review the existing branching strategy and merge practice for weak spots.
Hook Automation
Set up pre-commit and pre-push hooks for linting, tests, and commit conventions.
Team Training
Teach rebase, cherry-pick, and conflict resolution hands-on across the team.
10. Summary
git log --follow
Core command
git log --follow with exactly one file path
Limitation
Only works with exactly one file path
For lines
git blame -C -M complements --follow at the line level
Threshold
Default 50 percent, adjustable via -M