Configuring Git the Way You Want It
The three config levels, your editor, line endings, and the handful of settings that remove daily friction — each introduced by the annoyance it fixes.
The three config levels, your editor, line endings, and the handful of settings that remove daily friction — each introduced by the annoyance it fixes.
In the last lesson you typed your name and email into Git and moved on. That was not a one-off ritual. It was your first two settings, written as two lines of plain text into a file that Git reads before every single command it runs.
Git arrives knowing nothing about you: not your name, not which editor you can survive, not how your operating system ends a line of text. It has a default for all of it, and a few of those defaults are older than you would like. By the end of this lesson you will have a config file you can read line by line, explain to someone else, and carry to every machine you ever own.
When you ran git config --global user.name "Ada Lovelace", Git did
not tuck your name into a database somewhere. It appended a line to
a text file in your home folder. That is the whole mechanism: a
setting is a key, a value, and a file.
Keys always come in two parts joined by a dot — a section, then a
name inside it. user.name is the name entry in the user
section. core.editor is the editor entry in core. Once you see
that shape, config keys stop looking like magic words you have to
memorise from blog posts:
git config --global <section>.<key> "<value>"You already know the pattern from lesson two. Everything else in this lesson is the same command with different keys.
Here is the awkward situation Git has to handle. You have one computer and many projects on it. Most of what you want is the same everywhere — your name is your name, your editor is your editor. But not everything. The repositories you touch for your employer should carry your work address, not the personal one you use for weekend projects. And if other people log into the same machine, some settings should apply to all of them.
One file cannot express that, so Git reads three and stacks them.
Lives in that repo's .git/config. Set it by running
git config inside the repo, with --local or with no level
flag at all. This is where a work email belongs.
Lives at ~/.gitconfig, reached with --global. Roughly
ninety percent of your settings belong here, including the
name and email from the last lesson.
Lives at /etc/gitconfig, reached with --system. Editing it
usually needs administrator rights, and in practice it holds
whatever your installer put there. You will likely never write
to it.
Think of a shared workshop. There are rules posted at the entrance that apply to everyone who walks in, the way you personally like your own bench arranged, and a note taped to one specific job saying "this one is different". The specific note wins.
Git works exactly that way: the most specific level wins. Local beats global, and global beats system. When a setting is not what you swear you set, ask Git where each value came from:
Every line is prefixed with the file it was read from:
Read that last pair together. Two files set user.email, and
because .git/config is the local file, the work address is the one
in effect here. That is not a bug; it is the everyday reason local
config exists. Inside your employer's repository, run:
No --global, so the value lands in that repository's .git/config
and nowhere else. Every other project on the machine keeps your
personal address, and no commit ever again carries the wrong one to
the wrong place.
When Git creates a repository it has to call the first line of
history something. For most of Git's life that name was hardcoded to
master. In 2020 the community moved to main, GitHub and GitLab
followed for new repositories, and Git 2.28 added a setting so you
could pick your own instead of renaming by hand every time.
Two concrete reasons to set it. First, a repo whose first branch is
master while the hosting service expects main is a small, dumb
source of friction the first time you push. Second, until you decide,
Git prints a paragraph of advice about it on every single git init,
and advice you have read forty times is noise.
Some of what Git needs from you does not fit on a command line. A commit message that deserves a paragraph of explanation. A note about why a merge happened. A list of instructions for rewriting several commits at once. Git does not have a text box of its own, so it does the sensible thing: it opens a text editor, waits for you to write and save, then reads the file back.
If you never chose an editor, you get whatever the system considers
default. On a lot of machines that is vim — a fine editor, and a
genuinely alarming surprise if you have never met it and cannot work
out how to get out. Choose deliberately instead:
The --wait on the VS Code line is not decoration, and leaving it
off produces a confusing failure. Normally code hands the file to
the VS Code window you already have open and exits immediately.
Git sees the editor finish about a tenth of a second after launching
it, reads a file you have not typed in yet, finds it empty, and
aborts with "Aborting commit due to empty commit message" — while
your commit message sits there half-written on screen. --wait
keeps code running in the foreground until you close the tab, and
closing the tab is the signal Git is waiting for.
Picture this. A colleague on a different operating system opens a
file you wrote, changes one word, and saves. Now git diff reports
that every single line in the file changed. Four hundred lines, none
of them different to a human eye. Reviewing that is impossible, and
merging two branches that both did it produces conflicts about
nothing at all.
The cause is two characters you cannot see. Windows ends a line of text with a carriage return followed by a line feed; macOS and Linux use a line feed alone. Many editors rewrite the whole file to their own convention on save. Git stores exactly the bytes it is handed, so to Git every line really is different.
The fix is to agree that history stores line feeds, and let each machine do what it likes on disk. Which value you want depends only on what your own editors expect.
core.autocrlf true
Strips carriage returns on the way into a commit and puts them back when files land on disk — which is what a Windows editor expects to find.
core.autocrlf input
Strips them on the way in and leaves your files alone on the way out, which is right on systems that never wanted carriage returns in the first place.
Teams eventually
pin this per project in a .gitattributes file so it does not
depend on everyone configuring their laptop correctly, but your
global setting is what protects you today.
None of these are required, and Git works without them. Each one removes a specific small irritation you would otherwise meet weekly.
When you pull a branch that moved on while you were working, Git has
to decide what to do with your local commits, and by default it
stops to ask. pull.rebase true answers the question once and for
all: replay your commits on top of what arrived, so history stays a
straight line instead of collecting merge commits that say nothing.
push.default simple makes a bare git push mean "send the branch
I am on to the branch of the same name upstream" — the reading you
almost certainly intended, and the one least likely to push
something you forgot about.
color.ui auto colourises output in your terminal so added and
removed lines are instantly distinguishable, while staying plain
text when you pipe output into another program that would only be
confused by colour codes.
core.pager "less -FR" fixes two nuisances at once: -F skips the
pager entirely when the output already fits on one screen, so a
three-line log stops taking over your terminal and demanding
q, and -R lets those colours through instead of showing
you the raw codes.
help.autocorrect prompt catches git stauts and asks whether you
meant status, rather than refusing and making you retype. Set it
to a number like 20 instead and Git waits two seconds and runs the
corrected command itself, which is fine until the day it guesses
something you did not want.
You are going to type git status thousands of times. Not
figuratively — a working day involves dozens of them, and that adds
up over a career. An alias is a nickname for a longer command,
stored in your config like any other setting:
You call them by name: git st, git lg, or
git unstage notes.txt. Git expands the alias and runs the real
command underneath, and anything extra you type passes straight
through to it — which is why git unstage notes.txt works even
though the alias never mentions a filename.
Keep the list short and readable. An alias you cannot decode six months from now is worse than the command it replaced, and an alias you rely on is a small trap on any machine that is not yours.
Every --global command in this lesson has been editing one plain
text file: ~/.gitconfig in your home folder. On Windows that is
C:\Users\YourName\.gitconfig, which Git Bash still lets you write
as ~/.gitconfig. Nothing is hidden from you — open it whenever you
like:
That opens the file in the editor you just configured. After following this lesson it looks roughly like this:
Look at the shape of it and the dotted keys finally make sense. The
[user] header is the section, name is the key inside it, and
together they are the user.name you have been typing all along.
Editing this file by hand does exactly the same thing as running
git config — the command only saves you from typos in the
punctuation.
Two more commands and you can manage this file with confidence. To ask what a single setting is right now:
--get respects the same precedence as everything else, so it
answers with the value actually in effect — run it inside that work
repository and you get the work address, not the global one. That
makes it the fastest way to check yourself before a first commit in
an unfamiliar folder.
To remove a setting you regret, name the level and the key:
Removing is better than blanking. Setting a key to an empty string
leaves it in the file, still overriding whatever the system config
said; --unset deletes the line so the level below shows through
again. If a key somehow appears twice in one file, --unset refuses
to guess which you meant and --unset-all removes every copy.
Every command from this lesson, in one place:
Git now behaves like a tool that was set up for you rather than for
nobody in particular. Your commits carry the right identity, new
repositories start on main, an editor you can actually use opens
when Git needs prose, line endings stay sane across operating
systems, and a handful of small settings have quietly stopped
getting in your way.
Next comes the part you have been setting up for: your first repository and commit. You will turn an ordinary folder into a repo, choose what goes into a snapshot, and save it — and several settings from this lesson will show up in the result without you thinking about them again.
Before you move on, spend two minutes proving the setup to yourself.
Run git config --list --show-origin and read it line by line until
you can name what each setting does and which file it came from.
Then change one — swap the editor, add an alias, remove it again —
and watch the file follow you. Config you understand is config you
can fix at midnight.
file:/etc/gitconfig init.defaultbranch=main
file:/home/ada/.gitconfig user.name=Ada Lovelace
file:/home/ada/.gitconfig user.email=ada@example.com
file:.git/config user.email=ada@bigcorp.comgit config --list --show-origingit config user.email "ada@bigcorp.com"git config --global init.defaultBranch maingit config --global core.editor "code --wait" # VS Code
git config --global core.editor "nano" # nano
git config --global core.editor "vim" # vimgit config --global core.autocrlf true # Windows
git config --global core.autocrlf input # macOS and Linuxgit config --global pull.rebase true
git config --global push.default simple
git config --global color.ui auto
git config --global core.pager "less -FR"
git config --global help.autocorrect promptgit config --global alias.st "status -sb"
git config --global alias.lg "log --oneline --graph --decorate"
git config --global alias.unstage "restore --staged --"git config --global --editgit config --get user.emailgit config --global --unset alias.lggit config --global <key> "<value>" # set for all your repos
git config <key> "<value>" # set for this repo only
git config --list --show-origin # every value + its file
git config --global --list # only your own settings
git config --get user.email # the value in effect now
git config --global --unset alias.lg # delete one setting
git config --global --unset-all <key> # delete every copy
git config --global --edit # open ~/.gitconfig
git config --global init.defaultBranch main # name the first branch
git config --global core.editor "code --wait" # editor Git opens
git config --global core.autocrlf true # line endings: Windows
git config --global core.autocrlf input # line endings: mac/Linux
git config --global pull.rebase true # rebase instead of merge
git config --global push.default simple # push this branch only
git config --global color.ui auto # colourised output
git config --global core.pager "less -FR" # sane pager behaviour
git config --global help.autocorrect prompt # offer typo fixes
git config --global alias.st "status -sb" # makes `git st` work[user]
name = Ada Lovelace
email = ada@example.com
[init]
defaultBranch = main
[core]
editor = code --wait
autocrlf = input
pager = less -FR
[pull]
rebase = true
[push]
default = simple
[color]
ui = auto
[help]
autocorrect = prompt
[alias]
st = status -sb
lg = log --oneline --graph --decorate
unstage = restore --staged --