Essential Git setup
Every craftsman must know their tools. For us, developers, the tool all of us are using independently of the programming language and technology stack is Git.
Here are some tips and tricks on how I set up Git on all my computers.
Different emails
When you first run Git on a bare machine, you will be prompted to enter your name and email address. By default, Git suggests storing them in your global config. However, I often have both work and personal projects on the same computer. And while my name doesn’t change, the email I use to author commits is different.
I could have set it up for each repository individually, but I have too many and don’t want to bother.
Instead, I put work projects in ~/work
directory and personal to ~/my
and use the following conditional setup in my ~/.gitconfig
:
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
[includeIf "gitdir:~/my/"]
path = ~/.gitconfig-personal
Each of them may contain any of Git settings, such as:
[user]
email = victor.kropp@example.com
Use these config files for any other environment-specific option, such as commit signing.
Commit signing
To verify the authenticity of your commits, you may want to sign them. You can do this either with GPG or (starting with Git 2.34) with an SSH key.
I use a Yubikey and a GPG key stored on it, so my setup looks like this:
[commit]
gpgSign = true
[user]
signingkey = 4AEFD688FCE4ADBD
But a more convenient way for many would be to use 1Password as SSH agent and also to sign commits with the same key.
Sync main branch only
Another option I desperately need with some of the huge repositories I need to deal with at work is
to check out only the master
branch.
Usually, the repository’s .git/config
contains the following remote
section
[remote "origin"]
url = ssh://git@github.com/kropp/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
It tells git to check out all remote branches and all their commits. If hundreds of developers push thousands of commits to the repository every day, it will slow down all fetches significantly. And you don’t need most of those commits anyway, so why spend time, network bandwidth and power doing this?
Instead, change remote.fetch
to:
fetch = +refs/heads/master:refs/remotes/origin/master
From now Git will only sync master
branch and ignore all work in progress.
Oh My Zsh
Another option useful in such repositories
and when your terminal is running zsh
with Oh My Zsh is
to tell it not to show branch name in the prompt.
[oh-my-zsh]
hide-info = 1
With this, your prompt will appear instantly no matter the size of the repository.
Fixup commits
I strive to make commit history as clean as possible.
It often involves creating fixup!
commits, for example, after rebasing feature branch on top of main
/master
.
After adding a bunch of fixups, one needs to run interactive rebase.
Before doing this, enable the option via git config --global rebase.autosquash true
or add these lines to your .gitconfig
[rebase]
autosquash = true
With this option, commits are arranged automatically.
Prune remotes
Another useful option is git config --global fetch.prune true
, which is equivalent of running git fetch --prune
.
It removes local references to deleted remote branches and is extremely useful when,
for example, you often merge your feature branches externally after code review.
Update Jan 3, 2024 Even with this option, you’ll likely have dangling local branches referring to the old remotes, if you’ve ever checked out one. There is no option to get rid of them too, but here is a command I use to achieve that:
git branch -r | awk "{print \$1}" | \
egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk "{print \$1}" | \
xargs git branch -fd
Let’s break it down:
- The first line lists all branch names
- The second one filters out only those, which remotes are gone.
The complicated syntax with
-f /dev/fd/0
here is needed because we essentially provideegrep
with two inputs: list of all branches, and list of existing branches. Because of-v
flag it output lines that do not match the list of existing branches. - And the last line finally deletes the branches
Globally ignore files
Update Apr 16, 2024 It is possible to set up a global .gitignore
file with
git config --global core.excludesFile '~/.gitignore'
And then on macOS one can prevent any .DS_Store
sneaking into the repo with
echo .DS_Store >> ~/.gitignore
I need more options!
Update Feb 21, 2024 I’ve recently stumbled upon a few useful articles on this topic, which I’d like to share.
Julia Evans, known for her explanatory zines, explains a lot of options on her blog.
Scott Chacon (the author of Pro Git) wrote a series on useful options and latest features of Git. Also available as a video of his FOSDEM talk.
That’s a wrap!
I just wish some of these options would be default in Git.
And what are your favorite Git hidden gems?
Subscribe to all blog posts via RSS