Git, CI/CD & GitOpsXXVI · Git ConfigurationGitConfig
Config scopes — system, global, local, worktree, and where each value lives
What you'll learn
- Name the five config scopes Git reads and the file each scope corresponds to
- State the precedence order from most-specific to least-specific
- Use --list, --list --show-origin, --get, and --unset to inspect and modify config
- Recognise why shared CI runners must use --system sparingly and per-repo config must use --local
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
Git configuration is a flat key-value store, but the keys come from
five different scopes and are merged in a fixed precedence order
before any command sees them. The merged view is what git commit,
git push, or any other command reads; the file a value was loaded
from is the origin that git config --list --show-origin reports.
Understanding the scopes is the difference between “I set the
identity and it still says wrong author” (you set the wrong scope)
and “I set the identity and now nothing works” (you set the wrong
scope for a shared runner).
The five scopes and their files
Each scope corresponds to a file. From most general to most specific:
- system (
--system) — a single file for the whole machine, conventionally/etc/gitconfig. Requires root to write. Used for shared policy on multi-tenant runners, not for personal preferences. - global (
--global) — the current user’s file. On Linux it is$XDG_CONFIG_HOME/git/configor, if that variable is unset,$HOME/.gitconfig. On macOS it is$HOME/.gitconfig. On Windows it is%USERPROFILE%\.gitconfig. This is where a personal identity, personal aliases, and personal signing keys live. - local (
--local, the default when inside a repo) — the per-repository file,.git/config. This is where per-repo remotes, per-repo identity overrides, per-repo credential helpers, and per-repo LFS settings live. - worktree (
--worktree) —.git/worktrees/<name>/config.worktree, a per-worktree file introduced to support multiple working trees checked out from a single bare repository. Rare in single-worktree workflows; common in large monorepos withgit-worktree. - branch (no flag; written through other commands) — a
per-branch section in
.git/configproduced bygit config branch.<name>.<key> <value>. Examples arebranch.main.remoteandbranch.main.merge, which Git writes automatically when yougit checkoutorgit branch --set-upstream-to.
flowchart TB
SYS["system: /etc/gitconfig"] --> M["Merged view"]
GLB["global: $HOME/.gitconfig"] --> M
LOC["local: ./.git/config"] --> M
WT["worktree: .git/worktrees/X/config.worktree"] --> M
BR["branch: branch.NAME.* in local file"] --> LOC
M --> CMD["git commit, git push, ..."]
Precedence
When the same key appears in more than one scope, the most specific scope wins:
- branch (highest precedence; lives inside local)
- worktree
- local
- global
- system (lowest precedence)
A value set with git config --global will be overridden by a
value set with git config --local in that repository. A value
set with --system is the floor; every other scope overrides it.
This is why shared CI runners must use --system sparingly: a
--system user.email will leak to every job that runs on the
runner, including jobs that should use a different identity.
git config --list --show-origin
# file:/etc/gitconfig user.name=Shared CI
# file:/etc/gitconfig user.email=ci@example.invalid
# file:$HOME/.gitconfig user.name=Alice Engineer
# file:$HOME/.gitconfig user.email=alice@corp.example.com
# file:.git/config user.email=alice@customer.example.com
# file:.git/config remote.origin.url=git@github.com:customer/repo.git
The bottom email wins for any git commit in this repository.
--show-origin is the difference between guessing where a value
came from and knowing.
Reading, setting, and unsetting
The four operations you will use most often:
git config --list # merged view, no origins
git config --list --show-origin # merged view with file paths
git config --get user.email # print the value Git would use
git config --global user.name "Alice"
git config --local user.email "alice@corp.example.com"
git config --system commit.gpgsign true
git config --unset user.email # remove a single-value key
git config --unset-all alias.co # remove all entries for a multi-value key
--get is the right command for scripts and for verifying what
Git actually sees: it returns the merged value, not the contents
of any one file. --unset errors if the key does not exist;
--unset-all is the form to use for multi-value keys like
alias.* and remote.origin.url (which only ever has one value
but is technically a list).
Where each value should live
A short rule of thumb that maps the scope to the kind of value:
- system: shared hooks path, shared safe.directory entries on multi-user hosts, default branch name policy for CI runners.
- global: identity (name, email), signing key, personal aliases, default editor, default branch name, color/ui preferences, pull behaviour.
- local: remotes, per-repo user overrides, per-repo credential helpers, per-repo LFS filters, submodule URLs.
- worktree: per-worktree overrides for the same key (rare; the
presence of the file usually means the worktree was created by
git-worktree addand inherited nothing useful). - branch: written by
git branch --set-upstream-toand similar; rarely edited by hand.
Production discipline
- Set identity once, at global scope, on every workstation.
Run
git config --global user.nameandgit config --global user.emailon every new machine before the first commit. - Override identity per-repo, at local scope, only when the repository requires a different identity. A customer repository is the canonical case. The override belongs in the local config so it does not leak to other repositories.
- Reserve
--systemfor shared CI runners. A system-scope identity is a system-scope identity for every job that runs on the machine. Prefer per-job configuration throughgit -c key=valueor through environment variables over a permanent--systemwrite. - Use
--show-originwhenever a value looks wrong. A surprisinguser.emailis almost always the wrong scope, not the wrong key. - Never edit
.git/configby hand unless you are debugging.git config --localwrites the file safely (atomic rename, correct formatting); a hand-edit can corrupt it.
Quiz
Knowledge check · 4 questions
Q1. A shared CI runner has `user.email=ci@example.invalid` set in `/etc/gitconfig` (system scope) and `user.email=alice@corp.example.com` in `$HOME/.gitconfig` (global scope). Which email will `git commit` record for a job running on that runner?
Q2. `git config --list --show-origin` shows the merged view of every config value together with the file each value came from.
Q3. Name the five config scopes in precedence order from highest to lowest, and state which file each one corresponds to on Linux.
Q4. Diagnose why a per-repo identity override is not taking effect, and recommend the right command to confirm what Git actually sees.
An engineer checks out a customer repository that requires commits to be authored as `contractor@vendor.example.com`. The engineer runs `git config --local user.email contractor@vendor.example.com` from inside the working tree, commits a change, and the remote rejects the push with 'commit author email does not match the contractor domain'. The engineer suspects the override did not apply and wants to confirm what value Git actually sees.
Passing score: 75%. Answers are checked in this browser.