Mert Özen Further With Every Line
DevOps Git & GitHub — #02

Installing Git and First Settings: Starting Right with git config

Mert Özen Nov 6, 2026 9 min 2 views
Installing Git and First Settings: Starting Right with git config

We install Git on macOS, Windows and Linux, check the version and look at the system/global/local config layers, user.name and user.email, why init.defaultBranch main matters, core.editor and core.autocrlf. We compare the git config list/get/set/unset subcommands (2.46+) with the old --get form.

Version note: The commands in this post were tested with Git 2.54. The git config get/set/list/unset subcommands arrived in Git 2.46; on an older version, use the classic form at the end of this post.

Guess First

You have just installed Git and configured nothing. In an empty folder you run git init hello. What shows up on the screen? What will the repo's first branch be called? And one more: if you made the first commit in this repo, whose name would be recorded as its author?

In this post we see the answers and set Git up once, with the right settings. You make these settings one time and then leave them alone for years.

Installing and Checking the Version

On macOS the shortest way to get Git is the Xcode Command Line Tools: run xcode-select --install in a terminal and Apple's build of Git arrives as /usr/bin/git. Apple updates this package on its own schedule, so it is usually a release or two behind. If you want the latest version, install it with Homebrew using brew install git; Homebrew's Git comes first in your PATH, so that is the one that runs.

On Windows, use the Git for Windows installer from git-scm.com, or run winget install --id Git.Git -e --source winget. The package also includes a terminal called Git Bash and Git Credential Manager. On Linux your distribution's package manager is enough: sudo apt install git on Debian/Ubuntu, sudo dnf install git on Fedora. Distribution packages also tend to lag a little behind the latest release. I did not run these install commands here; each one is specific to its platform.

However you install it, the first command is the same:

$ which -a git
/usr/bin/git
$ git --version
git version 2.54.0 (Apple Git-157)

My machine has exactly one Git, Apple's package: version 2.54.0, and Apple Git-157 in parentheses is Apple's own patch number. At the time of writing the latest stable release is Git 2.56.0. The commands in this series were tested with 2.54, and the ones in this post work the same on every version from 2.46 on. If which -a git prints more than one line, the first line tells you which Git actually runs.

Three Layers of Settings: System, Global, Local

Git settings live at three levels. The system level applies to every user on the machine and is kept in a file where Git is installed; you read it with --system. The global level belongs to your user and lives in ~/.gitconfig in your home folder. The local level belongs to a single repo and lives in that repo's .git/config.

When the same setting exists at more than one level, the narrowest one wins: local overrides global, and global overrides system. Think of CSS specificity: a general rule applies to everyone, but wherever you write a more specific rule, that one takes effect. Using your company email in work repos and your own email in personal projects is done with exactly these layers.

Apple Git on macOS also has a fourth layer of its own. Look at the list for a user who has not set anything yet and you see this:

$ git config list --show-origin --show-scope
unknown	file:/Applications/Xcode.app/Contents/Developer/usr/share/git-core/gitconfig	credential.helper=osxkeychain
unknown	file:/Applications/Xcode.app/Contents/Developer/usr/share/git-core/gitconfig	init.defaultbranch=main

The scope shows as unknown because this file is not one of the three levels Git knows about; Apple added it to its package. If only the Command Line Tools are installed, the path is under /Library/Developer/CommandLineTools. The file holds two settings: a credential helper that stores passwords in the macOS keychain, and init.defaultBranch=main. I took the outputs in the rest of this post without this layer, which is how Git from Homebrew or on Linux behaves, and we will make the settings ourselves as if it did not exist.

The First Repo and the master Hint

Back to the opening question. With no settings at all:

$ git init hello
hint: Using 'master' as the name for the initial branch. This default branch name
hint: will change to "main" in Git 3.0. To configure the initial branch name
hint: to use in all of your new repositories, which will suppress this warning,
hint: call:
hint:
hint: 	git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: 	git branch -m <name>
hint:
hint: Disable this message with "git config set advice.defaultBranchName false"
Initialized empty Git repository in /.../hello/.git/
$ cd hello
$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

Git created the repo, but it named the first branch master and printed a long hint. The second line of the hint matters: this default becomes main in Git 3.0. GitHub has also used main as the default branch for new repos since October 1, 2020. Starting your local repo on master and the GitHub repo on main creates a pointless mismatch that confuses people on the first push.

We can fix this repo, which has no commits yet, the way the hint suggests:

$ git branch -m main
$ git status
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)

But instead of doing this in every new repo, it is better to write the setting once, globally.

Tell Git Who You Are: user.name and user.email

Every commit contains an author name and an email address. Git takes them from the user.name and user.email settings. If they are not set, it guesses from your user name and your machine's host name, and the result is often something like ada@Adas-MacBook-Pro.local. On most machines the commit still goes through, and Git just prints a warning that starts with "Your name and email address were configured automatically". That answers the third opening question: without the setting, what ends up in the author field is down to luck. Let's make the four basic settings together:

# These commands print nothing; silence means success
$ git config set --global user.name "Ada Lovelace"
$ git config set --global user.email ada@example.com
$ git config set --global init.defaultBranch main
$ git config set --global core.editor "code --wait"

Besides your name and email there are two more settings here. init.defaultBranch main makes every future git init start on main. core.editor is the editor Git opens when it needs you to write a message; it opens when you run git commit without -m. For VS Code we write code --wait, because without --wait Git thinks the file was closed before you wrote the message. If you would rather stay in the terminal, nano or vim work too.

A note on email: GitHub links commits to your account through the email address. If the address you put here is not added to your GitHub account, your commits will not show up on your profile. If you don't want to expose your address, you can use the noreply address GitHub gives you in its email settings.

