Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXXVI · Git ConfigurationGitConfig

Config scopes — system, global, local, worktree, and where each value lives

Intermediate⏱ ~18 mingit

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

Not yet marked complete on this device.

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/config or, 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 with git-worktree.
  • branch (no flag; written through other commands) — a per-branch section in .git/config produced by git config branch.<name>.<key> <value>. Examples are branch.main.remote and branch.main.merge, which Git writes automatically when you git checkout or git 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:

  1. branch (highest precedence; lives inside local)
  2. worktree
  3. local
  4. global
  5. 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 add and inherited nothing useful).
  • branch: written by git branch --set-upstream-to and similar; rarely edited by hand.

Production discipline

  1. Set identity once, at global scope, on every workstation. Run git config --global user.name and git config --global user.email on every new machine before the first commit.
  2. 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.
  3. Reserve --system for shared CI runners. A system-scope identity is a system-scope identity for every job that runs on the machine. Prefer per-job configuration through git -c key=value or through environment variables over a permanent --system write.
  4. Use --show-origin whenever a value looks wrong. A surprising user.email is almost always the wrong scope, not the wrong key.
  5. Never edit .git/config by hand unless you are debugging. git config --local writes the file safely (atomic rename, correct formatting); a hand-edit can corrupt it.

Quiz

Knowledge check · 4 questions

  1. 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?

  2. Q2. `git config --list --show-origin` shows the merged view of every config value together with the file each value came from.

  3. Q3. Name the five config scopes in precedence order from highest to lowest, and state which file each one corresponds to on Linux.

  4. 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.