Excuse the pun in the title, I couldn't resist. In this post I will walk you through my git setup. I recently had to install a new MacBook for work, which included configuring my git setup like new, and I found some interesting tidbits I thought would be worth sharing.
Installing git
I work from macOS, which has the incredible package manager Homebrew available. This makes installing git on the command line as simple as 1-2-3:
$ brew install git
And that's git!
GUI or CLI?
I can be very short on this. I prefer using the CLI, so that's what I'll be covering.
Configuration
In order to make git as enjoyable as possible to work with, I've accumulated some configuration options over the years:
Global ignore file
It is good practice to have a "global .gitignore
file" to make sure certain files or folders are ignored in every single project. The "local" .gitignore
file in each project will override these settings. I store mine in my home folder:
$ git config --global core.excludesFile
/Users/sven/.gitignore
You can set your own by appending a path to the command from above:
$ git config --global core.excludesFile $HOME/.gitignore
The contents of this file should be user-specific files you want to ignore that have no bearing on the project(s) you work on. This includes folders like .vscode/
and .idea/
, but also files such as .DS_Store
, .Trashes
, or .phpunit.result.cache
. Mine looks like this:
# JetBrains IDE
.idea/
# PhpUnit
.phpunit.result.cache
# macOS
*.DS_Store
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
Don't let git guess
To avoid possible confusion for some configuration options, I like to set user.useConfigOnly
to true
. This way I know that git won't try to "guess" some of my config values from the system, but it always uses whatever is in the .gitconfig
file (whether it be the global one, or the one in the project I'm working on):
$ git config --global user.useConfigOnly true
Default branch
Whether you like it or not, main
as a default branch name is here to stay. You can configure git to name the default branch whatever you want with the init.defaultBranch
configuration option. Mine will remain main
.
$ git config --global init.defaultBranch main
What's the status?
I like the output of git status
to be nice and short, as well as easy to scan. I've gotten used to seeing it with the options --short
and --branch
included, which of course, have configuration options!
$ git config --global status.short true
$ git config --global status.branch true
There's also the option to show all untracked files, even those in new, untracked directories. I'm playing around with this one at the moment, but I'm not sure if I like it yet:
$ git config --global status.showUntrackedFiles all
Signing things
To make sure other people know it's really me who's pushing commits and tags, I try to always sign everything I can with my GPG key. This has the added benefit of my commits getting a nice "Signed!" badge next to them in the GitHub/GitLab UI.
$ git config --global commit.gpgSign true
$ git config --global gpg.program <path to gpg executable>
$ git config --global user.signingKey <gpg signing key>
For more information on—and a guide on how to set up—GPG signing in git, I recommend GitHub's help articles.
I also force myself to sign any annotated tag I create (which is all of them) by setting the tag.forceSignAnnotated
option to true
:
$ git config --global tag.forceSignAnnotated true
Making the log pretty
To make sure the output of git log
is readable, I wrote my own "pretty" formatter, and an alias to make it more accessible:
$ git config --global alias.lg "log --graph --date=human --pretty=default"
$ git config --global pretty.default "format:%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cd) %C(bold blue)<%an>%Creset"
I then use it with git lg
, which is so embedded into my muscle memory that I barely even think about it anymore 😅
Aliases
I have a couple of aliases defined. None of them are super groundbreaking, but they're worth sharing.
# `git st`, because why use many word when few do trick?
$ git config --global alias.st "status"
# `git df` to tell git you're talking about diffing files.
$ git config --global alias.df "diff --"
# `git ap` to "add in patch mode".
$ git config --global alias.ap "add --patch"
# `git nah` for when you're not feeling the changes you made.
$ git config --global alias.nah "\!git reset --hard && git clean -df"
# `git publish` to push a new local branch to the remote and set it as the tracking branch.
$ git config --global alias.publish "\!git push origin \$(git symbolic-ref --short HEAD) -u"
# `git track` to track a remote branch. I use this very rarely because I push new branches with `git publish`.
$ git config --global alias.track "\!git branch --set-upstream-to origin/\$1"
SSH configuration
I use SSH to clone all of my repositories. This allows me to revoke access from certain devices (eg. work MacBook) by removing the SSH key from my GitHub account without needing to change the password and having to change configuration around on my other devices. Another benefit is that I can "name" certain remotes, saving me on valuable characters and time. My ~/.ssh/config
file looks like this:
#
# Add the following SSH keys to the session
#
IdentityFile ~/.ssh/id_ed25519
#
# Personal Connections
#
Host github
User git
HostName github.com
Host gitlab
User git
HostName gitlab.com
Host *
IdentityFile ~/.ssh/id_ed25519
AddKeysToAgent yes
UseKeychain yes
PubkeyAuthentication yes
IdentitiesOnly yes
UseRoaming no
Port 22
This SSH configuration allows me to clone repositories with git clone github:<username>/<repo>.git
, so it skips on me having to type out github.com
every time. It also makes sure the right SSH key is used on every connection (unless explicitly specified), and sets the (undocumented) UseRoaming
option to no
for all hosts to mitigate exposure to CVE-2016-0777 and CVE-2016-0778.
Conclusion
I try to stay as close to the "core" of git as possible, so I don't use overly complex aliases or configuration that might be hard to understand. This is because I don't want to have to debug my own setup when I inevitably encounter a rebase that's messing up, or a three-way merge that's failing somewhere. The resulting ~/.gitconfig
, ~/.gitignore
, and ~/.ssh/config
files are available as a gist if you don't want to manually type out all the commands.
Everybody's git experience is different, and I'd love to know what configuration options you think I'm "missing", or are worth checking out. How do you use git? Do you prefer GUI or CLI? Did this post help out in any way? I'd appreciate it if you could let me know on Twitter!
I had a blast writing this, and I even discovered some new configuration options I previously had no idea existed. Hopefully this post was helpful to you in some way, and if you're in the mood for more git config
goodness, I highly recommend you take a look at the official documentation.