Reading Settings: list, get and --show-origin

Let's check what we wrote:

$ git config list --global --show-origin
file:/.../.gitconfig	user.name=Ada Lovelace
file:/.../.gitconfig	user.email=ada@example.com
file:/.../.gitconfig	init.defaultbranch=main
file:/.../.gitconfig	core.editor=code --wait
$ cat ~/.gitconfig
[user]
	name = Ada Lovelace
	email = ada@example.com
[init]
	defaultBranch = main
[core]
	editor = code --wait

--show-origin tells you which file each setting comes from. I shortened the start of the path to ...; you will see the full path of your home folder. As you can see, the file itself is plain text: a section name in square brackets, with key = value lines under it. You can edit it by hand, but writing through the command removes the risk of typos. Also notice that the keys are lowercased in the list: init.defaultbranch. Section and key names in Git config are case-insensitive.

To read a single setting there is get. Let's use it to look at the line ending setting too:

$ git config get user.name
Ada Lovelace
# If the key is not set, there is no output and the exit code is 1
$ git config get core.autocrlf
$ git config get --default=false core.autocrlf
false

core.autocrlf controls the conversion between Windows line endings (CRLF) and macOS and Linux line endings (LF). On Windows, the Git for Windows installer usually sets it to true: files come out into the working folder with CRLF and go into the repo with LF. On macOS and Linux, leaving it unset is the least troublesome choice. If your team uses different operating systems, the more robust solution is a .gitattributes file that puts the line ending rule inside the repo; we will see it briefly in the .gitignore post. --default tells Git what to return when the key is not set, which helps in scripts.

Repo-Specific Settings and Who Wins

Now let's create a repo and give it an email of its own:

$ git init bookstore
Initialized empty Git repository in /.../bookstore/.git/
$ cd bookstore
$ git branch --show-current
main
# A different email for this repo only
$ git config set user.email ada@bookstore.example.com
$ git config list --show-scope --show-origin
global	file:/.../.gitconfig	user.name=Ada Lovelace
global	file:/.../.gitconfig	user.email=ada@example.com
global	file:/.../.gitconfig	init.defaultbranch=main
global	file:/.../.gitconfig	core.editor=code --wait
local	file:.git/config	core.repositoryformatversion=0
local	file:.git/config	core.filemode=true
local	file:.git/config	core.bare=false
local	file:.git/config	core.logallrefupdates=true
local	file:.git/config	core.ignorecase=true
local	file:.git/config	core.precomposeunicode=true
local	file:.git/config	user.email=ada@bookstore.example.com

The new repo started straight on main, with no hint. The list shows two scopes side by side: the global lines come from ~/.gitconfig, the local ones from .git/config. The core.* settings written by git init are local too; the core.ignorecase=true line comes from the macOS file system being case-insensitive. If there were system-level settings, system lines would show up at the very top. user.email appears twice, so let's ask which one is in effect:

$ git config get --show-origin user.email
file:.git/config	ada@bookstore.example.com
$ git config get --show-scope --all user.email
global	ada@example.com
local	ada@bookstore.example.com
$ git config unset user.email
$ git config get --show-origin user.email
file:/.../.gitconfig	ada@example.com

On its own, get gives you the winning value and where it came from. --all shows the values from every level in order. Once we removed the local setting, the global value took effect again. This is the first command to reach for when you are investigating why commits in a repo went out with the "wrong" email.

The Old Form: --get, --global, --list

Before Git 2.46, git config had no subcommands; options and the number of arguments decided what it did. Most posts on the internet, StackOverflow answers and CI scripts still use this form:

$ git config --get user.email
ada@example.com
$ git config --global user.name
Ada Lovelace
$ git config --global --list
user.name=Ada Lovelace
user.email=ada@example.com
init.defaultbranch=main
core.editor=code --wait

With one argument it reads, with two it writes: git config --global user.name "Ada Lovelace" writes the setting in the old form. The Git 2.46 release notes deprecated these mode options (--get, --list and the others) and introduced the get, set, list and unset subcommands in their place. The old form still works; recognize it when you see it, and use the subcommands in anything new you write. On a server with Git older than 2.46, git config set will not work, and you fall back to the old form there.

Common Pitfalls

The first pitfall is assuming Apple Git is current. It is common to install a new Git with Homebrew and find the terminal still running /usr/bin/git; if the PATH order is wrong, the new version never kicks in. When in doubt, check with which -a git and git --version.

The second is writing a setting at the wrong level. Forget --global and the setting goes only into the repo you happen to be in, so the next repo does not have it. Conversely, if you run git config set without --global outside a repo, Git stops with fatal: not in a git directory.

The third is not matching user.email to your GitHub address. The commits go through but don't show up on your profile, and your teammates have to guess who the author is. This mistake is tedious to fix later, because the email is written into the commit and is part of the commit's hash.

The fourth is setting VS Code as core.editor without --wait. Git opens the editor, the editor immediately says "done", and Git finds an empty message and aborts the commit.

A Small Experiment

★☆☆ Run git --version and which -a git. Find out which Git you are using and how many releases it is behind the latest one. Then list every setting on your machine, with the file it comes from, using git config list --show-origin --show-scope.

★★☆ Set user.name, user.email, init.defaultBranch and core.editor globally. Run git init in an empty folder and confirm with git branch --show-current that the branch is called main.

★★★ Give that repo a user.email of its own. Look at both values together with git config get --show-scope --all user.email, then remove the local setting and check that the global value is back. Do the same thing once more with the old --get and --unset form.

Git is ready and knows who you are. In the next post we build the bookstore repo from scratch: git init, the working tree, staging area and repository trio, git status, git add and the first commit.