skip to content

Configuration, Ignore Rules, and Attributes

You will learn how Git is configured across scopes, how .gitignore and .gitattributes shape what is tracked and how it is treated, and how commits and tags are signed. These are the settings that quietly cause cross-platform bugs and reviewer confusion when nobody owns them.

part ofGitoverview, primer and where to startread it →
on this pageshow

questions

25

In Git, how do the --system, --global and --local config scopes differ, and which one wins?

level: juniorimportance: must knowfreq 70%

answer

  1. three files, one cascade
  2. who owns each file: machine, user, repo
  3. the default target inside a repo surprises people
  4. narrower scope wins
  5. -c on the command line beats every file

basics

~10 s

They 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 lines
bash
git 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 file

go deeper

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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

context

open as a page

In Git, why does adding an already-tracked file to .gitignore have no effect?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Git ignore rules apply only to untracked paths. Once a file has an entry in the index, Git keeps reporting and committing its changes whatever .gitignore says. Untrack it with git rm --cached, then commit that removal.

open as a page

In Git, why does a file show every line as changed after a Windows teammate edits it?

level: juniorimportance: must knowfreq 50%

basics

~10 s

The file's line endings switched between LF and CRLF. Git compares whole lines, so an invisible carriage return at the end of every line makes every line differ and the whole file looks rewritten.

open as a page

In Git, what does a .gitattributes file control, and how are its rules resolved?

level: middleimportance: must knowfreq 50%

basics

~10 s

A .gitattributes file assigns per-path attributes that change how Git diffs, merges, filters and archives matching files. Patterns use .gitignore syntax; within one file the last matching line wins, and .git/info/attributes outranks committed files.

open as a page

In a Git .gitignore file, what do a leading slash, a trailing slash, and ** match?

level: middleimportance: must knowfreq 66%

basics

~20 s

A leading slash anchors a Git ignore pattern to the directory holding the .gitignore instead of matching at any depth. A trailing slash restricts the match to directories. Double asterisk matches across directory separators, which a single asterisk never does.

open as a page

In Git, why does `* text=auto` in .gitattributes beat asking everyone to set core.autocrlf?

level: middleimportance: must knowfreq 40%

basics

~20 s

In Git, .gitattributes is committed, so the rule travels with every clone and CI checkout and cannot be forgotten on a new machine. It also overrides per-machine config and allows per-path exceptions, which core.autocrlf cannot express.

open as a page

What do Git's core.autocrlf values true, input, and false each do?

level: middleimportance: must knowfreq 45%

basics

~20 s

In Git, core.autocrlf=true converts CRLF to LF on commit and back to CRLF on checkout; input converts to LF on commit but never converts on checkout; false, the default, stores and checks out the bytes unchanged.

open as a page

How do you configure Git to sign every commit and tag automatically?

level: middleimportance: must knowfreq 42%

basics

~20 s

In Git, set user.signingKey to your key, commit.gpgSign and tag.gpgSign to true, and gpg.format to ssh if you are signing with an SSH key rather than the default OpenPGP. Individual commands can still opt out with --no-gpg-sign.

open as a page

What does a verified Git commit signature prove, given that author fields are free text?

level: seniorimportance: must knowfreq 36%

basics

~20 s

It proves the holder of that key produced that exact commit object and it has not changed since. Git's author and committer fields come from local config and are unverified text, so the signature attests the signer, never the person named as author.

open as a page

In Git, how do you define an alias, and what changes when it starts with an exclamation mark?

level: middleimportance: should knowfreq 45%

basics

~20 s

An alias is a config key under the alias section, so git config --global alias.st status makes git st work. A leading exclamation mark makes the value a shell command instead of Git subcommand arguments, run from the top of the working tree.

open as a page

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

level: middleimportance: should knowfreq 40%

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

open as a page

In Git, what does marking a path binary in .gitattributes actually change?

level: middleimportance: should knowfreq 42%

basics

~10 s

The binary attribute is a built-in macro expanding to -diff -merge -text. Git then reports the file as differing rather than showing a textual diff, refuses to content-merge it, and applies no end-of-line conversion.

