Git 2.4 — atomic pushes, push to deploy, and more
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…
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.
Atomic pushes
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
:
$ 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
The --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]
Push-to-deploy improvements
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:
- There is now a
push-to-checkout
hook, 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. Thepush-to-checkout
hook 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] - Push-to-deploy formerly didn’t work correctly when pushing to a server that is on an “unborn branch”. (An “unborn branch” is what Git calls a branch that doesn’t yet have any commits on it, as for example immediately after a Git repository is initialized.) Now this works as expected, which will hopefully reduce confusion for users who are trying to set up push-to-deploy for a new project. [source]
Inverted grep
for logs
git log
is a very powerful command, with a bewildering variety of options. One class of useful options includes --grep=<pattern>
, --author=<pattern>
, --committer=<pattern>
, and --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
Advanced usage
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.
Other minor improvements
-
git status
now allows the--verbose
option 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 usuallog
output, now shows not only the currentHEAD
, but also indicates which branch it currently points at, in the format(HEAD -> master)
. [source] - There is now a configuration setting
push.followTags
, to turn ongit push
‘s--follow-tags
option by default. [source] - The HTTP-based transports now send
Accept-Language
headers 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]
The rest of the iceberg
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
Looking to level up your Git game? Browse the docs on the main Git website, grab a copy of Pro Git, or read GitHub’s Guide to setting up Git.
Happy collaborating!
Tags:
Written by
Related posts
What the EU’s new software legislation means for developers
The EU Cyber Resilience Act will introduce new cybersecurity requirements for software released in the EU. Learn what it means for your open source projects and what GitHub is doing to ensure the law will be a net win for open source maintainers.
Game Off 2024 theme announcement
GitHub’s annual month-long game jam, where creativity knows no limits! Throughout November, dive into your favorite game engines, libraries, and programming languages to bring your wildest game ideas to life. Whether you’re a seasoned dev or just getting started, it’s all about having fun and making something awesome!
Highlights from Git 2.47
Git 2.47 is here, with features like incremental multi-pack indexes and more. Check out our coverage of some of the highlights here.