Developer Tools

Git Config Overhaul: Configuring the Ultimate Dev Terminal

By DexNox Dev Team Published May 30, 2026

Default Git ships with settings optimized for backward compatibility across two decades of workflows. That means no commit signing, a raw less pager for diffs, manual upstream tracking on every new branch, and verbose commands for routine operations. This guide walks through a full ~/.gitconfig overhaul — aliases, delta pager, SSH signing, conditional identity, and every advanced toggle worth enabling — so your terminal matches how you actually work.

The Complete ~/.gitconfig

Here is the full configuration file. Every section is explained in detail below. You can copy this entire block into ~/.gitconfig (or $XDG_CONFIG_HOME/git/config if you prefer XDG paths) and adjust the values marked with angle brackets.

[user]
  name = <Your Name>
  email = <your@personal-email.com>
  signingkey = ~/.ssh/id_ed25519.pub

[core]
  editor = nvim
  autocrlf = input
  pager = delta
  excludesfile = ~/.gitignore_global
  fsmonitor = true
  untrackedcache = true

[init]
  defaultBranch = main

[commit]
  gpgsign = true
  verbose = true

[gpg]
  format = ssh

[gpg "ssh"]
  allowedSignersFile = ~/.ssh/allowed_signers

[push]
  autoSetupRemote = true
  default = current
  followTags = true

[pull]
  rebase = true

[fetch]
  prune = true
  prunetags = true
  parallel = 0

[merge]
  conflictstyle = zdiff3
  tool = nvim

[diff]
  algorithm = histogram
  colorMoved = default
  renames = copies

[rebase]
  autosquash = true
  autostash = true
  updateRefs = true

[rerere]
  enabled = true

[column]
  ui = auto

[branch]
  sort = -committerdate

[tag]
  sort = -version:refname

[interactive]
  diffFilter = delta --color-only

[delta]
  navigate = true
  light = false
  side-by-side = true
  line-numbers = true
  syntax-theme = Dracula
  file-style = bold yellow ul
  hunk-header-style = omit

[alias]
  co = checkout
  br = branch
  ci = commit
  st = status -sb
  last = log -1 HEAD --stat
  lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
  unstage = reset HEAD --
  amend = commit --amend --no-edit
  wip = !git add -A && git commit -m 'wip: checkpoint [skip ci]'
  nuke = !git clean -fd && git checkout -- .
  aliases = config --get-regexp alias
  branches = branch -a --sort=-committerdate --format='%(color:yellow)%(refname:short)%(color:reset) %(color:green)%(committerdate:relative)%(color:reset) %(color:blue)%(authorname)%(color:reset)'
  contributors = shortlog -sn --no-merges
  undo = reset --soft HEAD~1
  find = log --all --pretty=format:'%C(yellow)%h %Cgreen%ad %Cblue%an%Creset %s' --date=short --grep
  what = show --stat --format='%C(yellow)%h %Cgreen%ci%Creset %s%n%b'
  sync = !git fetch --all --prune && git rebase origin/main
  gone = !git branch -vv | grep ': gone]' | awk '{print $1}'
  prune-gone = !git gone | xargs git branch -D

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

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

That is roughly 100 lines. The rest of this article breaks down every section so you understand what each setting does and why it is there.

Git Aliases: 18 Commands Worth Memorizing

Aliases eliminate the repetitive typing that slows down terminal workflows. Here is what each one does:

Basic Shortcuts

AliasExpands ToWhat It Does
cocheckoutSwitch branches or restore files
brbranchList, create, or delete branches
cicommitRecord changes to the repository
ststatus -sbShort-format status with branch info — two columns instead of verbose paragraphs

Log and History

AliasExpands ToWhat It Does
lastlog -1 HEAD --statShows the most recent commit with file-level diff stats
lglog --graph --pretty=format:...Compact, colorized branch graph — the single most useful alias in this list
whatshow --stat --format=...Displays a specific commit’s message body plus changed files
findlog --all --pretty=... --grepSearches all branches for commits matching a keyword in the message
contributorsshortlog -sn --no-mergesRanks authors by commit count, excluding merge commits

Staging and Undo

AliasExpands ToWhat It Does
unstagereset HEAD --Removes files from the staging area without discarding changes
amendcommit --amend --no-editAdds staged changes to the previous commit, keeping the same message
undoreset --soft HEAD~1Undoes the last commit but keeps all changes staged — safe undo
wipadd -A && commit -m 'wip: checkpoint [skip ci]'Stages everything and commits with a throwaway message. The [skip ci] tag prevents CI pipelines from triggering on work-in-progress pushes

