If every line is attributed to the latest commit, that implies that every line was in fact touched in the latest commit.
Usually this sort of thing happens when someone changes every line ending from a single newline to a CR/LF pair, or vice versa.
Aha, the above would be true without -S
, but what -S
means, for git blame
, is to insert grafts into the commit graph.
This is a special feature intended to work with cvsserver (according to the comments), but it does not work with the way you're using it.
The format is simply a series of <commit-ID> <parent-ID> ...\n
lines. Since, in this case, you want to start git blame
at HEAD
and stop it at HEAD~50
, we can do this:
git rev-parse HEAD~50 | git blame -S /dev/stdin $file
The git rev-parse
outputs the SHA-1 of commit HEAD~50
, with no additional lines, which makes that commit have no parent commit(s), which makes git blame
stop the history traversal at that point.
(The command you used to feed the -S
input, git rev-list HEAD~50 HEAD
, lists every commit reachable from HEAD~50
or from HEAD
, as just a single commit ID, omitting the parents. This is the same as git rev-list HEAD
, since HEAD~50
is itself reachable from HEAD
. Because this listed every commit with no parent IDs, this made git blame
treat each commit as a root commit. It then started with the HEAD
commit, tried to find its parents, considered HEAD
to be a root commit, and stopped there—which made that commit responsible for every source line. In other words, by listing the HEAD
commit ID, you got git blame
to stop there. That's why we want to list, instead, HEAD~50
.)