CentOS Stream already includes Git in AppStream, so most systems only need one DNF package before they can clone repositories, track configuration changes, or push code to a remote service. To install Git on CentOS Stream, start with the package-managed path and move to the source-build path only when you need an upstream tag that is newer than the repository package.
The package name is git, and it provides the normal command-line client, including git clone. You do not need a separate Git Bash or Git client package on CentOS Stream because the standard Linux shell runs Git directly.
These steps support CentOS Stream 10 and CentOS Stream 9. The DNF and source-build commands work the same on both versions unless a section notes otherwise.
Install Git on CentOS Stream
Start with the DNF package unless you have a clear source-build requirement. AppStream keeps Git inside normal system updates, while source builds install outside the RPM database and need manual maintenance.
| Method | Version Source | Best For | Update Path |
|---|---|---|---|
| DNF package | CentOS Stream AppStream | Most servers, desktops, CI hosts, and production systems | Normal dnf upgrade |
| Source archive | kernel.org Git tarballs | Testing Git itself, custom build flags, or a newer upstream tag | Manual rebuild or helper script |
Searches such as yum install git still map to the same RPM package family, but use DNF on current CentOS Stream systems. DNF replaced Yum as the normal package manager and gives clearer dependency and transaction output.
Update CentOS Stream Before Installing Git
Refresh repository metadata and apply pending package updates before installing Git:
sudo dnf upgrade --refresh
Package commands use
sudobecause DNF changes system files. If your account cannot run sudo yet, switch to an administrator account before continuing.
On an already current system, DNF may finish with Nothing to do. That result is fine; it means the enabled repositories did not have pending updates.
Check Whether Git Is Already Installed
Some developer images already include Git. Check the current state before installing another copy:
git --version
If Git is missing, Bash returns a command-not-found message similar to this:
bash: git: command not found
Install Git with DNF
Install the AppStream Git package:
sudo dnf install git
DNF resolves the supporting packages automatically, including git-core, Git documentation, and Perl modules used by several Git tools. Press y when DNF asks you to confirm the transaction.
Verify the installed command:
git --version
Current CentOS Stream 10 and 9 AppStream metadata returns the 2.52 series:
git version 2.52.0
Confirm the package owner when you need RPM-level proof:
rpm -q git
Relevant output includes the CentOS Stream release suffix:
git-2.52.0-1.el10.x86_64
CentOS Stream 9 shows the same upstream Git version with an el9 package suffix. The exact revision can change after repository updates, so treat the suffix as a package-manager snapshot.
Build Git from Source Archive
Use the source archive when you need an upstream Git tag before AppStream receives it, want to test Git itself, or need custom compile-time options. For normal development and server administration, the DNF package is easier to update and remove.
Install the compiler group, library headers, archive tools, GnuPG verifier, and Python runtime used by the source-build helper:
sudo dnf groupinstall "Development Tools"
sudo dnf install libcurl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker wget curl-minimal tar xz gnupg2 python3
CentOS Stream minimal images normally use curl-minimal, which provides the curl command without replacing the base package set. Avoid switching to the full curl package unless you have a separate reason, because it can conflict with curl-minimal on Stream 9.
Install tcl and tk only if you need the optional gitk or git gui tools:
sudo dnf install tcl tk
Without those optional GUI packages, the build can still complete while printing a tclsh: command not found warning for Git GUI indexing.
The source-build commands use
v2.54.0as a concrete example. Check the Git source install page before building, and substitute a newer stable tag consistently in the archive filename, signature filename, extracted directory, and install prefix when one is available.
Create a dedicated build workspace so the archive, extracted source, and update helper stay together:
mkdir -p ~/git-source-build
cd ~/git-source-build
Download the Git maintainer key from the kernel.org PGP key repository, then confirm its fingerprint before using it for release verification:
curl -fsSLO https://git.kernel.org/pub/scm/docs/kernel/pgpkeys.git/plain/keys/20D04E5A713660A7.asc
gpg --import 20D04E5A713660A7.asc
gpg --fingerprint 20D04E5A713660A7
The primary fingerprint should match this value:
96E0 7AF2 5771 9559 80DA D100 20D0 4E5A 7136 60A7
Download the source archive and detached signature from kernel.org. The wget command examples guide covers additional download patterns if you need retries, custom filenames, or resume support:
wget https://www.kernel.org/pub/software/scm/git/git-2.54.0.tar.xz
wget https://www.kernel.org/pub/software/scm/git/git-2.54.0.tar.sign
Verify the compressed archive against the detached signature before extraction:
xz -cd git-2.54.0.tar.xz | gpg --verify git-2.54.0.tar.sign -
gpg: Good signature from "Junio C Hamano <gitster@pobox.com>"
GnuPG may also warn that the imported key is not certified with a trusted signature. That warning is normal for a newly imported public key; the fingerprint check ties the key to the expected maintainer key before verification.
Extract the archive and enter the source directory. The guide to open gz and tgz files in Linux explains the archive format in more detail.
tar -xf git-2.54.0.tar.xz
cd git-2.54.0
Compile Git into a versioned prefix under /usr/local. The -j "$(nproc)" option uses the CPU cores available on your system:
make -j "$(nproc)" prefix=/usr/local/git-2.54.0 all
Install the compiled files, then create stable symlinks for future updates. The guard stops before overwriting a custom non-symlinked /usr/local/bin/git file:
if [ -e /usr/local/bin/git ] && [ ! -L /usr/local/bin/git ]; then
echo "/usr/local/bin/git already exists and is not a symlink. Move it before continuing."
else
sudo make prefix=/usr/local/git-2.54.0 install
sudo ln -sfn /usr/local/git-2.54.0 /usr/local/git-current
sudo ln -sfn /usr/local/git-current/bin/git /usr/local/bin/git
fi
Refresh your shell command cache and verify the active binary:
hash -r
git --version
command -v git
git version 2.54.0 /usr/local/bin/git
The /usr/local/bin/git symlink wins over the DNF package at /usr/bin/git in the normal PATH order. Keeping source builds in versioned prefixes also makes rollback and cleanup less fragile than installing every file directly into /usr/local.
Create a Source-Build Update Script
Source builds do not receive AppStream security or bug-fix updates. Create a reusable helper that checks the latest stable Git tag, skips release-candidate tags, and installs each release into its own versioned prefix:
mkdir -p ~/git-source-build
cat > ~/git-source-build/update-git.sh <<'SCRIPT'
#!/bin/bash
set -euo pipefail
INSTALL_PREFIX="/usr/local"
BUILD_DIR="$HOME/git-source-build"
CURRENT_LINK="$INSTALL_PREFIX/git-current"
BIN_LINK="/usr/local/bin/git"
REQUESTED_TAG="${1:-}"
if [ "$#" -gt 1 ]; then
echo "Usage: $0 [vX.Y.Z]"
exit 1
fi
if [ "$(id -u)" -eq 0 ]; then
echo "Run this script as your regular user. It will ask for sudo only during install."
exit 1
fi
for cmd in awk curl gpg python3 tar xz make gcc grep nproc readlink sudo; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Error: $cmd is required."
echo "Run: sudo dnf groupinstall \"Development Tools\" && sudo dnf install libcurl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker wget curl-minimal tar xz gnupg2 python3"
exit 1
fi
done
if [ -x "$CURRENT_LINK/bin/git" ]; then
CURRENT_VERSION=$("$CURRENT_LINK/bin/git" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n 1 || true)
else
CURRENT_VERSION=""
fi
CURRENT_VERSION=${CURRENT_VERSION:-none}
if [ -n "$REQUESTED_TAG" ]; then
if [[ ! "$REQUESTED_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: tag must use a stable format such as v2.54.0."
exit 1
fi
LATEST_TAG="$REQUESTED_TAG"
TARGET_LABEL="Requested version"
else
echo "Checking for latest stable Git tag..."
if ! LATEST_TAG=$(
python3 - <<'PY'
import json
import re
import urllib.request
with urllib.request.urlopen("https://api.github.com/repos/git/git/tags") as response:
tags = json.load(response)
for tag in tags:
name = tag.get("name", "")
if re.fullmatch(r"v[0-9]+\.[0-9]+\.[0-9]+", name):
print(name)
break
else:
raise SystemExit("No stable Git tag found")
PY
); then
echo "Error: Could not fetch a stable Git tag from GitHub."
exit 1
fi
TARGET_LABEL="Latest available"
fi
LATEST_VERSION=${LATEST_TAG#v}
echo "Current version: $CURRENT_VERSION"
echo "$TARGET_LABEL: $LATEST_VERSION"
if [ "$CURRENT_VERSION" = "$LATEST_VERSION" ]; then
echo "Already up to date."
exit 0
fi
if ! sudo true; then
echo "Error: sudo access is required for installation under /usr/local."
exit 1
fi
if [ -e "$BIN_LINK" ] && [ ! -L "$BIN_LINK" ]; then
echo "Error: $BIN_LINK already exists and is not a symlink."
echo "Move it before running this source-build updater."
exit 1
fi
if [ -e "$CURRENT_LINK" ] && [ ! -L "$CURRENT_LINK" ]; then
echo "Error: $CURRENT_LINK already exists and is not a symlink."
echo "Move it before running this source-build updater."
exit 1
fi
TARGET_PREFIX="$INSTALL_PREFIX/git-$LATEST_VERSION"
if [ -e "$TARGET_PREFIX" ]; then
echo "Error: $TARGET_PREFIX already exists."
echo "Move or remove that directory before reinstalling the same Git version."
exit 1
fi
KEY_URL="https://git.kernel.org/pub/scm/docs/kernel/pgpkeys.git/plain/keys/20D04E5A713660A7.asc"
KEY_ID="20D04E5A713660A7"
KEY_FINGERPRINT="96E07AF25771955980DAD10020D04E5A713660A7"
GNUPG_DIR="$BUILD_DIR/gnupg"
KEY_FILE="$BUILD_DIR/${KEY_ID}.asc"
ARCHIVE="git-${LATEST_VERSION}.tar.xz"
SIGNATURE="git-${LATEST_VERSION}.tar.sign"
URL_BASE="https://www.kernel.org/pub/software/scm/git"
mkdir -p "$BUILD_DIR" "$GNUPG_DIR"
chmod 700 "$GNUPG_DIR"
cd "$BUILD_DIR"
rm -rf git-[0-9]*/
if ! GNUPGHOME="$GNUPG_DIR" gpg --batch --list-keys "$KEY_ID" >/dev/null 2>&1; then
echo "Importing Git maintainer key..."
curl -fsSL -o "$KEY_FILE" "$KEY_URL"
GNUPGHOME="$GNUPG_DIR" gpg --batch --import "$KEY_FILE" >/dev/null
fi
ACTUAL_FINGERPRINT=$(GNUPGHOME="$GNUPG_DIR" gpg --batch --with-colons --fingerprint "$KEY_ID" | awk -F: '/^fpr:/ {print $10; exit}')
if [ "$ACTUAL_FINGERPRINT" != "$KEY_FINGERPRINT" ]; then
echo "Error: Git maintainer key fingerprint did not match the expected value."
exit 1
fi
echo "Downloading $LATEST_TAG..."
curl -fL --progress-bar -o "$ARCHIVE" "$URL_BASE/$ARCHIVE"
curl -fsSL -o "$SIGNATURE" "$URL_BASE/$SIGNATURE"
echo "Verifying $ARCHIVE..."
xz -cd "$ARCHIVE" | GNUPGHOME="$GNUPG_DIR" gpg --batch --verify "$SIGNATURE" - >/dev/null
tar -xf "$ARCHIVE"
rm -f "$ARCHIVE" "$SIGNATURE"
cd "git-${LATEST_VERSION}"
echo "Compiling Git $LATEST_VERSION..."
make -j "$(nproc)" prefix="$TARGET_PREFIX" all
echo "Installing Git $LATEST_VERSION..."
sudo make prefix="$TARGET_PREFIX" install
PREVIOUS_TARGET=""
if [ -L "$CURRENT_LINK" ]; then
PREVIOUS_TARGET=$(readlink "$CURRENT_LINK")
fi
sudo ln -sfn "$TARGET_PREFIX" "$CURRENT_LINK"
sudo ln -sfn "$CURRENT_LINK/bin/git" "$BIN_LINK"
hash -r
if ! NEW_VERSION=$("$BIN_LINK" --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n 1); then
echo "Error: installed Git did not run correctly."
if [ -n "$PREVIOUS_TARGET" ]; then
sudo ln -sfn "$PREVIOUS_TARGET" "$CURRENT_LINK"
else
sudo rm -f "$CURRENT_LINK" "$BIN_LINK"
fi
exit 1
fi
echo "Successfully installed Git $NEW_VERSION"
SCRIPT
chmod +x ~/git-source-build/update-git.sh
The final chmod command makes the helper executable. See the chmod command guide if you want more detail on Linux execute permissions, or the curl command guide for the download flags used inside the helper.
Run the helper from any directory when you want to check for the latest stable tag. To rebuild a specific validated release instead, pass a stable tag such as v2.54.0; the helper rejects release-candidate formats.
~/git-source-build/update-git.sh
When the installed source version already matches the latest stable tag, the script exits without rebuilding:
Checking for latest stable Git tag... Current version: 2.54.0 Latest available: 2.54.0 Already up to date.
Do not run source-build updates from cron. Compilation can fail because of network outages, GitHub API limits, missing libraries, or local build errors, and those failures need interactive review.
Configure Git on CentOS Stream
Git needs a username and email address before it can create commits. Use the same email address you use on GitHub, GitLab, or Bitbucket if you want commits to connect to your web account.
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
Review the saved values:
git config --global --list
user.name=Your Name user.email=your.email@example.com
The deeper guide to configure Git username and email covers repository-specific identities, privacy addresses, and verification checks. For public repositories, consider the no-reply email options from GitHub or GitLab.
Set the Default Branch Name
Set main as the default branch name for new repositories if that matches your team or hosting platform:
git config --global init.defaultBranch main
Cache HTTPS Credentials Temporarily
Most hosting providers require a personal access token for HTTPS pushes instead of your account password. The cache helper stores credentials in memory for a limited time, which reduces repeated prompts without writing the token permanently to disk.
git config --global credential.helper 'cache --timeout=14400'
The timeout is in seconds, so 14400 keeps credentials for four hours. Use SSH keys instead when you need stronger long-term authentication for frequent repository work.
Use Basic Git Commands
After installation and identity setup, these commands cover the first local repository workflow.
Initialize a Repository
Create a project directory and initialize it as a Git repository:
mkdir ~/my-project
cd ~/my-project
git init
Initialized empty Git repository in /home/user/my-project/.git/
Git stores repository history and local metadata in the hidden .git directory. If you later need to change a branch name, use the guide to rename a local and remote Git branch.
Stage and Commit Files
Create a file, stage it, and commit it with a short message:
printf "hello\n" > hello.txt
git add hello.txt
git commit -m "Add hello file"
The -m option supplies the commit message inline. If you commit the wrong file or message, the guide to undo the last Git commit explains the safe rollback choices.
Connect to a Remote Repository
Add a remote repository after you create the matching project on GitHub, GitLab, Bitbucket, or another Git server:
git remote add origin https://github.com/username/repository.git
Verify the remote URL before pushing:
git remote -v
origin https://github.com/username/repository.git (fetch) origin https://github.com/username/repository.git (push)
Push and Pull Changes
Push your local branch to the remote and set upstream tracking on the first push:
git push -u origin main
Pull remote changes back into the local branch later:
git pull
For branch switching workflows beyond this first repository, see the guide to switch Git branches. If files keep appearing in commits after you add them to .gitignore, the guide to clear Git cache covers the index cleanup path.
Troubleshoot Git on CentOS Stream
Git Command Not Found After Installation
If the shell still cannot find Git after the DNF install, confirm the package state first:
rpm -q git
If RPM reports that Git is not installed, install the package again:
sudo dnf install git
If RPM shows the package but the current shell still misses the command, refresh the shell command cache and check the resolved path:
hash -r
command -v git
/usr/bin/git
Source Build Still Shows the AppStream Version
If git --version still shows the AppStream package after a source install, check which binary the shell resolves first:
command -v git
ls -l /usr/local/bin/git /usr/local/git-current 2>/dev/null || true
Recreate the source-build symlinks when the versioned prefix exists but the active command still points to /usr/bin/git:
sudo ln -sfn /usr/local/git-2.54.0 /usr/local/git-current
sudo ln -sfn /usr/local/git-current/bin/git /usr/local/bin/git
hash -r
git --version
git version 2.54.0
Source Build Fails With Missing Dependencies
Missing header or helper errors usually mean one build dependency was skipped. Use the error text to install the matching package, then rerun the make command from the source directory.
| Error Message | Missing Package | Fix Command |
|---|---|---|
curl/curl.h: No such file | libcurl-devel | sudo dnf install libcurl-devel |
expat.h: No such file | expat-devel | sudo dnf install expat-devel |
openssl/ssl.h: No such file | openssl-devel | sudo dnf install openssl-devel |
zlib.h: No such file | zlib-devel | sudo dnf install zlib-devel |
curl: command not found | curl-minimal | sudo dnf install curl-minimal |
tclsh: command not found | tcl and tk | sudo dnf install tcl tk |
The tclsh warning only affects optional Git GUI helper indexing. Install tcl and tk if you need those tools; otherwise, the command-line Git build can still succeed.
HTTPS Clone Fails With Certificate Errors
If HTTPS clones fail with certificate verification errors, confirm the CA certificate bundle is installed and current:
sudo dnf install ca-certificates
sudo update-ca-trust
Corporate networks with custom certificate authorities may also need that internal CA added to the system trust store before Git can verify HTTPS remotes.
Update or Remove Git on CentOS Stream
Update DNF-Installed Git
Use normal system updates for the AppStream package:
sudo dnf upgrade --refresh git
Update Source-Built Git
Run the helper script created in the source-build section:
~/git-source-build/update-git.sh
Remove DNF-Installed Git
Remove the package-managed Git installation:
sudo dnf remove git
Verify that the RPM package is gone:
rpm -q git || echo "git package is not installed"
package git is not installed git package is not installed
If DNF lists unused dependencies afterward, review them before running a broader cleanup:
sudo dnf autoremove
Remove Source-Built Git
Source builds installed with make install are not tracked by RPM. Review the managed source-build paths before deleting anything:
find /usr/local -maxdepth 1 \( -type d -o -type l \) \( -name 'git-[0-9]*' -o -name 'git-current' \) -print
ls -l /usr/local/bin/git 2>/dev/null || true
The removal block permanently deletes versioned Git source-build prefixes under
/usr/local, the/usr/local/git-currentsymlink, the article-created/usr/local/bin/gitsymlink, and the helper directory in your home folder. Do not run it if you intentionally keep custom directories with the same naming pattern.
cd "$HOME"
sudo rm -f /usr/local/bin/git
sudo rm -f /usr/local/git-current
sudo rm -rf /usr/local/git-[0-9]*
rm -rf ~/git-source-build
hash -r
Confirm which Git binary remains active. If the DNF package is still installed, the command falls back to /usr/bin/git; if no Git method remains, the fallback message appears instead.
command -v git || echo "git command not found"
/usr/bin/git
If you removed the DNF package too, the same check prints git command not found.
Conclusion
Git is ready on CentOS Stream through AppStream or a managed source prefix, depending on how much control you need over the installed version. From here, tighten your identity settings with the Git username and email guide, then move into daily workflows such as switching Git branches.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><a href="https://example.com">link</a><blockquote>quote</blockquote>