Useful Git settings
Written on
In my dotfiles I always have a .gitconfig
file
with some of the settings preconfigured.
Without them using Git for me would be much more cumbersome.
Here’s a list of them along with some comments and sources.
Core settings
core.autocrlf
Line ending characters are one big mess due to backwards compatibility.
Windows systems use CRLF
, Unix and consequently Linux uses LF
.
More than that, older Macs used CR
,
so the only missing option was LFCR
🤠.
Thankfully Apple switched to LF
with Mac OS X 10.0,
so we’re left with two variants.[1]
Git was created by Linus Torvalds
so it’s no surprise that it prefers LF
by default,
and warns you if you try committing files with other line ending characters.
Theoretically you can stick to e.g. CRLF
if you’re doing a strictly Windows project,
but I find it much easier to always commit LF
,
and configure Git (and my CI[2]) to guarantee that.
In practice, you might notice that a lot of files
in a lot of repositories in your project
were already committed with CRLF
. Such is life.
Because of that I don’t like setting autocrlf
to true
,
as it tells Git to always convert from CRLF
to LF
when doing a commit.
This is a bit annoying, as then it shows that the whole file was modified (which is in fact true) in the commit view, and it makes much harder to see the actual changes. This depends on the tool you’re using as some of them can hide line ending changes pretty well. Still, I would advocate to isolate line ending changes from other types of changes.
E.g. you might want to do a single commit in a repo
in which you clean it up for all files[3],
and then add this commit to .git-blame-ignore-revs
,
so it doesn’t makes mess with the history regardless of the tool
and/or settings you are using.
TLDR
My rule of thumb:
git config --global core.autocrlf true 1 git config --global core.autocrlf input 2
- Use that setting on Windows
- Use that setting otherwise (Linux, Mac, WSL)
core.excludesFile
Have you noticed that due to your setup,
i.e. editor or IDE you’re using,
you are the person who always adds particular entries to .gitignore
in all repos?
Are you tired of that?
excludesFile
is the solution for you.
It’s a globally defined .gitignore
which Git uses even if a .gitignore
is missing in a repo.
I like to keep it in my home folder along with .gitconfig
,
and I use the name .gitignore_global
:
git config --global core.excludesFile '~/.gitignore_global'
core.sshcommand
I’m a Windows user, but I prefer bash. As terminal is very important in my workflow, I use WSL both at work and in my private projects. There are a lot of quirks about WSL and one of them is having Git remember you SSH passwords. There are Linux solutions for that, and I experimented with them. Unfortunately, it wasn’t working out great for me (but maybe something has changed in recent years). Another solution is to use password-less keys, but you don’t want to do that, right?
Thankfully we can use Windows built-in SSH agent to remember passwords for us even in WSL.[4]
To do that, we have to tell WSL Git to use SSH binary from Windows:
git config --global core.sshcommand '/mnt/c/Windows/System32/OpenSSH/ssh.exe'
Credential settings
credential.helper
This is quite similar to core.sshcommand
I mentioned above when using Git in WSL.
You can choose to use HTTPS over SSH when talking with Git remotes.
But WSL doesn’t have a built-in way to store these credentials,
and using plaintext file .git-credentials
is not something you want to be doing.
Again, Windows comes to the rescue. Here, you have to install Git on Windows to be able to use its credential manager from WSL Git. Then, you can authenticate once, usually via a browser window, and Git will remember this.
git config --global credential.helper '/mnt/c/Program\ Files/Git/mingw64/bin/git-credential-manager.exe'
Make sure to quote the path
as space in Program Files
would cause some issues otherwise.
And use the actual path of where you have Git for Windows installed,
other default alternative is AppData
of your Windows user.
Git features
feature.manyFiles
Git introduced this feature a few years ago to improve support for bigger repos. I naively enabled it after reading the change log and it instantly broke some tools for me, particularly Matlab Git integration was completely broken. After these experiences I disabled this feature, so my setting repeats the current default.[5]
git config --global feature.manyFiles false
If you’re struggling with Git performance you might consider to enable it. Hopefully after a few years tools already support it.
git commit
commit.verbose
git commit
has a --verbose
flag
which includes committed diff in a commit window.
The commit windows is nothing more but an automatically opened file
which you edit, save, and quit,
which makes git do the commit.
This option always adds this flag, so you can see what are you committing every time.
git config --global commit.verbose true
git fetch
fetch.prune
You can remove any remote-tracking references
(i.e. origin/<branch-name>
)
which were removed on the remote with git fetch --prune
.
I find it very convenient to be the default,
as branches are usually removed after merging pull request,
so you can quickly have a lot of dangling references.
git config --global fetch.prune true
If you have multiple remotes, and would like to prune references from only some of them, it’s also an option. For that, there is another command:
git config --global remote.<name>.prune true
<name>
can be anything,
and by convention origin
is used as the default remote name.
You might be wondering if setting something like
git config --global remote.prune true
is allowed.
Yes, it is, and it does pretty much the same thing
as setting fetch.prune
to true.[6]
git pull
pull.rebase
I like
explanation from this video
why default behavior of git pull
is a bit annoying.
Its title is a little bit clickbaitish,
but I agree with the arguments.
I was also a bit surprised
that author didn’t mentioned option we’re talking about here.
To do git pull --rebase
automatically with each pull set:
git config --global pull.rebase true
pull.ff only
There is also another option
which enforces fast-forward merge in git pull.
The difference is that --ff-only
merge works
only if there are no newer commits coming from the remote.
Otherwise it fails,
and you have to do something with it manually.
That means you are forced to do the rebase on your own.
This adds you more work but one can advocate that it’s safer, as you have to explicitly rebase to the new version.
This is in line with the approach never use git pull (i.e. use git fetch and git rebase instead). If you’re into that, consider using this option.
git merge
merge.conflictstyle
I think I don’t know a person who likes to solve merge conflicts. But I noticed that many people are more scared about them then they probably should be.
Setting a better conflict style in Git is one of the steps to make them less scary.
zdiff3
is a relatively new conflict style in Git
which was introduced in 2022 with version 2.35.
That means that you still can work with systems
which don’t have this feature,
so make sure to see if your Git version is new enough.
But what is zdiff3
?
It’s an updated version of diff3
which is much older algorithm[7]
and which has one big benefit:
in classic conflict view you see two versions of the code,
ours
and theirs
, as Git labels it.
diff3
also shows what was before that,
which we can name a common ancestor of both commits.[8]
This simple addition often adds much needed context, to solve merge conflict faster, and do it correctly.
Unfortunately, diff3
isn’t ideal,
and sometimes creates more noisy conflicts,
marking more lines as conflicting.[9]
zdiff3
addresses these issues.
Interestingly enough, z
in the name comes from the word zealous.
git config --global merge.conflictstyle zdiff3
git init
init.defaultBranch
Like it or not, but main
is the new master
in Git,
and it doesn’t seem to be changing any time soon.
Git though still uses default master
, so this option has to be set.
git config --global init.defaultBranch main
Git aliases
git git
I borrowed that idea from Anthony Sottile[10].
It allows you to type git git <command>
and make the command still work.
In fact, you might type as many git
commands as you like,
as it’s recursive.
Why? Same as Anthony,
I sometimes type git
in the terminal,
then start thinking about something,
then and go back to typing,
often typing git
again due to muscle memory.
Adding an alias is simpler than fighting with it, apparently 😛
git config --global alias.git '!git'
git l
Quickly output oneline log for all branches. Very handy to quickly orient around in the repo. This is one of my most-used git commands.
git config --global alias.l 'log --graph --all --oneline'
git lg
git l
with a bit more details.
It uses more formatting and
I copied it from someone’s dotfiles years ago.
git config --global alias.lg "log --graph --all --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%cr)%C(reset) %C(white)%s%C(reset) %C(bold yellow)- %cn%C(reset)%C(bold red)%d%C(reset)' --abbrev-commit --date=relative"
git llg
Even more detailed log. Also copied from that same person.
git config --global alias.llg "log --graph --all --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%cD%C(reset) %C(bold green)(%cr)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(bold yellow)- %cn%C(reset)' --abbrev-commit"
git k
You migth not know this,
but Git comes with a GUI called gitk
.
It isn’t always installed (e.g. MacOS Git via homebrew),
but when you have it,
it might be handy in some cases.
I’m used to Git commits starting with git <cmd>
,
so I added an alias which reflects it.
git config --global alias.k '!gitk &'
git ka
Another gitk
alias to run the program with --all
flag
which then shows all branches.
git config --global alias.ka '!gitk --all &'
- I recommend a video from Scott Hanselman if you want to learn more about this topic ↩
pre-commit
with itsmixed-line-ending
hook set tolf
is a great tool for that ↩pre-commit
mentioned above is a great way to do this. Also make sure to set up it to check it with each new commit or theseCRLF
files might start reappearing, as we live in a society ↩- Refer to MS docs on how to enable SSH agent ↩
- When writing this post, I did some research, and it seems that this might be due to
libgit2
incomparability withmanyFiles
. More details on Stack Overflow ↩ - There is quite a good summary of the topic again on SO ↩
- Introduced in 2007 with Git 1.5.0 ↩
- A pretty nice explanation of that is in this blog post ↩
- Again, SO for the rescue ↩
- Here’s his video about it: https://youtu.be/BkUW2NgfZao ↩