Partnering with EU policymakers to ensure the Cyber Resilience Act works for developers
We’re looking forward to working with policymakers to improve cybersecurity and support developers.
A look at some of the new features in the latest Git release.
The open source Git project just released Git 2.22 with features and bug fixes brought to you from over 74 contributors, 18 of them new. Here’s our look at some of the most exciting features and changes introduced since Git 2.21.
You might have used
git rebase before to alter the history of your repository. If you haven’t, here’s a quick primer:
git rebase replays a series of commits on a new initial commit. For example, you might have used
git rebase to make sure that your feature branch was based on the latest changes from upstream. Say that you have a repository structure that looks like:
o (my-feature) / o --- o --- o (master)
Let’s say that while you were working on
my-feature, the branch you started from (
master) changed, so now your repository looks like:
o --- o --- o (my-feature) / o --- o --- o --- o --- o (master)
How do you make sure that your branch
my-feature merges into
master cleanly? You could just merge it, but the end result might be hard for others to understand if you have to resolve conflicts. If you haven’t yet shared the branch with reviewers, they might prefer to see your commits as if they had been written directly on top of the current
So, you might rebase it. Remember: rebasing is an operation that takes a series of commits and applies them on top of a new base. So, after rebasing
my-feature on the latest from
master, you’ll instead get:
o --- o --- o (my-feature) / o --- o --- o --- o --- o (master)
git rebase did was take the first “new” commit in (my-feature), applied it on top of the new tip of master, and then so on through all of the remaining commits on
my-feature in order, until there were none left.
Now, what if your example is more complicated? Let’s say that
my-feature has its own branching structure that you want to preserve while replaying the commits. To further complicate things, let’s also assume that you want to use some of
rebase‘s interactive features, like dropping, reordering, and renaming commits.
If you didn’t mind giving up those interactive features, you could have used
--preserve-merges. You can use
git rebase -i --preserve-merges and edit the history interactively, but some of your repository’s structure might not remain intact.
Git has a new option
--rebase-merges, since 2.18, but in 2.22 the old option is officially deprecated in favor of
--rebase-merges allows you to preserve the structure of your changes, while also giving you the full power of interactivity.
Here’s an example. Let’s say that you have a branching structure based on
master, but upstream (say,
origin/master) has changed since you created your branch. You want to replay your commits on the latest from upstream, preserve the branching structure, and make a few modifications to a commit message along the way (we’ll simulate this by fixing a typo).
In Git 2.22, this is what that might look like:
Given some set of two or more branches, how can you tell what history they have in common? As it turns out, Git has a precise way to answer this question. Git calls this a merge base: the most recent common ancestor among a set of commits.
When might you want to compute a merge base in practice? The obvious answer is: when you’re merging! Git computes this common ancestor as the base for a three-way merge of the content (hence the name “merge base”). But you might also want to use this common ancestor as a cutoff point for listing commits. Running
git log A...B will show all of the commits in
B down to their common ancestor; in other words, what happened since the two diverged (you can also use
--left-right to see which commit is on which side).
This “triple-dot” notation is associated with merge bases in other contexts, too. Running
git diff A...B will show the differences between
B and the merge base of
B. That’s another way of showing what has happened on
What’s another instance that you might want to use a merge-base? Say that you’re working on a feature branch, and you decide that part of the way through, you’d like to start over on a different branch. Let’s also say that you’d like to start at the same place on
master that your existing feature branch was cut from.
How do you create such a thing? Well, you could manually inspect the
git log, but this might be cumbersome if your history is particularly large. You could invoke:
$ git merge-base master my-feature
to compute the SHA-1 of the merge base between
my-feature, copy the SHA-1 you got back, and then paste it into:
$ git branch my-other-feature <sha-1>
Now in Git 2.22,
git branch and
git checkout -b have both learned the triple-dot merge base syntax. To specify that you’d like to create a branch from the merge base of two other branches (say,
B), you can now run:
$ git branch my-other-feature A...B # or... $ git checkout -b my-other-feature A...B
Here’s an example:
git symbolic-ref(if you were aware of its existence), or hacked up the output of
git branchwith a combination of
awk, but both seem like rather unfulfilling options.
git branch --show-currentto get the name of the branch you currently have checked out. (If you want to use this output in a machine-readable setting,
git symbolic-refis still preferred, though.) [source]
dir/directory as it was on
master. To do this, you might run
git checkout master -- dir. But what if one of the files,
dir/file.txt, wasn’t present at all on
master? What does it mean to “go back” to that state?
masterinto your copy of
dir/. It will copy any contents that are present in
masterinto your working tree, but won’t delete tracked files that
masterdoesn’t have. The result is a combination of the two: what you had before and what was in
dir/into the exact set of contents from
master, both adding and deleting as appropriate. In Git 2.22, there’s now a way to express that:
git checkout --no-overlay -- dir. The default behavior remains unchanged (i.e., the same as passing
git diff --function-context(to show the function context nearest each hunk), it also will accept
git diff --no-function-context. This is a result of using Git’s own
parse-optionsAPI, which is used across many sub-commands to ensure that command-line options are parsed consistently.
git diffcommand was written prior to the
parse-optionsAPI, so it had its own hand-crafted parser. In Git 2.22,
git diffnow uses the
parse-optionsAPI, meaning you should expect more consistent command-line options parsing in more parts of Git. [source]
--formatoption has allowed you to list those trailers as part of a custom format.
$ git log --pretty="%(trailers:key=Reviewed-by,valueonly)" | grep '.' | sort | uniq -c | sort -rn | head -5
git bisecttries to show you a pretty version of the commit. But ever since it was introduced in 2005,
bisecthas used the
diff-treeplumbing to show you the commit, which is a long way from “pretty”. By default, it shows you only the top-level of the tree. So you might find out that the commit in question changed the
src/directory. Not helpful. It also shows the machine-readable
--rawdiff format, giving you only the before and after hashes, with no clue as to what actually changed. And for merge commits, it shows nothing at all!
bisectwill show a full
--statdiff, summarizing the changes to each file by line count (for merges, this counts the differences that were brought in by the merge). It stops short of showing the full content-level diff, but you can view that yourself with
git show. [source]
v2.22.0tag points to this commit.
$ git tag -f -m "updated message" <my-tag> <my-tag>
Which you may have written if you meant “update
<my-tag> with this message and leave the thing it points to unchanged”. But, that invocation creates a new tag which points at the old tag, when you most likely meant to point it at the thing that the old tag points at.To help prevent you from making this mistake, Git now warns you when you create a tag pointing to another tag. [source]
That’s just a sampling of changes from the latest version. Read the full release notes for 2.22, or find the release notes for previous versions in the Git repository.