With git recently celebrating its 20th birthday, this seemed like a good time to share and review my current git configuration.

Most of the settings here are things that I’ve picked up from years of messing up rebases using git, I also highly recommend reading GitButler’s blog post on How Core Git Developers Configure Git where I stole copied a couple of interesting and useful settings.

Read on for a section by section breakdown or just skip ahead to the full gitconfig.

Table of Contents

Autocorrect

Typos in your git commands? The following setting will search for the closest matching valid command and suggest that to you.

git config --global help.autocorrect prompt

A prompt will only be shown if there is only one command that’s a significantly close match, otherwise it will exit and list all the commands that are close matches.

$ git branhc
WARNING: You called a Git command named 'branhc', which does not exist.
Run 'branch' instead? (y/N)y
* main

$ git com
git: 'com' is not a git command. See 'git --help'.

The most similar commands are
        commit
        column

Aliases

Git allows you to configure your own custom commands and to run external commands.

git config --global alias.lg "log --oneline --graph --abbrev-commit --date=relative"

Alternatively you can edit your .gitconfig file directly. If you have any external scripts, you can call them from git with !/path/to/your/script.

[alias]
  bbr = !better-git-branch.sh
  br = branch
  cm = commit
  di = diff
  dis = diff --staged
  lg = log --oneline --graph --abbrev-commit --date=relative
  rs = restore
  rss = restore --staged
  st = status -sb
  sh = stash
  sw = switch

When your aliases are registered in git, they behave as valid git commands which means they also benefit from git’s autocompletion and autocorrect.

# tab completion works for all aliases
$ git rs
rs    rss

# if autocorrect is enabled
$ git cx
WARNING: You called a Git command named 'cx', which does not exist.
Run 'cm' instead? (y/N)y
On branch main
nothing to commit, working tree clean

Sorting

Sort branches by the most recent commit date.

git config --global branch.sort -committerdate
$ git branch
  feat-3
  feat-2
  bugfix-1
* main

This setting treats version numbers as integers instead of the full tag name as a string.

git config --global tag.sort version:refname
# default "sort" for tags
$ git tag
1.0.0
1.0.1000
1.0.101
1.0.2

# when tag.sort is set to version:refname
$ git tag
1.0.0
1.0.2
1.0.101
1.0.1000

Fetch

Deletes references to all branches and tags that do not exist on the remote repository. This will not delete local branches or tags.

git config --global fetch.prune true
git config --global fetch.pruneTags true

Push

Simplifies pushing to just git push in most cases.

git config --global push.default simple
git config --global push.autoSetupRemote true
git config --global push.followTags true

The push.default = simple setting pushes the current branch to the branch with the same name on the remote (default since v2.0), while push.autoSetupRemote = true gets rid of the following error which you might be familiar with:

$ git push
fatal: The current branch my-branch-name has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin my-branch-name

push.followTags = true automatically pushes local tags that aren’t on the remote, otherwise you need to explicitly use the --tags flag.

Diff

git config --global diff.colorMoved plain
git config --global diff.mnemonicPrefix true
git config --global diff.renames true
git config --global diff.algorithm histogram

diff.colorMoved displays moved lines in a different color from added/deleted lines while diff.renames detects if a file has been renamed.

diff.mnemonicPrefix instructs git to show a different prefix (defaults to a/, b/) depending on what is being compared e.g. git diff will use i/ and w/ as it compares (i)ndex and (w)ork tree. Refer to the docs for the full list.

There are 4 diff algorithms that are shipped with git: myers (default), minimal, patience, and histogram.

Here’s an example of two diffs using different algorithms, the first one using the default myers algorithm and the other one using histogram.

void func1() {
     x += 1
+}
+
+void functhreehalves() {
+    x += 1.5
}
 
void func2() {
     x += 2
}
void func1() {
     x += 1
}

+void functhreehalves() {
+    x += 1.5
+}
+
void func2() {
     x += 2
}

Editor

Q: What is the best text editor?

A: The correct answer is vi.

This setting tells git what editor to use editing commit and tag messages.

git config --global core.editor vi

Global gitignore

Effectively a .gitignore that applies to all of your repositories.

git config --global core.excludesfile ~/.gitignore-global
.DS_Store
*.orig
*.swp

.idea/
.vscode/

External tooling: delta

delta is a syntax-highlighting pager for git, diff, grep, and blame output.

git config --global core.pager delta
git config --global interactive.diffFilter 'delta --color-only'
git config --global merge.conflictStyle zdiff3

The configuration I use shows a side-by-side diff view (instead of the default unified diff) for the Solarized Dark colorscheme.

delta has a lot of features and is very customizable, check out the docs for all available configuration options and sample usages.

[delta]
	navigate = true  # use n and N to move between diff sections
    dark = true      # or light = true, or omit for auto-detection
	features = side-by-side unobtrusive-line-numbers decorations
	whitespace-error-style = 22 reverse
	syntax-theme = Solarized (dark)
	plus-style = "syntax #003800"
	minus-style = "syntax #3f0001"

