skip to content

A Git setting has a value you never set — how do you find which config file provides it?

level: middleimportance: should knowfreq 40%

answer

  1. the value is right, the source is unknown
  2. print the file next to each key
  3. read order tells you who won
  4. one flag names the layer instead
  5. --show-origin and --show-scope

basics

~20 s

Run git config --list --show-origin, which prints the file path in front of every resolved key, and add --show-scope to label each as system, global, local, worktree or command. Filter to one key with git config --show-origin --get <key>.

solid answer

~40 s

`git config --list --show-origin` dumps the fully resolved configuration with `file:<path>` in front of each entry, so you can see exactly which file supplied a value — including files pulled in by `include.path` or a conditional `includeIf`, which report their own path rather than the file that included them. Modern Git also has `--show-scope`, which labels each line `system`, `global`, `local`, `worktree` or `command`. For a single key, `git config --show-origin --get <key>` is the fast form, and `--get-all` shows every value when the key is multi-valued and the last one is not the whole story. Remember values can also come from outside files: `git -c key=value` on the command line, and the `GIT_CONFIG_COUNT`/`GIT_CONFIG_KEY_n`/`GIT_CONFIG_VALUE_n` environment variables, both of which show up in the listing as coming from the command line.

code

console · 6 lines
console
$ git config --list --show-origin --show-scope | grep user.email
global  file:/home/ada/.gitconfig       [email protected]
global  file:/home/ada/.gitconfig-work  [email protected]

$ git config --show-origin --get user.email
file:/home/ada/.gitconfig-work  [email protected]

go deeper

for a junior

Recall the one command that answers where a setting came from: git config --list --show-origin. Run it inside the repository you are debugging, not from your home directory.

for a middle

Explain that entries print in read order so the last one wins, that included files report their own path, and that --show-scope labels the layer while --get narrows the output to one key.

for a senior

Demonstrate the full diagnosis: command-line -c overrides and GIT_CONFIG_* environment injection as non-file sources, --get-all for multi-valued keys, and reproducing the problem in the user's own directory.

for a principal

Be ready to explain why a fleet needs a reproducible configuration story at all — hermetic CI environments, provisioned system files, and the debugging cost of a setting that only appears under one wrapper script.

## The problem this solves Git resolves configuration by reading several files in order and letting later ones override earlier ones. When the effective value is not what you expect, `git config <key>` tells you *what* the value is but not *where* it came from, and hunting through four possible files by hand is slow — especially once conditional includes are in play and the relevant line is in a file you did not know was being read. ## --show-origin `git config --list --show-origin` prints every resolved key prefixed with the origin of the line, in the form `file:/home/ada/.gitconfig [email protected]`. Key properties: - Entries appear in **read order**, so for a single-valued key the *last* occurrence is the effective one. Reading the listing top to bottom shows you the override chain, not just the winner. - Included files report **their own path**. If your global config has `[includeIf "gitdir:~/work/"] path = ~/.gitconfig-work`, the settings from that file are attributed to `~/.gitconfig-work`, which is precisely what you need to know. - Values injected on the command line with `-c` are attributed to the command line rather than to a file. ## --show-scope Modern Git adds `--show-scope`, which prefixes each line with the scope name — `system`, `global`, `local`, `worktree`, `command` — instead of, or in addition to, the path. Combining both (`git config --list --show-origin --show-scope`) is the most informative form: you get the layer *and* the file. Scope is also selectable when reading: `git config --global --get user.email` reads only the global file, which is a quick way to prove a value is not there. ## Narrowing to one key Dumping everything is noisy. `git config --show-origin --get user.email` prints just the effective value and its source. When a key can hold multiple values — `remote.origin.fetch`, `include.path`, `credential.helper` — `--get` returns only the last, which can be actively misleading; `--get-all --show-origin` lists every contributing line with its file. `--get-regexp <pattern>` is the way to survey a family of keys, for example every alias. ## Sources that are not files Three non-file sources catch people out: - `git -c <key>=<value> <command>` — a one-shot override that outranks every file. Scripts and wrappers use it, so a value can appear only when a command is run through that wrapper. - `GIT_CONFIG_COUNT` with `GIT_CONFIG_KEY_0`/`GIT_CONFIG_VALUE_0` (and so on) — environment-injected configuration, common in CI images. - `GIT_CONFIG_GLOBAL` and `GIT_CONFIG_SYSTEM` — these *redirect* the global and system files to a given path, and pointing them at `/dev/null` effectively disables that layer. Test harnesses use them to get a hermetic environment; if a setting mysteriously vanishes inside a test, this is usually why. Because these participate in resolution, the listing reflects the environment you run it in. Debugging someone else's problem means running the command in *their* shell, in *their* directory, or you may resolve a different set of files entirely. ## Where directory matters Run the command inside the repository in question. Outside a working tree there is no local scope at all, and conditional includes keyed on `gitdir:` will not fire, so you can get a completely different answer two directories away. Similarly, in a linked worktree the worktree scope may add values that the main worktree does not have. ## What this is not Configuration is only one of Git's per-repository behaviour knobs. If the surprising behaviour concerns which files are ignored, or how a file is diffed or merged, the source is `.gitignore` or `.gitattributes` — tracked files with their own precedence rules that `--show-origin` will never mention. Those have their own debugging commands. Knowing which mechanism you are actually chasing is half of the diagnosis; `--show-origin` answers only the config half, and answers it definitively.

  • Why does --show-origin sometimes point at a file you never configured directly?
    Because `include.path` and `includeIf` pull other files into the cascade, and Git attributes each setting to the file it was literally read from. That is the point: it shows you the conditional-include file that is actually supplying the value rather than the file that included it.
  • Why can --get be misleading for some keys?
    `--get` returns the last value only. Multi-valued keys such as `remote.<name>.fetch` accumulate across files, so the last one is not the whole configuration. Use `--get-all --show-origin` to see every contributing line and where each came from.
  • Does running the command from a different directory change the answer?
    Yes. Outside a repository there is no local scope, and conditional includes matched on `gitdir:` only fire for repositories under the matching path. Always run the diagnosis inside the repository whose behaviour you are investigating.
  • How do you check the configuration in isolation from the user's own files?
    Point `GIT_CONFIG_GLOBAL` and `GIT_CONFIG_SYSTEM` at an empty path so those layers contribute nothing, then run the command. That is how test suites get a hermetic environment, and it quickly proves whether a surprising value comes from your personal files or from the repository.

saying these in an interview costs you the question

  • Greps ~/.gitconfig by hand and stops when nothing matches
  • Assumes only the three main files can supply a value
  • Forgets included files supply settings under their own path
  • Runs the check outside the repository being debugged
  • Uses --get on a multi-valued key and reports the last value as the whole truth

context