Highlights from Git 2.24
Take a look at some of the new features in the latest Git release.
The open source Git project just released Git 2.24 with features and bug fixes from over 78 contributors, 21 of them new. Here’s our look at some of the most exciting features and changes introduced since Git 2.23.
Feature macros
Since the very early days, Git has shipped with a configuration subsystem that lets you configure different global or repository-specific settings. For example, the first time you wrote a commit on a new machine, you might have been reminded to set your user.name
and user.email
settings[1] if you haven’t already.
It turns out, git config is used for many things ranging anywhere from the identity you commit with and what line endings to use all the way to configuring aliases to other Git commands and what algorithm is chosen to produce diffs.
Usually, configuring some behavior requires only a single configuration change, like enabling or disabling any of the aforementioned values. But what about when it doesn’t? What do you do when you don’t know which configuration values to change? For example, let’s say you want to live on the bleeding-edge of the latest from upstream Git, but don’t have a chance to discover all the new configurable options. In Git 2.24, you can now opt into feature macros—one Git configuration that implies many others. These are hand-selected by the developers of Git, and they let you opt into a certain feature or adopt a handful of settings based on the characteristics of your repository.
For example, let’s pretend that you have a particularly large repository, and you’re noticing some slow-downs. With enough searching, you might find that setting index.version
to 4
could help, but discovering this can seem like a stretch. Instead, you can enable feature.manyFiles
with:
$ git config feature.manyFiles true
Now you’re opted into the features that will make your experience with Git the smoothest it can be. Setting this signals to Git that you’re willing to adopt whichever settings Git developers feel can make your experience smoothest (right now, this means that the index.version
and core.untrackedCache
to enable path-prefix compression and the untracked cache, respectively). Not only that, but you can feel even better knowing that any new features in a release that might help your use case will be included in the macro.
[source]
Commit graphs by default
You may remember commit graphs, a feature that we have discussed in some of our previous highlights. Since its introduction in Git 2.19, this feature has received a steady stream of attention. When enabled, and kept reasonably up to date, commit graphs can represent an order of magnitude improvement in the performance of loading commits.
Now in Git 2.24, commit graphs are enabled by default, meaning that your repository will see an improvement the next time you run git gc
. Previously, this feature was an opt-in behind an experimental core.commitGraph
configuration (as well as a handful of others), but after extensive testing[2], it’s ready for prime time.
Besides being the new standard, here are a few other changes to commit graphs:
- All
commit-graph
sub-commands (e.g.,git commit-graph write
,git commit-graph verify
, and so on) now have support for the-[no-]progress
. Now the progress meters for these commands behave in the usual way: writing only to a terminal by default, and respecting-[no-]progress
to override that. - A new configuration value to automatically update the
commit-graph
file while fetching has been introduced, which takes advantage of commit graph chains to write a portion of history onto a commit-graph chain for later compaction. This means that every time you get new commits from a remote, they are guaranteed to be in acommit-graph
immediately, and you don’t have to wait around for the next auto-gc. To try this out today, set thefetch.writeCommitGraph
configuration variable totrue
. - Lots of bug fixes to improve the performance and reliability of the
commit-graph
commands, especially when faced with corrupt repositories. - The
commit-graph
commands now also support Git’s latest tracing mechanism, trace2.
[source, source, source, source, source, source, source]
Adopting the Contributor Covenant
Since the last release, the Git project has discussed at length adopting a code of conduct to solidify welcoming and inclusive behavior on the mailing list where Git development takes place.
Because communication between Git developers happens over email, it can be intimidating or unwelcoming to new contributors who may not be familiar with the values of the people contributing to Git. The Git community has long relied on a policy of “be nice, as much as possible” (to quote this thread). This approach is in the right spirit, but it may not be readily apparent to new contributors unfamiliar with the existing culture. Likewise, it can make an individual feel uncomfortable engaging with a project that has not solidified its values.
By adopting a code of conduct, the Git project is making it clear which behaviors it encourages and which it won’t tolerate. New contributors are able to see explicitly what the project’s values are, and they can put their trust in Git’s choice of using the well-trusted and widely-adopted Contributor Covenant. This code of conduct is enforced by the project’s leadership, who will handle any case in which an individual does not adhere to the guidelines.
New contributors can be assured that the Git community is behind this adoption with the introduction of the Code of Conduct, Acked-by
16 prominent members of the Git community.
[source]
Alternative history rewriting tools
If you’ve ever wanted to perform a complicated operation over the history of your repository—like expunging a file from a repository’s history or extracting the history pertaining to just one directory—you may have visited the documentation for git filter-branch
.
git filter-branch
is a long-standing and powerful tool for rewriting history[3]. With git filter-branch
, you can do all of those aforementioned operations and much more. However, this flexibility comes at a hefty cost: git filter-branch
is notoriously complicated to use (not to mention slow), and can often lead its users towards unintended changes, including repository corruption and data loss.
In other words, git filter-branch
is starting to show its age. Now, as of Git 2.24, the Git project instead recommends a new, independent tool, git filter-repo
.
git filter-repo
serves to avoid many of the pitfalls that users experienced with git filter-branch
. Instead of reprocessing every commit in order, git filter-repo
operates on an efficient, stream representation of history to run much faster. The tool is extremely powerful, and all of its capabilities are documented thoroughly. Here are a few highlights about how you can use git filter-repo
:
git filter-repo --analyze
provides a human-readable selection of metrics profiling the size of your repository. This includes how many objects of each kind there are, which files and directories are largest, which extensions take up the most space, and so on. And this isn’t your only option in this space. For additional metrics on the shape of your repository, check out another tool,git sizer
.- You can also filter the history of your repository to contain only certain paths, with
--path-{glob,regex}
and similar options. [source] - Likewise, you can run a “find and replace” operation over history, as well as strip blobs that are larger than a fixed threshold. [source]
- When rewriting history, any rewritten commits (along with their ancestors) will get a new SHA-1 to identify them. By default,
git filter-repo
updates all other references to these SHA-1s, like other commit messages that reference them. By a similar token,git filter-repo
also has options to rewrite the names of contributors using.mailmap
. [source, source] - Finally,
git filter-repo
is extensible. It provides a flexible interface for specifying callbacks in Python (e.g., calling a function whengit filter-repo
encounters a blob/tree/commit, new filetype, etc.), as well as defining new sub-commands entirely. View a portfolio ofdemo
extensions, or define your own to support a complex history rewrite. [source]
git filter-branch
will, however, remain included in the usual distributions of Git for some time. git filter-repo
is another alternative for performing complex modifications to your repository’s history, and it’s now the official recommendation from upstream.
[source]
Tidbits
You might be aware that many Git commands take one or more optional reference names as arguments. For example, git log
without arguments will display a log of everything that’s reachable from the currently checked-out branch, but git log my-feature ^master
will show you only what’s on my-feature
and not on master
.
But what if your branch is called --super-dangerous-option
, you probably don’t want to invoke git log
since it’ll interpret the argument as an option, not a branch name. You could try and disambiguate by invoking git log 'refs/heads/--super-dangerous-option'
, but if you’re scripting, you may not know under what namespace the argument you’re getting belongs.
Git 2.24 has a new way to prevent this sort of option injection attack using --end-of-options
. When Git sees this as an argument to any command, it knows to treat the remaining arguments as such, and won’t interpret them as more options.
So, instead of the string previously mentioned, you could write the following to get the history of your (admittedly, pretty oddly named) branch:
$ git log --end-of-options --super-dangerous-option
Not using the standard --
was an intentional choice here, since this is already a widely-used mechanism in Git to separate reference names from files. In this example, you could have also written git log --end-of-options --super-dangerous-option ^master -- path/to/file
to get only the history over that range which modified that specific file.
[source]
In a previous post, we talked about git rebase
‘s new --rebase-merges
option, which allows users to perform rebases while preserving the structure of history. But when Git encounters a merge point how does it unify the two histories? By default, it uses a strategy known internally as “recursive”, which is most likely the merge strategy you’re currently using.
You might not know that you can tell Git which merge strategy to use, and picking one over the other may result in a different resolution[4].
Now, git rebase --rebase-merges
supports the --strategy
and --strategy-option
options of git rebase
, so you can rebase history while both preserving its structural integrity and specifying your own merge resolution strategy.
[source]
Git supports a number of hooks, which are specially-named executable files that Git will run at various points during your workflow. For example, a pre-push
hook is invoked after running git push
but before the push actually occurs and so on.
A new hook has been added to allow callers to interact with Git after a merge has been carried out, but before the resulting commit is written. To intercept this point, callers can place an executable file of their choice in .git/hooks/pre-merge-commit
.
[source]
Git has learned a handful of new tricks since the last release to handle partial clones. For those who aren’t up to date on what partial cloning in Git looks like, here’s a quick primer. When cloning a repository, users can specify that they would only like some of its objects by using a filter. When doing so, the remote from which the user clones is designated as a “promisor,” meaning that it promises to send the remaining objects later on if the user requests them down the road.
Up until 2.24, Git only supported a single promisor remote, but this latest release now supports more than one promisor remote. This is especially interesting since it means that users can configure a handful of geographically “close” remotes, and not all of those remotes have to have all of the objects.
There’s more work planned in this area, so stay tuned for more updates on this feature in the future.
[source]
Last but not least, Git’s command-line completion engine has learned how to complete configuration variables on per-command configurations.
Git has a hierarchy of places where gitconfig
files can be found: your repository (via .git/config
), your home directory, and so on. But, Git also supports the top-level -c
flag to specify configuration variables for each command. Here’s an example:
$ git -c core.autocrlf=false add path/to/my/file
This invocation will disable Git’s auto-CRLF conversion just for the duration of the git add
(and any other internal commands that Git may run as a part of git add
).
However, if you forgot the name of the variable that you were trying to set Git’s command-line completion engine learned how to provide a completion list of configuration variable names in Git 2.24. So, if you ever forget where you are in the middle of a -c ...
, all you need to do is press tab.
[source]
[1] When you create a commit, these are the configuration values that Git is looking at to generate the signature; Git-parlance for the name/email-pair that is shown wherever an identity is expected.
[2] Including at GitHub, where we’ve used the commit-graph
file behind the scenes since August to achieve speed-ups of over 50 percent on operations which traverse history.
[3] In fact, this tool appeared in git/git
with 6f6826c52b
which was first released in v1.5.3 over 12 years ago.
[4] For example, the patience
merge strategy is widely regarded as one that will “move up” chunks of your diff (these are usually known as “hunks”) that look textually related (for example, closing function braces), but in fact produce awkward-looking diffs.
Learn more
That’s just a sample of changes from the latest version. Check out the release notes for 2.24 or any previous versions in the Git repository.
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.