Configuring Git

Before you make a single commit, Git needs to know who you are and how you like your tools to behave. git config is the command that reads and writes Git’s settings — everything from your name and email to your default editor, branch name, and custom shortcuts. Get it right once and every repository you touch on that machine behaves the way you expect.

Overview: How Git Configuration Works

Git configuration is stored as plain text key-value pairs, grouped into sections like [user] or [core]. These settings live in three (sometimes four) layers, each overriding the one below it:

  • System — applies to every user on the machine. Stored in /etc/gitconfig (Linux/macOS) or a similar path on Windows. Written with git config --system.
  • Global — applies to every repository for the current user. Stored in ~/.gitconfig or ~/.config/git/config. Written with git config --global. This is where almost everyone sets their identity.
  • Local — applies only to the current repository. Stored in .git/config inside that repo. Written with git config --local, or just git config with no scope flag, which defaults to local.
  • Worktree — a rarer fourth layer for repositories using multiple worktrees, stored per-worktree when extensions.worktreeConfig is enabled.

When Git looks up a setting, it reads all applicable files and lets the narrowest scope win: local beats global, and global beats system. This is why a value you set with --local in one repository can silently override the identity you set with --global everywhere else — useful for a work repo that needs a different email, but a common source of confusion when forgotten.

These settings aren’t just cosmetic. Some of them get baked directly into Git’s object model. When you run git commit, Git creates a commit object containing a pointer to a tree (the snapshot of your files), a pointer to the parent commit(s), and author and committer lines built from user.name, user.email, and the current timestamp. Once that commit object is written and hashed with SHA-1 (or SHA-256 on newer repos), that identity is permanent — changing your global config afterward does not retroactively fix past commits. Other settings, like core.editor or init.defaultBranch, don’t touch objects at all; they only change how Git’s commands behave on your machine.

Syntax

git config [--system|--global|--local|--worktree] <key> [<value>]
git config --list [--show-origin]
git config --get <key>
git config --unset <key>
git config --edit [--global]
Flag Meaning
--system Write/read the machine-wide config file (usually needs admin rights).
--global Write/read the per-user config file; the usual place for your identity.
--local Write/read the current repository’s config only. This is the default scope if none is given.
--list Print all settings Git can currently see, merged across scopes.
--show-origin Combined with --list, prints which file each value came from.
--get <key> Print the effective value of a single key.
--unset <key> Remove a key from the given scope.
--edit Open the config file for that scope directly in your configured editor.
--add Add a new value to a key that can hold multiple values, instead of replacing it.

Examples

Example 1: Setting your identity globally

git config --global user.name "Jane Doe"
git config --global user.email "jane.doe@example.com"
git config --global --list

Output:

user.name=Jane Doe
user.email=jane.doe@example.com

This writes both keys into ~/.gitconfig. From now on, any commit you make on this machine — in any repository that doesn’t override it locally — will record Jane Doe and that email address as the author and committer.

Example 2: Setting a default editor and default branch name

git config --global core.editor "code --wait"
git config --global init.defaultBranch main
git config --get core.editor

Output:

code --wait

The first line tells Git to open VS Code (waiting for you to close the tab) whenever it needs an editor, such as for a merge commit message or an interactive rebase. The second line makes every future git init create a branch named main instead of the older default master. Without this setting, brand-new repositories on older Git installs may still default to master.

Example 3: A per-repository override and an alias

cd ~/work/internal-api
git config --local user.email "jane.doe@company.com"
git config --global alias.st status
git config --global alias.co switch
git st

Output:

On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

The --local call only edits .git/config inside internal-api, so commits made in that one repository use the work email while every other repository on the machine still uses the global personal email. The two alias lines create shortcuts: git st now runs git status, and git co runs git switch. Aliases are just config entries under the [alias] section — nothing magic.

How It Works Step by Step

  1. You run a config-writing command, e.g. git config --global user.name "Jane Doe".
  2. Git determines the target file for that scope (~/.gitconfig for --global) and creates it if it doesn’t exist.
  3. Git parses the file’s existing [section] blocks, finds or creates the matching section and key, and writes the new value — the rest of the file is left untouched.
  4. Later, when any Git command needs a setting (say, git commit needing user.email), Git reads system, then global, then local (then worktree) config files in that order, layering values so the most specific file wins.
  5. For user.name/user.email specifically, that final resolved value is copied into the author and committer lines of the new commit object at the moment you commit — it is not looked up again later, so history is unaffected by future config changes.

Common Mistakes

Mistake 1: Forgetting the scope flag

git config user.name "Jane Doe"

Run with no --global, this defaults to --local and only takes effect in whichever repository your terminal happens to be in — or fails with fatal: --local can only be used inside a git repository if you’re not in one at all. If you meant this to apply everywhere, you need --global:

git config --global user.name "Jane Doe"

Mistake 2: An old local override shadows your global identity

You update your global email after changing jobs, but commits in one old cloned repository still show your previous address. That repository has a [user] section in its own .git/config left over from before, and local always wins over global. Check with:

git config --list --show-origin | grep user\.

Then remove the stale override:

git config --local --unset user.email

Mistake 3: Hand-editing the config file and breaking its syntax

Opening .git/config in a text editor and leaving a section bracket unclosed, like [user instead of [user], breaks every Git command in that repository with fatal: bad config line ... in file .git/config. Prefer git config --edit, which still opens the same file in your editor but makes it easy to remember exactly which file you’re touching, and always double-check bracket and quote pairing before saving.

Best Practices

  • Set user.name and user.email globally first, on any new machine, before your first commit.
  • Use a --local override in specific repositories (e.g. work vs. open source) instead of switching your global identity back and forth.
  • Set init.defaultBranch main globally so new repositories don’t fall back to master.
  • Pick a core.editor you’re actually comfortable finishing a commit message in — a wrong default (like vim for someone unfamiliar with it) is a common source of stuck, seemingly-frozen commits.
  • Use git config --global pull.rebase false|true deliberately rather than letting Git nag with its default-behavior warning on every pull.
  • Build a small set of aliases for commands you type constantly (status, switch, log --oneline --graph) — they live under [alias] in your global config and cost nothing.
  • Use git config --list --show-origin whenever a setting seems to have the “wrong” value — it tells you exactly which file is winning.
  • Never store a plaintext password in config for HTTPS remotes; use a credential helper (git config --global credential.helper store or your OS’s native helper) together with a Personal Access Token or SSH key.

Practice Exercises

  • Exercise 1: On a fresh machine (or a scratch directory), set your global user.name, user.email, and init.defaultBranch. Then run git config --list --show-origin and confirm all three appear as coming from your global config file.
  • Exercise 2: Create two directories, personal-project and work-project, run git init in each, and give work-project a local user.email override. Make a commit in each and use git log --format="%an <%ae>" to confirm each repo recorded a different email.
  • Exercise 3: Create a global alias named lg that runs log --oneline --graph --all. Verify it works by running git lg in any repository with at least two commits.

Summary

  • git config reads and writes settings across three main scopes: system, global, and local, with local overriding global overriding system.
  • user.name and user.email get written directly into each commit’s author/committer fields the moment you commit — changing config later doesn’t rewrite past history.
  • Use --global for your everyday identity and editor preferences; use --local only when a specific repository genuinely needs different values.
  • git config --list --show-origin is the fastest way to debug “why is this setting wrong here.”
  • Aliases, stored under [alias] in any scope, are a simple, powerful way to shorten commands you type often.
  • Edit config files through git config commands or git config --edit rather than hand-editing, to avoid breaking the file’s syntax.