Git Config Options Worth Knowing

Git has hundreds of configuration options, and most users only ever touch a handful of them. To find out which ones actually matter in practice, a Mastodon poll asked users to share their favorite git config options. The responses revealed several widely used settings that many developers had never heard of.

All of the options below are documented in man git-config.

Avoiding Surprise Merge Commits

Two options dominate this category: pull.ff only and pull.rebase true. Both prevent an accidental merge commit when you run git pull on a branch where the upstream has diverged.

  • pull.rebase true behaves like running git pull --rebase every time.
  • pull.ff only behaves like running git pull --ff-only every time.

Setting both at once doesn’t make sense, since --ff-only takes precedence over --rebase.

Some users skip these entirely: modern Git already throws an error when your branch has diverged, which is similar to what pull.ff only does.

Better Merge Conflict Output

merge.conflictstyle diff3 and the newer merge.conflictstyle zdiff3 were both extremely popular recommendations. The standard Git conflict format only shows the two conflicting versions:

<<<<<<< HEAD
def parse(input):
    return input.split("\n")
=======
def parse(text):
    return text.split("\n\n")
>>>>>>> somebranch

With diff3, you get a third piece of context: the original code before either side’s changes appears in the middle of the conflict:

<<<<<<< HEAD
def parse(input):
    return input.split("\n")
||||||| b9447fc
def parse(input):
    return input.split("\n\n")
=======
def parse(text):
    return text.split("\n\n")
>>>>>>> somebranch

This extra information makes resolution much easier. In the example above, it becomes clear that one side changed a delimiter (\n\n to \n) while the other side renamed a variable (input to text). The correct resolution is to combine both changes: return text.split("\n").

Many users report that zdiff3 improves on diff3, primarily by reducing the amount of "noise" from lines that were only changed on one side.

Making Common Workflows Simpler

Rebase Autosquash

rebase.autosquash true automatically adds the --autosquash flag to git rebase. This flag makes it trivial to modify old commits:

  1. You have a commit you want to merge with another commit made three commits ago.
  2. Commit your changes with git commit --fixup OLD_COMMIT_ID, which prefixes the message with fixup!.
  3. Running git rebase --autosquash main automatically folds each fixup! commit into its target commit.

Rebase Autostash

rebase.autostash true automatically runs git stash before a rebase and git stash pop afterward. While convenient, it can leave you with merge conflicts after the rebase, which is why some users are reluctant to enable it.

Push Behavior

Several options control what happens when you push a branch without explicit remote arguments:

  • push.default simple is the Git default; it pushes the current branch to its upstream if one exists.
  • push.default current pushes the local branch to a remote branch with the same name, always.
  • push.autoSetupRemote true automatically sets up tracking on the first push of a branch.

push.autoSetupRemote true is more useful than push.default current because you can also git pull from the matching remote branch afterward (though you must push at least once first). The main risk with these is accidentally pushing to an unrelated remote branch that happens to share your local branch’s name; established branch naming conventions, like julia/my-change, make that unlikely.

Saner Defaults

  • init.defaultBranch main creates a main branch instead of master in new repositories.
  • commit.verbose true opens your editor for commit messages with the full diff displayed below, a useful reminder of what you’re committing.

Improving Code Review and History Reading

Conflict Resolution Memory

rerere.enabled true turns on reuse recovered resolution. Git remembers how you resolved a conflict during a rebase and will apply the same resolution automatically if it encounters the same conflict again.

Better Diff Output

Git’s default diff algorithm does poorly when functions are reordered — it often produces confusing output:

