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 true
Every 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.
Bonus: I do this a lot, g pull -r origin main (g is my shell alias for git) to keep my branch up to date with main. You can also add
git config --global branch.main.rebase true
and now I just do g pull origin main. Sure it's only two less characters, but one less thing for me to think about.
Auto set upstream on push
git config --global push.autoSetupRemote true
You 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 tracking
upstream, 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 true
Stale 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 histogram
The 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.js
index 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.js
index 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 true
Rerere 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!
Top comments (24)
Couldn't agree more with this one. Mergig has its place in git, but when pulling commits before pushing everything, 99% of the time I just want to put my work on top of the existing work.
Nick, I've been using Git for over a year and somehow never knew about
pull.rebase = trueuntil this post. 😅Just updated my global config and — wow. Why did no one tell me this sooner? No more random merge commits cluttering my history.
This is why I still read DEV in 2026. Little productivity gems like this. 🙏
Thanks for giving it a read and glad you found it useful! Fun fact. I used to work at DEV!
push.autoSetupRemote is the one that changed my life. I can't believe I spent years typing --set-upstream on every new branch.
One I'd add:
git config --global rerere.enabled true. It remembers how you resolved merge conflicts so if you hit the same conflict again (common during long-lived feature branches), Git just auto-resolves it. Saves a surprising amount of time during rebase-heavy workflows.That one’s in there too at the end! Love rerere!
ha, missed that! rerere is one of those features where once you start using it you can't go back. saved me so many times during long-running rebase sessions.
The histogram diff algorithm is one I don't see talked about enough. Switched to it about 6 months ago and the diffs on larger files are noticeably cleaner — especially when you're moving functions around.
One more I'd throw in:
git config --global init.defaultBranch main. Not a productivity thing exactly, but it saves you from that awkward moment when you push a new repo and realize it created amasterbranch that doesn't match your remote's default.Thanks for giving it a read! Yeah, much cleaner diffs.
wow this is very helpful, sometimes i need to take over some of my team git, the push auto upstream and pull reball help me alot thanks
Hi, thanks very useful guide here.
Glad you found it helpful!
ya, though my personal preference is not to use use 'git config --global push.autoSetupRemote true' to avoid flooding remote repos with feature branches, but I do believe in frequent & meaningful, compilable commits to branches.
Always learning new stuff from you, Nick! Nice tips here.
Thanks for giving it a read Andy! Hope all is well. 😎
useful!
Noice!
I painfully remember fixing the same conflict again and again after a big merge.
Rererecould have saved me a lot of pain. Good to know!Awesome config. I didn't think that I could put the prune on fetch by default. Rebase is my life with git like the "squash on merge" for integrating on main branch. Thanks.
Thanks for reading Pascual and glad you found some new additions for your git config!
Some comments may only be visible to logged-in visitors. Sign in to view all comments.