Cleanup

AliasExpands ToWhat It Does
nukeclean -fd && checkout -- .Destroys all untracked files and reverts all modifications — nuclear reset to HEAD
syncfetch --all --prune && rebase origin/mainPulls the latest remote state and rebases your current branch on top of main
gonebranch -vv | grep ': gone]' | awk '{print $1}'Lists local branches whose upstream has been deleted
prune-gonegone | xargs branch -DDeletes every local branch returned by gone — cleans up stale feature branches after merge
aliasesconfig --get-regexp aliasPrints all your configured aliases — useful when you forget what you set up
branchesbranch -a --sort=-committerdate --format=...Lists all branches sorted by last commit date with author and relative time

Installing and Configuring Delta

The built-in Git diff output renders additions and deletions as raw +/- lines in monochrome green and red. Delta replaces this with syntax-highlighted, side-by-side diffs directly in your terminal. It is written in Rust and adds no measurable latency to git diff or git log -p.

Installation

# macOS (Homebrew)
brew install git-delta

# Ubuntu / Debian / WSL2
sudo apt-get install git-delta

# If your distro packages an old version, install via cargo
cargo install git-delta

# Windows (Scoop)
scoop install delta

# Verify installation
delta --version

On Ubuntu 22.04+, apt ships delta 0.16+. On older Ubuntu releases, the apt package may be outdated or missing entirely — use cargo install git-delta in that case. You need the Rust toolchain installed (curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh).

The Delta Config Block

The full [delta] section in your gitconfig:

[core]
  pager = delta

[interactive]
  diffFilter = delta --color-only

[delta]
  navigate = true
  light = false
  side-by-side = true
  line-numbers = true
  syntax-theme = Dracula
  file-style = bold yellow ul
  hunk-header-style = omit

What each option does:

  • navigate: Enables n/N key bindings to jump between diff hunks inside the pager. This is the single biggest quality-of-life improvement over plain less.
  • light: Set to true if your terminal uses a light background. Keep false for dark themes.
  • side-by-side: Splits the terminal into left (old) and right (new) columns. Requires a terminal width of at least 120 columns to be useful. If your terminal is narrower, Delta falls back to unified view automatically.
  • line-numbers: Displays line numbers in the gutter of each side.
  • syntax-theme: Any theme supported by bat. Run delta --list-syntax-themes to see all options. Dracula, Nord, gruvbox-dark, and Monokai Extended are popular choices.
  • file-style: Formats the filename header — bold yellow ul gives you bold, yellow, underlined filenames separating each file’s diff.
  • hunk-header-style: Set to omit to remove the @@ ... @@ hunk headers, which are noise when you already have line numbers enabled.

SSH Commit Signing

GPG signing works but comes with real operational friction: separate key generation, expiration management, keyserver uploads, and a gpg-agent daemon that occasionally locks up. SSH signing, available since Git 2.34, replaces all of that with the key pair you already use for repository access.

Step 1: Generate an Ed25519 Key (If You Do Not Have One)

ssh-keygen -t ed25519 -C "your@email.com"

Accept the default path (~/.ssh/id_ed25519). Set a passphrase — your SSH agent will cache it so you are not prompted on every commit.

Step 2: Create the Allowed Signers File

Git needs a list of trusted public keys to verify signatures. Create ~/.ssh/allowed_signers:

echo "your@email.com $(cat ~/.ssh/id_ed25519.pub)" > ~/.ssh/allowed_signers

This file maps email addresses to public keys. When you run git log --show-signature, Git checks this file to validate each commit’s signer.

Step 3: Configure Git

These three sections in your gitconfig enable signing:

[user]
  signingkey = ~/.ssh/id_ed25519.pub

[gpg]
  format = ssh

[gpg "ssh"]
  allowedSignersFile = ~/.ssh/allowed_signers

[commit]
  gpgsign = true

Step 4: Register Your Key on GitHub / GitLab

On GitHub: Go to Settings → SSH and GPG keys → New SSH key. Change the key type dropdown from “Authentication Key” to “Signing Key”. Paste the contents of ~/.ssh/id_ed25519.pub. You need to add the key as both an authentication key and a signing key — they are registered separately even if they use the same public key.

