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 withgit config --system. - Global — applies to every repository for the current user. Stored in
~/.gitconfigor~/.config/git/config. Written withgit config --global. This is where almost everyone sets their identity. - Local — applies only to the current repository. Stored in
.git/configinside that repo. Written withgit config --local, or justgit configwith no scope flag, which defaults to local. - Worktree — a rarer fourth layer for repositories using multiple worktrees, stored per-worktree when
extensions.worktreeConfigis 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
- You run a config-writing command, e.g.
git config --global user.name "Jane Doe". - Git determines the target file for that scope (
~/.gitconfigfor--global) and creates it if it doesn’t exist. - 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. - Later, when any Git command needs a setting (say,
git commitneedinguser.email), Git reads system, then global, then local (then worktree) config files in that order, layering values so the most specific file wins. - For
user.name/user.emailspecifically, 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.nameanduser.emailglobally first, on any new machine, before your first commit. - Use a
--localoverride in specific repositories (e.g. work vs. open source) instead of switching your global identity back and forth. - Set
init.defaultBranch mainglobally so new repositories don’t fall back tomaster. - Pick a
core.editoryou’re actually comfortable finishing a commit message in — a wrong default (likevimfor someone unfamiliar with it) is a common source of stuck, seemingly-frozen commits. - Use
git config --global pull.rebase false|truedeliberately 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-originwhenever 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 storeor 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, andinit.defaultBranch. Then rungit config --list --show-originand confirm all three appear as coming from your global config file. - Exercise 2: Create two directories,
personal-projectandwork-project, rungit initin each, and givework-projecta localuser.emailoverride. Make a commit in each and usegit log --format="%an <%ae>"to confirm each repo recorded a different email. - Exercise 3: Create a global alias named
lgthat runslog --oneline --graph --all. Verify it works by runninggit lgin any repository with at least two commits.
Summary
git configreads and writes settings across three main scopes: system, global, and local, with local overriding global overriding system.user.nameanduser.emailget written directly into each commit’s author/committer fields the moment you commit — changing config later doesn’t rewrite past history.- Use
--globalfor your everyday identity and editor preferences; use--localonly when a specific repository genuinely needs different values. git config --list --show-originis 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 configcommands orgit config --editrather than hand-editing, to avoid breaking the file’s syntax.
