Better Git diff output for Ruby, Python, Elixir, Go and more
The regular Git users amongst you will be familiar with the diff output that breaks down into “hunks” like so:
@@ -24,7 +24,7 @@ class TicketPdf
ApplicationController.render(
"tickets/index.html.haml",
layout: "tickets",
- assigns: { tickets: tickets }
+ assigns: { tickets: tickets, event_name: event_name }
)
end
The first line (starting @@
) is known as the hunk header, and is there to help orientate the change. It gives us the line numbers for the change (the numbers between the @@..@@
), but also a textual description for the enclosing context where the change happened, in this example "class TicketPdf"
. Git tries to figure out this enclosing context, whether it’s a function, module or class definition. For C-like languages it’s pretty good at this. But for the Ruby example above it’s failed to show us the immediate context, which is actually a method called tickets_as_html
. That’s because out of the box Git isn’t able to recognise the Ruby syntax for a method definition, which would be def ticket_as_html
.
What we really want to see is:
@@ -24,7 +24,7 @@ def tickets_as_html
ApplicationController.render(
"tickets/index.html.haml",
layout: "tickets",
- assigns: { tickets: tickets }
+ assigns: { tickets: tickets, event_name: event_name }
)
end
And it’s not just Ruby where Git struggles to figure out the correct enclosing context. Many other programming languages and file formats also get short-changed when it comes to the hunk header context.
Thankfully, it’s not only possible to configure a custom regex specific to your language to help Git better orient itself, there’s even a pre-defined set of patterns for many languages and formats right there in Git. All we have to do is tell Git which patterns to use for our file extensions.
We can do this by defining a gitattributes file inside our repo that maps the Ruby file extensions to the diff pattern for Ruby:
*.rb diff=ruby
*.rake diff=ruby
Some open source projects define their own .gitattributes
file. There’s one in Rails. There’s even one in the Git source that enables the diff patterns for Perl and Python.
Configure a global .gitattributes file
Instead of adding a .gitattributes
file to every repo we can configure a global .gitattributes
file. Just create a .gitattributes
file in your home directory, fill it with all the file formats you are interested in and point Git at it:
$ git config --global core.attributesfile ~/.gitattributes
I’ve put together an example .gitattributes file with some common file formats to get you started.
I have no idea why Git doesn’t have these file format patterns configured by default. Thanks to Tom whose exasperated tweet brought this non-obvious feature to my attention.
Update: Bonus diff pattern for Rspec users
Thanks to @jryan727 for sharing a custom pattern for Rspec. Add this to your Git config:
[diff "rspec"]
xfuncname = "^[ \t]*((RSpec|describe|context|it|before|after|around|feature|scenario)[ \t].*)$"
And *_spec.rb diff=rspec
to your .gitattributes
file for improved hunk headers for Rspec files.