On GitLab: Go to Preferences → SSH Keys. GitLab does not distinguish between authentication and signing keys — a single entry covers both.

After pushing a signed commit, your commits will display a “Verified” badge in the web UI.

Advanced Git Settings Explained

Beyond aliases and pagers, these global settings change how Git handles branching, merging, and remote tracking.

fetch.prune and fetch.prunetags

[fetch]
  prune = true
  prunetags = true
  parallel = 0

When a teammate deletes a remote branch after merging a PR, your local copy of that remote-tracking reference (origin/feature-xyz) sticks around until you manually run git fetch --prune. Setting fetch.prune = true makes every git fetch and git pull automatically clean up stale references. prunetags does the same for deleted remote tags. parallel = 0 tells Git to use as many parallel connections as your system supports when fetching from multiple remotes.

pull.rebase

[pull]
  rebase = true

By default, git pull creates a merge commit every time your local branch has diverged from the remote. Over weeks, this produces a commit graph full of meaningless “Merge branch ‘main’ into feature-x” entries. Setting pull.rebase = true replays your local commits on top of the fetched remote state, keeping history linear.

push.autoSetupRemote

[push]
  autoSetupRemote = true
  default = current
  followTags = true

Without this, every new branch requires git push --set-upstream origin branch-name on the first push. With autoSetupRemote = true, a plain git push automatically creates the upstream tracking relationship. followTags = true pushes annotated tags along with commits, so you do not need a separate git push --tags.

merge.conflictstyle = zdiff3

[merge]
  conflictstyle = zdiff3

The default merge conflict style shows two sides: yours and theirs. diff3 adds a third section showing the common ancestor — the original code before either side changed it. This context makes it dramatically easier to resolve conflicts because you can see what both sides intended to change. zdiff3 is a refinement that reduces noise by omitting unchanged regions within the conflict block.

rerere.enabled

[rerere]
  enabled = true

“Reuse Recorded Resolution.” When you resolve a merge conflict, Git records the resolution. If the same conflict appears again (common during long-running rebases or cherry-picks), Git applies your previous resolution automatically. This is especially useful when you repeatedly rebase a feature branch onto a moving main branch.

diff.algorithm = histogram

[diff]
  algorithm = histogram
  colorMoved = default
  renames = copies

The default myers diff algorithm sometimes produces confusing diffs when code blocks are moved or when functions are reordered. The histogram algorithm (a variant of patience) produces cleaner, more readable diffs with better alignment of changed blocks. colorMoved = default highlights lines that were moved rather than changed, coloring them differently from true additions and deletions. renames = copies detects both renamed and copied files.

rebase.autosquash and rebase.updateRefs

[rebase]
  autosquash = true
  autostash = true
  updateRefs = true

autosquash makes git rebase -i automatically reorder commits prefixed with fixup! or squash! next to their target — a clean workflow for iterative code review. autostash stashes uncommitted changes before a rebase and pops them after, so you do not need to manually stash before rebasing. updateRefs (Git 2.38+) automatically updates stacked branch pointers during an interactive rebase, which is critical if you use stacked PRs.

column.ui and branch.sort

[column]
  ui = auto

[branch]
  sort = -committerdate

column.ui = auto displays branch lists and tag lists in columns (like ls does for files) when your terminal is wide enough. branch.sort = -committerdate sorts branches by most recent commit first, so the branch you were just working on appears at the top instead of buried alphabetically.

SettingDefault ValueRecommended ValueWhy Change It
core.pagerlessdeltaSyntax-highlighted, side-by-side diffs in the terminal
diff.algorithmmyershistogramCleaner diffs with better hunk alignment for moved code
merge.conflictstylemergezdiff3Shows the common ancestor in conflicts, reducing guesswork
fetch.prunefalsetrueAutomatically removes stale remote-tracking branches
pull.rebasefalse (merge)trueKeeps commit history linear, avoids noise merge commits
push.autoSetupRemotefalsetrueEliminates --set-upstream on first push of new branches
push.followTagsfalsetruePushes annotated tags automatically with commits
rerere.enabledfalsetrueRecords and replays merge conflict resolutions
rebase.autosquashfalsetrueAuto-reorders fixup!/squash! commits during interactive rebase
rebase.autostashfalsetrueStashes dirty working tree before rebase, pops after
rebase.updateRefsfalsetrueUpdates stacked branch pointers during rebase
commit.verbosefalsetrueShows the full diff in your commit message editor
init.defaultBranchmastermainAligns with GitHub/GitLab defaults
gpg.formatopenpgpsshUses existing SSH keys for signing, no GPG tooling needed
column.uineverautoColumnar display for branch and tag lists
branch.sortalphabetical-committerdateMost recently active branches appear first