open as a page

In Git, how do you find which ignore file and line is excluding a specific path?

level: middleimportance: should knowfreq 44%

basics

~20 s

Run git check-ignore -v <path>. Git prints the source file, line number and pattern that excluded the path, and exits with status 1 when nothing matches it. Add --no-index to check a path that is already tracked.

open as a page

In Git, why can a ! negation line in .gitignore fail to re-include a file?

level: middleimportance: should knowfreq 42%

basics

~10 s

Git never descends into an excluded directory, so no pattern inside it can re-include anything. Exclude the directory's contents with a pattern like assets/* instead of assets/, then negate the paths you want back.

open as a page

How do you check from the command line whether Git commits and tags are signed?

level: middleimportance: should knowfreq 32%

basics

~20 s

In Git, use git log --show-signature for a range, git verify-commit or git verify-tag for a single object and a scriptable exit status, and the %G? pretty format placeholder for a compact per-commit status column.

open as a page

How do you make Git use a work email under ~/work and a personal one everywhere else?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Use a conditional include in your global config: an includeIf section with a gitdir condition pointing at ~/work/ pulls in a second config file whose user.email is the work address, so every repository under that directory gets the work identity automatically.

open as a page

In Git, what do clean and smudge filters do, and when does each one run?

level: seniorimportance: should knowfreq 32%

basics

~20 s

A clean filter transforms content on its way into the repository, when a file is staged; a smudge filter transforms it on the way out, at checkout. Both are named by a filter attribute and defined by commands in Git config.

open as a page

A generated CHANGELOG conflicts on every merge in Git. How can .gitattributes fix that?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Assign the built-in union merge driver in Git with a line such as CHANGELOG.md merge=union. Git then keeps lines from both sides instead of writing conflict markers, which suits append-only, order-insensitive text and nothing else.

open as a page

Where should Git ignore rules for editor and OS files live instead of the repo .gitignore?

level: seniorimportance: should knowfreq 40%

basics

~10 s

Personal, cross-repository rules belong in the file named by core.excludesFile; personal rules for one clone belong in .git/info/exclude. The committed .gitignore should hold only what every contributor produces, such as build output.

open as a page

How do you renormalize an existing Git repo to LF after adding `* text=auto`?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Commit the .gitattributes rule, then run git add --renormalize . and commit the result. Git re-applies the new conversion to every tracked file, producing one commit that rewrites the stored line endings without changing any visible text.

open as a page

Why do amend, rebase, and cherry-pick drop a Git commit's existing signature?

level: seniorimportance: should knowfreq 26%

basics

~20 s

Those operations create new commit objects rather than editing old ones, and a signature only validates the exact object it was made over. The new commit has different parents, committer or timestamp, so the old signature cannot carry over and must be replaced.

open as a page

How would you roll out commit signing across a team, and what does it actually buy?

level: principalimportance: should knowfreq 20%

basics

~20 s

Start with signed release tags, then extend to commits on protected branches. Signing only pays off if verification runs somewhere that matters and a trust list of principals and keys is actively maintained, including rotation and revocation.

open as a page

In Git, what determines which editor opens for a commit message, and how do you change it?

level: middleimportance: nice to knowfreq 28%

basics

~20 s

Git picks the first of GIT_EDITOR, the core.editor config value, VISUAL and EDITOR that is set, falling back to a built-in default. Set core.editor globally to change it, and check the result with git var GIT_EDITOR.

open as a page

How would you roll out a standard Git configuration across a team, given .git/config is untracked?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Ship a tracked config fragment in the repository and have each clone reference it once with git config include.path, provision personal defaults through dotfiles or a system file, and enforce anything that actually matters with server-side hooks rather than trusting client configuration.

open as a page

When is a custom .gitattributes driver the wrong way to solve a Git repository problem?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Custom diff, merge and filter drivers are named in a committed file but defined in per-machine Git config, so behaviour silently differs by machine. When correctness depends on the driver, prefer built-in attributes or remove the artifact from the repository.

open as a page