Git 2.13 has been released
The open source Git project has just released Git 2.13.0, with features and bugfixes from over 65 contributors. Before we dig into the new features, we have a brief security…
The open source Git project has just released Git 2.13.0, with features and bugfixes from over 65 contributors. Before we dig into the new features, we have a brief security announcement.
For those running their own Git hosting server, Git 2.13 fixes a vulnerability in the git shell
program in which an untrusted Git user can potentially run shell commands on a remote host. This only affects you if you’re running a hosting server and have specifically configured git shell
. If none of that makes sense to you, you’re probably fine. See this announcement for more details. As neither GitHub.com nor GitHub Enterprise uses git shell
, both are unaffected.
Phew. With that out of the way, let’s get on to the fun stuff.
SHA-1 collision detection
Did I say fun? Oops, we’re not there yet.
You may have heard that researchers recently found the first collision in SHA-1, the hash function Git uses to identify objects. Their techniques may eventually be used to conduct collision-based attacks against Git users. Fortunately those same researchers also provided a way to detect content that is trying to exploit this technique to create collisions. In March, GitHub.com began using that implementation to prevent it being used as a potential platform for conducting collision attacks.
Git 2.13 ships with similar changes, and will detect and reject any objects that show signs of being part of a collision attack. The collision-detecting SHA-1 implementation is now the default. The code is included with Git, so there’s no need to install any additional dependencies. Note that this implementation is slower than the alternatives, but in practice this has a negligible effect on the overall time of most Git operations (because Git spends only a small portion of its time computing SHA-1 hashes in the first place).
In other collision detection news, efforts have continued to develop a transition plan and to prepare the code base for handling new hash functions, which will eventually allow the use of stronger hash algorithms in Git.
[collision detection, SHA-1 transition 1, SHA-1 transition 2]
More convenient pathspecs
You’ve probably passed path arguments to Git before, like:
$ git log foo.c
$ git grep my_pattern program.rb
But you may not have known that to Git, the foo.c
and program.rb
arguments are actually pathspecs, a Git-specific pattern for matching paths. Pathspecs can be literal paths, prefixes, or wildcards:
$ git log Documentation/ # Everything under the Documentation/ directory
$ git log '*.c' # C files anywhere in the tree
But they also have a powerful extension syntax. Pathspecs starting with :(magic)
enable special matching features. The complete list can be found in the pathspec
section of git help glossary
, but let’s look at a few here.
For instance, you may want to exclude some files from a grep, which you can do with the :(exclude)
directive:
$ git grep this.is.a src
src/foo.c:this is a C file
src/foo.rb:this is a ruby file
$ git grep this.is.a -- src ':(exclude)*.c'
src/foo.rb:this is a ruby file
There are a few things to note in that example. The first is that we had to put our pathspec after a --
(double-dash) separator. This is necessary because most Git commands actually take a combination of revisions and pathspecs. The full syntax is [<revisions>] -- [<pathspecs>]
. If you omit the double-dash, Git will check each argument to see if it’s either a valid object name or a file in the filesystem. But since our exclude pattern is neither, without the double-dash Git would give up and complain (this may change in a future version of Git; wildcards like *.c
used to have the same problem, but the rules were recently loosened to resolve them as pathspecs). More information is available via git help cli
.
The second thing to note is that typing :(exclude)
is a pain, and we have to quote it from the shell. But there’s a solution for that: short form pathspec magic. The short form for exclude
is !
(exclamation point). This is easy to remember, since it matches the syntax in other parts of Git, like .gitignore
files.
$ git grep this.is.a -- src ':!*.c'
src/foo.rb:this is a ruby file
That’s shorter than exclude
, but we still have to quote, since the exclamation point triggers history expansion in most shells. Git 2.13 adds ^
(caret) as a synonym for the exclamation point, letting you do the same thing without any shell quoting:
$ git grep this.is.a -- src :^*.c
src/foo.rb:this is a ruby file
Ah, much better. Technically we would need to also quote the *.c
wildcard from the shell, but in practice it works out. Unless you have a file that starts with :^
and ends in .c
, the shell will realize that the wildcard matches nothing and pass it through to Git verbatim.
But wait, there’s more! Git 2.13 also adds the attr
token, which lets you select files based on their gitattributes values. For instance, if you use Git LFS, you may want to get a list of files which have been configured to use it:
$ git ls-files
.gitattributes
README
video.mp4
$ git ls-files ':(attr:filter=lfs)'
video.mp4
You can even define your own attributes in order to group files. Let’s say you frequently want to grep
a certain set of files. You can define an attribute, and then select those files using that attribute:
$ echo 'libfoo/ vendored' >>.gitattributes
$ echo 'imported-tool/ vendored' >>.gitattributes
$ git grep -i license -- ':(attr:vendored)'
And if you want to get really fancy, you can combine the attr
and exclude
tokens:
$ git grep foobar -- ':(exclude,attr:vendored)'
Note that the attr
token is not yet supported in all parts of the code. Some commands may report that it cannot be used with them, but this is likely to be expanded in future versions of Git.
[negative pathspecs, attribute pathspecs]
Conditional configuration
Git’s configuration system has several levels of priority: you can specify options at the system level, the user level, the repository level, or for an individual command invocation (using git -c
). In general, an option found in a more specific location overrides the same option found in a less specific one. Setting user.email
in a repository’s .git/config
file will override the user-level version you may have set in ~/.gitconfig
.
But what if you need to set an option to one value for a group of repositories, and to another value for a different group? For example, you may use one name and email address when making commits for your day job and another when working on open source. You can set the open source identity in the user-level config in your home directory and then override it in the work repositories. But that’s tedious to keep up to date, and if you ever forget to configure a new work repository, you’ll accidentally make commits with the wrong identity!
Git 2.13 introduces conditional configuration includes. For now, the only supported condition is matching the filesystem path of the repository, but that’s exactly what we need in this case. You can configure two conditional includes in your home directory’s ~/.gitconfig
file:
[includeIf "gitdir:~/work/"]
path = .gitconfig-work
[includeIf "gitdir:~/play/"]
path = .gitconfig-play
Now you can put whatever options you want into those files:
$ cat ~/.gitconfig-work
[user]
name = Serious Q. Programmer
email = serious.programmer@business.example.com
$ cat ~/.gitconfig-play
[user]
name = Random J. Hacker
email = rmsfan1979@example.com
The appropriate config options will be applied automatically whenever you’re in a repository that’s inside your work
or play
directories.
Bits and bobs
-
--decorate=auto
is now the default forgit log
. When output is sent to the user’s terminal, commits that are pointed to directly by a branch or tag will be “decorated” with the name of the branch. [source] -
git branch
‘s output routines have been ported to theref-filter
system shared bygit for-each-ref
andgit tag
. This means you can now usegit branch --format=
to get custom output. Seegit help for-each-ref
for the list of substitutions. As a side note, these patches are from Karthik Nayak, Git’s Google Summer of Code student from 2015. Though his GSoC project to introduceref-filter
was completed almost two years ago, he’s continued contributing to the project. Great work! [source] -
git branch
,git tag
, andgit for-each-ref
all learned the--no-contains
option to match their existing--contains
option. This can let you ask which tags or branches don’t have a particular bug (or bugfix). [source] -
git stash
now accepts pathspecs. You can use this to create a stash of part of your working tree, which is handy when picking apart changes to turn into clean commits. [source] -
The special branch names
@{upstream}
,@{u}
, and@{push}
are now case-insensitive. This is especially convenient as both@
and{
require holding down the shift key on most keyboards, making it easy to accidentally type a capitalU
. Now you can hold that shift key AS LONG AS YOU WANT. [source] -
More commands have learned to recurse into submodules in the past few versions of Git, including
checkout
,grep
, andls-files
.git status --short
also now reports more information about submodules. [source, source, source, source] -
The last few versions of Git have cleaned up many corner cases around repository discovery and initialization. As a final step in that work, Git 2.13 introduced a new assertion to catch any cases that were missed. After being tested for months in development versions, this shouldn’t trigger. But it’s possible that you may see
BUG: setup_git_env called without repository
. If you do, please consider making a bug report. [source]
The whole kit and caboodle
That’s just a sampling of the changes in Git 2.13, which contains over 700 commits. 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.