Setting Up Git Config Correctly: Global, Local, and System-Wide
AI generated
git
HEAD
Git · Config · Workflow · Best Practices
Setting up Git config correctly
Global, local, and system-wide under control

Using Git without a thoughtful configuration means committing with the wrong identity, fighting a messy history, and losing time when switching between projects. The three config scopes system, global, and local follow a clear precedence, letting you set up editor, rebase behavior, aliases, and per-project overrides via includeIf in a clean, predictable way.

12 min. read system · global · local · includeIf Git 2.x · CLI · Team setup

1. Why the right Git configuration matters

Your Git configuration is the foundation for every commit, every merge, and every collaboration with a team, yet most projects set it up once with a quick git config --global user.name and never touch it again. The result: wrong commit authors on company projects because a personal identity leaks in from the global scope, inconsistent merge strategies across the team because pull.rebase was never set, and an editor that unexpectedly opens vi the first time git commit runs without a message.

A well thought out Git configuration solves these problems structurally instead of ad hoc. Instead of manually setting identity and behavior for every new project, you use the three config scopes system, global, and local together with conditional includes to cleanly separate work and personal setups. The following sections show how the scopes interact, which settings have the biggest effect on daily work, and how includeIf automates per-project overrides so you never have to remember them when switching between repositories.

2. The three config scopes: system, global, and local

Git has three configuration scopes with a clearly defined precedence: --system applies to every user and every repository on a machine and usually lives under /etc/gitconfig. --global applies to the current user across all repositories and lives in ~/.gitconfig or ~/.config/git/config. --local applies only to a single repository and lives in .git/config inside that project. Whenever values conflict, the more specific scope always wins: local overrides global, global overrides system.

This precedence is the core of every sensible Git config setup: global defaults for editor, aliases, and general workflow behavior belong in --global, while project-specific exceptions such as a different email address for a client project belong in --local. Since Git 2.x there's an additional, informal fourth layer: conditional includes via includeIf, which automatically pull in extra config files based on the directory, without having to maintain them manually per repository. git config --list --show-origin lets you trace, at any time, exactly which file an active value actually came from.


# System scope: applies to every user on this machine
sudo git config --system core.autocrlf input

# Global scope: applies to the current user across all repositories
git config --global user.name "Jane Developer"
git config --global user.email "jane@example.com"

# Local scope: applies only to the current repository
cd ~/projects/client-shop
git config --local user.email "jane@client-domain.com"

# Precedence check: local wins over global, global wins over system
git config --get user.email          # effective value
git config --list --show-origin      # shows which file each value came from

3. Base configuration: user.name, user.email, and core.editor

user.name and user.email are the two values baked into every commit, later showing up in git log, git blame, and every hosting provider's contributor statistics. Without a global default, modern Git versions refuse the first commit with a clear error message, which feels inconvenient but prevents exactly the empty or wrong identity commits that used to slip through unnoticed in older Git versions. The email address should match exactly the one registered with your Git hosting provider, otherwise commits won't be attributed to your profile.

core.editor determines which editor opens for git commit without -m, for git rebase -i, and during merge conflicts. Without an explicit setting, Git falls back to the $EDITOR environment variable and, lacking any alternative, often lands on vi, which regularly surprises developers with no Vim experience. A deliberately configured editor such as VS Code, Sublime Text, or Nano with the correct wait flag prevents Git from regaining control before the editor has actually closed.


# ~/.gitconfig - global identity and editor defaults
[user]
    name = Jane Developer
    email = jane@example.com

[core]
    editor = code --wait
    autocrlf = input
    excludesfile = ~/.gitignore_global

[init]
    defaultBranch = main

[color]
    ui = auto

4. Workflow settings: pull.rebase and init.defaultBranch

pull.rebase decides whether git pull internally performs a merge or a rebase against the remote branch. Git's default value false creates an extra merge commit on every pull with local commits, unnecessarily branching the history and quickly becoming confusing on teams that pull often. With git config --global pull.rebase true, Git instead rebases local commits cleanly onto the current remote state, producing a linear, readable history as long as nobody rewrites commits that have already been pushed.

init.defaultBranch sets the name of the first branch created by git init. Without this setting, Git still defaults to master, while practically every hosting provider and CI system now expects main as the standard. Skip this setting and every new local repository produces a branch name that has to be manually renamed on the first push. Two more settings with a noticeable everyday effect: fetch.prune = true automatically removes stale remote-tracking branches on every fetch, and push.default = current always pushes the current branch to its identically named remote counterpart, without having to repeat the branch name.