[delta "unobtrusive-line-numbers"]
	line-numbers = true
	line-numbers-minus-style = "#444444"
	line-numbers-zero-style = "#444444"
	line-numbers-plus-style = "#444444"
	line-numbers-left-format = "{nm:>4}┊"
	line-numbers-right-format = "{np:>4}│"
	line-numbers-left-style = blue
	line-numbers-right-style = blue

[delta "decorations"]
	commit-decoration-style = bold yellow box ul
	file-style = bold yellow ul
	file-decoration-style = none
	hunk-header-decoration-style = yellow box

Side-by-side diff output:

git-delta side-by-side

Rebase

Some quality of life changes if you use rebase as your merge strategy.

git config --global pull.rebase true
git config --global rebase.autoSquash true
git config --global rebase.autoStash true
git config --global rebase.updateRefs true

pull.rebase tells git what to do when you run into conflicts when pulling changes.

rebase.autoStash automatically stashes your working tree changes (if there are any) when the rebase operation starts and applies it after the operation ends.

rebase.updateRefs automatically force-update any branches that point to commits that are being rebased.

rebase.autoSquash used in combination with fixup commits allows you to amend a series of commits automatically to maintain a clean history. I recommend reading GitButler’s autosquash explainer on their blog.

Reuse Recorded Resolution

This setting is useful if you’re rebasing multiple commits and run into the same conflict again and again.

With rerere.enabled set, git will remember how to resolve a conflict and reuse it when it encounters the same conflict. rerere.autoUpdate automatically stages the changes that resulted from the resolution.

git config --global rerere.enabled true
git config --global rerere.autoUpdate true

Signing commits and tags

You can use GPG or SSH to sign tags and commits locally.

# tell git about your signing key
git config --global user.signingKey 49CC7B953A9FA12D
# sign commits
git config --global commit.gpgSign true
# sign tags
git config --global commit.gpgSign true

Alternatively, you can also sign commits and tags with a SSH key.

git config --global gpg.format ssh
git config --global user.signingkey /path/to/.ssh/key.pub

Git hosting providers e.g. GitHub will then mark signed commits (or tags) that are verifiable as Verified.

GitHub verified commit

Multiple profiles

Imagine the following scenario, you have repositories that you want to associate with your work email and your personal email for the other ones.

$ tree
.
├── .gitconfig
├── .gitconfig-oss
├── .gitconfig-work
├── oss
│   ├── oss-repo-1
│   └── oss-repo-2
└── work
    └── work-repo-1

git provides a facility to conditionally load config files that meet a certain condition.

In the example below, a different user email and signingKey is used depending on the location of the current repository. This is done with git config’s conditional includes and the gitdir property.

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

[includeIf "gitdir:~/work/"]
	path = ~/.gitconfig-work
# .gitconfig-oss
[user]
	email = my-oss-email.com
	signingKey = ABC123DEF
# .gitconfig-work
[user]
	email = my-work-email.com
	signingKey = 101XYZ101

My gitconfig

[user]
	name = Jordan Duabe

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

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

[init]
	defaultBranch = main

[help]
	autoCorrect = prompt

[alias]
	bbr = !better-git-branch.sh
	br = branch
	cm = commit
	di = diff
	dis = diff --staged
	lg = log --oneline --graph --abbrev-commit --date=relative
	rs = restore
	rss = restore --staged
	st = status --short
	sh = stash
	sw = switch

[core]
	editor = vi
	pager = delta

[merge]
	conflictstyle = zdiff3

[delta]
	features = side-by-side unobtrusive-line-numbers decorations
	whitespace-error-style = 22 reverse
	syntax-theme = Solarized (dark)
	plus-style = "syntax #003800"
	minus-style = "syntax #3f0001"
	navigate = true

[delta "unobtrusive-line-numbers"]
	line-numbers = true
	line-numbers-minus-style = "#444444"
	line-numbers-zero-style = "#444444"
	line-numbers-plus-style = "#444444"
	line-numbers-left-format = "{nm:>4}┊"
	line-numbers-right-format = "{np:>4}│"
	line-numbers-left-style = blue
	line-numbers-right-style = blue

[delta "decorations"]
	commit-decoration-style = bold yellow box ul
	file-style = bold yellow ul
	file-decoration-style = none
	hunk-header-decoration-style = yellow box

[pull]
	rebase = true

[rebase]
	autoSquash = true
	autoStash = true
	updateRefs = true

[interactive]
	diffFilter = delta --color-only

[diff]
	algorithm = histogram
	colorMoved = default
	mnemonicPrefix = true
	renames = true

[branch]
	sort = -committerdate

[commit]
	gpgSign = true

[tag]
	sort = version:refname
	gpgSign = true

[rerere]
	enabled = true
	autoUpdate = true

[fetch]
	prune = true
	pruneTags = true

[push]
	simple = true
	autoSetupRemote = true
	followTags = true

[filter "lfs"]
	smudge = git-lfs smudge -- %f
	process = git-lfs filter-process
	required = true
	clean = git-lfs clean -- %f

Try out and experiment with any of these settings and see what fits with your workflow.

Remember, the best configuration is the one that works for you.