Git 2.9 has been released
The open source Git project has just released Git 2.9.0, with a variety of features and bug fixes. Here’s our look at some of the most interesting new features: Faster…
The open source Git project has just released Git 2.9.0, with a variety of features and bug fixes. Here’s our look at some of the most interesting new features:
Faster and more flexible submodules
In the last release, we showed you how to use the --jobs=<N>
option to fetch submodules in parallel, which can be a real time saver.
$ git fetch --recurse-submodules --jobs=4
Now you can also use the --jobs
option when cloning or updating submodules:
$ git clone --recurse-submodules --jobs=4 ...
$ git submodule update --jobs=4 ...
If you always want submodules to be processed in parallel, you can set the submodule.fetchJobs
config option. [source]
There are also some new conveniences for specifying how you want your submodule clones and fetches to behave. Git will now pass command-line configuration options down to submodules commands. So if you need to set a one-off variable for all of your submodule fetches, you can do so with git -c http.proxy=... clone --recursive
. In the last release we mentioned how Git LFS can use -c
config to speed up the initial checkout. Now it can apply the same trick to cloning submodules. Similarly, you can pass --shallow-submodules
to git clone
to have it make shallow clones of all of your submodules. [source, source, source]
Beautiful diffs
If you’ve used Git, you’ve probably looked at a lot of diffs. Most of the time the diff shows exactly what you did: changed some lines, added some new ones, or took some away. But because Git generates the diff only from seeing the “before” and “after” states of your files, sometimes the way it shows the changes can be pretty confusing. For example, look at this diff that adds a new loop to an existing function:
diff --git a/foo.rb b/foo.rb
index 64bb579..a2b5573 100644
--- a/foo.rb
+++ b/foo.rb
@@ -3,6 +3,10 @@ module Foo
def finalize(values)
values.each do |v|
+ v.prepare
+ end
+
+ values.each do |v|
v.finalize
end
end
The author probably didn’t write the loop as two out-of-order halves. But because the outer lines of the old and new loops are the same, it’s equally valid to attribute the lines either way (it’s tempting to say that the problem can be solved by always starting the added lines at the top, but that only fixes this case; you’d have the opposite problem if the new loop were added after the old).
In 2.9, Git’s diff engine learned a new heuristic: it tries to keep hunk boundaries at blank lines, shifting the hunk “up” whenever the bottom of the hunk matches the bottom of the preceding context, until we hit a blank line. The result shows what the author actually wrote in this case:
diff --git a/foo.rb b/foo.rb
index 64bb579..a2b5573 100644
--- a/foo.rb
+++ b/foo.rb
@@ -2,6 +2,10 @@ module Foo
def finalize(values)
+ values.each do |v|
+ v.prepare
+ end
+
values.each do |v|
v.finalize
end
Ah, much better. This new heuristic is still experimental, and may change in the future, or even become the default. For now, you can enable it with the --compaction-heuristic
option on the command line, or by setting diff.compactionHeuristic
in your git config. [source, source]
But maybe the minutae of diff hunk alignment isn’t your thing (it’s OK, we all have our niche interests). Let’s talk about colors.
You probably already know that Git can colorize its diff output, and you can even customize the colors yourself. But there are also scripts that will filter Git’s output and change it even further. For instance, there’s a diff-highlight
script that will put an extra emphasis on the changed part of a line:
diff --git a/foo.rb b/foo.rb
index bff273..9741ad8 100644
--- a/foo.rb
+++ b/foo.rb
@@ -1,6 +1,6 @@
module Foo
def output
- puts "hello, world!"
+ puts "goodbye, world!"
end
end
There are a few ways to make use of this script, but the simplest is to configure Git to filter your diffs whenever it’s showing them in a pager:
$ git config pager.log 'diff-highlight | less'
$ git config pager.show 'diff-highlight | less'
$ git config pager.diff 'diff-highlight | less'
That covers almost everything, but there’s one spot missing: diffs shown by the interactive patch-staging tool (you are using interactive
staging, right?). In Git 2.9, you can now ask it to filter the diffs it shows through any script you like:
$ git config interactive.diffFilter diff-highlight
Phew, with that set you can avoid the horror of ever seeing an unhighlighted diff again. [source]
And if you really want to go nuts, check out the diff-so-fancy project, which plugs in in the same way. These diffs are so fancy, you’ll pop your monocle.
Testing all the commits with rebase -x
If you’re a neat freak when it comes to commit history, you may have used Git’s interactive rebase. It’s great for fixing commit messages, or squashing, splitting, or reordering commits. But at it’s heart, interactive rebase is really just a recipe of instructions to follow: pick this commit, then squash that one, now edit the message on this one, and so on.
What many people don’t know is that you can insert any command you like into the instruction sheet, and Git will run it at the appropriate time, stopping the run only if the command fails. This makes it perfect for testing each commit in a series. The rebase stops at the first buggy commit, allowing you to fix it, re-run the tests, and amend the commit before continuing. If your project does commit-by-commit review, polishing your commits like this (rather than lumping on bug fixes at the end) is a great way to be kind to your reviewers. Rather than complain about your bugs in the early commits only to find them fixed later on, they get to see more directly the changes you propose, in a logical flow that builds towards the final goal.
Of course, writing out exec make test
for each commit in your instruction
sheet gets pretty tedious. That’s why git rebase
supports a -x
option to add it for each commit, and as of this release, -x
implies -i
. And because rebase
already defaults to your @{upstream} tracking branch
, testing a full branch is as easy as:
$ git rebase -x 'make test'
Executing: make test
Result: OK
Executing: make test
Result: FAIL
make: *** [test] Error 2
Execution failed: make test
You can fix the problem, and then run
git rebase --continue
Oh no, a bug! We can fix it up and continue on our way:
$ hack hack hack
$ make test
Result: OK
$ git commit -a --amend
$ git rebase --continue
Executing: make test
Result: OK
Successfully rebased and updated refs/heads/your-branch.
And now our perfect result is ready to be shared. [source]
Git Tidbits (Gitbits? Tidgits?)
- The
git describe
command gives a human-readable (but still uniquely identifiable) name to commits. In its--contains
mode, it finds a tag that contains a given commit, which makes the names it generates a good shorthand for “about when did this commit get released?”. Older versions of Git looked for the topologically “closest” tag, but this could sometimes produce counter-intuitive results when newer tags had been merged in. Git 2.9 has a new heuristic that is much easier to explain: pick the oldest tag that contains the commit, and describe the path between them in the shortest way possible. [source] -
If you’re a fan of ASCII art (and let’s be honest; who isn’t?), you’ll be pleased to know that
git log
will now expand tabs in commit messages to account for its 4-space indentation. That turns messy and broken alignment like this:$ git log commit 8d7fca1d690ff2ffb678dc00fbca1954df5e5b90 Author: Mu-An Chiou <muan@users.noreply.github.com> Date: Mon Sep 23 09:21:03 2013 +0900 _____ ╲ ╲ │ │ │ │ ___________________________ └─#──┘ ######## / | ## ##~ ~ ~ ## / | ##### ########## < My circuits are frazzled! | ##### ########## | ### #######╱╱####### ___________________________| | / ####╱╱ HUBOT ## . / ##╱╱########### . ╱╱############ | . ############# ### ############# #### ###### #### ### XX ## #
into this:
$ git log commit 8d7fca1d690ff2ffb678dc00fbca1954df5e5b90 Author: Mu-An Chiou <muan@users.noreply.github.com> Date: Mon Sep 23 09:21:03 2013 +0900 _____ ╲ ╲ │ │ │ │ ___________________________ └─#──┘ ######## / | ## ##~ ~ ~ ## / | ##### ########## < I have expanded your tabs! | ##### ########## | ### #######╱╱####### ___________________________| | / ####╱╱ HUBOT ## . / ##╱╱########### . ╱╱############ | . ############# ### ############# #### ###### #### ### XX ## #
Oh, and you can use it for serious things like tables and diagrams, too. [source]
- Rename detection is now enabled by default for diffs. You may have heard that Git doesn’t record renames. It’s true!
Git infers on the fly when a file has been renamed by looking for similarities between the contents of the old and new files. This feature, which has existed since the early days of Git, is now enabled by default. [source] - You can now specify a custom path for hooks. Git calls hook scripts to allow you to implement custom policy or actions when certain events occur. These hook scripts are found inside the
.git
directory of each repository, making them a pain to manage if you have a standard set of hooks for all of your repositories. Now you can store the hooks in one place and point all of the repositories at them with the newcore.hooksPath
config. [source] - The
git-p4
tool can now mapp4
users to Git identities, as well as recordp4
job information in the commit messages. Thanks togit-p4
and other tools, you don’t have to forego the joy of using Git as a client, even if the project you’re working on uses Perforce (or SVN, or
Hg, or CVS, or…). [source, source]
The rest of the iceberg
That’s just a sampling of the changes in Git 2.9. Check out the the full release notes for the complete list.
Written by
Related posts
Securing Git: Addressing 5 new vulnerabilities
Git is releasing several new versions to address five CVEs. Upgrading to the latest Git version is essential to protect against these vulnerabilities.
Just launched: Second cohort of the DPG Open Source Community Manager Program!
Are you looking to have a positive impact in open source development? This program may be for you! Apply by May 30 to join.
Create a home for your community with GitHub Discussions
GitHub Community-in-a-box provides the tooling, resources, and knowledge you need to build internal communities of learning at scale with GitHub Discussions.