5. Git aliases for everyday productivity

Aliases shorten frequently typed commands down to a few characters, cutting not just typing effort but also the error rate on complex commands with many flags. A simple alias like co for checkout saves noticeable time across dozens of daily branch switches. More elaborate aliases prefixed with ! run arbitrary shell commands instead of plain Git subcommands, enabling multi-step operations such as showing a compact, graphical log history with colors, graph lines, and relative timestamps in a single line.

Aliases almost always belong in the global scope, since they're useful regardless of the project. An exception is project-specific aliases, such as a shortcut for a company-specific deploy branch, which then belong specifically in --local. One thing to keep in mind for maintenance: aliases should follow shell conventions developers already know, so they stick in muscle memory without having to look them up, and should be documented in a team onboarding document so new colleagues use the same shortcuts.


# ~/.gitconfig - aliases for everyday commands
[alias]
    co = checkout
    br = branch
    st = status -sb
    cm = commit -m
    amend = commit --amend --no-edit
    unstage = restore --staged
    last = log -1 HEAD --stat

    # Compact graph log with colors and relative dates
    lg = log --graph --pretty=format:'%C(yellow)%h%Creset -%C(auto)%d%Creset %s %C(green)(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit

    # Shell alias: prune local branches already merged into main
    cleanup = "!git branch --merged main | grep -v '\\* main' | xargs -r git branch -d"

6. Per-project overrides with includeIf

Anyone who works with different Git identities for their job and their personal projects knows the problem: a commit on a company project accidentally lands with the personal email address, because the global configuration wasn't overridden for that project. Manually setting user.email in every single repository works, but is routinely forgotten on new clones. The robust solution is includeIf with a gitdir condition: as soon as a repository lives under a specific directory, Git automatically loads an additional config file with the matching values.

The structure for this: all work projects consistently live under ~/work/, all personal projects under ~/personal/. In ~/.gitconfig, an includeIf "gitdir:~/work/" block points to a separate ~/.gitconfig-work with the company email and, if needed, a different signing key. The path must end with a slash, otherwise the condition isn't recognized as a directory prefix. This separation works fully automatically for every new clone inside the respective directory structure, with no manual extra step per repository.


# ~/.gitconfig - base config with conditional includes
[init]
    defaultBranch = main

# Load work identity for everything under ~/work/
[includeIf "gitdir:~/work/"]
    path = ~/.gitconfig-work

# Load private identity for everything under ~/personal/
[includeIf "gitdir:~/personal/"]
    path = ~/.gitconfig-personal

# ~/.gitconfig-work - only read for repositories inside ~/work/**
[user]
    name = Jane Developer
    email = jane@company.de
    signingkey = ABCD1234EFGH5678
[commit]
    gpgsign = true

7. Credential handling and commit signing

Without credential.helper, Git asks for a username and password, or a personal access token, on every HTTPS push, which in practice means credentials end up unencrypted in shell history or scripts. The built-in cache helper holds credentials in memory for a configurable timeout, the store helper writes them permanently in plain text to ~/.git-credentials and should only be used on fully encrypted, single-user machines. On macOS and Windows, osxkeychain and manager integrate credentials directly into the operating system's encrypted credential store.

Commit signing cryptographically proves that a commit really came from the identity it claims, and both GitHub and GitLab reward it with a visible "Verified" badge. commit.gpgsign = true enables automatic signing for every commit, user.signingkey references the key to use. Since Git 2.34, gpg.format = ssh additionally supports signing with an SSH key you already have, making a separate GPG key management setup unnecessary for many teams.


# Cache credentials in memory for 4 hours instead of asking every push
git config --global credential.helper 'cache --timeout=14400'

# macOS: use the encrypted system keychain instead
git config --global credential.helper osxkeychain

# Sign commits with an existing SSH key (Git 2.34+)
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true

# Verify a signed commit
git log --show-signature -1

8. Diagnosis: finding where a config value comes from

When a commit lands with the wrong email address even though includeIf appears to be set up correctly, the first place to check is git config --list --show-origin --show-scope. The command lists every active value together with the exact file and scope (system, global, local, worktree) it came from, immediately showing whether a local override file is shadowing an expected global or conditional value. Without this command, debugging across multiple config scopes is pure guesswork.

