1

What is the format of the revs-file argument for git blame -S?

I have tried running it with the output of git rev-list as follows but the blame attributes every line to the HEAD commit.

git blame -S <(git rev-list HEAD~50 HEAD) $file

I have also tried running <(git rev-list ... | tac) to reverse the revs-file in case that was the issue but it seems to produce the same output.

The goal is to blame each line of $file to a commit between HEAD~50 and HEAD, defaulting to HEAD~50 if the same line is present in all commits.

arcyqwerty
  • 10,325
  • 4
  • 47
  • 84
  • `git rev-list HEAD~50 HEAD` outputs the same thing as `git rev-list HEAD`. Did you intend to use `git rev-list HEAD~50..HEAD`? If so, the two dots are critical here. – torek Sep 24 '15 at 18:12

1 Answers1

1

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.)

torek
  • 448,244
  • 59
  • 642
  • 775
  • Everything is `\n` and I was able to get different results using `git blame $file | while read line; do grep "$(echo -n '^'; echo "$line" | sed -E 's/^\^?([a-f0-9]{7,8}).*$/\1/g')" <(git rev-list HEAD~50 HEAD) > /dev/null && echo "$line"; done` – arcyqwerty Sep 24 '15 at 19:52