In Git, how do the --system, --global and --local config scopes differ, and which one wins?
answer
- three files, one cascade
- who owns each file: machine, user, repo
- the default target inside a repo surprises people
- narrower scope wins
- -c on the command line beats every file
basics
~10 sThey name three config files: machine-wide, per-user, and per-repository. Git reads them in that order and the narrower scope wins, so a repository's .git/config overrides your home-directory settings, which override the system file.
solid answer
~40 s`--system` writes the machine-wide file (typically `/etc/gitconfig`), `--global` writes your per-user file (`~/.gitconfig`, or `~/.config/git/config`), and `--local` — the default when you are inside a repository — writes that repository's `.git/config`. Git reads all of them, lowest scope first, and for a single-valued key the last value read wins, so local beats global beats system. Beyond those three there is `--worktree`, which needs `extensions.worktreeConfig` enabled and applies to one worktree only, and above everything a one-shot `git -c key=value <command>` on the command line. The practical consequence people meet first is identity: setting `user.email` globally and then overriding it per repository is how one machine commits with a work address in one clone and a personal address in another.
code
bash · 4 linesgit config --global user.email "[email protected]" # ~/.gitconfig
git config user.email "[email protected]" # this repo's .git/config
git config user.email # [email protected]
git -c [email protected] commit -m "one-off" # beats every filego deeper
Remember the three files and the direction of precedence: local beats global beats system. Know that plain git config inside a repo writes the local file, so use --global when you mean your user settings.
Explain the read order including the worktree scope and the -c command-line override, and the difference between single-valued keys where the last read wins and multi-valued keys that accumulate.
Show how you reason about which layer a value belongs in, and why .git/config being untracked shapes how a team distributes required settings rather than assuming a clone carries them.
Own the layering as policy: what a provisioned system file may impose, what remains a personal choice, and why client-side configuration can never be a real enforcement mechanism for anything that matters.
## The files behind the flags Git configuration is not a database; it is a set of INI-style text files that Git reads in a fixed order. The scope flags simply choose which file `git config` writes to. - **system** — one file for the whole machine, usually `/etc/gitconfig` (its exact location depends on how Git was installed). Written with `git config --system`, which usually needs administrator rights. Rare on a personal laptop, common in a managed image or a container base. - **global** — one file per user: `~/.gitconfig`, or `$XDG_CONFIG_HOME/git/config` (commonly `~/.config/git/config`) if that exists. This is where identity, aliases and personal preferences live. - **local** — one file per repository: `.git/config` inside the repository. This is the default target of `git config` when you run it inside a working tree, which surprises people who meant to set something globally. - **worktree** — `.git/config.worktree`, applying to a single linked worktree. It is inert until you turn on `extensions.worktreeConfig`, after which `git config --worktree` writes there. ## Precedence Git reads system, then global, then local, then worktree. For a normal single-valued key, later reads override earlier ones — so the narrowest scope that mentions the key is the one that takes effect. Above all files sits the command line: `git -c [email protected] commit` applies for that one invocation and beats every file. Environment variables can also inject configuration (`GIT_CONFIG_COUNT` with `GIT_CONFIG_KEY_n`/`GIT_CONFIG_VALUE_n`), and `GIT_CONFIG_GLOBAL` / `GIT_CONFIG_SYSTEM` can redirect or disable those two files — useful in tests and CI where you want a hermetic configuration. A subtlety: not every key is single-valued. Some, such as `remote.<name>.fetch` or `include.path`, are *multi-valued*, and a later file **adds** rather than replaces. `git config --get <key>` returns the last value; `git config --get-all <key>` returns all of them. Assuming everything is last-wins is a common source of confusion when a repository seems to fetch more refspecs than you configured. ## Reading and writing `git config <key>` reads the effective value. `git config <scope> <key> <value>` writes. `git config --list` dumps everything as it is currently resolved, and `git config <scope> --list` shows one file's contents. `git config --edit` (with a scope flag) opens the file in your editor, which is often the fastest way to see and fix the real text. `git config --unset <key>` removes a key from a scope; deleting a global value does not remove a local one that shadows nothing. Because these are plain files, you can also open them directly. Editing `~/.gitconfig` by hand is entirely legitimate; `git config` merely saves you from getting the INI syntax wrong. ## The classic mistakes **Running `git config user.email …` inside a repository when you meant globally.** Without a scope flag, `git config` writes to `.git/config`, so the change silently applies to exactly one repository and every other clone still uses the old address. **Expecting the global file to override a repository setting.** It cannot; precedence runs the other way. If a repository's commits use the wrong identity, the value is almost always in that repository's `.git/config`. **Assuming `.git/config` is shared.** It is not tracked and never travels with a clone or a push. Anything you need every clone to have must be distributed some other way — an included file, a documented setup step, or machine provisioning. **Confusing config with ignore rules and attributes.** `.gitignore` and `.gitattributes` are tracked files with their own precedence rules; they are not part of the config cascade at all. ## Why the layering exists The split maps cleanly onto ownership. The system file belongs to whoever provisions the machine. The global file belongs to the human. The local file belongs to the project checkout, and is the escape hatch for the case where your defaults are wrong for this one repository — a different identity, a different editor, a different merge behaviour. Knowing which layer a value should live in is most of the skill: put personal preferences global, per-project deviations local, and reserve system for things a fleet operator genuinely needs to impose.
- Where does git config write when you run it inside a repository with no scope flag?To that repository's `.git/config` — the local scope is the default. This is why `git config user.email …` typed inside one clone silently fails to change anything anywhere else. Add `--global` when you mean your user-wide file.
- How do you override a setting for a single command without changing any file?`git -c <key>=<value> <command>`, for example `git -c core.pager=cat log`. The value applies to that invocation only and takes precedence over every configuration file, which makes it ideal for scripts and one-off experiments.
- Is .git/config included when someone clones your repository?No. `.git/config` is not a tracked file, so it never travels with a clone, a fetch or a push. Clone-time settings such as the origin remote are generated locally by `git clone`. Anything every developer must have has to be provisioned or included from a tracked file.
- What is the --worktree scope for?It stores settings in `.git/config.worktree` that apply to one linked worktree rather than the whole repository, and it only takes effect once `extensions.worktreeConfig` is set to true. It is how two worktrees of the same repository can differ in a setting that is otherwise repository-wide.
saying these in an interview costs you the question
- Says the global file overrides a repository's setting
- Thinks git config without a flag writes the global file
- Believes .git/config is cloned with the repository
- Assumes every key is last-wins, ignoring multi-valued keys
- Confuses .gitignore or .gitattributes with the config cascade