Conditional Includes: Separate Work and Personal Identities

If you contribute to your employer’s repositories and your own side projects from the same machine, you need different user.name and user.email values depending on which repository you are in. Conditional includes solve this without requiring per-repo configuration.

The relevant block in your main ~/.gitconfig:

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

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

Then create each override file. For your work identity:

# ~/.gitconfig-work
[user]
  name = Your Legal Name
  email = you@company.com
  signingkey = ~/.ssh/id_ed25519_work.pub

And your personal identity:

# ~/.gitconfig-personal
[user]
  name = Your Handle
  email = you@personal.com
  signingkey = ~/.ssh/id_ed25519.pub

Git evaluates gitdir: against the .git directory of the repository you are currently in. Any repository cloned under ~/work/ will use your work email. Anything under ~/projects/ gets your personal email. Repositories outside both paths fall back to whatever [user] block exists in the main ~/.gitconfig.

The trailing slash on ~/work/ is important — it tells Git to match all subdirectories recursively. Without the slash, only repositories whose .git directory is exactly at ~/work/.git would match.

You can verify which identity is active in any repo by running:

# Run this from inside a repository
git config user.email
# Shows which email is active

git config --show-origin user.email
# Shows which file the value comes from

Common Pitfalls

Delta not rendering correctly over SSH: Delta requires a terminal with 256-color or truecolor support. If you SSH into a machine and diffs look garbled, make sure your SSH client forwards TERM properly. Setting COLORTERM=truecolor in your remote shell profile usually fixes this.

Rebase conflicts on shared branches: If your team pushes directly to main and you have pull.rebase = true, rebasing can rewrite commits that others have already based work on. Use rebase for your feature branches. For shared long-lived branches, either set pull.rebase = false in the repo’s local config or use git pull --no-rebase explicitly.

SSH signing fails with “error: Load key … invalid format”: This happens when user.signingkey points to the private key instead of the public key. The value must end in .pub. Git reads the public key for signing metadata and invokes ssh-keygen -Y sign with the private key via your SSH agent.

fsmonitor slows down on network-mounted drives: The core.fsmonitor = true setting uses a filesystem watcher daemon to speed up git status and git diff on large repositories. On NFS or SMB mounts, the watcher generates excessive events. Disable it per-repo with git config --local core.fsmonitor false if you notice slowdowns on network volumes.

rerere replays a bad resolution: If you resolve a conflict incorrectly and rerere records it, every future occurrence of that conflict gets the wrong resolution. Clear the bad recording with git rerere forget <pathspec> to force Git to prompt you again.

Wrapping Up

A tuned gitconfig removes friction from every interaction with version control — faster branch switching, readable diffs, automatic upstream tracking, signed commits without GPG overhead, and separate identities for work and personal projects. Copy the full config from the top of this article, adjust the angle-bracketed values, and start using it immediately. Every setting here is backward-compatible and can be overridden per-repository with git config --local when needed.

Frequently Asked Questions

What is the benefit of signing commits with SSH keys instead of GPG?

SSH commit signing reuses the key pair you already use for push/pull access, eliminating the need to install GPG, generate a separate keypair, manage expiration dates, and distribute public keys through a keyserver. One key does both authentication and signing.

Will Delta break my existing Git GUI tools like GitKraken or Fork?

No. Delta only activates when Git uses a terminal pager, which is the CLI path. GUI tools render their own diff views and ignore core.pager entirely, so they remain unaffected.

How do conditional includes work if I have three or more identities?

You can chain as many includeIf blocks as you need. Git evaluates them top-to-bottom and applies every block whose condition matches. For example, you could have separate includes for personal projects, your employer, and an open-source foundation — each keyed to a different directory path.

Does pull.rebase cause problems with merge commits from shared branches?

It can rewrite local merge commits if you are not careful. The safest default is pull.rebase = true for topic branches combined with explicit git pull --no-rebase on long-lived shared branches where merge commits carry meaningful context.

How do I check which gitconfig values are actually active and where they come from?

Run git config --list --show-origin --show-scope. This prints every active setting alongside the file path and scope (system, global, local, worktree) it was loaded from, making it easy to trace overrides.