Five Git Config Settings Every Dev Needs
Table of Contents
You’ve probably added some settings to your Git Configuration, but here are some you might not have configured. If you haven’t set these up yet, you’re doing more manual work than you need to.
Rebase on pull instead of merge#
git config --global pull.rebase trueEvery time you pull without this, Git creates a merge commit. Do that a few times a day across a team and your git log turns into a mess of “Merge branch ‘main’ into main” entries that tell you nothing. With rebase, your commits stay on top of the latest changes and your history actually reads like a coherent timeline. Before setting this in my git config, I’d do a lot of this, g pull -r origin main (g is my shell alias for git). Now I just do g pull origin main. Sure it’s only two. characters, but one less thing to think about.
Auto set upstream on push#
git config --global push.autoSetupRemote trueYou create a new branch, do your work, push, and Git hits you with this:
fatal: The current branch my-branch has no upstream branch.To push the current branch and set the remote as upstream, use
git push --set-upstream origin my-branch
To have this happen automatically for branches without a trackingupstream, see 'push.autoSetupRemote' in 'git help config'.Every single time. This setting makes that whole thing go away. Git sets the upstream automatically on your first push to a new branch.
Auto prune on fetch#
git config --global fetch.prune trueStale remote branches pile up silently. Someone merged and deleted their branch weeks ago, but your local still shows it when you run git branch -r. This cleans out those dead references every time you fetch so your branch list reflects what actually exists on the remote.
I had this alias in my shell to prune local and remote branches.
rmmerged() { git branch --merged | grep -Ev "(\*|master|main)" | xargs -n 1 git branch -d && git remote prune origin}Now, it’s simplified to only pruning local branches since the fetch prune setting handles remote ones.
rmmerged() { git branch --merged | grep -Ev "(\*|master|main)" | xargs -n 1 git branch -d}A better diff algorithm#
git config --global diff.algorithm histogramThe default diff algorithm works, but histogram produces cleaner diffs when there are lots of similarly structured lines, think repeated return statements, closing braces, or blank lines. The default algorithm can get confused about which identical lines to match and produces diffs that interleave additions and deletions in ways that are hard to follow. Histogram handles that better. The bigger the file and the more repetitive the structure, the more noticeable the improvement. It’s a drop-in upgrade with no downside.
Here’s a fictitious example since I had a hard time finding a good example in my own recent commits showing the difference.
Myers Algorithm (Default)#
diff --git a/tmp/example_before.js b/tmp/example_after.jsindex 30d9ab3c..8ec95ef5 100644--- a/tmp/example_before.js+++ b/tmp/example_after.js@@ -8,15 +8,10 @@ function validateUser(user) { if (!user.name) { return { error: 'Name is required' }; }- return { valid: true };-}--function processData(data) {- const result = transform(data);- if (!result) {- return { error: 'Transform failed' };+ if (!user.id) {+ return { error: 'ID is required' }; }- return result;+ return { valid: true }; }
function validateProduct(product) {@@ -26,13 +21,28 @@ function validateProduct(product) { if (!product.price) { return { error: 'Price is required' }; }+ if (!product.name) {+ return { error: 'Name is required' };+ } return { valid: true }; }
+function processData(data) {+ const result = transform(data);+ if (!result) {+ return { error: 'Transform failed' };+ }+ return result;+}+ function saveToDatabase(item) { const connection = getConnection(); if (!connection) { return { error: 'Database connection failed' }; }+ const validated = validateItem(item);+ if (!validated) {+ return { error: 'Validation failed' };+ } return connection.save(item); }Histogram Algorithm#
diff --git a/tmp/example_before.js b/tmp/example_after.jsindex 30d9ab3c..8ec95ef5 100644--- a/tmp/example_before.js+++ b/tmp/example_after.js@@ -8,6 +8,22 @@ function validateUser(user) { if (!user.name) { return { error: 'Name is required' }; }+ if (!user.id) {+ return { error: 'ID is required' };+ }+ return { valid: true };+}++function validateProduct(product) {+ if (!product) {+ return { error: 'Product is required' };+ }+ if (!product.price) {+ return { error: 'Price is required' };+ }+ if (!product.name) {+ return { error: 'Name is required' };+ } return { valid: true }; }
@@ -19,20 +35,14 @@ function processData(data) { return result; }
-function validateProduct(product) {- if (!product) {- return { error: 'Product is required' };- }- if (!product.price) {- return { error: 'Price is required' };- }- return { valid: true };-}- function saveToDatabase(item) { const connection = getConnection(); if (!connection) { return { error: 'Database connection failed' }; }+ const validated = validateItem(item);+ if (!validated) {+ return { error: 'Validation failed' };+ } return connection.save(item); }Rerere#
git config --global rerere.enabled trueRerere stands for “reuse recorded resolution.” When you resolve a merge conflict, Git remembers how you resolved it. The next time the same conflict comes up, Git applies your previous resolution automatically. If you’ve ever rebased a long-lived branch and had to resolve the same conflict over and over, this is the fix. It won’t silently merge things for you in a way you can’t review. It records your resolutions and replays them so you don’t have to redo the same work.
Want to see what you currently have set? Run git config --global --list and see what’s missing.
If you enjoy tips like this, I have a newsletter, OneTipAWeek.com. One developer tip a week. Short & valuable. That’s it!
If you want to stay in touch, all my socials are on nickyt.online.
Until the next one!