For targeted checks, git config --get user.email returns only the effective value without context, while git config --get-all also shows values set multiple times across different scopes. To edit directly, git config --global --edit opens the global file in the configured editor, and git config --local --edit opens the local repository config. To reset a setting entirely, use git config --unset for individual values or git config --remove-section to remove an entire config block, instead of editing the file manually and risking broken INI syntax.

9. Git config patterns compared side by side

The table below summarizes the most common Git config decisions and shows which variant leads to fewer mistakes and a cleaner history in practice.

Task Fragile / error-prone Recommended Git config pattern Benefit
Per-project identity Set email manually in every repo includeIf "gitdir:~/work/" Correct identity automatically, no manual step
Pull behavior pull.rebase not set pull.rebase = true Linear, readable history without extra merges
Default branch git init creates master init.defaultBranch = main Consistent with remote standards
Credentials Plain-text password in scripts credential.helper with keychain/manager Encrypted, secure storage
Commit provenance Unverified commits with no signature commit.gpgsign = true + gpg.format ssh Cryptographically verified authorship

In practice, these patterns reinforce each other: teams that use includeIf for identity only get the full benefit once pull.rebase and init.defaultBranch are also set consistently across projects, because then every new repository immediately follows the right workflow with no manual extra step.

Mironsoft

Git workflows, developer tooling, and CI/CD for Magento and Hyvä teams

Ready to standardize your team's Git workflow?

We set up your Git configuration, branching strategy, and CI/CD pipeline so commits stay traceable, identities are correctly separated, and new team members are ready to go in minutes.

Git setup audit

Review and optimize config scopes, aliases, and includeIf structure for your team

Branching strategy

Introduce workflow rules, mandatory signing, and pull request processes

CI/CD integration

Anchor Git hooks, automated deployments, and commit signing in the pipeline

10. Summary

A clean Git configuration solves recurring problems structurally instead of ad hoc: the three scopes system, global, and local, with clear precedence, separate machine-wide, user-wide, and project-specific settings. user.name, user.email, and core.editor belong in the global scope as a baseline, while pull.rebase and init.defaultBranch ensure a linear history and consistent branch names across every new repository. Aliases cut typing effort and error rate on frequently used commands.

The biggest lever for teams working across mixed contexts is includeIf: work and personal identity load automatically based on the directory path, with no manual extra step on any new clone. credential.helper and commit.gpgsign round out the setup on the security side, keeping credentials encrypted and making commits cryptographically verifiable. With git config --list --show-origin --show-scope, every one of these settings can be traced back at any time, which drastically simplifies debugging unexpected behavior.

Setting Up Git Config Correctly - The Essentials at a Glance

Three scopes, clear precedence

system for the machine, global for the user, local for a repository. local beats global, global beats system.

Baseline settings

user.name, user.email, core.editor, init.defaultBranch = main, and pull.rebase = true belong in every global setup.

includeIf for work and personal

gitdir conditions automatically load the right identity, based on the repository's directory path.

Security & diagnosis

credential.helper for encrypted credentials, commit.gpgsign for signatures, --show-origin for debugging.

11. FAQ: Setting Up Git Config Correctly

1What is the difference between --system, --global, and --local?
--system applies machine-wide, --global to the user across all repositories, --local only to the current repository. The more specific scope always wins on conflicts.
2Where do the respective config files live?
System under /etc/gitconfig, global under ~/.gitconfig or ~/.config/git/config, local under .git/config in the respective repository.
3How do I set my name and email correctly?
git config --global user.name and git config --global user.email. The email must match the address registered with your hosting provider.
4What does pull.rebase do and why enable it?
Controls merge vs. rebase for git pull. pull.rebase = true produces a linear history without unnecessary merge commits.
5How do I change the default branch name?
git config --global init.defaultBranch main. New repositories then start with main instead of master.
6How does includeIf work for work and personal identity?
includeIf "gitdir:~/work/" automatically loads an extra config file once a repository lives under that directory. The path must end with a slash.
7How do I find where a config value comes from?
git config --list --show-origin --show-scope shows the file and scope for every active value and immediately reveals overridden values.
8How do I set up commit signing?
Since Git 2.34 with SSH keys via gpg.format ssh, or classically with a GPG key as user.signingkey and commit.gpgsign true.
9What are useful Git aliases for everyday work?
co, st, cm for short commands, a graphical lg alias for the log history, and ! aliases for more complex shell operations.
10How do I edit the config directly?
git config --global --edit or --local --edit opens the file in the configured editor. For individual values, git config --unset is safer than manual deletion.