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 truebehaves like runninggit pull --rebaseevery time.pull.ff onlybehaves like runninggit pull --ff-onlyevery 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:
- You have a commit you want to merge with another commit made three commits ago.
- Commit your changes with
git commit --fixup OLD_COMMIT_ID, which prefixes the message withfixup!. - Running
git rebase --autosquash mainautomatically folds eachfixup!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 simpleis the Git default; it pushes the current branch to its upstream if one exists.push.default currentpushes the local branch to a remote branch with the same name, always.push.autoSetupRemote trueautomatically 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 maincreates amainbranch instead ofmasterin new repositories.commit.verbose trueopens 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 duringgit blame, preventing large renames from polluting blame output.branch.sort -committerdate: sorts branches by most recently used instead of alphabetically.tag.sort taggerdatedoes the same for tags.log.date iso: displays dates like2023-05-25 13:54:51instead ofThu 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 optionallyfetch.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.origbackup files after conflict resolution.help.autocorrect: described above under autocorrect.tag.sort sort: discussed withbranch.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.



