How-to: Find Who Wrote A Line in git Repository
I’m building on a previous article where I categorize all the rubocop issues.
My goal is to find out who committed the infraction. Not to point fingers, just to see if there are problems I can solve further up in the development process.
I review two ways to figure out how to get who wrote a line of code in
a git repository using git blame, one using grep and another using
additional git blame syntax: -L.
Understanding what built-in options to a program are can solve problems better than working around it with external tools.
This article will take you less than four minutes to read.
Introduction
I would just like a command that tells me who wrote a specific line of a file.
Something like:
who-wrote filename line_numberIs that too much to ask for?? :-)
git blame
If the version control of the code I am investigating uses git, then
there’s git blame:
git blame <filename>For example from my TDD Callbacks repository:
$ git blame spec/model/order_spec.rb
4aefedef (Andrew Leung 2019-01-18 22:58:31 +0000 1) require 'rails_helper'
4aefedef (Andrew Leung 2019-01-18 22:58:31 +0000 2)
4aefedef (Andrew Leung 2019-01-18 22:58:31 +0000 3) RSpec.describe Order, type: :model do
68b6a236 (Andrew Leung 2019-01-18 22:59:51 +0000 4) it 'new orders are created with :open status' do
68b6a236 (Andrew Leung 2019-01-18 22:59:51 +0000 5) order = Order.new
68b6a236 (Andrew Leung 2019-01-18 22:59:51 +0000 6)
68b6a236 (Andrew Leung 2019-01-18 22:59:51 +0000 7) order.save
68b6a236 (Andrew Leung 2019-01-18 22:59:51 +0000 8)
68b6a236 (Andrew Leung 2019-01-18 22:59:51 +0000 9) expect(order.status).to eq('open')
68b6a236 (Andrew Leung 2019-01-18 22:59:51 +0000 10) end
...The format of the output of git blame is:
<Commit SHA> (<Author> <Timestamp> <Line number>) <code>This is good, it has what I want: for a given filename and line number, return who wrote the code for a specific line.
It’s great to get details of the whole file. How can I narrow this down further?
grepgit blame -L
Let’s try both and see how they work.
How: grep
One way to do this is to use grep for the specific line number you want:
git blame <file> | grep <line number>For example, line 25:
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ git blame spec/models/order_spec.rb | grep 25
5bcbbe57 (Andrew Leung 2019-01-18 23:10:06 +0000 25) order.saveDone, right?
What if I want line 33?
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ git blame spec/models/order_spec.rb | grep 33
8c9461cc (Andrew Leung 2019-01-18 23:15:53 +0000 31) order = Order.create(:amount => 0.33)
8c9461cc (Andrew Leung 2019-01-18 23:15:53 +0000 32) order.received = 0.33
8c9461cc (Andrew Leung 2019-01-18 23:15:53 +0000 33)Oh, it’s easy to just grep for 33) instead:
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ git blame spec/models/order_spec.rb | grep 33)
bash: syntax error near unexpected token `)'What if I escape it:
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ git blame spec/models/order_spec.rb | grep 33\)
8c9461cc (Andrew Leung 2019-01-18 23:15:53 +0000 31) order = Order.create(:amount => 0.33)
8c9461cc (Andrew Leung 2019-01-18 23:15:53 +0000 33)Better, not exactly what I want and it would produce false positives
or require additional filtering, definitely possible with awk.
Is there a better way? Let’s try the next option: git blame -L
How: -L
git blame has a feature to list a specific range of lines of a file:
git blame <file> -L <start>,<end>vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ git blame spec/models/order_spec.rb -L 25
5bcbbe57 (Andrew Leung 2019-01-18 23:10:06 +0000 25) order.save
5bcbbe57 (Andrew Leung 2019-01-18 23:10:06 +0000 26)
5bcbbe57 (Andrew Leung 2019-01-18 23:10:06 +0000 27) expect(order.status).to eq('received')
5bcbbe57 (Andrew Leung 2019-01-18 23:10:06 +0000 28) end
8c9461cc (Andrew Leung 2019-01-18 23:15:53 +0000 29)
8c9461cc (Andrew Leung 2019-01-18 23:15:53 +0000 30) it 'also handles potential irrational numbers' do
8c9461cc (Andrew Leung 2019-01-18 23:15:53 +0000 31) order = Order.create(:amount => 0.33)
...Hmm, too much output. I wonder if I can just specify the beginning to be the same as the end.
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ git blame spec/models/order_spec.rb -L 25,25
5bcbbe57 (Andrew Leung 2019-01-18 23:10:06 +0000 25) order.savePerfect.
What about line 33? That failed miserably using grep:
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ git blame spec/models/order_spec.rb -L 33,33
8c9461cc (Andrew Leung 2019-01-18 23:15:53 +0000 33)Nice! Exactly the output expected. No additional processing needed.
Conclusion
Figuring out who wrote a specific line of code is best using the following syntax:
git blame <filename> -L <line number>,<line number>Which returns the output in the following format:
<Commit SHA> (<Author> <Timestamp> <Line number>) <code>I am glad I dug a bit more into git documentation as using grep
would have been an OK solution, overlapping numbers in the code
section would require additional (surprising) work.