Git’s 10-year birthday celebrations notwithstanding, the Git community has been busy preparing another major new release of the Git command-line utility. Release 2.4.0 is weighted towards cleanups, bug fixes, and other small improvements, but here we would like to take a moment to highlight a few new features that you might find useful.
Until now, when you tried to push multiple branches to a remote Git server, some of the updates might have succeeded while others failed. For example, somebody else might have pushed to one of the branches, meaning that you have to reconcile your changes with theirs and try pushing that branch again.
But for some purposes you might want to push a set of reference changes atomically, meaning that either all of the reference updates are accepted, or none of them are. Now that is possible, using the new
--atomic option to
$ git push --atomic origin branch1 branch2 ...
This feature is probably most useful for automated tools. Suppose you have a tool that runs continuous integration on a branch, and if the test succeeds it merges the branch to master, creates a new tag, and adds a note to the commit. All three things can be pushed at the same time using
$ git push --atomic origin master refs/tags/release-17 refs/notes/test-results
--atomic option guarantees that either all three references will be updated on the remote, or none of them will.
Please note that
git push --atomic is still somewhat experimental, and it is possible to experience partial updates if you try to push something unusual. But if you use
--atomic, the most common reason for a reference update to be rejected — the dreaded “non-fast-forward” update — will no longer leave your push half-accepted, half-rejected. [source]
The last major Git release, Git 2.3, introduced the ability to push directly to a branch that is checked out on a remote Git server, making it an easy way to deploy a new version of a website just by pushing. (But please read our last Git release blog post to learn about some caveats that apply to this approach.)
Git 2.4 improves push-to-deploy in two ways:
push-to-checkouthook, which can be installed on the server to customize exactly what happens when a user pushes to the checked-out branch. For example, by default such a push fails if there have been any changes to the working tree on the server. The
push-to-checkouthook could instead try to merge any server-side edits with the new branch contents, or it could unconditionally overwrite any local changes with a pristine copy of the pushed branch contents. [source]
git log is a very powerful command, with a bewildering variety of options. One class of useful options includes
--grep-reflog=<pattern>, which limit the output to commits whose commit message, author, committer, or reflog entry, respectively, matches the specified regular expression pattern.
There is a new option,
--invert-grep, that inverts the sense of the other pattern-matching options. When this option is used,
git log lists the commits that don’t match the specified pattern(s). For example, to search for merge commits that do not include a “Fixes” annotation in their commit messages, you could run
$ git log --all --merges --invert-grep --grep=Fixes
It is not possible to combine pattern-matching options into arbitrary expressions like “match A and B but not C” in a single
git log command. But now, thanks to
--invert-grep, you can do so by stringing commands together in pipelines, though it is a bit subtle. For example, suppose you want to find the non-merge commits in Git’s
master branch that were written by its maintainer, Junio Hamano, but are missing “Signed-off-by” lines:
$ git rev-list --no-merges --author="Junio C Hamano" master | git log --stdin --no-walk --invert-grep --grep='^Signed-off-by:'
Note that the first command uses
git rev-list, which just lists the SHA-1s of matching commits rather than showing their commit messages etc.
git rev-list takes many of the same options as
git log. Its output is used as the input to a second command, which uses
--stdin --no-walk to read commit SHA-1s from its standard input and only process those commits. (Without
--no-walk, the second command would also process the ancestors of the commits passed to it.) The second command thus skips any commits that contain “Signed-off-by” lines, and outputs the rest.
It turns out that many of the commits listed by the previous command are “revert” commits, which don’t really need
Signed-off-by lines, so let’s exclude revert commits and count how many are left:
$ git rev-list --no-merges --author="Junio C Hamano" master | git rev-list --stdin --no-walk --invert-grep --grep='^Signed-off-by:' | git rev-list --stdin --no-walk --invert-grep --grep='^Revert ' | wc -l 76
As you can see, it is possible to put together quite sophisticated queries using these building blocks.
git statusnow allows the
--verboseoption to be specified twice, in which case it shows the changes that have been staged but not yet committed and also the changes in the working tree that have yet to be staged. [source]
git log --decorate, which lists branch names alongside the usual
logoutput, now shows not only the current
HEAD, but also indicates which branch it currently points at, in the format
(HEAD -> master). [source]
push.followTags, to turn on
--follow-tagsoption by default. [source]
Accept-Languageheaders when making requests. This opens the way to internationalizing the informational messages emitted by the Git server, though that effort has not yet begun. [source]
Aside from the highlights listed here, there have been myriad small improvements to Git since version 2.3.0 — over 400 commits in all, by 76 different contributors. For full details, see the Git 2.4.0 release notes. Or, even better, view the commits using Git itself:
$ git clone https://github.com/git/git.git $ cd git $ git log --oneline --graph v2.3.0..v2.4.0