-.header {
+.footer {
     margin: 0;
 }

-.footer {
+.header {
     margin: 0;
+    color: green;
 }

Setting diff.algorithm histogram yields a much clearer diff for code movement:

-.header {
-    margin: 0;
-}
-
 .footer {
     margin: 0;
 }

+.header {
+    margin: 0;
+    color: green;
+}

The patience algorithm is an alternative, but histogram is notably more common.

Related suggestions included diff.colorMoved default to highlight moved lines differently, diff.colorMovedWS allow-indentation-change to tolerate indentation changes while doing so, diff.context 10 for extra diff context, and commit.cleanup scissors, which lets you write #include in a commit message without Git treating the # as a comment.

Smarter History Browsing

  • blame.ignoreRevsFile .git-blame-ignore-revs: specify a file with commit IDs to skip during git blame, preventing large renames from polluting blame output.
  • branch.sort -committerdate: sorts branches by most recently used instead of alphabetically. tag.sort taggerdate does the same for tags.
  • log.date iso: displays dates like 2023-05-25 13:54:51 instead of Thu May 25 13:54:51 2023.

Safer and Cleaner History

  • rebase.updateRefs true: simplifies rebasing stacked branches.
  • rebase.missingCommitsCheck error: prevents accidentally deleting commits during a rebase.
  • fetch.prune true (and optionally fetch.pruneTags): automatically deletes remote tracking branches that no longer exist on the remote.
  • push.followTags true: pushes tags along with the commits they reference.

Customizing Your Git Environment

Terminal and Diff Tools

core.pager controls how Git displays the output of commands like git diff and git log. delta is a popular choice for syntax-highlighted diffs, while some users prefer less -x5,9 to fix tab stops, less -F -X, or simply cat to disable paging entirely. delta users often pair it with interactive.diffFilter delta --color-only to get syntax highlighting inside git add -p.

For external diff and merge displays, people set diff.tool difftastic, diff.tool meld, or merge.tool meld/nvimdiff to hand off visual work to a dedicated tool.

Editor and Credentials

core.editor overrides the default terminal editor for commit messages. credential.helper osxkeychain delegates credential storage to the macOS Keychain. Other settings mentioned repeatedly: color.ui false to disable color output, gpg.format ssh to allow signing commits with SSH keys, and core.autocrlf false, commonly set on Windows to avoid line-ending churn alongside Unix colleagues.

A Global Ignore File

core.excludesFile ~/.gitignore points every repository at a single extra ignore file, useful for .idea, .DS_Store, and anything else you never want tracked anywhere. Note that the default location is ~/.config/git/ignore.

Splitting Configs by Path

You can scope settings to different directories to automatically use different email addresses for work and personal repositories:

[includeIf "gitdir:~/code/<work>/"]
path = "~/code/<work>/.gitconfig"

URL Rewriting

It’s easy to accidentally clone over HTTPS and end up with the wrong remote URLs. A url."[email protected]:".insteadOf rule rewrites HTTPS GitHub remotes to SSH automatically:

[url "[email protected]:"]
	insteadOf = "https://github.com/"

One person noted they instead use pushInsteadOf so that pulls from public repositories don’t require unlocking an SSH key. A handful of users add insteadOf = "gh:" to abbreviate remotes, letting them run git remote add gh:user/repo.

To set any option globally, the syntax is straightforward:

git config --global OPTION_NAME VALUE

When you want to see your global configuration file, it typically looks like:

[diff]
	algorithm = histogram

Editing ~/.gitconfig directly is the easiest way to remove entries.

Autocorrect and Safety Checks

Git defaults to catching typos (git ocmmit) and suggesting a correction, but it won’t execute it. Set help.autocorrect to automatically run the suggested command: 1 waits 0.1 seconds, 10 waits one second, immediate runs instantly, and prompt asks first.

A more serious safeguard is fetch.fsckObjects true (or receive.fsckObjects true), which eagerly checks downloaded objects for data corruption. As one person put it: it’s rarely needed, but has saved an entire team a couple of times.

transfer.fsckobjects = true
fetch.fsckobjects = true
receive.fsckObjects = true

Submodule Support

Developers working regularly with submodules frequently enable three settings: status.submoduleSummary true shows submodule changes in git status; diff.submodule log includes commit summaries in git diff; and submodule.recurse true makes many commands recurse into submodules automatically.

Less Common But Useful Options

Several other settings appeared frequently in the original discussion:

  • merge.keepbackup false: prevents Git from leaving .orig backup files after conflict resolution.
  • help.autocorrect: described above under autocorrect.
  • tag.sort sort: discussed with branch.sort.

When changing Git’s behavior, go slow. Whether it’s branch.sort -committerdate or diff.algorithm histogram, adjusting too many options at once makes it difficult to notice what changed — or when a new setting introduces an unexpected regression.