git diff
will show a file as "renamed" if it passes a "similarity test". To make sure it's occurring, specify the -M
option (I'll do that below).1
$ mkdir /tmp/temprepo && cd /tmp/temprepo && git init
$ cat << end > file
> Here is some text for a file.
> We want it to be long enough
> that renames and copies can
> be detected fairly easily.
> If we put ten lines into the
> file, then each percent of
> similarity is one tenth of
> each of the lines, i.e., each
> line represents 10% similarity
> to the earlier file.
> end
$ git commit -m initial
[master (root-commit) 842824a] initial
1 file changed, 10 insertions(+)
create mode 100644 file
$ git mv file newname
$ git diff --cached --name-status
R100 file newname
Let's go ahead and commit this so that we can git diff
two commits, rather than the commit and the index:
$ git commit -m B
[master a2380eb] B
1 file changed, 0 insertions(+), 0 deletions(-)
rename file => newname (100%)
Just to see what that did:
$ git diff --name-status -M HEAD^ HEAD
R100 file newname
Now let's make a minor change:
$ ed newname
279
9s/10/about 10/p
line represents about 10% similarity
w
285
q
$ git commit -a -m C
[master 57fce41] C
1 file changed, 1 insertion(+), 1 deletion(-)
$ git diff --name-status -M HEAD~2 HEAD
R087 file newname
The diff (with --name-status
mode selected) between HEAD~2
(the initial commit) and the latest commit now shows that file file
was renamed to newname
with only 87% similarity, rather than 100% similarity (as now seen between HEAD~2
and HEAD~1
).2
To see copies you generally have to run git diff
with -C
and/or --find-copies-harder
.
You can control how similar files must be to have renames (and copies) detected with numeric values after -M
(and -C
), e.g., -M90%
causes the above git diff --name-status
to treat the rename as an add-and-delete pair:
$ git diff --name-status -M90% HEAD~2 HEAD
D file
A newname
The -B
option also affects rename detection, as do git config
settings diff.renameLimit
and diff.renames
(see the documentation). To defeat rename detection entirely—which makes writing git hook scripts with --diff-filter
a lot easier in many cases—use --no-renames
(or any of the porcelain commands, which don't obey diff.renames
; see also footnote 1).
1This similarity test defaults to "50% similar" when diff.renames
is enabled and "exact match only" when it is disabled. In Git versions prior to 2.9, diff.renames
defaulted to false
(disabled); in 2.9 and later it defaults to true
(enabled) and can also be set to copy
or copies
to enable the -C
option as well. Note that the diff.renames
setting applies to git diff
, which is what Git calls a porcelain (or user-facing) command, but not to, e.g., git diff-tree
, which is what Git calls a plumbing command. You must use the -M
or --find-renames
option if you want the plumbing commands to do rename detection.
2Earlier, we used HEAD^
and HEAD
, but we added commit C
since then, so the initial commit is now HEAD^^
or HEAD~2
.