Skip to content

Configuring Git

Git is deeply configurable. Every aspect of its behavior - from how it displays output to how it handles line endings - can be tuned through configuration. Understanding the configuration system lets you build a Git setup that matches your workflow, your editor, and your team's conventions.


Configuration Levels

Git reads configuration from four levels, each overriding the previous:

Level Flag Location Scope
System --system /etc/gitconfig Every user on the machine
Global --global ~/.gitconfig or ~/.config/git/config Your user account
Local --local .git/config in the repo One repository
Worktree --worktree .git/config.worktree One worktree (Git 2.20+)

A setting at a more specific level overrides the same setting at a broader level. Local overrides global, global overrides system.

# See all settings and where they come from
git config --list --show-origin

# See just global settings
git config --global --list

# See just local (repo) settings
git config --local --list

Reading and Writing Configuration

Setting Values

# Set a value
git config --global user.name "Jane Developer"

# Set with a specific scope
git config --local core.autocrlf input

# Set a boolean
git config --global color.ui true

Reading Values

# Get a specific value
git config user.name

# Get with scope
git config --global user.email

# Get showing the origin
git config --show-origin user.name

Removing and Editing

# Remove a specific key
git config --global --unset core.editor

# Remove a section
git config --global --remove-section alias

# Open config in editor
git config --global --edit

Aliases

Aliases let you create shortcuts for Git commands you use frequently. They range from simple abbreviations to complex shell commands.

Simple Aliases

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.sw switch

Now git st runs git status, git co main runs git checkout main, and so on.

Compound Aliases

# Compact log with graph
git config --global alias.lg "log --oneline --graph --all --decorate"

# Unstage files
git config --global alias.unstage "reset HEAD --"

# Show last commit
git config --global alias.last "log -1 HEAD --stat"

# Diff of staged changes
git config --global alias.staged "diff --staged"

# List branches sorted by last commit date
git config --global alias.recent "branch --sort=-committerdate --format='%(committerdate:relative)%09%(refname:short)'"

Shell Command Aliases

Prefix with ! to run arbitrary shell commands:

# Delete all merged branches except main/master
git config --global alias.cleanup '!git branch --merged | grep -v "main\|master\|\*" | xargs -r git branch -d'

# Show all aliases
git config --global alias.aliases '!git config --get-regexp ^alias\. | sed s/alias\.//'

# Open the repo in the browser (GitHub)
git config --global alias.browse '!open $(git remote get-url origin | sed "s/git@/https:\/\//" | sed "s/\.git$//" | sed "s/:/\//")'

Custom Diff and Merge Tools

Git's built-in diff and merge output works in the terminal, but graphical tools can be easier for complex diffs and multi-file merge conflicts.

Configuring a Diff Tool

# Use vimdiff
git config --global diff.tool vimdiff

# Use VS Code
git config --global diff.tool vscode
git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'

# Use meld (Linux)
git config --global diff.tool meld

Run the visual diff:

git difftool                    # Working dir vs staging
git difftool --staged           # Staging vs HEAD
git difftool main feature/auth  # Between branches

Configuring a Merge Tool

# Use vimdiff
git config --global merge.tool vimdiff

# Use VS Code
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait --merge $REMOTE $LOCAL $BASE $MERGED'

# Show the base version in conflicts (highly recommended)
git config --global merge.conflictstyle diff3

Run the merge tool during a conflict:

git merge feature/branch    # Conflict occurs
git mergetool               # Opens each conflicted file in the configured tool

diff3 conflict style

merge.conflictstyle = diff3 adds a third section to conflict markers showing the base version (what the code looked like before either change). This makes it much easier to understand the intent of both changes. Set it globally - you'll wonder how you ever resolved conflicts without it.


Conditional Includes

If you use Git for both work and personal projects, you need different identities (name, email, signing key) for each. Conditional includes (Git 2.13+) let you load different configuration files based on the repository's location:

# ~/.gitconfig

[user]
    name = Jane Developer
    email = jane@personal.com

[includeIf "gitdir:~/work/"]
    path = ~/.gitconfig-work

[includeIf "gitdir:~/opensource/"]
    path = ~/.gitconfig-opensource
# ~/.gitconfig-work

