Trace the history of a function using git

4 minute read

tl;dr: git log -L :funcname:file

Recently I had the need to know which commit in a git repository introduced a certain change to some function’s logic.

I already knew how to see the change history of an entire file using git log --patch -- <file>. However, there was a lot of history and the file was big, so git log generated lots of output I did not care for.

I wondered if it was possible to only see the change history of some function in a file instead of the entire file’s. Guess what, it is possible: git log -L :funcname:file.

Example

Let us look at a simple example. Consider we have the following source code in the file example.c:

void a_function(double a_parameter) {
    // I wish I was special
    // So very special
    // But I'm a function
}

int some_function(int some_parameter) {
    // Do stuff to the parameters
}

void our_function() {
    // I'm your function
    // Here I am
    // This is me
}

double some_other_function() {
    // I am also here
}

Now consider that we made some commits with changes to example.c. For example, here is our hypothetical history of the changes to example.c in chronological order, courtesy of git log --patch --reverse -- example.c:1

$ git log --patch --reverse -- example.c
commit dc74fc966e1abaf5ff9b5c2763b59f82b917f190
Author: John Doe <john.doe@domain.com>
Date:   Tue Jan 19 03:14:03 2038 +0000

    Add example.c

diff --git a/example.c b/example.c
new file mode 100644
index 0000000..d44971c
--- /dev/null
+++ b/example.c
@@ -0,0 +1,19 @@
+void a_function(double a_parameter) {
+    // I wish I was special
+    // So very special
+    // But I'm a function
+}
+
+int some_function(int some_parameter) {
+    // Do stuff to the parameters
+}
+
+void our_function() {
+    // I'm your function
+    // Here I am
+    // This is me
+}
+
+double some_other_function() {
+    // I am also here
+}

commit 3851c5f09f8c73859dc0b5feed90677e58e61f97
Author: John Doe <john.doe@domain.com>
Date:   Tue Jan 19 03:14:04 2038 +0000

    Change our_function

diff --git a/example.c b/example.c
index d44971c..e14a2cf 100644
--- a/example.c
+++ b/example.c
@@ -10,8 +10,7 @@ int some_function(int some_parameter) {
 
 void our_function() {
     // I'm your function
-    // Here I am
-    // This is me
+    // And I'm going through changes
 }
 
 double some_other_function() {

commit f3373bc8b9bf64c0af4eb6349a4e9789a5721389
Author: John Doe <john.doe@domain.com>
Date:   Tue Jan 19 03:14:05 2038 +0000

    Change a_function

diff --git a/example.c b/example.c
index e14a2cf..3d16bc9 100644
--- a/example.c
+++ b/example.c
@@ -2,6 +2,8 @@ void a_function(double a_parameter) {
     // I wish I was special
     // So very special
     // But I'm a function
+    // This guy is a weirdo
+    // What the hell am I doing here
 }
 
 int some_function(int some_parameter) {

commit d82c3535871fd47ee7bcb0b0f6ff9f5dd30fa6fb
Author: John Doe <john.doe@domain.com>
Date:   Tue Jan 19 03:14:06 2038 +0000

    Change some_function and some_other_function

diff --git a/example.c b/example.c
index 3d16bc9..dac2191 100644
--- a/example.c
+++ b/example.c
@@ -8,6 +8,7 @@ void a_function(double a_parameter) {
 
 int some_function(int some_parameter) {
     // Do stuff to the parameters
+    // Return the stuff done to the parameters
 }
 
 void our_function() {
@@ -17,4 +18,5 @@ void our_function() {
 
 double some_other_function() {
     // I am also here
+    // Thanks for acknowledging me
 }

commit dde094c8f47c4f7bfa7176e25653120178aaad51
Author: John Doe <john.doe@domain.com>
Date:   Tue Jan 19 03:14:07 2038 +0000

    Change a_function and our_function

diff --git a/example.c b/example.c
index dac2191..f437330 100644
--- a/example.c
+++ b/example.c
@@ -4,6 +4,7 @@ void a_function(double a_parameter) {
     // But I'm a function
     // This guy is a weirdo
     // What the hell am I doing here
+    // I don't belong here
 }
 
 int some_function(int some_parameter) {
@@ -14,6 +15,7 @@ int some_function(int some_parameter) {
 void our_function() {
     // I'm your function
     // And I'm going through changes
+    // So long and thanks for all the changes
 }
 
 double some_other_function() {

Now let us image we are interested in seeing the evolution of our_function(). Some commits change our_function() and some do not. This is a lot of output we do not care about. Here comes the -L parameter to the rescue, git log -L :our_function:example.c --reverse:

$ git log -L :our_function:example.c --reverse
commit dc74fc966e1abaf5ff9b5c2763b59f82b917f190
Author: John Doe <john.doe@domain.com>
Date:   Tue Jan 19 03:14:03 2038 +0000

    Add example.c

diff --git a/example.c b/example.c
--- /dev/null
+++ b/example.c
@@ -0,0 +11,6 @@
+void our_function() {
+    // I'm your function
+    // Here I am
+    // This is me
+}
+

commit 3851c5f09f8c73859dc0b5feed90677e58e61f97
Author: John Doe <john.doe@domain.com>
Date:   Tue Jan 19 03:14:04 2038 +0000

    Change our_function

diff --git a/example.c b/example.c
--- a/example.c
+++ b/example.c
@@ -11,6 +11,5 @@
 void our_function() {
     // I'm your function
-    // Here I am
-    // This is me
+    // And I'm going through changes
 }
 

commit dde094c8f47c4f7bfa7176e25653120178aaad51
Author: John Doe <john.doe@domain.com>
Date:   Tue Jan 19 03:14:07 2038 +0000

    Change a_function and our_function

diff --git a/example.c b/example.c
--- a/example.c
+++ b/example.c
@@ -14,5 +15,6 @@
 void our_function() {
     // I'm your function
     // And I'm going through changes
+    // So long and thanks for all the changes
 }

Boom! Thanks git. :-)

  1. --patch shows the patch in the log, and --reverse shows the log in chronological order.