The version flag prints your version, and the dashless form works too, because Git accepts both. Everything past that is knowing which number to care about.
Checking the version is a ten-second job. The reason people search for it is usually the next question: a tutorial requires a feature, a command fails with an unknown-option error, or a security advisory names a version and you need to know whether you are past it.
Table of contents
- Checking it
- Reading the number
- Versions that actually changed something
- Why yours is old, and how to fix it
- Version numbers in your own project
- How this fits the rest of the stack
- FAQ
Checking it
git --version
# git version 2.51.0
# The dashless form does the same thing
git version
# On Windows, the build string carries the packaging suffix
# git version 2.51.0.windows.1
# On macOS with Xcode command line tools
# git version 2.39.5 (Apple Git-154)
Those last two forms matter. A Windows suffix means Git for Windows, which ships its own patch numbering. An Apple suffix means the Git bundled with Xcode command line tools, which is typically well behind upstream and updates only when you update Xcode.
To see where the binary is coming from - useful when you have installed a newer Git and are still getting the old one:
which -a git
# /opt/homebrew/bin/git
# /usr/bin/git
git --exec-path
# /opt/homebrew/Cellar/git/2.51.0/libexec/git-core
If the system Git is listed first, your path is ordered so the old one wins. That is the single most common reason someone installs a new Git and the version output does not change.
Reading the number
Git uses a major, minor, and patch scheme, but not with the semantic-versioning meanings people assume.
- Major bumps are rare and historically not breaking. Git 2.0 arrived in 2014 and there has been no 3.0 since.
- Minor bumps are the real releases, roughly quarterly. Features land here.
- Patch bumps are fixes and security releases, backported to several minor versions at once.
So the number that tells you what your Git can do is the minor version. When documentation says a feature requires 2.28, it means minor 28 or later, and the patch is irrelevant to the question.
Security advisories work the other way. A fix is usually released across multiple lines simultaneously, so the relevant comparison is against the patched release on your own minor line, not the newest version overall.
For scripts that need to gate on version, parse it rather than string-comparing, because 2.9 sorts after 2.10 lexically and before it numerically.
ver=$(git --version | awk '{print $3}')
minor=$(echo "$ver" | cut -d. -f2)
if [ "$minor" -lt 28 ]; then
echo "Git $ver is too old; need 2.28+ for init.defaultBranch" >&2
exit 1
fi
Versions that actually changed something
A handful of minor releases are worth knowing because they are the ones tutorials assume and old installs lack.
- 2.23 - the switch and restore commands, splitting the overloaded checkout into two clearer commands.
- 2.28 - a configurable default branch name, so init can create main without a rename step.
- 2.34 - SSH signing for commits and tags, so you no longer need GPG to sign.
- 2.37 - automatic upstream setup on push, so a plain push on a new branch stops erroring with a long suggestion.
- 2.38 - the safe directory configuration, relevant to the ownership errors that appear in containers and CI.
- 2.44 - notable performance work on large repositories.
# Three settings worth having, all version-gated
git config --global init.defaultBranch main # 2.28+
git config --global push.autoSetupRemote true # 2.37+
git config --global pull.rebase true
If a command fails with an unknown-option error, the version is the first thing to check. That error is Git telling you the flag does not exist in your build, not that you typed it wrong.
Why yours is old, and how to fix it
Three environments account for nearly every outdated Git.
macOS. The system Git comes from Xcode command line tools and lags upstream by a long way. Install a current one and make sure it comes first on your path.
brew install git
# Then confirm the shell is picking up the new one
hash -r
git --version
Debian and Ubuntu. Distribution packages are pinned at release time and only receive security backports. Ubuntu 22.04 shipped Git 2.34 and will keep shipping it.
sudo add-apt-repository ppa:git-core/ppa
sudo apt update
sudo apt install git
# On RHEL-family systems
sudo dnf install git
Docker images and CI runners. A slim base image often carries whatever Git was current when the base was built. If a CI step depends on a Git feature, pin the image and check the version in the job rather than assuming.
# Cheap first line in a CI job
git --version
On Windows, Git for Windows updates itself with its own built-in update command, which is the simplest of the three.
Version numbers in your own project
The other reason people search this phrase is wanting a version number derived from Git for their own builds. The describe command is the tool.
git describe --tags
# v1.4.2-17-gab29059
# ^tag ^commits since ^abbreviated sha
# Always produce something, even with no tags
git describe --tags --always --dirty
# ab29059-dirty
The dirty suffix appends a marker when the working tree has uncommitted changes, which is how you catch a build made from an unclean checkout. That is worth having in any artifact that reaches a server, because a version string that cannot be checked out again is a debugging dead end.
# Bake it into a Go binary at build time
go build -ldflags "-X main.version=$(git describe --tags --always --dirty)"
# Or into a Docker image label
docker build --label "version=$(git describe --tags --always)" .
In CI, note that a shallow clone breaks describe, because the tags are not fetched. Most CI systems default to a shallow clone for speed, so you need to request full history if your build depends on it.
How this fits the rest of the stack
A version string baked into the artifact is only useful if you can match it to what is actually running. Deploy history and runtime logs sit together on RunxBuild, so the commit that produced a live route is one lookup rather than a guess, and rolling back to the previous deploy is a button. The RunxBuild hosting calculator shows what a service, its database, and its bandwidth cost side by side before you commit to the shape.
Useful related references:
- Check Ubuntu Version: The Command, and Why the Number Matters
- WordPress Speed Optimization: The Order That Actually Moves the Number
- Python NaN: The Number That Breaks Equality and Quietly Spreads
- Services on RunxBuild
FAQ
How do I check my Git version?
Run the version flag, or the dashless version subcommand - both work. The output includes a build suffix on some platforms, such as a Windows packaging number or an Apple build identifier for the version bundled with Xcode command line tools.
Why is my Git version old after installing a new one?
Your path is finding the old binary first. List every Git on your path in order to see which one wins. On macOS the system Git often shadows a newer one installed by a package manager - fix the path order and clear the shell’s cached command lookup.
What Git version do I need for git switch?
Git 2.23 or later, released in August 2019. It introduced the switch and restore commands to split the overloaded checkout into two clearer commands. Checkout still works and is not deprecated.
Does the Git patch version matter?
For features, no - documentation that names a version means the minor number. For security, yes: fixes are backported across multiple minor lines simultaneously, so compare against the patched release on your own line rather than the newest version overall.
How do I get a version number from Git for my build?
Use the describe command with tags, always, and dirty flags. It produces the nearest tag plus commits-since and an abbreviated commit hash, falls back to a bare hash when no tags exist, and appends a dirty marker when the working tree has uncommitted changes. Shallow CI clones break it unless you fetch full history.