How many times, looking a piece of code, have you thought "who the heck has written this?!?!" With other VCS all you can do is to scroll the list of changes made to that particular file and look at what is changed.

But Git provides you the blame command that eventually answers your question in a fast and easy way. And, most of the times, you will not like the answer[1].

$ git blame main.c
^7a797d5 (Luca 2016-09-03 13:54:26 +0200  1) #include <stdio.h>
^7a797d5 (Luca 2016-09-03 13:54:26 +0200  2) 
964efcc1 (Luca 2016-09-03 13:55:42 +0200  3) ////////////////////////////////
964efcc1 (Luca 2016-09-03 13:55:42 +0200  4) // "Hello World" program in C //
964efcc1 (Luca 2016-09-03 13:55:42 +0200  5) ////////////////////////////////
^7a797d5 (Luca 2016-09-03 13:54:26 +0200  6) int main(void)
^7a797d5 (Luca 2016-09-03 13:54:26 +0200  7) {
25e19cf2 (Luca 2016-09-03 14:01:46 +0200  8) // argc and argv are missing
25e19cf2 (Luca 2016-09-03 14:01:46 +0200  9) 
9c5a7590 (Luca 2016-09-03 13:55:00 +0200 10)   printf("Hello World!\n");
^7a797d5 (Luca 2016-09-03 13:54:26 +0200 11)   return 0;
^7a797d5 (Luca 2016-09-03 13:54:26 +0200 12) }

The rows that start with a ^ are those left untouched since the initial commit.

You can see a whole file (or just some rows, with the flag -L) annotated with the author and the hash of the commit where each line has been added or changed. And you can also ignore changes in white-spaces with -w.

$ git blame -L 6,12 -w main.c
^7a797d5 (Luca 2016-09-03 13:54:26 +0200  6) int main(void)
^7a797d5 (Luca 2016-09-03 13:54:26 +0200  7) {
25e19cf2 (Luca 2016-09-03 14:01:46 +0200  8) // argc and argv are missing
25e19cf2 (Luca 2016-09-03 14:01:46 +0200  9) 
^7a797d5 (Luca 2016-09-03 13:54:26 +0200 10)   printf("Hello World!\n");
^7a797d5 (Luca 2016-09-03 13:54:26 +0200 11)   return 0;
^7a797d5 (Luca 2016-09-03 13:54:26 +0200 12) }

As you can see, the output is restricted to the selected rows and the line number 10 has a different commit id because of the flag -w.

Sometimes it may be useful to detect if the same lines have been moved inside the file: the flag -M cover this requirement. But what if the lines have been moved from a file to another?

This is the purpose of the flag -C. If the move has been done in the same commit (typically during a refactoring session) the output shows also the file name where those rows originally were.

Check out the man page for all the other options.


Cover image by PrinzKoks taken from Deviant Art and licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 license.

Post last updated on 2020/08/31


  1. spoiler: it's you! ↩︎