[user]
    email = jane.developer@company.com
    signingkey = ~/.ssh/work_ed25519.pub

[commit]
    gpgsign = true

Any repository under ~/work/ automatically uses the work email and signing key. Repositories elsewhere use the personal defaults.

The gitdir: condition matches the .git directory location. Other conditions:

Condition Matches
gitdir:~/work/ Repos under ~/work/
gitdir/i:~/Work/ Case-insensitive match (useful on macOS)
onbranch:main When the main branch is checked out
hasconfig:remote.*.url:*github.com* Repos with a GitHub remote (Git 2.36+)

Core Settings

The core.* namespace contains Git's most fundamental behavior settings.

Line Endings

Line ending handling is critical for cross-platform teams. Windows uses CRLF (\r\n), Linux and macOS use LF (\n).

# On Linux/macOS: convert CRLF to LF on commit, no conversion on checkout
git config --global core.autocrlf input

# On Windows: convert LF to CRLF on checkout, CRLF to LF on commit
git config --global core.autocrlf true

# No conversion (if your team standardizes on LF and uses .gitattributes)
git config --global core.autocrlf false

Use .gitattributes for line endings

core.autocrlf is a per-user setting - it only works if everyone configures it. .gitattributes is committed to the repository and enforces line ending rules for the whole team. See the .gitattributes section below.

Other Core Settings

# Set your editor (for commit messages, rebase, etc.)
git config --global core.editor "vim"
# git config --global core.editor "code --wait"
# git config --global core.editor "nano"

# Set the pager (for log, diff, etc.)
git config --global core.pager "less -FRX"

# Detect whitespace problems
git config --global core.whitespace trailing-space,space-before-tab

# Improve performance on large repos with filesystem monitor
git config --global core.fsmonitor true

.gitattributes

While .gitconfig controls your personal Git behavior, .gitattributes is committed to the repository and controls per-file behavior for the whole team: line endings, diff drivers, merge strategies, and LFS tracking.

Line Endings

# .gitattributes - normalize line endings

# Default: auto-detect
* text=auto

# Force LF for source files
*.py text eol=lf
*.js text eol=lf
*.css text eol=lf
*.html text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.json text eol=lf

# Force CRLF for Windows-specific files
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf

# Binary files (no conversion, no diff)
*.png binary
*.jpg binary
*.gif binary
*.ico binary
*.zip binary
*.pdf binary

Custom Diff Drivers

# Show meaningful diffs for specific file types
*.md diff=markdown
*.py diff=python
*.rb diff=ruby

These tell Git to use language-aware diff heuristics - better function/class detection in diff headers.

Linguist Overrides

# Exclude from GitHub language statistics
docs/* linguist-documentation
vendor/* linguist-vendored
*.min.js linguist-generated

Environment Variables

Git reads several environment variables that override configuration:

Variable Overrides Use case
GIT_AUTHOR_NAME user.name Set author for the current command
GIT_AUTHOR_EMAIL user.email Set author email for the current command
GIT_COMMITTER_NAME user.name Set committer identity
GIT_COMMITTER_EMAIL user.email Set committer email
GIT_DIR default .git Path to the .git directory
GIT_WORK_TREE default parent of .git Path to the working tree
GIT_EDITOR core.editor Editor for commit messages
GIT_PAGER core.pager Pager for output
GIT_SSH_COMMAND core.sshCommand Custom SSH command
# One-off commit with a different author
GIT_AUTHOR_NAME="Pair Partner" GIT_AUTHOR_EMAIL="pair@example.com" git commit -m "Paired on auth fix"

# Use a different SSH key for one command
GIT_SSH_COMMAND="ssh -i ~/.ssh/deploy_key" git push

.mailmap: Author Normalization

Over time, contributors may use different names or emails across commits. .mailmap normalizes these for git log and git shortlog:

# .mailmap
Jane Developer <jane@current.com> <jane.dev@oldcompany.com>
Jane Developer <jane@current.com> <jdev@personal.com>
Bob Smith <bob@company.com> Bobby <bobby@typo.com>

Now git shortlog -sne shows unified author counts instead of splitting the same person across multiple entries.


Further Reading


Previous: Stashing and the Worktree | Next: The Object Model | Back to Index

Comments